|
@@ -264,8 +264,11 @@ Object.assign(Vector2.prototype,
|
|
|
{
|
|
|
var angle = Math.atan2(this.y, this.x);
|
|
|
|
|
|
- if(angle < 0) angle += 2 * Math.PI;
|
|
|
-
|
|
|
+ if(angle < 0)
|
|
|
+ {
|
|
|
+ angle += 2 * Math.PI;
|
|
|
+ }
|
|
|
+
|
|
|
return angle;
|
|
|
},
|
|
|
|
|
@@ -415,17 +418,28 @@ Matrix.prototype.premultiply = function(mat)
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
- * Compose this transformation matrix with position scale and rotation.
|
|
|
+ * Compose this transformation matrix with position scale and rotation and origin point.
|
|
|
*/
|
|
|
-Matrix.prototype.compose = function(px, py, sx, sy, a)
|
|
|
+Matrix.prototype.compose = function(px, py, sx, sy, ox, oy, a)
|
|
|
{
|
|
|
this.m = [1, 0, 0, 1, px, py];
|
|
|
|
|
|
- var c = Math.cos(a);
|
|
|
- var s = Math.sin(a);
|
|
|
- this.multiply(new Matrix([c, s, -s, c, 0, 0]));
|
|
|
+ if(a !== 0)
|
|
|
+ {
|
|
|
+ var c = Math.cos(a);
|
|
|
+ var s = Math.sin(a);
|
|
|
+ this.multiply(new Matrix([c, s, -s, c, 0, 0]));
|
|
|
+ }
|
|
|
|
|
|
- this.scale(sx, sy);
|
|
|
+ if(ox !== 0 || oy !== 0)
|
|
|
+ {
|
|
|
+ this.multiply(new Matrix([1, 0, 0, 1, -ox, -oy]));
|
|
|
+ }
|
|
|
+
|
|
|
+ if(sx !== 1 || sy !== 1)
|
|
|
+ {
|
|
|
+ this.scale(sx, sy);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -616,6 +630,11 @@ function Object2D()
|
|
|
*/
|
|
|
this.position = new Vector2(0, 0);
|
|
|
|
|
|
+ /**
|
|
|
+ * Origin of the object used as point of rotation.
|
|
|
+ */
|
|
|
+ this.origin = new Vector2(0, 0);
|
|
|
+
|
|
|
/**
|
|
|
* Scale of the object.
|
|
|
*/
|
|
@@ -667,7 +686,28 @@ function Object2D()
|
|
|
*
|
|
|
* If true the onPointerDrag callback is used to update the state of the object.
|
|
|
*/
|
|
|
- this.draggable = true;
|
|
|
+ this.draggable = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Flag to indicate wheter this objet ignores the viewport transformation.
|
|
|
+ */
|
|
|
+ this.ignoreViewport = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Flag to indicate if the context of canvas should be saved before render.
|
|
|
+ */
|
|
|
+ this.saveContextState = true;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Flag to indicate if the context of canvas should be restored after render.
|
|
|
+ */
|
|
|
+ this.restoreContextState = true;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Flag to indicate if the context of canvas should be restored after render.
|
|
|
+ */
|
|
|
+ this.restoreContextState = true;
|
|
|
+
|
|
|
|
|
|
/**
|
|
|
* Flag indicating if the pointer is inside of the element.
|
|
@@ -740,7 +780,7 @@ Object2D.prototype.updateMatrix = function(context)
|
|
|
{
|
|
|
if(this.matrixNeedsUpdate)
|
|
|
{
|
|
|
- this.matrix.compose(this.position.x, this.position.y, this.scale.x, this.scale.y, this.rotation);
|
|
|
+ this.matrix.compose(this.position.x, this.position.y, this.scale.x, this.scale.y, this.origin.x, this.origin.y, this.rotation);
|
|
|
this.globalMatrix.copy(this.matrix);
|
|
|
|
|
|
if(this.parent !== null)
|
|
@@ -753,6 +793,16 @@ Object2D.prototype.updateMatrix = function(context)
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+/**
|
|
|
+ * Apply the transform to the rendering context.
|
|
|
+ *
|
|
|
+ * Can also be used for pre rendering logic.
|
|
|
+ */
|
|
|
+Object2D.prototype.transform = function(context, viewport)
|
|
|
+{
|
|
|
+ this.globalMatrix.tranformContext(context);
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* Draw the object into the canvas.
|
|
|
*
|
|
@@ -760,7 +810,7 @@ Object2D.prototype.updateMatrix = function(context)
|
|
|
*
|
|
|
* @param context Canvas 2d drawing context.
|
|
|
*/
|
|
|
-Object2D.prototype.draw = function(context){};
|
|
|
+Object2D.prototype.draw = function(context, viewport){};
|
|
|
|
|
|
/**
|
|
|
* Callback method called every time before the object is draw into the canvas.
|
|
@@ -795,7 +845,10 @@ Object2D.prototype.onPointerOver = null;
|
|
|
*
|
|
|
* Receives (pointer, viewport, delta) as arguments. Delta is the movement of the pointer already translated into local object coordinates.
|
|
|
*/
|
|
|
-Object2D.prototype.onPointerDrag = null;
|
|
|
+Object2D.prototype.onPointerDrag = function(pointer, viewport, delta)
|
|
|
+{
|
|
|
+ this.position.add(delta);
|
|
|
+};
|
|
|
|
|
|
/**
|
|
|
* Callback method called while the pointer button is pressed.
|
|
@@ -1363,7 +1416,7 @@ Renderer.prototype.update = function(object, viewport)
|
|
|
for(var i = 0; i < objects.length; i++)
|
|
|
{
|
|
|
var child = objects[i];
|
|
|
- var childPoint = child.inverseGlobalMatrix.transformPoint(viewportPoint);
|
|
|
+ var childPoint = child.inverseGlobalMatrix.transformPoint(child.ignoreViewport ? point : viewportPoint);
|
|
|
|
|
|
// Check if the pointer pointer is inside
|
|
|
if(child.isInside(childPoint))
|
|
@@ -1436,7 +1489,7 @@ Renderer.prototype.update = function(object, viewport)
|
|
|
var child = objects[i];
|
|
|
|
|
|
if(child.beingDragged)
|
|
|
- {
|
|
|
+ {
|
|
|
var lastPosition = pointer.position.clone();
|
|
|
lastPosition.sub(pointer.delta);
|
|
|
|
|
@@ -1446,9 +1499,6 @@ Renderer.prototype.update = function(object, viewport)
|
|
|
// Mouse delta in world coordinates
|
|
|
positionWorld.sub(lastWorld);
|
|
|
|
|
|
- // Update child position
|
|
|
- child.position.add(positionWorld);
|
|
|
-
|
|
|
if(child.onPointerDrag !== null)
|
|
|
{
|
|
|
child.onPointerDrag(pointer, viewport, positionWorld);
|
|
@@ -1482,11 +1532,24 @@ Renderer.prototype.update = function(object, viewport)
|
|
|
|
|
|
// Render into the canvas
|
|
|
for(var i = 0; i < objects.length; i++)
|
|
|
- {
|
|
|
- context.save();
|
|
|
- objects[i].globalMatrix.tranformContext(context);
|
|
|
+ {
|
|
|
+ if(objects[i].saveContextState)
|
|
|
+ {
|
|
|
+ context.save();
|
|
|
+ }
|
|
|
+
|
|
|
+ if(objects[i].ignoreViewport)
|
|
|
+ {
|
|
|
+ context.setTransform(1, 0, 0, 1, 0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ objects[i].transform(context, viewport);
|
|
|
objects[i].draw(context, viewport);
|
|
|
- context.restore();
|
|
|
+
|
|
|
+ if(objects[i].restoreContextState)
|
|
|
+ {
|
|
|
+ context.restore();
|
|
|
+ }
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -1574,7 +1637,7 @@ Viewport.prototype.updateMatrix = function()
|
|
|
{
|
|
|
if(this.matrixNeedsUpdate)
|
|
|
{
|
|
|
- this.matrix.compose(this.position.x, this.position.y, this.scale, this.scale, this.rotation);
|
|
|
+ this.matrix.compose(this.position.x, this.position.y, this.scale, this.scale, 0, 0, this.rotation);
|
|
|
this.inverseMatrix = this.matrix.getInverse();
|
|
|
//this.matrixNeedsUpdate = false;
|
|
|
}
|
|
@@ -1807,6 +1870,22 @@ Circle.prototype.draw = function(context)
|
|
|
|
|
|
function Helpers(){}
|
|
|
|
|
|
+
|
|
|
+/**
|
|
|
+ * Create a rotation tool
|
|
|
+ */
|
|
|
+Helpers.rotateTool = function(object)
|
|
|
+{
|
|
|
+ var tool = new Circle();
|
|
|
+ tool.radius = 4;
|
|
|
+ tool.layer = object.layer + 1;
|
|
|
+ tool.onPointerDrag = function(pointer, viewport, delta)
|
|
|
+ {
|
|
|
+ object.rotation += delta.x * 1e-3;
|
|
|
+ };
|
|
|
+ object.add(tool);
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* Create a box resize helper and attach it to an object to change the size of the object box.
|
|
|
*
|
|
@@ -1814,11 +1893,11 @@ function Helpers(){}
|
|
|
*
|
|
|
* This method required to object to have a box property.
|
|
|
*/
|
|
|
-Helpers.createBoxResize = function(object)
|
|
|
+Helpers.boxResizeTool = function(object)
|
|
|
{
|
|
|
if(object.box === undefined)
|
|
|
{
|
|
|
- console.warn("trenette.js: Helpers.createBoxResize(), object box property missing.");
|
|
|
+ console.warn("trenette.js: Helpers.boxResizeTool(), object box property missing.");
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -1832,8 +1911,12 @@ Helpers.createBoxResize = function(object)
|
|
|
|
|
|
var topRight = new Circle();
|
|
|
topRight.radius = 4;
|
|
|
+ topRight.layer = object.layer + 1;
|
|
|
+ topRight.draggable = true;
|
|
|
topRight.onPointerDrag = function(pointer, viewport, delta)
|
|
|
{
|
|
|
+ Object2D.prototype.onPointerDrag.call(this, pointer, viewport, delta);
|
|
|
+
|
|
|
object.box.min.copy(topRight.position);
|
|
|
updateHelpers();
|
|
|
};
|
|
@@ -1841,8 +1924,12 @@ Helpers.createBoxResize = function(object)
|
|
|
|
|
|
var topLeft = new Circle();
|
|
|
topLeft.radius = 4;
|
|
|
+ topLeft.layer = object.layer + 1;
|
|
|
+ topLeft.draggable = true;
|
|
|
topLeft.onPointerDrag = function(pointer, viewport, delta)
|
|
|
{
|
|
|
+ Object2D.prototype.onPointerDrag.call(this, pointer, viewport, delta);
|
|
|
+
|
|
|
object.box.max.x = topLeft.position.x;
|
|
|
object.box.min.y = topLeft.position.y;
|
|
|
updateHelpers();
|
|
@@ -1851,8 +1938,12 @@ Helpers.createBoxResize = function(object)
|
|
|
|
|
|
var bottomLeft = new Circle();
|
|
|
bottomLeft.radius = 4;
|
|
|
+ bottomLeft.layer = object.layer + 1;
|
|
|
+ bottomLeft.draggable = true;
|
|
|
bottomLeft.onPointerDrag = function(pointer, viewport, delta)
|
|
|
{
|
|
|
+ Object2D.prototype.onPointerDrag.call(this, pointer, viewport, delta);
|
|
|
+
|
|
|
object.box.max.copy(bottomLeft.position);
|
|
|
updateHelpers();
|
|
|
};
|
|
@@ -1860,8 +1951,12 @@ Helpers.createBoxResize = function(object)
|
|
|
|
|
|
var bottomRight = new Circle();
|
|
|
bottomRight.radius = 4;
|
|
|
+ bottomRight.layer = object.layer + 1;
|
|
|
+ bottomRight.draggable = true;
|
|
|
bottomRight.onPointerDrag = function(pointer, viewport, delta)
|
|
|
{
|
|
|
+ Object2D.prototype.onPointerDrag.call(this, pointer, viewport, delta);
|
|
|
+
|
|
|
object.box.min.x = bottomRight.position.x;
|
|
|
object.box.max.y = bottomRight.position.y;
|
|
|
updateHelpers();
|
|
@@ -1874,7 +1969,7 @@ Helpers.createBoxResize = function(object)
|
|
|
/**
|
|
|
* Box object draw a box.
|
|
|
*/
|
|
|
-function Box(resizable)
|
|
|
+function Box()
|
|
|
{
|
|
|
Object2D.call(this);
|
|
|
|
|
@@ -1892,11 +1987,6 @@ function Box(resizable)
|
|
|
* Background color of the box.
|
|
|
*/
|
|
|
this.fillStyle = "#FFFFFF";
|
|
|
-
|
|
|
- if(resizable)
|
|
|
- {
|
|
|
- Helpers.createBoxResize(this);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
Box.prototype = Object.create(Object2D.prototype);
|
|
@@ -2047,7 +2137,7 @@ Image.prototype.isInside = function(point)
|
|
|
|
|
|
Image.prototype.draw = function(context)
|
|
|
{
|
|
|
- context.drawImage(this.image, 0, 0);
|
|
|
+ context.drawImage(this.image, 0, 0, this.image.naturalWidth, this.image.naturalHeight, this.box.min.x, this.box.min.y, this.box.max.x - this.box.min.x, this.box.max.y - this.box.min.y);
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -2072,10 +2162,16 @@ function DOM(parent, type)
|
|
|
this.element.style.position = "absolute";
|
|
|
this.element.style.top = "0px";
|
|
|
this.element.style.bottom = "0px";
|
|
|
- this.element.style.width = "100px";
|
|
|
- this.element.style.height = "100px";
|
|
|
- this.element.style.backgroundColor = "rgba(0.0, 0.0, 0.0, 0.8)";
|
|
|
this.element.style.transformOrigin = "0px 0px";
|
|
|
+ this.element.style.overflow = "auto";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Size of the DOM element (in world coordinates).
|
|
|
+ */
|
|
|
+ this.size = new Vector2(100, 100);
|
|
|
+
|
|
|
+ this.origin.set(50, 50);
|
|
|
+
|
|
|
parent.appendChild(this.element);
|
|
|
}
|
|
|
|
|
@@ -2083,10 +2179,14 @@ DOM.prototype = Object.create(Object2D.prototype);
|
|
|
|
|
|
DOM.prototype.draw = function(context, viewport)
|
|
|
{
|
|
|
+ // CSS trasnformation matrix
|
|
|
var projection = viewport.matrix.clone();
|
|
|
projection.multiply(this.globalMatrix);
|
|
|
-
|
|
|
this.element.style.transform = projection.cssTransform();
|
|
|
+
|
|
|
+ // Size of the element
|
|
|
+ this.element.style.width = this.size.x + "px";
|
|
|
+ this.element.style.height = this.size.y + "100px";
|
|
|
};
|
|
|
|
|
|
-export { Box, Box2, Circle, DOM, EventManager, Image, Key, Line, Matrix, Object2D, Pointer, Renderer, Text, UUID, Vector2, Viewport };
|
|
|
+export { Box, Box2, Circle, DOM, EventManager, Helpers, Image, Key, Line, Matrix, Object2D, Pointer, Renderer, Text, UUID, Vector2, Viewport };
|