You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by jb...@apache.org on 2010/08/10 16:18:54 UTC
svn commit: r984027 -
/commons/sandbox/gsoc/2010/scxml-js/trunk/demo/drawing-tool/behaviour/canvas.xml
Author: jbeard
Date: Tue Aug 10 14:18:53 2010
New Revision: 984027
URL: http://svn.apache.org/viewvc?rev=984027&view=rev
Log:
Made serious revisions to use of statecharts in this demo. Centralized logic in the canvas. Started implementing this. This is an intermediate, non-tested, broken commit.
Modified:
commons/sandbox/gsoc/2010/scxml-js/trunk/demo/drawing-tool/behaviour/canvas.xml
Modified: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/drawing-tool/behaviour/canvas.xml
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/demo/drawing-tool/behaviour/canvas.xml?rev=984027&r1=984026&r2=984027&view=diff
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/demo/drawing-tool/behaviour/canvas.xml (original)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/demo/drawing-tool/behaviour/canvas.xml Tue Aug 10 14:18:53 2010
@@ -1,3 +1,52 @@
+<!--
+Natural-language behavioural specification:
+
+We have a canvas, nodes, rotation and scale handles.
+
+Canvas has three tools: drawing rects, drawing ellipses, and transforming them.
+Selects between tools by clicking buttons on a toolbar.
+
+Only one tool can be selected at a time.
+
+Nodes can be selected or not selected. When selected, they are surrounded by a dashed rect.
+
+When transform tool is selected:
+ If there are selected nodes, then either the rotate or translate handles will be visible, and will eb psoitioned on the aggregate bbox of all selected nodes; otherwise, if no nodes are selected, then neither rotate nor transform handles will be shown.
+
+ Dragging rotation handle rotates the nodes about the center point of their aggregate bbox. On mouseup, handle positions are reset.
+ Dragging scale handle scales the nodes.
+ Dragging selected node results in all selected nodes being dragged.
+ Dragging non-selected node results in node being selected, and all other nodes being deselected.
+ Dragging (no shift) on canvas results in marquee being drawn, all nodes inside of marquee being selected, and all other nodes being deselected,
+ Dragging (with shift) on canvas results in marquee being drawn, all nodes inside of marquee being selected (added to selection).
+
+ Mouseclick (no shift) on selected node results in rotation/scale handles being toggled.
+
+ Dragging node (no shift) will deselect other nodes, drag the node, and select this node.
+ Dragging node (with shift) has same effect as dragging on canvas.
+
+ Additionally, when no nodes are selected, and nodes are first selected, then the scale rotation handles will be shown first
+ UNLESS, the last time nodes were selected, rotation handles were shown when they were deselected;
+ AND, the way the nodes are being selected now is via a drag (not a click)
+
+When rect or ellipse drawing tools are selected:
+ Rotate and translate handles will never be visible.
+ When only one node is selected, then that node's resize/roundness controls will be shown.
+
+ Mouseclick (no shift) on selected node HAS NO EFFECT.
+
+ Dragging (on canvas, rect, wherever) results in a new element being draw, created and selected. On mouseup, all other elements are deselect.
+
+In both:
+ Mouseclick (no shift) on canvas results in all nodes being selected.
+ Mouseclick (with shift) on node results in node's selection state being toggled.
+
+ Mouseclick (no shift) on non-selected node results in node being selected, and all other nodes being deselected.
+
+ Ctrl+A selects all
+ Delete deletes selected nodes (and deselects them in the process, natch)
+-->
+
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
version="1.0"
@@ -28,134 +77,325 @@
node.rx.baseVal.value = dxByTwo;
node.ry.baseVal.value = dyByTwo;
}
+
+ function updateTransformHandles(rotationButtonHandle,scaleButtonHandle){
+ var sePtX = cachedBBox.width + cachedBBox.x;
+ var sePtY = cachedBBox.height + cachedBBox.y;
+
+ var sScale = scaleButtonHandle.transform.baseVal.getItem(0);
+ var newM = identity.translate(sePtX,sePtY);
+ sScale.setMatrix(newM);
+
+ var rScale = rotationButtonHandle.transform.baseVal.getItem(0);
+ var newM = identity.translate(sePtX,sePtY);
+ rScale.setMatrix(newM);
+ }
+
+ function getCenterPoint(canvas){
+ var bbox = canvas.getBBox();
+ var cPt = {
+ 'x': bbox.x + (bbox.width / 2),
+ 'y': bbox.y + (bbox.height / 2)
+ }
+
+ return cPt;
+ }
+
+
+ function computeRDelta(oldEvent,newEvent,cachedCenterPoint){
+ return _computeRotationAngle(cachedCenterPoint,{x:oldEvent.localX,y:oldEvent.localY},{x:newEvent.localX,y:newEvent.localY});
+ }
+
+ function _computeRotationAngle(cpt,pt1,pt2){
+ var a1 = _computeAngle(cpt,pt1)
+ var a2 = _computeAngle(cpt,pt2)
+ var angleInDegrees = a2-a1
+ return angleInDegrees;
+ }
+
+ function _computeAngle(cpt,pt){
+ var angle=0;
+
+ //put him on the trig circle
+ var signedPt= {
+ x:pt.x-cpt.x,
+ y:pt.y-cpt.y
+ }
+
+ //put him in upper right corner
+ var normalizedPt= {
+ x:Math.abs(signedPt.x),
+ y:Math.abs(signedPt.y)
+ };
+
+ //compute the angle
+ var angle=Math.abs(Math.atan2(normalizedPt.y, normalizedPt.x));
+
+ //get angle for signed coordinates
+ if(signedPt.x<0 && signedPt.y>=0){
+ angle=Math.PI-angle;
+ }else if(signedPt.x<0 && signedPt.y<0){
+ angle=Math.PI+angle;
+ }else if(signedPt.x>=0 && signedPt.y<0){
+ angle=2*Math.PI-angle;
+ }
+ return angle;
+ }
+
+ function applyTransformToBBox(bbox,matrix){
+ //takes a bbox and a matrix and returns the bbox with the matrix applied to all points
+ var nwX = bbox.x;
+ var nwY = bbox.y;
+ var neX = nwX + bbox.width;
+ var neY = nwY;
+ var swX = nwX;
+ var swY = nwX + bbox.height;
+ var seX = neX;
+ var seY = swY;
+
+ var nw = canvas.createSVGPoint();
+ nw.x = nwX;
+ nw.y + nwY;
+
+ var ne = canvas.createSVGPoint();
+ ne.x = neX;
+ ne.y = neY;
+
+ var sw = canvas.createSVGPoint();
+ sw.x = swX;
+ sw.y = swY;
+
+ var se = canvas.createSVGPoint();
+ se.x = seX;
+ se.y = seY;
+
+ nw = nw.matrixTransform(matrix);
+ ne = ne.matrixTransform(matrix);
+ sw = sw.matrixTransform(matrix);
+ se = se.matrixTransform(matrix);
+
+ var newWidth = Math.abs(ne.x - nw.x);
+ var newHeight = Math.abs(se.y - ne.y);
+
+ return {x:nw.x,y:nw.y,width:newWidth,height:newHeight}
+ }
+
+ function applyLeftTransform(canvas,transformMatrix){
+ var t = canvas.transform.baseVal.getItem(0);
+ var m = t.matrix;
+ var newM = transformMatrix.multiply(m); //note the left transform, pre-muliplication here
+ t.setMatrix(newM);
+ return t;
+ }
+
+ function applyLeftTransformFromNodeToOtherNode(fromNode,toNode){
+ var t = fromNode.transform.baseVal.getItem(0);
+ var m = t.matrix;
+ return applyLeftTransform(toNode,m);
+ }
+
]]></script>
<datamodel>
+ <!-- these get passed in on initiation -->
<data id="svg"/>
- <data id="api"/>
<data id="toolbarStatechart"/>
+ <data id="scaleButtonHandle"/>
+ <data id="rotationButtonHandle"/>
+ <data id="canvas"/>
+ <data id="transformModule"/>
<data id="scaleHandle"/>
<data id="rotationHandle"/>
- <data id="tDelta"/>
- <data id="firstEvent"/>
- <data id="eventStamp"/>
- <data id="nodeBeingDrawn"/>
<data id="svgNs" expr="'http://www.w3.org/2000/svg'"/>
+
<data id="selectedNodes" expr="[]"/>
+ <data id="nodesOnCanvas" expr="[]"/>
+
+ <data id="firstEvent"/>
+ <data id="eventStamp"/>
+ <data id="tDelta"/>
+ <data id="cachedCenterPoint"/>
+ <data id="cachedBBox"/>
+
</datamodel>
<state id="initial_default">
- <transition event="init" target="idle">
+ <transition event="init" target="main">
<assign location="svg" expr="_event.data.svg"/>
<assign location="toolbarStatechart" expr="_event.data.toolbarStatechart"/>
- <assign location="api" expr="_event.data.api"/>
+ <assign location="scaleButtonHandle" expr="_event.data.scaleButtonHandle"/>
+ <assign location="rotationButtonHandle" expr="_event.data.rotationButtonHandle"/>
</transition>
</state>
- <state id="idle">
- <transition event="mousedown" target="before_drawing_mode"
- cond="!(_event.data.target==scaleHandle || _event.data.target==rotationHandle)">
-
- <assign location="firstEvent" expr="_event.data"/>
- <assign location="eventStamp" expr="_event.data"/>
- </transition>
- <transition event="mousedown" target="idle" cond="_event.data.target==scaleHandle">
- <script>
- //send event to selected guys
- $(selectedNodes).each(function(n){
- n["scale_mousedown"](_event.data);
- });
- </script>
- </transition>
- <transition event="mousedown" target="idle" cond="_event.data.target==rotationHandle">
- <script>
- //send event to selected guys
- $(selectedNodes).each(function(n){
- n["rotation_mousedown"](_event.data);
- });
- </script>
- </transition>
-
- <!-- TODO: refactor the following to be static reactions (eventless transitions) -->
- <transition event="mousemove" target="idle">
- $(selectedNodes).each(function(n){
- n["mousemove"](event._data);
- });
- </transition>
- <transition event="CSGROUP_HAS_MOVED" target="idle">
- <!-- TODO -->
- </transition>
- <transition event="OBJ_FINISHED_TRANSFORM" target="idle">
- <!-- TODO -->
- </transition>
- <transition event="OBJ_SELECTED" target="idle">
- <script>
- selectedNodes.push(_event.data.target);
-
- </script>
- </transition>
- <transition event="OBJ_DESELECTED" target="idle">
- <script>
- selectedNodes.splice(selectedNodes.indexOf(_event.data.target),1);
- </script>
- </transition>
- </state>
+ <parallel id="main">
- <state id="before_drawing_mode">
- <transition event="mouseup" target="idle">
- <script>
- //TODO
- //obj.canvasDeselect();
- </script>
- </transition>
- <transition event="mousemove" target="drawing_rect" cond="toolbarStatechart.$in(toolbarStatechart._states.rect_selected)">
- <assign location="tDelta" expr="computeTDelta(eventStamp,_event.data)"/>
- <script>
- nodeBeingDrawn = svg.rect(_event.data.clientX,_event.data.clientY,1,1,{"fill":"red","stroke":"black"});
- </script>
- </transition>
- <transition event="mousemove" target="drawing_ellipse" cond="toolbarStatechart.$in(toolbarStatechart._states.ellipse_selected)">
- <assign location="tDelta" expr="computeTDelta(eventStamp,_event.data)"/>
- <script>
- nodeBeingDrawn = svg.ellipse(_event.data.clientX,_event.data.clientY,1,1,{"fill":"blue","stroke":"black"});
- </script>
- </transition>
- </state>
-
- <state id="drawing">
- <transition event="mouseup" target="idle">
- <script>
- api.addBehaviourToNode(nodeBeingDrawn);
- </script>
- </transition>
+ <state id="presentation_state">
- <!-- if escape keypress, kill the node -->
- <!--
- <transition event="keydown">
- </transition>
- -->
+ <state id="transform_tool_selected">
- <state id="drawing_rect">
- <transition event="mousemove" target="drawing_rect">
- <assign location="tDelta" expr="computeTDelta(eventStamp,_event.data)"/>
- <script>
- updateRect(nodeBeingDrawn,tDelta);
- </script>
- </transition>
+ <state id="no_nodes_selected">
+ <transition target="nodes_selected" event="NODES_SELECTED_WITH_CLICK"/>
+ <transition target="nodes_selected_history" event="NODES_SELECTED_WITH_DRAG"/>
+ </state>
+
+ <state id="nodes_selected" initial="ready_to_scale">
+
+ <history type="shallow" id="nodes_selected_history">
+ <target id="ready_to_scale"/>
+ </history>
+
+ <state id="ready_to_rotate">
+ <onentry>
+ <script>
+ rotationHandle.setAttributeNS(null,"visibility","visible");
+ </script>
+ </onentry>
+ <onexit>
+ <script>
+ rotationHandle.setAttributeNS(null,"visibility","hidden");
+ </script>
+ </onexit>
+ <target id="ready_to_scale" event="TOGGLE_TRANSFORM"/>
+ </state>
+
+ <state id="ready_to_scale">
+ <onentry>
+ <script>
+ scaleHandle.setAttributeNS(null,"visibility","visible");
+ </script>
+ </onentry>
+ <onexit>
+ <script>
+ scaleHandle.setAttributeNS(null,"visibility","hidden");
+ </script>
+ </onexit>
+ <target id="ready_to_rotate" event="TOGGLE_TRANSFORM"/>
+ </state>
+
+ <transition target="no_nodes_selected" event="CHECK_NODES" cond="selectedNodes.length === 0"/>
+
+ </state>
+
+ <transition target="circle_tool_selected" event="mousedown" cond="_event.data.target === rotationButtonHandle"/>
+ <transition target="rect_tool_selected" event="mousedown" cond="_event.data.target === scaleButtonHandle"/>
+ </state>
+
+ <state id="drawing_tool_selected">
+ <state id="circle_tool_selected">
+ <transition target="rect_tool_selected" event="mousedown" cond="_event.data.target === scaleButtonHandle"/>
+ </state>
+
+ <state id="rect_tool_selected">
+ <transition target="circle_tool_selected" event="mousedown" cond="_event.data.target === rotationButtonHandle"/>
+ </state>
+
+ <!-- TODO: add reference to transform button handle -->
+ <transition target="transform_tool_selected" event="mousedown" cond="_event.data.target === transformButtonHandle"/>
+ <state>
</state>
- <state id="drawing_ellipse">
- <transition event="mousemove" target="drawing_ellipse">
- <script>
- updateCircle(nodeBeingDrawn,eventStamp,_event.data);
- </script>
- </transition>
- </state>
+ <state id="processing_events">
+ <datamodel>
+ <data id="clickedNode"/>
+ </datamodel>
+
+ <state id="ready">
+ <onexit>
+ <assign location="clickedNode" expr="_event.data.target"/>
+ </onexit>
+
+ <transition target="after_mousedown_on_selected_nodes" event="mousedown"
+ cond="selectedNodes.indexOf(_event.data.target) !== -1 && !_event.data.shiftKey"/>
+
+ <transition target="after_mousedown_with_shift_key_on_selected_nodes" event="mousedown"
+ cond="selectedNodes.indexOf(_event.data.target) !== -1 && _event.data.shiftKey"/>
+
+ <transition target="after_mousedown_on_nonselected_nodes" event="mousedown"
+ cond="allNodes.indexOf(_event.data.target) !== -1 && !_event.data.shiftKey"/>
+
+ <transition target="after_mousedown_with_shift_key_on_nonselected_nodes" event="mousedown"
+ cond="allNodes.indexOf(_event.data.target) !== -1 && _event.data.shiftKey"/>
+
+ <transition target="after_mousedown_on_canvas" event="mousedown" cond="_event.data.target === canvas"/>
+ </state>
+
+ <state id="after_mousedown_on_selected_nodes">
+ <transition target="dragging" event="mousemove"/>
+
+ <transition target="ready" event="mouseup">
+ <send event="TOGGLE_TRANSFORM"/>
+ </transition>
+ </state>
+
+ <state id="after_mousedown_with_shift_key_on_selected_nodes">
+ <transition target="ready" event="mouseup">
+ <script>
+ //remove selected node from selection
+ selectedNodes.splice(selectedNodes.indexOf(clickNode),1);
+ </script>
+ <send event="CHECK_NODES"/>
+ </transition>
+
+ <!-- TODO: add marquee-drawing logic -->
+ <transition target="drawing_marquee" event="mousemove"/>
+ </state>
+
+ <!-- TODO: add dragging behaviour with the tdeltas and stuff -->
+ <state id="dragging">
+ <transition target="dragging" event="mousemove"/>
+ </state>
+
+ <state id="after_mousedown_on_nonselected_nodes">
+ <transition target="ready" event="mouseup">
+ <assign location="selectedNodes" expr="[clickedNode]"/>
+ <send event="NODES_SELECTED_WITH_CLICK"/>
+ </transition>
+ <transition target="dragging" event="mousemove">
+ <assign location="selectedNodes" expr="[clickedNode]"/>
+ <send event="NODES_SELECTED_WITH_DRAG"/>
+ <!-- TODO: add dragging logic -->
+ </transition>
+ </state>
+
+ <!-- TODO-->
+ <state id="after_mousedown_with_shift_key_on_nonselected_nodes">
+ <transition target="ready" event="mouseup">
+ <script>
+ selectedNodes.push(clickNode);
+ </script>
+ <send event="NODES_SELECTED_WITH_CLICK"/>
+ </transition>
+
+ <!-- TODO: add marquee-drawing logic -->
+ <transition target="drawing_marquee" event="mousemove"/>
+ </state>
+
+ <state id="after_mousedown_on_canvas">
+ <transition target="ready" event="mouseup">
+ <assign location="selectedNodes" expr="[]"/>
+ <send event="CHECK_NODES"/>
+ </transition>
+
+ <transition target="drawing_marquee" event="mousemove"/>
+ </state>
+
+ <!-- TODO: add marquee-drawing logic -->
+ <state id="drawing_marquee">
+ <transition target="ready" event="mouseup">
+ <script>
+ <!-- TODO: select what is inside marquee -->
+ </script>
+ <send event="CHECK_NODES"/>
+ </transition>
- </state>
+ <transition target="drawing_marquee" event="mousemove"/>
+ </state>
+ </state>
+ </parallel>
</scxml>