diff options
author | Jens Bache-Wiig <jbache@trolltech.com> | 2010-08-18 09:59:14 +0200 |
---|---|---|
committer | Jens Bache-Wiig <jbache@trolltech.com> | 2010-08-18 09:59:14 +0200 |
commit | 6525a0183ba87159d4fd52b64100335e2455a4ec (patch) | |
tree | 4807accbda02da768b1e53f7a997bb64274b5c4d | |
parent | 71d22bc5737842f97d8fb222e000de2549825891 (diff) |
More examples, some cleanups in context code
-rw-r--r-- | examples/blob/Button.qml | 83 | ||||
-rw-r--r-- | examples/blob/blob.qml | 56 | ||||
-rw-r--r-- | examples/blob/blobsallad.js | 1139 | ||||
-rw-r--r-- | examples/simple/Spiro.qml | 2 | ||||
-rw-r--r-- | examples/simple/simple.qml | 19 | ||||
-rw-r--r-- | src/context2d.cpp | 116 | ||||
-rw-r--r-- | src/context2d.h | 8 |
7 files changed, 1306 insertions, 117 deletions
diff --git a/examples/blob/Button.qml b/examples/blob/Button.qml new file mode 100644 index 0000000..f961261 --- /dev/null +++ b/examples/blob/Button.qml @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import Qt 4.7 + +Rectangle { + id: container + + property string text: "Button" + SystemPalette{ + id:activePalette + } + + signal clicked + + width: buttonLabel.width + 40; height: buttonLabel.height + 8 + border { width: 1; color: Qt.darker(activePalette.button) } + smooth: true + radius: 4 + + gradient: Gradient { + GradientStop { + position: 0.0 + color: { + if (mouseArea.pressed) + return activePalette.dark + else + return activePalette.light + } + } + GradientStop { position: 1.0; color: activePalette.button } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: container.clicked(); + } + + Text { + id: buttonLabel + anchors.centerIn: container + color: activePalette.buttonText + text: container.text + } +} diff --git a/examples/blob/blob.qml b/examples/blob/blob.qml new file mode 100644 index 0000000..5dfdded --- /dev/null +++ b/examples/blob/blob.qml @@ -0,0 +1,56 @@ +import "../../Canvas" +import "blobsallad.js" as Blob +import Qt 4.7 + +Canvas { + width:500 + height:500 + + property color color: "#ffee00" + + Timer { + id: heartbeat; + interval: 25; + repeat: true + running:true + onTriggered: { + Blob.update(); + updateCanvas(); + } + } + MouseArea { + id:mousearea + hoverEnabled:true + anchors.fill: parent + onPressed: { Blob.mousePressed(mouseX, mouseY) } + onReleased: { Blob.mouseReleased(mouseX, mouseY) } + onPositionChanged: { Blob.mouseMoved(mouseX, mouseY) } + } + + Row { + spacing:10 + anchors.top:parent.top + anchors.left:parent.left + anchors.margins:10 + Button { + id:join + text: "Split" + onClicked: Blob.blobColl.split(); + } + Button { + anchors.margins:16 + text: "Join" + onClicked: Blob.blobColl.join(); + } + Button { + anchors.margins:16 + text: "Gravity" + onClicked: Blob.toggleGravity(); + } + } + + onPaint: { Blob.draw(ctx) } + + Component.onCompleted: Blob.init() +} + diff --git a/examples/blob/blobsallad.js b/examples/blob/blobsallad.js new file mode 100644 index 0000000..cc1457d --- /dev/null +++ b/examples/blob/blobsallad.js @@ -0,0 +1,1139 @@ +/* + This example is adapted from code written by: bjoern.lindberg@gmail.com + and can be found at http://www.blobsallad.se/ +*/ + +function Vector(x, y) +{ + this.x = x; + this.y = y; + + this.equal = function(v) + { + return this.x == v.getX() && this.y == v.getY(); + } + this.getX = function() + { + return this.x; + } + this.getY = function() + { + return this.y; + } + this.setX = function(x) + { + this.x = x; + } + this.setY = function(y) + { + this.y = y; + } + this.addX = function(x) + { + this.x += x; + } + this.addY = function(y) + { + this.y += y; + } + this.set = function(v) + { + this.x = v.getX(); + this.y = v.getY(); + } + this.add = function(v) + { + this.x += v.getX(); + this.y += v.getY(); + } + this.sub = function(v) + { + this.x -= v.getX(); + this.y -= v.getY(); + } + this.dotProd = function(v) + { + return this.x * v.getX() + this.y * v.getY(); + } + this.length = function() + { + return Math.sqrt(this.x * this.x + this.y * this.y); + } + this.scale = function(scaleFactor) + { + this.x *= scaleFactor; + this.y *= scaleFactor; + } + this.toString = function() + { + return " X: " + this.x + " Y: " + this.y; + } +} + +function Environment(x, y, w, h) +{ + this.left = x; + this.right = x + w; + this.top = y; + this.buttom = y + h; + this.r = new Vector(0.0, 0.0); + + this.collision = function(curPos, prevPos) + { + var collide = false; + var i; + + if (curPos.getX() < this.left) { + curPos.setX(this.left); + collide = true; + } else if(curPos.getX() > this.right) { + curPos.setX(this.right); + collide = true; + } + if (curPos.getY() < this.top) { + curPos.setY(this.top); + collide = true; + } else if(curPos.getY() > this.buttom) { + curPos.setY(this.buttom); + collide = true; + } + return collide; + } + this.draw = function(ctx, scaleFactor) + { + } +} + +function PointMass(cx, cy, mass) +{ + this.cur = new Vector(cx, cy); + this.prev = new Vector(cx, cy); + this.mass = mass; + this.force = new Vector(0.0, 0.0); + this.result = new Vector(0.0, 0.0); + this.friction = 0.01; + + this.getXPos = function() + { + return this.cur.getX(); + } + this.getYPos = function() + { + return this.cur.getY(); + } + this.getPos = function() + { + return this.cur; + } + this.getXPrevPos = function() + { + return this.prev.getX(); + } + this.getYPrevPos = function() + { + return this.prev.getY(); + } + this.getPrevPos = function() + { + return this.prev; + } + this.addXPos = function(dx) + { + this.cur.addX(dx); + } + this.addYPos = function(dy) + { + this.cur.addY(dy); + } + this.setForce = function(force) + { + this.force.set(force); + } + this.addForce = function(force) + { + this.force.add(force); + } + this.getMass = function() + { + return this.mass; + } + this.setMass = function(mass) + { + this.mass = mass; + } + this.move = function(dt) + { + var t, a, c, dtdt; + + dtdt = dt * dt; + + a = this.force.getX() / this.mass; + c = this.cur.getX(); + t = (2.0 - this.friction) * c - (1.0 - this.friction) * this.prev.getX() + a * dtdt; + this.prev.setX(c); + this.cur.setX(t); + + a = this.force.getY() / this.mass; + c = this.cur.getY(); + t = (2.0 - this.friction) * c - (1.0 - this.friction) * this.prev.getY() + a * dtdt; + this.prev.setY(c); + this.cur.setY(t); + } + this.setFriction = function(friction) + { + this.friction = friction; + } + this.getVelocity = function() + { + var cXpX, cYpY; + + cXpX = this.cur.getX() - this.prev.getX(); + cYpY = this.cur.getY() - this.prev.getY(); + + return cXpX * cXpX + cYpY * cYpY; + } + this.draw = function(ctx, scaleFactor) + { + ctx.lineWidth = 2; + ctx.fillStyle = '#000000'; + ctx.strokeStyle = '#000000'; + ctx.beginPath(); + ctx.arc(this.cur.getX() * scaleFactor, + this.cur.getY() * scaleFactor, + 4.0, 0.0, Math.PI * 2.0, true); + ctx.fill(); + } +} + +function ConstraintY(pointMass, y, shortConst, longConst) +{ + this.pointMass = pointMass; + this.y = y; + this.delta = new Vector(0.0, 0.0); + this.shortConst = shortConst; + this.longConst = longConst; + + this.sc = function() + { + var dist; + + dist = Math.abs(this.pointMass.getYPos() - this.y); + this.delta.setY(-dist); + + if (this.shortConst != 0.0 && dist < this.shortConst) { + var scaleFactor; + + scaleFactor = this.shortConst / dist; + this.delta.scale(scaleFactor); + pointMass.getPos().sub(this.delta); + } + else if (this.longConst != 0.0 && dist > this.longConst) { + var scaleFactor; + + scaleFactor = this.longConst / dist; + this.delta.scale(scaleFactor); + pointMass.getPos().sub(this.delta); + } + } +} + + +function Joint(pointMassA, pointMassB, shortConst, longConst) +{ + this.pointMassA = pointMassA; + this.pointMassB = pointMassB; + this.delta = new Vector(0.0, 0.0); + this.pointMassAPos = pointMassA.getPos(); + this.pointMassBPos = pointMassB.getPos(); + + this.delta.set(this.pointMassBPos); + this.delta.sub(this.pointMassAPos); + + this.shortConst = this.delta.length() * shortConst; + this.longConst = this.delta.length() * longConst; + this.scSquared = this.shortConst * this.shortConst; + this.lcSquared = this.longConst * this.longConst; + + this.setDist = function(shortConst, longConst) + { + this.shortConst = shortConst; + this.longConst = longConst; + this.scSquared = this.shortConst * this.shortConst; + this.lcSquared = this.longConst * this.longConst; + } + this.scale = function(scaleFactor) + { + this.shortConst = this.shortConst * scaleFactor; + this.longConst = this.longConst * scaleFactor; + this.scSquared = this.shortConst * this.shortConst; + this.lcSquared = this.longConst * this.longConst; + } + this.sc = function() + { + this.delta.set(this.pointMassBPos); + this.delta.sub(this.pointMassAPos); + + var dp = this.delta.dotProd(this.delta); + + if (this.shortConst != 0.0 && dp < this.scSquared) { + var scaleFactor; + + scaleFactor = this.scSquared / (dp + this.scSquared) - 0.5; + + this.delta.scale(scaleFactor); + + this.pointMassAPos.sub(this.delta); + this.pointMassBPos.add(this.delta); + } + else if(this.longConst != 0.0 && dp > this.lcSquared) { + var scaleFactor; + + scaleFactor = this.lcSquared / (dp + this.lcSquared) - 0.5; + + this.delta.scale(scaleFactor); + + this.pointMassAPos.sub(this.delta); + this.pointMassBPos.add(this.delta); + } + } +} + +function Stick(pointMassA, pointMassB) +{ + function pointMassDist(pointMassA, pointMassB) + { + + var aXbX = pointMassA.getXPos() - pointMassB.getXPos(); + var aYbY = pointMassA.getYPos() - pointMassB.getYPos(); + + return Math.sqrt(aXbX * aXbX + aYbY * aYbY); + } + + this.length = pointMassDist(pointMassA, pointMassB); + this.lengthSquared = this.length * this.length; + this.pointMassA = pointMassA; + this.pointMassB = pointMassB; + this.delta = new Vector(0.0, 0.0); + + this.getPointMassA = function() + { + return this.pointMassA; + } + this.getPointMassB = function() + { + return this.pointMassB; + } + this.scale = function(scaleFactor) + { + this.length *= scaleFactor; + this.lengthSquared = this.length * this.length; + } + this.sc = function(env) + { + var dotProd, scaleFactor; + var pointMassAPos, pointMassBPos; + + pointMassAPos = this.pointMassA.getPos(); + pointMassBPos = this.pointMassB.getPos(); + + this.delta.set(pointMassBPos); + this.delta.sub(pointMassAPos); + + dotProd = this.delta.dotProd(this.delta); + + scaleFactor = this.lengthSquared / (dotProd + this.lengthSquared) - 0.5; + this.delta.scale(scaleFactor); + + pointMassAPos.sub(this.delta); + pointMassBPos.add(this.delta); + } + this.setForce = function(force) + { + this.pointMassA.setForce(force); + this.pointMassB.setForce(force); + } + this.addForce = function(force) + { + this.pointMassA.addForce(force); + this.pointMassB.addForce(force); + } + this.move = function(dt) + { + this.pointMassA.move(dt); + this.pointMassB.move(dt); + } + this.draw = function(ctx, scaleFactor) + { + this.pointMassA.draw(ctx, scaleFactor); + this.pointMassB.draw(ctx, scaleFactor); + + ctx.lineWidth = 3; + ctx.fillStyle = '#000000'; + ctx.strokeStyle = '#000000'; + ctx.beginPath(); + ctx.moveTo(this.pointMassA.getXPos() * scaleFactor, + this.pointMassA.getYPos() * scaleFactor); + ctx.lineTo(this.pointMassB.getXPos() * scaleFactor, + this.pointMassB.getYPos() * scaleFactor); + ctx.stroke(); + } +} + +function Spring(restLength, stiffness, damper, pointMassA, pointMassB) +{ + this.restLength = restLength; + this.stiffness = stiffness; + this.damper = damper; + this.pointMassA = pointMassA; + this.pointMassB = pointMassB; + this.tmp = Vector(0.0, 0.0); + + this.sc = function(env) + { + env.collistion(this.pointMassA.getPos(), this.pointMassA.getPrevPos()); + env.collistion(this.pointMassB.getPos(), this.pointMassB.getPrevPos()); + } + + this.move = function(dt) + { + var aXbX; + var aYbY; + var springForce; + var length; + + aXbX = this.pointMassA.getXPos() - this.pointMassB.getXPos(); + aYbY = this.pointMassA.getYPos() - this.pointMassB.getYPos(); + + length = Math.sqrt(aXbX * aXbX + aYbY * aYbY); + springForce = this.stiffness * (length / this.restLength - 1.0); + + var avXbvX; + var avYbvY; + var damperForce; + + avXbvX = this.pointMassA.getXVel() - this.pointMassB.getXVel(); + avYbvY = this.pointMassA.getYVel() - this.pointMassB.getYVel(); + + damperForce = avXbvX * aXbX + avYbvY * aYbY; + damperForce *= this.damper; + + var fx; + var fy; + + fx = (springForce + damperForce) * aXbX; + fy = (springForce + damperForce) * aYbY; + + this.tmp.setX(-fx); + this.tmp.setY(-ft); + this.pointMassA.addForce(this.tmp); + + this.tmp.setX(fx); + this.tmp.setY(ft); + this.pointMassB.addForce(this.tmp); + + this.pointMassA.move(dt); + this.pointMassB.move(dt); + } + this.addForce = function(force) + { + this.pointMassA.addForce(force); + this.pointMassB.addForce(force); + } + this.draw = function(ctx, scaleFactor) + { + this.pointMassA.draw(ctx, scaleFactor); + this.pointMassB.draw(ctx, scaleFactor); + + ctx.fillStyle = '#000000'; + ctx.strokeStyle = '#000000'; + ctx.beginPath(); + ctx.moveTo(this.pointMassA.getXPos() * scaleFactor, + this.pointMassA.getYPos() * scaleFactor); + ctx.lineTo(this.pointMassB.getXPos() * scaleFactor, + this.pointMassB.getXPos() * scaleFactor); + ctx.stroke(); + } +} + +function Blob(x, y, radius, numPointMasses) +{ + this.x = x; + this.y = y; + this.sticks = new Array(); + this.pointMasses = new Array(); + this.joints = new Array(); + this.middlePointMass; + this.radius = radius; + this.drawFaceStyle = 1; + this.drawEyeStyle = 1; + this.selected = false; + + numPointMasses = 8; + + var f = 0.1; + var low = 0.95, high = 1.05; + var t, i, p; + + function clampIndex(index, maxIndex) + { + index += maxIndex; + return index % maxIndex; + } + + for (i = 0, t = 0.0; i < numPointMasses; i++) + { + this.pointMasses[i] = new PointMass(Math.cos(t) * radius + x, Math.sin(t) * radius + y, 1.0); + t += 2.0 * Math.PI / numPointMasses; + } + + this.middlePointMass = new PointMass(x, y, 1.0); + + this.pointMasses[0].setMass(4.0); + this.pointMasses[1].setMass(4.0); + + for (i = 0; i < numPointMasses; i++) { + this.sticks[i] = new Stick(this.pointMasses[i], this.pointMasses[clampIndex(i + 1, numPointMasses)]); + } + + for (i = 0, p = 0; i < numPointMasses; i++) { + this.joints[p++] = new Joint(this.pointMasses[i], this.pointMasses[clampIndex(i + numPointMasses / 2 + 1, numPointMasses)], low, high); + this.joints[p++] = new Joint(this.pointMasses[i], this.middlePointMass, high * 0.9, low * 1.1); // 0.8, 1.2 works + } + + this.addBlob = function(blob) + { + var index = this.joints.length; + + this.joints[index] = new Joint(this.middlePointMass, blob.getMiddlePointMass(), 0.0, 0.0); + var ddist = this.radius + blob.getRadius(); + this.joints[index].setDist(ddist * 0.95, 0.0); + } + this.getMiddlePointMass = function() + { + return this.middlePointMass; + } + this.getRadius = function() + { + return this.radius; + } + this.getXPos = function() + { + return this.middlePointMass.getXPos(); + } + this.getYPos = function() + { + return this.middlePointMass.getYPos(); + } + this.scale = function(scaleFactor) + { + var i; + + for (i = 0; i < this.joints.length; i++) + this.joints[i].scale(scaleFactor); + for (i = 0; i < this.sticks.length; i++) + this.sticks[i].scale(scaleFactor); + this.radius *= scaleFactor; + } + + this.move = function(dt) + { + var i; + + for (i = 0; i < this.pointMasses.length; i++) + this.pointMasses[i].move(dt); + this.middlePointMass.move(dt); + } + this.sc = function(env) + { + var i, j; + + for (j = 0; j < 4; j++) { + for (i = 0; i < this.pointMasses.length; i++) { + if (env.collision(this.pointMasses[i].getPos(), this.pointMasses[i].getPrevPos()) == true) + this.pointMasses[i].setFriction(0.75); + else + this.pointMasses[i].setFriction(0.01); + } + for (i = 0; i < this.sticks.length; i++) + this.sticks[i].sc(env); + for (i = 0; i < this.joints.length; i++) + this.joints[i].sc(); + } + } + this.setForce = function(force) + { + var i; + + for (i = 0; i < this.pointMasses.length; i++) + this.pointMasses[i].setForce(force); + this.middlePointMass.setForce(force); + } + this.addForce = function(force) + { + var i; + + for (i = 0; i < this.pointMasses.length; i++) + this.pointMasses[i].addForce(force); + this.middlePointMass.addForce(force); + this.pointMasses[0].addForce(force); + this.pointMasses[0].addForce(force); + this.pointMasses[0].addForce(force); + this.pointMasses[0].addForce(force); + } + this.moveTo = function(x, y) + { + var i, blobPos; + + blobPos = this.middlePointMass.getPos(); + x -= blobPos.getX(x); + y -= blobPos.getY(y); + + for (i = 0; i < this.pointMasses.length; i++) + { + blobPos = this.pointMasses[i].getPos(); + blobPos.addX(x); + blobPos.addY(y); + } + blobPos = this.middlePointMass.getPos(); + blobPos.addX(x); + blobPos.addY(y); + } + this.setSelected = function(selected) + { + this.selected = selected; + } + + this.drawEars = function(ctx, scaleFactor) + { + ctx.strokeStyle = "#000000"; + ctx.fillStyle = "#FFFFFF"; + ctx.lineWidth = 2; + + ctx.beginPath(); + ctx.moveTo((-0.55 * this.radius) * scaleFactor, (-0.35 * this.radius) * scaleFactor); + ctx.lineTo((-0.52 * this.radius) * scaleFactor, (-0.55 * this.radius) * scaleFactor); + ctx.lineTo((-0.45 * this.radius) * scaleFactor, (-0.40 * this.radius) * scaleFactor); + ctx.fill(); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo((0.55 * this.radius) * scaleFactor, (-0.35 * this.radius) * scaleFactor); + ctx.lineTo((0.52 * this.radius) * scaleFactor, (-0.55 * this.radius) * scaleFactor); + ctx.lineTo((0.45 * this.radius) * scaleFactor, (-0.40 * this.radius) * scaleFactor); + ctx.fill(); + ctx.stroke(); + } + + this.drawHappyEyes1 = function(ctx, scaleFactor) + { + ctx.lineWidth = 1; + ctx.fillStyle = "#FFFFFF"; + ctx.beginPath(); + ctx.arc((-0.15 * this.radius) * scaleFactor, + (-0.20 * this.radius) * scaleFactor, + this.radius * 0.12 * scaleFactor, 0, 2.0 * Math.PI, false); + ctx.fill(); + ctx.stroke(); + + ctx.beginPath(); + ctx.arc(( 0.15 * this.radius) * scaleFactor, + (-0.20 * this.radius) * scaleFactor, + this.radius * 0.12 * scaleFactor, 0, 2.0 * Math.PI, false); + ctx.fill(); + ctx.stroke(); + + ctx.fillStyle = "#000000"; + ctx.beginPath(); + ctx.arc((-0.15 * this.radius) * scaleFactor, + (-0.17 * this.radius) * scaleFactor, + this.radius * 0.06 * scaleFactor, 0, 2.0 * Math.PI, false); + ctx.fill(); + + ctx.beginPath(); + ctx.arc(( 0.15 * this.radius) * scaleFactor, + (-0.17 * this.radius) * scaleFactor, + this.radius * 0.06 * scaleFactor, 0, 2.0 * Math.PI, false); + ctx.fill(); + } + this.drawHappyEyes2 = function(ctx, scaleFactor) + { + ctx.lineWidth = 1; + ctx.fillStyle = "#FFFFFF"; + ctx.beginPath(); + ctx.arc((-0.15 * this.radius) * scaleFactor, + (-0.20 * this.radius) * scaleFactor, + this.radius * 0.12 * scaleFactor, 0, 2.0 * Math.PI, false); + ctx.stroke(); + + ctx.beginPath(); + ctx.arc(( 0.15 * this.radius) * scaleFactor, + (-0.20 * this.radius) * scaleFactor, + this.radius * 0.12 * scaleFactor, 0, 2.0 * Math.PI, false); + ctx.stroke(); + + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo((-0.25 * this.radius) * scaleFactor, + (-0.20 * this.radius) * scaleFactor); + ctx.lineTo((-0.05 * this.radius) * scaleFactor, + (-0.20 * this.radius) * scaleFactor); + ctx.stroke(); + + ctx.beginPath(); + ctx.moveTo(( 0.25 * this.radius) * scaleFactor, + (-0.20 * this.radius) * scaleFactor); + ctx.lineTo(( 0.05 * this.radius) * scaleFactor, + (-0.20 * this.radius) * scaleFactor); + ctx.stroke(); + } + this.drawHappyFace1 = function(ctx, scaleFactor) + { + ctx.lineWidth = 2; + ctx.strokeStyle = "#000000"; + ctx.fillStyle = "#000000"; + ctx.beginPath(); + ctx.arc(0.0, 0.0, + this.radius * 0.25 * scaleFactor, 0, Math.PI, false); + ctx.stroke(); + } + this.drawHappyFace2 = function(ctx, scaleFactor) + { + ctx.lineWidth = 2; + ctx.strokeStyle = "#000000"; + ctx.fillStyle = "#000000"; + ctx.beginPath(); + ctx.arc(0.0, 0.0, + this.radius * 0.25 * scaleFactor, 0, Math.PI, false); + ctx.fill(); + } + this.drawOohFace = function(ctx, scaleFactor) + { + ctx.lineWidth = 2; + ctx.strokeStyle = "#000000"; + ctx.fillStyle = "#000000"; + ctx.beginPath(); + ctx.arc(0.0, (0.1 * this.radius) * scaleFactor, + this.radius * 0.25 * scaleFactor, 0, Math.PI, false); + ctx.fill(); + + ctx.beginPath(); + + ctx.moveTo((-0.25 * this.radius) * scaleFactor, (-0.3 * this.radius) * scaleFactor); + ctx.lineTo((-0.05 * this.radius) * scaleFactor, (-0.2 * this.radius) * scaleFactor); + ctx.lineTo((-0.25 * this.radius) * scaleFactor, (-0.1 * this.radius) * scaleFactor); + + ctx.moveTo((0.25 * this.radius) * scaleFactor, (-0.3 * this.radius) * scaleFactor); + ctx.lineTo((0.05 * this.radius) * scaleFactor, (-0.2 * this.radius) * scaleFactor); + ctx.lineTo((0.25 * this.radius) * scaleFactor, (-0.1 * this.radius) * scaleFactor); + + ctx.stroke(); + } + this.drawFace = function(ctx, scaleFactor) { + if (this.drawFaceStyle == 1 && Math.random() < 0.05) + this.drawFaceStyle = 2; + else if(this.drawFaceStyle == 2 && Math.random() < 0.1) + this.drawFaceStyle = 1; + + if (this.drawEyeStyle == 1 && Math.random() < 0.025) + this.drawEyeStyle = 2; + else if(this.drawEyeStyle == 2 && Math.random() < 0.3) + this.drawEyeStyle = 1; + + if (this.middlePointMass.getVelocity() > 0.004) { + this.drawOohFace(ctx, scaleFactor); + } else { + if (this.drawFaceStyle == 1) + this.drawHappyFace1(ctx, scaleFactor, 0.0, -0.3); + else + this.drawHappyFace2(ctx, scaleFactor, 0.0, -0.3); + + if (this.drawEyeStyle == 1) + this.drawHappyEyes1(ctx, scaleFactor, 0.0, -0.3); + else + this.drawHappyEyes2(ctx, scaleFactor, 0.0, -0.3); + } + } + this.getPointMass = function(index) + { + index += this.pointMasses.length; + index = index % this.pointMasses.length; + return this.pointMasses[index]; + } + this.drawBody = function(ctx, scaleFactor) + { + var i; + + ctx.strokeStyle = "#000000"; + if (this.selected == true) + ctx.fillStyle = "#FFCCCC"; + else + ctx.fillStyle = "#FFFFFF"; + ctx.lineWidth = 5; + ctx.beginPath(); + ctx.moveTo(this.pointMasses[0].getXPos() * scaleFactor, + this.pointMasses[0].getYPos() * scaleFactor); + + for(i = 0; i < this.pointMasses.length; i++) { + var px, py, nx, ny, tx, ty, cx, cy; + var prevPointMass, currentPointMass, nextPointMass, nextNextPointMass; + + prevPointMass = this.getPointMass(i - 1); + currentPointMass = this.pointMasses[i]; + nextPointMass = this.getPointMass(i + 1); + nextNextPointMass = this.getPointMass(i + 2); + + tx = nextPointMass.getXPos(); + ty = nextPointMass.getYPos(); + + cx = currentPointMass.getXPos(); + cy = currentPointMass.getYPos(); + + px = cx * 0.5 + tx * 0.5; + py = cy * 0.5 + ty * 0.5; + + nx = cx - prevPointMass.getXPos() + tx - nextNextPointMass.getXPos(); + ny = cy - prevPointMass.getYPos() + ty - nextNextPointMass.getYPos(); + + px += nx * 0.16; + py += ny * 0.16; + + px = px * scaleFactor; + py = py * scaleFactor; + + tx = tx * scaleFactor; + ty = ty * scaleFactor; + + ctx.bezierCurveTo(px, py, tx, ty, tx, ty); + } + + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + } + this.drawSimpleBody = function(ctx, scaleFactor) { + for(i = 0; i < this.sticks.length; i++) + this.sticks[i].draw(ctx, scaleFactor); + } + + this.draw = function(ctx, scaleFactor) + { + var i; + var up, ori, ang; + + this.drawBody(ctx, scaleFactor); + + ctx.strokeStyle = "#000000"; + ctx.fillStyle = "#000000" + + ctx.save(); + ctx.translate(this.middlePointMass.getXPos() * scaleFactor, + (this.middlePointMass.getYPos() - 0.35 * this.radius) * scaleFactor); + + up = new Vector(0.0, -1.0); + ori = new Vector(0.0, 0.0); + ori.set(this.pointMasses[0].getPos()); + ori.sub(this.middlePointMass.getPos()); + ang = Math.acos(ori.dotProd(up) / ori.length()); + if (ori.getX() < 0.0) + ctx.rotate(-ang); + else + ctx.rotate(ang); + + this.drawEars(ctx, scaleFactor); + this.drawFace(ctx, scaleFactor); + + ctx.restore(); + } +} + +function BlobCollective(x, y, startNum, maxNum) +{ + this.maxNum = maxNum; + this.numActive = 1; + this.blobs = new Array(); + this.tmpForce = new Vector(0.0, 0.0); + this.selectedBlob = null; + + this.blobs[0] = new Blob(x, y, 0.4, 8); + + this.split = function() + { + var i, maxIndex = 0, maxRadius = 0.0; + var emptySlot; + var motherBlob, newBlob; + + if (this.numActive == this.maxNum) + return; + + emptySlot = this.blobs.length; + for (i = 0; i < this.blobs.length; i++) { + if (this.blobs[i] != null && this.blobs[i].getRadius() > maxRadius) { + maxRadius = this.blobs[i].getRadius(); + motherBlob = this.blobs[i]; + } + else if (this.blobs[i] == null) { + emptySlot = i; + } + } + + motherBlob.scale(0.75); + newBlob = new Blob(motherBlob.getXPos(), + motherBlob.getYPos(), motherBlob.getRadius(), 8); + + for(i = 0; i < this.blobs.length; i++) { + if(this.blobs[i] == null) + continue; + this.blobs[i].addBlob(newBlob); + newBlob.addBlob(this.blobs[i]); + } + this.blobs[emptySlot] = newBlob; + + this.numActive++; + } + + this.findSmallest = function(exclude) + { + var minRadius = 1000.0, minIndex; + var i; + + for (i = 0; i < this.blobs.length; i++) { + if (i == exclude || this.blobs[i] == null) + continue; + if (this.blobs[i].getRadius() < minRadius) { + minIndex = i; + minRadius = this.blobs[i].getRadius(); + } + } + return minIndex; + } + this.findClosest = function(exclude) + { + var minDist = 1000.0, foundIndex, dist, aXbX, aYbY; + var i; + var myPointMass, otherPointMass; + + myPointMass = this.blobs[exclude].getMiddlePointMass(); + for (i = 0; i < this.blobs.length; i++) { + if (i == exclude || this.blobs[i] == null) + continue; + + otherPointMass = this.blobs[i].getMiddlePointMass(); + aXbX = myPointMass.getXPos() - otherPointMass.getXPos(); + aYbY = myPointMass.getYPos() - otherPointMass.getYPos(); + dist = aXbX * aXbX + aYbY * aYbY; + if(dist < minDist) { + minDist = dist; + foundIndex = i; + } + } + return foundIndex; + } + this.join = function() + { + var blob1Index, blob2Index, blob1, blob2; + var r1, r2, r3; + + if (this.numActive == 1) + return; + + blob1Index = this.findSmallest(-1); + blob2Index = this.findClosest(blob1Index); + + r1 = this.blobs[blob1Index].getRadius(); + r2 = this.blobs[blob2Index].getRadius(); + r3 = Math.sqrt(r1 * r1 + r2 * r2); + + this.blobs[blob1Index] = null; + this.blobs[blob2Index].scale(0.945 * r3 / r2); + + this.numActive--; + } + + this.selectBlob = function(x, y) + { + var i, minDist = 10000.0; + var otherPointMass; + var selectedBlob; + var selectOffset = null; + + if (this.selectedBlob != null) + return; + for (i = 0; i < this.blobs.length; i++) { + + if (this.blobs[i] == null) + continue; + + otherPointMass = this.blobs[i].getMiddlePointMass(); + var aXbX = x - otherPointMass.getXPos(); + var aYbY = y - otherPointMass.getYPos(); + var dist = aXbX * aXbX + aYbY * aYbY; + if (dist < minDist) { + minDist = dist; + if (dist < this.blobs[i].getRadius() * 0.5) { + this.selectedBlob = this.blobs[i]; + selectOffset = { x : aXbX, y : aYbY }; + } + } + } + + if (this.selectedBlob != null) + this.selectedBlob.setSelected(true); + return selectOffset; +} +this.unselectBlob = function() +{ + if (this.selectedBlob == null) + return; + this.selectedBlob.setSelected(false); + this.selectedBlob = null; +} +this.selectedBlobMoveTo = function(x, y) +{ + if (this.selectedBlob == null) + return; + this.selectedBlob.moveTo(x, y); +} + +this.move = function(dt) +{ + var i; + + for (i = 0; i < this.blobs.length; i++) { + if (this.blobs[i] == null) + continue; + this.blobs[i].move(dt); + } +} +this.sc = function(env) +{ + var i; + + for (i = 0; i < this.blobs.length; i++) { + if(this.blobs[i] == null) + continue; + this.blobs[i].sc(env); + } + if (this.blobAnchor != null) + this.blobAnchor.sc(); +} +this.setForce = function(force) +{ + var i; + + for(i = 0; i < this.blobs.length; i++) { + if(this.blobs[i] == null) + continue; + if(this.blobs[i] == this.selectedBlob) { + this.blobs[i].setForce(new Vector(0.0, 0.0)); + continue; + } + this.blobs[i].setForce(force); + } +} +this.addForce = function(force) +{ + var i; + for (i = 0; i < this.blobs.length; i++) { + if(this.blobs[i] == null) + continue; + if(this.blobs[i] == this.selectedBlob) + continue; + this.tmpForce.setX(force.getX() * (Math.random() * 0.75 + 0.25)); + this.tmpForce.setY(force.getY() * (Math.random() * 0.75 + 0.25)); + this.blobs[i].addForce(this.tmpForce); + } +} +this.draw = function(ctx, scaleFactor) +{ + var i; + + for(i = 0; i < this.blobs.length; i++) { + if(this.blobs[i] == null) + continue; + this.blobs[i].draw(ctx, scaleFactor); + } +} +} + +function debug(msg, okFunc, cancelFunc) +{ + if (confirm(msg) == true && okFunc != null) + okFunc(); + else if(cancelFunc != null) + cancelFunc(); +} + +var env; +var width = 600.0; +var height = 400.0; +var scaleFactor = 200.0; +var blobColl; +var gravity; +var stopped; +var savedMouseCoords = null; +var selectOffset = null; + +function update() +{ + var dt = 0.05; + + if (savedMouseCoords != null && selectOffset != null) + blobColl.selectedBlobMoveTo(savedMouseCoords.x - selectOffset.x, + savedMouseCoords.y - selectOffset.y); + + blobColl.move(dt); + blobColl.sc(env); + blobColl.setForce(gravity); +} + +function draw(ctx) +{ + ctx.clearRect(0, 0, width, height); + blobColl.draw(ctx, scaleFactor); +} + +function init() +{ + env = new Environment(0.2, 0.2, 2.6, 1.6); + blobColl = new BlobCollective(1.0, 1.0, 1, 200); + gravity = new Vector(0.0, 10.0); + stopped = false; +} + +function mousePressed(x, y) +{ + selectOffset = blobColl.selectBlob(x/scaleFactor, y/scaleFactor); +} + +function mouseReleased(x, y) +{ + blobColl.unselectBlob(); + savedMouseCoords = null; + selectOffset = null; +} + +function mouseMoved(x, y) +{ + if (selectOffset == null) + return; + blobColl.selectedBlobMoveTo(x/scaleFactor - selectOffset.x, y/scaleFactor - selectOffset.y); +} + +function stop() +{ + stopped = true; +} + +function start() +{ + stopped = false; + timeout(); +} + +function toggleGravity() +{ + if(gravity.getY() > 0.0) + gravity.setY(0.0); + else + gravity.setY(10.0); +} diff --git a/examples/simple/Spiro.qml b/examples/simple/Spiro.qml index 04e7aff..0bbb5b5 100644 --- a/examples/simple/Spiro.qml +++ b/examples/simple/Spiro.qml @@ -22,7 +22,7 @@ Canvas { ctx.rotate(mousearea.mouseY/width) drawSpirograph(ctx,20*(2)/(1),-8*(3)/(1),mousearea.mouseX/2); ctx.globalAlpha = 0.5; - drawSpirograph(ctx,20*(2)/(1),-8*(3)/(1),mousearea.mouseX/2 +10); + drawSpirograph(ctx,20*(2)/(1),-8*(3)/(1),mousearea.mouseX/2 -10); } function drawSpirograph(ctx,R,r,O){ diff --git a/examples/simple/simple.qml b/examples/simple/simple.qml index 4f4982e..a9f4a50 100644 --- a/examples/simple/simple.qml +++ b/examples/simple/simple.qml @@ -3,20 +3,11 @@ import "../../Canvas" Grid { columns:2 - spacing:0 - - Spiro { - color: "#eeaa44" - } - Spiro { - color: "#ee22ee" - } - Spiro { - color:"#dd2277" - } - Spiro { - color:"#cccc77" - } + spacing:0 + Spiro {color: "#eeaa44"} + Spiro {color: "#ee22ee"} + Spiro {color:"#dd2277"} + Spiro {color:"#cccc77"} } diff --git a/src/context2d.cpp b/src/context2d.cpp index 05713a2..c1e5708 100644 --- a/src/context2d.cpp +++ b/src/context2d.cpp @@ -465,7 +465,6 @@ void Context2D::clearRect(qreal x, qreal y, qreal w, qreal h) m_painter->setCompositionMode(QPainter::CompositionMode_Source); m_painter->fillRect(QRectF(x, y, w, h), QColor(0, 0, 0, 0)); m_painter->restore(); - scheduleChange(); } void Context2D::fillRect(qreal x, qreal y, qreal w, qreal h) @@ -475,7 +474,6 @@ void Context2D::fillRect(qreal x, qreal y, qreal w, qreal h) m_painter->setMatrix(m_state.matrix, false); m_painter->fillRect(QRectF(x, y, w, h), m_painter->brush()); m_painter->restore(); - scheduleChange(); } void Context2D::strokeRect(qreal x, qreal y, qreal w, qreal h) @@ -487,7 +485,6 @@ void Context2D::strokeRect(qreal x, qreal y, qreal w, qreal h) m_painter->setMatrix(m_state.matrix, false); m_painter->strokePath(path, m_painter->pen()); m_painter->restore(); - scheduleChange(); } @@ -603,7 +600,6 @@ void Context2D::fill() { beginPainting(); m_painter->fillPath(m_path, m_painter->brush()); - scheduleChange(); } @@ -615,7 +611,6 @@ void Context2D::stroke() QPainterPath tmp = m_state.matrix.inverted().map(m_path); m_painter->strokePath(tmp, m_painter->pen()); m_painter->restore(); - scheduleChange(); } @@ -655,65 +650,36 @@ Context2D::Context2D(QObject *parent, QPainter *painter) reset(); } -const QImage &Context2D::endPainting() -{ - if (m_painter->isActive()) - m_painter->end(); - return m_image; -} - void Context2D::beginPainting() { - - if (!m_painter->isActive()) { - // m_painter->begin(&m_image); - m_painter->setRenderHint(QPainter::Antialiasing); - if (!m_state.clipPath.isEmpty()) - m_painter->setClipPath(m_state.clipPath); + if ((m_state.flags & DirtyClippingRegion) && !m_state.clipPath.isEmpty()) + m_painter->setClipPath(m_state.clipPath); + if (m_state.flags & DirtyFillStyle) m_painter->setBrush(m_state.fillStyle); + if (m_state.flags & DirtyGlobalAlpha) m_painter->setOpacity(m_state.globalAlpha); - QPen pen; - pen.setBrush(m_state.strokeStyle); - if (pen.style() == Qt::NoPen) - pen.setStyle(Qt::SolidLine); - pen.setCapStyle(m_state.lineCap); - pen.setJoinStyle(m_state.lineJoin); - pen.setWidthF(m_state.lineWidth); - pen.setMiterLimit(m_state.miterLimit); + if (m_state.flags & DirtyGlobalCompositeOperation) + m_painter->setCompositionMode(m_state.globalCompositeOperation); + if (m_state.flags & MDirtyPen) { + QPen pen = m_painter->pen(); + if (m_state.flags & DirtyStrokeStyle) + pen.setBrush(m_state.strokeStyle); + if (m_state.flags & DirtyLineWidth) + pen.setWidthF(m_state.lineWidth); + if (m_state.flags & DirtyLineCap) + pen.setCapStyle(m_state.lineCap); + if (m_state.flags & DirtyLineJoin) + pen.setJoinStyle(m_state.lineJoin); + if (m_state.flags & DirtyMiterLimit) + pen.setMiterLimit(m_state.miterLimit); m_painter->setPen(pen); - } else { - if ((m_state.flags & DirtyClippingRegion) && !m_state.clipPath.isEmpty()) - m_painter->setClipPath(m_state.clipPath); - if (m_state.flags & DirtyFillStyle) - m_painter->setBrush(m_state.fillStyle); - if (m_state.flags & DirtyGlobalAlpha) - m_painter->setOpacity(m_state.globalAlpha); - if (m_state.flags & DirtyGlobalCompositeOperation) - m_painter->setCompositionMode(m_state.globalCompositeOperation); - if (m_state.flags & MDirtyPen) { - QPen pen = m_painter->pen(); - if (m_state.flags & DirtyStrokeStyle) - pen.setBrush(m_state.strokeStyle); - if (m_state.flags & DirtyLineWidth) - pen.setWidthF(m_state.lineWidth); - if (m_state.flags & DirtyLineCap) - pen.setCapStyle(m_state.lineCap); - if (m_state.flags & DirtyLineJoin) - pen.setJoinStyle(m_state.lineJoin); - if (m_state.flags & DirtyMiterLimit) - pen.setMiterLimit(m_state.miterLimit); - m_painter->setPen(pen); - } - m_state.flags = 0; } + m_state.flags = 0; } void Context2D::clear() { - return; - endPainting(); - m_image.fill(qRgba(0,0,0,0)); - scheduleChange(); + m_painter->fillRect(QRect(QPoint(0,0), size()), Qt::white); } void Context2D::reset() @@ -737,29 +703,6 @@ void Context2D::reset() clear(); } -void Context2D::setSize(int width, int height) -{ - endPainting(); - QImage newi(width, height, QImage::Format_ARGB32_Premultiplied); - newi.fill(qRgba(0,0,0,0)); - QPainter p(&newi); - p.drawImage(0, 0, m_image); - p.end(); - m_image = newi; - scheduleChange(); -} - -void Context2D::setSize(const QSize &size) -{ - setSize(size.width(), size.height()); -} - -QSize Context2D::size() const -{ - return m_image.size(); -} - - void Context2D::drawImage(const QVariant &var, qreal sx, qreal sy, qreal sw = 0, qreal sh = 0) { @@ -771,24 +714,9 @@ void Context2D::drawImage(const QVariant &var, qreal sx, qreal sy, m_painter->drawImage(QPointF(sx, sy), image->value()); else m_painter->drawImage(QRectF(sx, sy, sw, sh), image->value()); - scheduleChange(); -} - - -void Context2D::scheduleChange() -{ - if (m_changeTimerId == -1) - m_changeTimerId = startTimer(0); } -void Context2D::timerEvent(QTimerEvent *e) +QSize Context2D::size() const { - if (e->timerId() == m_changeTimerId) { - killTimer(m_changeTimerId); - m_changeTimerId = -1; - emit changed(endPainting()); - } else { - QObject::timerEvent(e); - } + return m_painter->viewport().size(); } -//! [2] diff --git a/src/context2d.h b/src/context2d.h index ed76cd6..a4c2421 100644 --- a/src/context2d.h +++ b/src/context2d.h @@ -122,8 +122,6 @@ class Context2D : public QObject public: Context2D(QObject *parent = 0, QPainter *painter = 0); - void setSize(int width, int height); - void setSize(const QSize &size); QSize size() const; void clear(); @@ -216,16 +214,10 @@ public slots: signals: void changed(const QImage &image); -protected: - void timerEvent(QTimerEvent *e); - private: void beginPainting(); - const QImage &endPainting(); - void scheduleChange(); int m_changeTimerId; - QImage m_image; QPainter *m_painter; QPainterPath m_path; |