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 19:13:06 UTC
svn commit: r984129 [1/3] - in /commons/sandbox/gsoc/2010/scxml-js/trunk:
demo/hierarchical-layout-drag-and-drop/ demo/hierarchical_layout/
src/javascript/scxml/cgf/ src/javascript/scxml/cgf/backends/js/
src/javascript/scxml/cgf/layout/ src/javascript/...
Author: jbeard
Date: Tue Aug 10 17:13:04 2010
New Revision: 984129
URL: http://svn.apache.org/viewvc?rev=984129&view=rev
Log:
Merge branch 'debugger'
Conflicts:
src/javascript/scxml/cgf/Transformer.js
src/javascript/scxml/cgf/backends/js/AbstractEnumeratedStatechartGenerator.js
src/javascript/scxml/cgf/backends/js/StatePatternStatechartGenerator.js
src/xslt/backends/js/AbstractStatechartGenerator.xsl
src/xslt/ir-compiler/numberStatesAndTransitions.xsl
test/testBrowserTransform.html
Added:
commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/
commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/drag-and-drop.xml (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/test.html (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/
commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/display.html (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test.html (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.sh (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test_with_dom.html (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/ForceTransferLayout.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/LinkOptimizer.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/PrepLayout.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/graphics.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/CrossingModule.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/HierarchicalLayout.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/HorizontalPositioner.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/LayeringModule.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/NodeWrapper.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/shrinkwrapLayout.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/listener/
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/listener/GraphicalSimulator.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/listener/Logger.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/listener/api.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/util/geometry.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/util/svg.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/util/xpath.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/xslt/layout/
commons/sandbox/gsoc/2010/scxml-js/trunk/src/xslt/layout/addTransitionTargetIds.xsl (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/xslt/layout/computeAncestorOrSelfAndChildOfLCA.xsl (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/src/xslt/layout/scxmlToSVG.xsl (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/test/batik/
commons/sandbox/gsoc/2010/scxml-js/trunk/test/batik/testBatik.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/test/batik/testBatik.sh (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/test/batik/testBatik.svg (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/test/in.scxml
commons/sandbox/gsoc/2010/scxml-js/trunk/test/in.xml (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/test/randomLayout.js (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/test/testBrowserTransformAndGraphicalSimulator.html (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/test/testBrowserTransformAndLayoutWithDragAndDropBehaviour.html (with props)
commons/sandbox/gsoc/2010/scxml-js/trunk/test/testBrowserTransformAndLogListener.html (with props)
Modified:
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/backends/js/AbstractEnumeratedStatechartGenerator.js
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/backends/js/StatePatternStatechartGenerator.js
commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/main.js
commons/sandbox/gsoc/2010/scxml-js/trunk/src/xslt/backends/js/AbstractStatechartGenerator.xsl
commons/sandbox/gsoc/2010/scxml-js/trunk/test/testBrowserTransform.html
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/drag-and-drop.xml
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/drag-and-drop.xml?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/drag-and-drop.xml (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/drag-and-drop.xml Tue Aug 10 17:13:04 2010
@@ -0,0 +1,83 @@
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+ profile="ecmascript"
+ id="scxmlRoot"
+ initial="initial_default">
+
+ <script>
+ function computeTDelta(oldEvent,newEvent){
+ //summary:computes the offset between two events; to be later used with this.translate
+ var dx = newEvent.clientX - oldEvent.clientX;
+ var dy = newEvent.clientY - oldEvent.clientY;
+
+ return {'dx':dx,'dy':dy};
+ }
+
+ function translate(rawNode,tDelta){
+ var tl = rawNode.transform.baseVal;
+ var t = tl.numberOfItems ? tl.getItem(0) : rawNode.ownerSVGElement.createSVGTransform();
+ var m = t.matrix;
+ var newM = rawNode.ownerSVGElement.createSVGMatrix().translate(tDelta.dx,tDelta.dy).multiply(m);
+ t.setMatrix(newM);
+ tl.initialize(t);
+ return newM;
+ }
+ </script>
+
+ <datamodel>
+ <data id="firstEvent"/>
+ <data id="eventStamp"/>
+ <data id="tDelta"/>
+ <data id="rawNode"/>
+ <data id="linkNodeList"/>
+ </datamodel>
+
+ <state id="initial_default">
+ <transition event="init" target="idle">
+ <assign location="rawNode" expr="_event.data.rawNode"/>
+ <assign location="linkNodeList" expr="_event.data.linkNodeList"/>
+ <assign location="LinkOptimizer" expr="_event.data.LinkOptimizer"/>
+ </transition>
+ </state>
+
+ <state id="idle">
+ <transition event="mousedown" target="dragging">
+ <assign location="firstEvent" expr="_event.data"/>
+ <assign location="eventStamp" expr="_event.data"/>
+
+ <script>
+ //make invisible
+ linkNodeList.forEach(function(n){
+ n.visualObject._rawNode.style.visibility = "hidden";
+ })
+ </script>
+ </transition>
+ </state>
+
+ <state id="dragging">
+
+ <transition event="mouseup" target="idle">
+ <script>
+ //re-layout the edges
+ linkNodeList.forEach(function(n){
+ LinkOptimizer.optimizeConnectionPorts(n);
+ });
+
+ //make visible
+ linkNodeList.forEach(function(n){
+ n.visualObject._rawNode.style.visibility = "visible";
+ })
+ </script>
+ </transition>
+
+ <transition event="mousemove" target="dragging">
+ <assign location="tDelta" expr="computeTDelta(eventStamp,_event.data)"/>
+ <script>
+ translate(rawNode,tDelta);
+ </script>
+ <assign location="eventStamp" expr="_event.data"/>
+ </transition>
+ </state>
+
+</scxml>
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/drag-and-drop.xml
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/test.html
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/test.html?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/test.html (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/test.html Tue Aug 10 17:13:04 2010
@@ -0,0 +1,78 @@
+<html>
+ <head>
+ <script src="../../lib/js/requirejs/require.js" type="text/javascript">true;</script>
+ <script src="../../lib/js/requirejs/require/xml.js" type="text/javascript">true;</script>
+ <script src="../../test/testHelpers.js" type="text/javascript">true;</script>
+ <script>
+ var resultText;
+
+ require(
+ {
+ "baseUrl":"/"
+ },
+ [ "src/javascript/scxml/cgf/SCXMLCompiler",
+ "xml!test/kitchen_sink/KitchenSink.xml",
+ "src/javascript/scxml/cgf/layout",
+ "xml!demo/hierarchical-layout-drag-and-drop/drag-and-drop.xml",
+ "src/javascript/scxml/cgf/util/xpath",
+ "src/javascript/scxml/cgf/layout/LinkOptimizer"],
+
+
+
+ function(compiler,scxmlDoc,layout,dragAndDropBehaviourSCXMLDoc,xpath,LinkOptimizer){
+
+ var layoutInfo = layout.applyHierarchicalLayout(scxmlDoc);
+ var svgDoc = layoutInfo.svgDoc,
+ linkNodeList = layoutInfo.linkNodeList,
+ entityNodeList = layoutInfo.entityNodeList;
+
+ //enough graphical stuff, let's do some compilation
+ compiler.compile({
+ inFiles:[dragAndDropBehaviourSCXMLDoc],
+ backend:"state",
+ beautify:true,
+ verbose:false,
+ log:false,
+ ie:false,
+ genListenerHooks:true
+ }, function(scArr){
+ var dragAndDropBehaviourTransformedJs = scArr[0];
+
+ console.log(dragAndDropBehaviourTransformedJs);
+
+ //hook up graphical node drag and drop behaviour
+ eval(dragAndDropBehaviourTransformedJs);
+
+ DragAndDropStatechartExecutionContext = StatechartExecutionContext;
+
+ var graphEntityNodes = xpath.query("//svg:g[@c:graphEntity = 'true']",svgDoc.documentElement)
+ graphEntityNodes.forEach(function(groupNode){
+ var compiledStatechartInstance = new DragAndDropStatechartExecutionContext();
+
+ //initialize
+ compiledStatechartInstance.initialize();
+
+ console.log(groupNode);
+
+ //pass in reference to rect
+ compiledStatechartInstance.init({rawNode:groupNode,linkNodeList:linkNodeList,LinkOptimizer:LinkOptimizer});
+
+ //hook up DOM events
+ ["mousedown","mouseup","mousemove"].forEach(function(eventName){
+ groupNode.addEventListener(eventName,
+ function(e){
+ e.preventDefault();
+ compiledStatechartInstance[eventName](e);
+ e.stopPropagation();
+ },
+ false);
+ });
+ });
+ });
+ }
+ );
+</script>
+ </head>
+ <body/>
+</html>
+
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical-layout-drag-and-drop/test.html
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/display.html
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/display.html?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/display.html (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/display.html Tue Aug 10 17:13:04 2010
@@ -0,0 +1,4 @@
+<html>
+ <head></head>
+ <body style="width:98%;height:98%"></body>
+</html>
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/display.html
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test.html
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test.html?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test.html (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test.html Tue Aug 10 17:13:04 2010
@@ -0,0 +1,26 @@
+<html>
+ <head>
+ <script src="../../lib/js/requirejs/require.js" type="text/javascript">true;</script>
+ <script src="../../lib/js/requirejs/require/xml.js" type="text/javascript">true;</script>
+ <script src="../../lib/js/requirejs/require/text.js" type="text/javascript">true;</script>
+
+ <script type="text/javascript">
+ var resultText;
+
+ require(
+ {
+ "baseUrl":"/"
+ },
+ [
+ "xml!test/kitchen_sink/KitchenSink.xml",
+ "src/javascript/scxml/cgf/layout"],
+
+ function(scxmlDoc,layout){
+ layout.applyHierarchicalLayout(scxmlDoc);
+ }
+ );
+ </script>
+
+ </head>
+ <body style="width:99%;height:98%"></body>
+</html>
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test.html
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.js
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.js?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.js (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.js Tue Aug 10 17:13:04 2010
@@ -0,0 +1,40 @@
+console = {
+ log : print,
+ debug : print,
+ error : print
+}
+
+var absoluteScriptDir = arguments[0];
+var pathToRequireJsDir = absoluteScriptDir + "/lib/js/requirejs/";
+
+load(pathToRequireJsDir + "require.js");
+load(pathToRequireJsDir + "require/rhino.js");
+load(pathToRequireJsDir + "require/text.js");
+load(pathToRequireJsDir + "require/xml.js");
+
+//bootstrap require.js
+require({
+ baseUrl : absoluteScriptDir
+ },
+ function(){
+ require(
+ [
+ "xml!test/kitchen_sink/KitchenSink.xml",
+ "src/javascript/scxml/cgf/layout",
+ "src/javascript/scxml/cgf/util/xml",
+ "src/javascript/scxml/cgf/util/file"
+ ],
+
+
+ function(scxmlDoc,layout,xml,file){
+
+ var layoutInfo = layout.applyHierarchicalLayout(scxmlDoc);
+ var svgDoc = layoutInfo.svgDoc;
+
+ //serialize to SVG, PDF, etc.
+ file.writeFile(xml.serializeToString(svgDoc),"out.svg");
+
+ }
+ )
+ }
+);
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.sh
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.sh?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.sh (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.sh Tue Aug 10 17:13:04 2010
@@ -0,0 +1,31 @@
+#!/bin/bash
+dn=`dirname $0`
+abspath=`cd $dn; cd ../../; pwd`
+
+java -cp \
+../../lib/java/batik-anim.jar:\
+../../lib/java/batik-awt-util.jar:\
+../../lib/java/batik-bridge.jar:\
+../../lib/java/batik-codec.jar:\
+../../lib/java/batik-css.jar:\
+../../lib/java/batik-dom.jar:\
+../../lib/java/batik-extension.jar:\
+../../lib/java/batik-ext.jar:\
+../../lib/java/batik-gui-util.jar:\
+../../lib/java/batik-gvt.jar:\
+../../lib/java/batik-parser.jar:\
+../../lib/java/batik-script.jar:\
+../../lib/java/batik-svg-dom.jar:\
+../../lib/java/batik-svggen.jar:\
+../../lib/java/batik-swing.jar:\
+../../lib/java/batik-transcoder.jar:\
+../../lib/java/batik-util.jar:\
+../../lib/java/batik-xml.jar:\
+../../lib/java/commons-cli.jar:\
+../../lib/java/js.jar:\
+../../lib/java/serializer.jar:\
+../../lib/java/xalan.jar:\
+../../lib/java/xercesImpl.jar:\
+../../lib/java/xml-apis.jar:\
+../../lib/java/xml-apis-ext.jar:\
+ org.mozilla.javascript.tools.shell.Main -debug testBatik.js $abspath
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.sh
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/testBatik.sh
------------------------------------------------------------------------------
svn:executable = *
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test_with_dom.html
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test_with_dom.html?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test_with_dom.html (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test_with_dom.html Tue Aug 10 17:13:04 2010
@@ -0,0 +1,98 @@
+<html>
+ <head>
+ <style type="text/css">
+ body {
+ height:95%;
+ }
+
+ textarea{
+ width:100%;
+ height:85%;
+ }
+
+ div{ height:100% }
+ </style>
+ <script type="text/javascript">
+ if((typeof console == "undefined") || !console.log){
+ console = {log : function(str){}}
+ }
+ </script>
+ <script src="../../lib/js/requirejs/require.js" type="text/javascript">true;</script>
+ <script src="../../lib/js/requirejs/require/xml.js" type="text/javascript">true;</script>
+ <script src="../../lib/js/requirejs/require/text.js" type="text/javascript">true;</script>
+
+ </head>
+ <body>
+ <textarea id="scxml_input"></textarea>
+ <br/>
+ <button id="LONGEST_PATH_LAYERING_BOTTOM_UP">Hierarchical Layout with Longest Path Layering Bottom Up Heuristic</button>
+ <br/>
+ <button id="LONGEST_PATH_LAYERING_TOP_DOWN">Hierarchical Layout with Longest Path Layering Top Down Heuristic</button>
+ <br/>
+ <button id="MINIMUM_WIDTH_LAYERING">Hierarchical Layout with Longest Path Minimum Width Layering Heuristic</button>
+ <script type="text/javascript">
+ //get DOM elements
+ var scxml_input = document.getElementById("scxml_input");
+ var LONGEST_PATH_LAYERING_BOTTOM_UP_button = document.getElementById("LONGEST_PATH_LAYERING_BOTTOM_UP");
+ var LONGEST_PATH_LAYERING_TOP_DOWN_button = document.getElementById("LONGEST_PATH_LAYERING_TOP_DOWN");
+ var MINIMUM_WIDTH_LAYERING_button = document.getElementById("MINIMUM_WIDTH_LAYERING");
+
+ var resultText;
+
+ require(
+ {
+ "baseUrl":"/"
+ },
+ [
+ "xml!test/kitchen_sink/KitchenSink.xml",
+ "src/javascript/scxml/cgf/layout",
+ "src/javascript/scxml/cgf/layout/hierarchical/HierarchicalLayout",
+ "src/javascript/scxml/cgf/util/xml"],
+
+ function(scxmlDoc,layout,HierarchicalLayout,xmlUtils){
+
+ //set the initial XML content in the textarea
+ var xmlString = xmlUtils.serializeToString(scxmlDoc);
+ scxml_input.textContent = xmlString;
+
+ //main callback on buttons
+ function applyHierLayout(heuristic,domAttachPoint,scxmlInputDoc){
+ layout.applyHierarchicalLayout(scxmlInputDoc,{heuristic : heuristic},domAttachPoint)
+ }
+
+ function parseTextareaPopupWindowAndApplyLayout(heuristic){
+ //parse from textarea dom
+ var scxmlInputDoc = xmlUtils.parseFromString(scxml_input.textContent);
+
+ //open new window and get DOM attach point
+ var w = window.open("display.html");
+ //debugger;
+ w.addEventListener("DOMContentLoaded",function(e){
+ var domAttachPoint = w.document.body;
+
+ //debugger;
+ //apply layout
+ console.log("dom attach point",domAttachPoint)
+ applyHierLayout(heuristic,domAttachPoint,scxmlInputDoc);
+ },true);
+
+ }
+
+ //hook up event listeners
+ LONGEST_PATH_LAYERING_BOTTOM_UP_button.addEventListener("mousedown",function(e){
+ parseTextareaPopupWindowAndApplyLayout(HierarchicalLayout.HEURISTICS_ENUM.LONGEST_PATH_LAYERING_BOTTOM_UP)
+ },true);
+ LONGEST_PATH_LAYERING_TOP_DOWN_button.addEventListener("mousedown",function(e){
+ parseTextareaPopupWindowAndApplyLayout(HierarchicalLayout.HEURISTICS_ENUM.LONGEST_PATH_LAYERING_TOP_DOWN)
+ },true);
+ MINIMUM_WIDTH_LAYERING_button.addEventListener("mousedown",function(e){
+ parseTextareaPopupWindowAndApplyLayout(HierarchicalLayout.HEURISTICS_ENUM.MINIMUM_WIDTH_LAYERING)
+ },true);
+
+ }
+ );
+ </script>
+
+
+ </body>
+</html>
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/demo/hierarchical_layout/test_with_dom.html
------------------------------------------------------------------------------
svn:eol-style = native
Modified: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/backends/js/AbstractEnumeratedStatechartGenerator.js
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/backends/js/AbstractEnumeratedStatechartGenerator.js?rev=984129&r1=984128&r2=984129&view=diff
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/backends/js/AbstractEnumeratedStatechartGenerator.js (original)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/backends/js/AbstractEnumeratedStatechartGenerator.js Tue Aug 10 17:13:04 2010
@@ -11,7 +11,9 @@ require.def(
"xml!src/xslt/ir-compiler/enumerateEvents.xsl",
"xml!src/xslt/ir-compiler/addEventRegularExpressions.xsl",
"xml!src/xslt/ir-compiler/expandStarEvent.xsl",
- "xml!src/xslt/ir-compiler/numberStatesAndTransitions.xsl" ],
+ "xml!src/xslt/ir-compiler/numberStatesAndTransitions.xsl"
+ "xml!src/xslt/layout/addTransitionTargetIds.xsl" ],
+
function(
AbstractStatechartGenerator,
flattenTransitions,
@@ -21,7 +23,8 @@ require.def(
enumerateEvents,
addEventRegularExpressions,
expandStarEvent,
- numberStatesAndTransitions
+ numberStatesAndTransitions,
+ addTransitionTargetIds
){
return {
"transformations" : AbstractStatechartGenerator.transformations.concat([flattenTransitions,
@@ -31,7 +34,8 @@ require.def(
enumerateEvents,
addEventRegularExpressions,
expandStarEvent,
- numberStatesAndTransitions] )
+ numberStatesAndTransitions,
+ addTransitionTargetIds] )
};
}
);
Modified: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/backends/js/StatePatternStatechartGenerator.js
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/backends/js/StatePatternStatechartGenerator.js?rev=984129&r1=984128&r2=984129&view=diff
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/backends/js/StatePatternStatechartGenerator.js (original)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/backends/js/StatePatternStatechartGenerator.js Tue Aug 10 17:13:04 2010
@@ -9,6 +9,7 @@ require.def(
"xml!src/xslt/ir-compiler/enumerateEvents.xsl",
"xml!src/xslt/ir-compiler/addEventRegularExpressions.xsl",
"xml!src/xslt/ir-compiler/expandStarEvent.xsl",
+ "xml!src/xslt/layout/addTransitionTargetIds.xsl",
"xml!build/StatePatternStatechartGenerator_combined.xsl" //preprocessed stylesheet
],
@@ -19,12 +20,14 @@ require.def(
enumerateEvents,
addEventRegularExpressions,
expandStarEvent,
+ addTransitionTargetIds,
StatePatternStatechartGenerator
){
return {
"transformations" : AbstractStatechartGenerator.transformations.concat([
appendTransitionInformation,
+ addTransitionTargetIds,
copyEnumeratedEventTransitions,
enumerateEvents,
addEventRegularExpressions,
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout.js
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout.js?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout.js (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout.js Tue Aug 10 17:13:04 2010
@@ -0,0 +1,84 @@
+/*
+This module provides a convenient front-end to one or more layout algorithms.
+*/
+require.def("src/javascript/scxml/cgf/layout",
+[
+ "xml!src/xslt/ir-compiler/generateUniqueStateIds.xsl",
+ "xml!src/xslt/ir-compiler/generateUniqueInitialStateIds.xsl",
+ "xml!src/xslt/ir-compiler/normalizeInitialStates.xsl",
+ "xml!src/xslt/ir-compiler/nameTransitions.xsl",
+ "xml!src/xslt/ir-compiler/splitTransitionTargets.xsl",
+ "xml!src/xslt/ir-compiler/computeLCA.xsl",
+ "xml!src/xslt/layout/computeAncestorOrSelfAndChildOfLCA.xsl",
+ "xml!src/xslt/layout/addTransitionTargetIds.xsl",
+ "xml!src/xslt/layout/scxmlToSVG.xsl",
+ "src/javascript/scxml/cgf/Transformer",
+ "src/javascript/scxml/cgf/layout/PrepLayout",
+ "src/javascript/scxml/cgf/layout/hierarchical/NodeWrapper",
+ "src/javascript/scxml/cgf/layout/hierarchical/HierarchicalLayout",
+ "src/javascript/scxml/cgf/layout/shrinkwrapLayout",
+ "src/javascript/scxml/cgf/layout/LinkOptimizer"],
+
+function(
+ nameTransitions,generateUniqueStateIds,generateUniqueInitialStateIds,normalizeInitialStates,
+ splitTransitionTargets,computeLCA,computeAncestorOrSelfAndChildOfLCA,addTransitionTargetIds,
+ scxmlToSvgXsl,
+ Transformer,
+ PrepLayout,NodeWrapper,HierarchicalLayout,shrinkwrapLayout,
+ LinkOptimizer){
+
+ return {
+ applyHierarchicalLayout : function(scxmlDoc,options,domAttachPoint){
+ var ir = Transformer(scxmlDoc,
+ [generateUniqueStateIds,normalizeInitialStates,generateUniqueInitialStateIds,
+ nameTransitions,splitTransitionTargets,computeLCA,
+ computeAncestorOrSelfAndChildOfLCA,addTransitionTargetIds
+ ],
+ null,"xml"); //transform to IR
+
+ var svgDoc = Transformer(ir,[scxmlToSvgXsl],null,"xml"); //transform scxml to non-laid out, svg graphical representation
+
+ //console.dirxml(ir);
+ svgDoc = PrepLayout.bootSVGDOM(svgDoc,domAttachPoint);
+
+ var lists = PrepLayout.scxmlToEntityNodeListAndLinkList(ir,svgDoc);
+ var rootEntity = lists.rootEntity,
+ entityNodeList = lists.entityList,
+ linkNodeList = lists.linkNodeList;
+
+ //perform a preorder traversal
+ //lay out all composites
+ //for each composite node, do hierarchical layout, followed by shrinkwrap layout of all children
+ //FIXME: we really need to know root nodes for this to be effective...
+ //TODO: try applying force-based layout as well...
+ //HierarchicalLayout.hierarchicalLayout(entityNodeList,linkNodeList);
+
+ // Initilize the node wrapper class attributes
+ //TODO: move this into hier layout
+ NodeWrapper.initilizeNodeWrapper()
+ HierarchicalLayout.recursivelyApplyHierarchicalLayout(rootEntity,
+ {heuristic : HierarchicalLayout.HEURISTICS_ENUM.LONGEST_PATH_LAYERING_BOTTOM_UP })
+
+ //shrinkwrap layout
+ console.log("Recursively applying shrinkwrap layout")
+ shrinkwrapLayout.recursivelyApplyShrinkwrapLayout(rootEntity)
+
+ //optimize links again
+ //linkNodeList.forEach(LinkOptimizer.optimizeConnectionPorts)
+ LinkOptimizer.optimizeLinks(linkNodeList);
+
+ //recentering push
+ console.log("Recentering")
+ rootEntity.visualObject.moveTo(0,0);
+
+ //adjust canvas viewbox
+ rootEntity.visualObject.setCanvasViewBox(rootEntity.visualObject.getBBoxInCanvasSpace());
+
+ return {
+ svgDoc:svgDoc,
+ linkNodeList:linkNodeList,
+ entityNodeList:entityNodeList
+ };
+ }
+ }
+});
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/ForceTransferLayout.js
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/ForceTransferLayout.js?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/ForceTransferLayout.js (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/ForceTransferLayout.js Tue Aug 10 17:13:04 2010
@@ -0,0 +1,261 @@
+/*
+Based on ForceTransfer.py by Denis Dube
+
+Applies force to all nodes that are too close to each other
+Loops until a stable configuration is reached
+Times out after 50 iterations, to avoid depriving the user of interactivity for too long.
+*/
+
+//require.def("src/javascript/scxml/cgf/debugger/layout/ForceTransfer",
+
+ForceTransfer = {
+ //need to be given graphical objects
+ applyLayout : function(options,svgGraphObjects,dc,statusText){
+
+ options = options || {
+ autoApply : false,
+ useStatusBar : false,
+ minNodeDist : 5,
+ minLinkDist : 5,
+ minControlDist : 5,
+ seperationForce : 1,
+ animationTime : 0,
+ maxAnimIterations : 15,
+ maxIterations : 50,
+ borderDistance : 30
+ };
+
+ //TODO: convert SVG objects to abstract graphical objects
+ var nodes = svgGraphObjects.nodes.map(function(n){
+ //get the bounding box from the object, I guess in Canvas coordinates
+ //although local coordinates might actually be more appropriate...
+ var bbox = n.getBBox(); //getBBoxInCanvasSpace(n);
+ var x = bbox.x, y = bbox.y, width = bbox.width, height=bbox.height;
+ var center = [ x + width/2, y + height/2 ]
+
+ return new NodeObject( n, center, [width,height], options.minNodeDist, [x,y] )
+ });
+ var edges = svgGraphObjects.edges.map(function(e){
+ var bbox = n.getBBox(); //getBBoxInCanvasSpace(e);
+ var x = bbox.x, y = bbox.y, width = bbox.width, height=bbox.height;
+ var center = [ x + width/2, y + height/2 ]
+
+ return new EdgeObject( obj, center, options.minLinkDist )
+ });
+ /*
+ var controlPoints = svgGraphObjects.controlPoints.map(function(cp){
+ new ControlPoint( c[i:i+2], options.minControlDist, itemHandler, i, dc )
+ });
+ */
+
+ graphicalObjectList = nodes.concat(edges);
+
+ /*
+ Sorts the nodes according to their distance from the origin (0,0)
+ This can have a large impact on performance, especially as the number
+ of objects in contact with one another goes up.
+ */
+ graphicalObjectList = graphicalObjectList.sort(function(n1,n2){return n1.distance > n2.distance});
+
+ var totalNodes = graphicalObjectList.length;
+
+ var isLayoutStable = false
+
+ // Keep at it till the layout is stable
+ var i = 0;
+ while(! isLayoutStable ){
+ isLayoutStable = true // Optimism is good...
+ calculationLoop()
+
+ if( i > options.maxIterations ){
+ break;
+ }
+
+ i++;
+ }
+
+
+ // Hijack the status bar to show what the FTA is doing...
+ if( i >= options.maxIterations ){
+ statusText.textValue = "FTA halted at max iterations, layout unstable";
+ }
+ else{
+ statusText.textValue = "FTA needed "+i+" iterations to find stable layout";
+ }
+
+ // Keep the whole thing in the viewable area of the canvas
+ minX = Math.min.apply(this,graphicalObjectList.map(function(o){return (o.topLeftPos && o.topLeftPos[0]) || o.pos[0]}));
+ minY = Math.min.apply(this,graphicalObjectList.map(function(o){return (o.topLeftPos && o.topLeftPos[1]) || o.pos[1]}));
+
+ minX = minX < options.borderDistance ? Math.abs(minX) + options.borderDistance : 0;
+ minY = minY < options.borderDistance ? Math.abs(minY) + options.borderDistance : 0;
+
+ // Push on it!
+ //graphicalObjectList.forEach(function(n){n.recenteringPush(minX, minY)});
+
+ // All that moving stuff around can mess up the connections...
+ //TODO
+ /*
+ if( selection )
+ optimizeConnectionPorts(atom3i, entityList=selection )
+ else
+ optimizeConnectionPorts(atom3i, doAllLinks=True )
+ */
+
+ function calculationLoop(){
+
+ // Go through all the nodes, and find the overlap forces
+ graphicalObjectList.forEach(function(n1){
+ graphicalObjectList.forEach(function(n2){
+ if(n1!==n2) calculateForce( n1, n2 );
+ })});
+
+ // Go through all the nodes and apply the forces to the positions
+ graphicalObjectList.forEach(function(n){n.commitForceApplication()});
+ }
+
+
+ function calculateForce( n1,n2 ){
+ /*
+ Evaluates distances betweens nodes (ie: do they overlap) and
+ calculates a force sufficient to pry them apart.
+ */
+
+ // Absolute distance along X and Y vectors between the nodes
+ var pointA = n1.pos
+ var pointB = n2.pos
+
+ var dx = Math.abs( pointB[0] - pointA[0] )
+ var dy = Math.abs( pointB[1] - pointA[1] )
+
+ // Zero division error prevention measures
+ if (dx == 0.0) dx = 0.1
+ if (dy == 0.0) dy = 0.1
+
+ // Node-Node Distances
+ var dist = Math.sqrt(dx*dx+dy*dy)
+
+ // Normalized-Vector
+ var norm = [ dx / dist , dy / dist ]
+
+ // Overlap due to size of nodes
+ var sizeA = n1.size
+ var sizeB = n2.size
+ var sizeOverlap = [ ( sizeA[0] + sizeB[0] ) / 2 , ( sizeA[1] + sizeB[1] ) / 2 ]
+
+ // Desired distance with resulting force
+ var minSeperationDist = Math.min( n1.seperationDist,n2.seperationDist )
+ var d1 = (1.0 / norm[0]) * (sizeOverlap[0] + minSeperationDist)
+ var d2 = (1.0 / norm[1]) * (sizeOverlap[1] + minSeperationDist)
+ var forceMagnitude = options.seperationForce * ( dist - Math.min(d1,d2) )
+
+ // The force should be less than -1 (or it won't be having much of an effect)
+ if (forceMagnitude < -1){
+ var force = [ forceMagnitude * norm[0], forceMagnitude * norm[1] ]
+
+ // Maximize compactness by only pushing nodes along a single axis
+ if( force[0] > force[1] )
+ force[0] = 0
+ else
+ force[1] = 0
+
+ // Determine the direction of the force
+ var direction = [ 1, 1 ]
+ if( pointA[0] > pointB[0] ) direction[0] = -1
+ if( pointA[1] > pointB[1] ) direction[1] = -1
+
+ // Add up the forces to the two interacting objects
+ n1.incrementForce( force )
+ n2.incrementForce( [ -force[0], -force[1] ] )
+
+ // If a force was applied this iteration, definately not stable yet
+ isLayoutStable = false
+ }
+ }
+
+ function GraphicalObject(visualObject, pos, size, seperationDist){
+ /*
+ A convenient class to store just the information necessary for the
+ application of the force transfer algorithm.
+ */
+
+ this.visualObject = visualObject
+ this.pos = pos
+ this.size = size
+ this.force = [0,0]
+ this.seperationDist = seperationDist
+ this.distance = Math.sqrt( pos[0]*pos[0] + pos[1]*pos[1] );
+
+ this.incrementForce = function(force){
+ this.force = force
+ }
+
+ this.commitForceApplication = function(){
+ /*
+ Moves the object to the origin, then to the position it is forced to
+ Forces are then reset
+ */
+
+ translate( this.visualObject, {dx:this.force[0],dy:this.force[1]} )
+ this.pos = [ this.pos[0] + this.force[0], this.pos[1] + this.force[1] ]
+ this.force = [0,0]
+ }
+
+ this.recenteringPush = function(x, y){
+ // Puts the object back onto the canvas if it got forced off
+ translate( this.visualObject, {dx:x,dy:y} )
+ }
+
+ }
+
+ function NodeObject(visualObject, pos, size, seperationDist, topLeftPos){
+ // Regular node entity with position and size attributes
+
+ this.topLeftPos = topLeftPos
+
+ GraphicalObject.apply(this,[visualObject, pos, size, seperationDist]);
+
+ }
+
+ function EdgeObject(visualObject, pos, seperationDist){
+ /*
+ Idea for improvement: find the label/drawing attached to the center of the
+ edge, and use its size instead of treating this as an object with no size.
+ */
+
+ GraphicalObject.apply(this,[visualObject, pos, [1,1],seperationDist]);
+ }
+
+ function ControlPoint(pos, seperationDist,itemHandler,index){
+ /*
+ Control point is merely a point along the edge, thus it needs a customized
+ approach for moving it around. It also has no real size concept.
+ */
+
+ GraphicalObject.apply(this,[None, pos, [1,1],seperationDist]);
+
+ this.itemHandler = itemHandler
+ this.index = index
+
+ /*
+ this.recenteringPush = function(x, y):
+ // No need for this since the Edge 'Move' method handles it
+ return
+ cCoords = this.dc.coords( this.itemHandler )
+ cCoords[this.index] += x
+ cCoords[this.index+1] += y
+ this.dc.coords( * [this.itemHandler] + cCoords )
+ */
+
+ this.commitForceApplication = function(){
+ var cCoords = dc.coords( this.itemHandler )
+ cCoords[this.index] += this.force[0]
+ cCoords[this.index+1] += this.force[1]
+ dc.coords( * [this.itemHandler] + cCoords )
+
+ this.pos = [ this.pos[0] + this.force[0], this.pos[1] + this.force[1] ]
+ this.force = [0,0]
+ }
+ }
+ }
+}
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/ForceTransferLayout.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/LinkOptimizer.js
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/LinkOptimizer.js?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/LinkOptimizer.js (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/LinkOptimizer.js Tue Aug 10 17:13:04 2010
@@ -0,0 +1,162 @@
+require.def("src/javascript/scxml/cgf/layout/LinkOptimizer",
+["src/javascript/scxml/cgf/util/geometry"],
+function(geometry){
+ function optimizeConnectionPorts(linkNode){
+ /*
+ given a set of linkNodes (preferably semantic objects to start out)
+ for each linkNode find what entities he connects to (in and out)
+ for each of these entities
+ snap to the nearest connection point on that entity
+ */
+
+ //find the closest two connection points
+
+ var startAndEndInfo = linkNode.getClosestStartAndEndConnectionPoints();
+
+ linkNode.visualObject.setStartPoint(startAndEndInfo.fromConnector);
+
+ linkNode.visualObject.setEndpoint(startAndEndInfo.toConnector);
+
+ }
+
+ function optimizeLinks(selectedLinks, setSmooth, setCurved, curveDirection){
+ /*
+ Optimizes the links associated with the nodes in entityList or all the nodes
+ in the graph if entityList is not provided.
+ The links are set straight, with the endpoints at the nearest connectors of
+ the nodes they connect.
+ Optionally, additional control points can be added to make a smooth arrow,
+ and give it some curvature (setCurved is a pixel distance perpendicular to
+ what would have been a straight arrow ).
+ It is possible to pass a list of link objects directly, and optimize those.
+ It is also possible to specify the direction of the curve, Random=-1, Right=0, Left=1
+
+ Created July 25, 2004 by Denis Dube
+ */
+ setSmooth = setSmooth || true;
+ setCurved = setCurved || 10;
+ selectedLinks= selectedLinks || [];
+ curveDirection = curveDirection || -1;
+
+ // Optimize all the selected links
+ //FIXME: s/obj/ln
+ selectedLinks.forEach(function(ln){
+
+ // Optimize the end point connection ports
+ optimizeConnectionPorts(ln)
+
+ // Find all the entities that the edge is linking to
+ // (general enough to handle hyperedges)
+ var graphicalObjectsFrom = ln.inConnections.map(function(en){return en.visualObject})
+ var graphicalObjectsTo = ln.outConnections.map(function(en){return en.visualObject})
+
+ // Edge with 2 endpoints
+ if(graphicalObjectsFrom.length == 1 && graphicalObjectsTo.length == 1){
+
+ // Edge with both endpoints on one object
+ if( graphicalObjectsFrom[0] == graphicalObjectsTo[0] ){
+ optimizeSelfLink(ln, graphicalObjectsFrom[0], setSmooth, setCurved)
+ }
+
+ // Regular edge
+ else{
+ optimizeRegularLink( ln, graphicalObjectsFrom[0], graphicalObjectsTo[0],
+ setSmooth, setCurved, curveDirection)
+ }
+ }
+ // Hyper-edge with multiple endpoints
+ else{
+ optimizerHyperLink( ln, graphicalObjectsFrom, graphicalObjectsTo,
+ setSmooth, setCurved, curveDirection )
+ }
+ })
+ }
+
+ function optimizeRegularLink( interObj, fromObj, toObj, setSmooth, curvature, curveDirection ){
+ /* Optimizes one edge with 2 endpoints in 2 different objects */
+
+ // Find the optimally near connector points
+ //TODO: here
+ var connectorInfo = fromObj.getClosestConnectorToGraphicalObject( toObj,interObj.visualObject );
+ var inPoint = connectorInfo.toConnector, outPoint = connectorInfo.fromConnector;
+
+ var newCenter = geometry.getMidpoint2D( inPoint, outPoint)
+
+ // Move the intermediate object into the new center point
+ var g = interObj.visualObject;
+ var oldCenter = g.getCenterCoord()
+ var dx = newCenter.x - oldCenter.x
+ var dy = newCenter.y - oldCenter.y
+
+ // Add a bit of curvature
+ var finalCenter;
+ if( curvature ){
+ var v = curvinator( inPoint, outPoint, curvature, curveDirection )
+ dx += v[0]
+ dy += v[1]
+ finalCenter = [ newCenter[0] + v[0], newCenter[1] + v[1] ]
+ }
+ else{
+ finalCenter = newCenter
+ }
+
+ // Move the intermediate object
+ g.move( dx, dy )
+
+ }
+
+ function curvinator( p1, p2, curveAmount, curveDirection ){
+ /*
+ Returns a vector that is orthongonal to the p1-p2 vector with length curveAmount
+ */
+ var v = [ - ( p1.y - p2.y ), p1.x - p2.x ]
+ var d = geometry.vectorLength2D( v )
+ d = d || 1
+
+ // Direction of the curvature bulge is random
+ if( curveDirection == -1 &&
+ (Math.round(Math.random()) || curveDirection == 1 )){
+ curveAmount = -curveAmount
+ }
+
+ // Normalized orthogonal vector times the curvature bulge
+ v = [ v[0] * curveAmount / d, v[1] * curveAmount / d ]
+
+ return v
+ }
+
+ function optimizeSelfLink( interObj, selfObj, setSmooth, curvature ){
+ /*
+ * Makes a nice loop positioned at upper right-hand corner.
+ */
+
+ // Require this much distance between link and entity
+ var labelMinDistY = 20,
+ labelMinDistX = 20,
+ bezierMinDistY = 40,
+ bezierMinDistX = 40;
+
+ // Add additional minimum distance according to link object size
+ var g = interObj.visualObject;
+
+ // Add additional minimum distance according to entity object size
+ var box = selfObj.getBBoxInCanvasSpace();
+
+ g.setStartPoint({x:box.width/2 + box.x,y:box.y})
+ g.setEndpoint({x:box.width + box.x,y:box.height/2 + box.y})
+
+ g.setControlPoint({x:box.width + box.x + bezierMinDistX,y:box.y - bezierMinDistY})
+ g.moveLabelTo(box.width + box.x + labelMinDistX,box.y - labelMinDistY)
+
+ }
+
+ function optimizerHyperLink( interObj, objectsFrom, objectsTo, setSmooth, curvature, curveDirection, newCenter ){
+ throw new Error("Hyperlinks are not supported yet.");
+ }
+
+ return {
+ optimizeConnectionPorts : optimizeConnectionPorts,
+ optimizeLinks: optimizeLinks
+ }
+
+})
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/LinkOptimizer.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/PrepLayout.js
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/PrepLayout.js?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/PrepLayout.js (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/PrepLayout.js Tue Aug 10 17:13:04 2010
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2010 jacob.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+
+require.def("src/javascript/scxml/cgf/layout/PrepLayout",
+[
+ "src/javascript/scxml/cgf/util/svg",
+ "src/javascript/scxml/cgf/Transformer",
+ "src/javascript/scxml/cgf/layout/graphics",
+ "src/javascript/scxml/cgf/util/xpath" ],
+
+function(commonSVG,Transformer,graphics,xpath){
+
+
+ //FIXME: this method is something that will need to be moved out as well, as we may use batik when running under Rhino
+ var bootSVGDOM = require.isBrowser?
+ function(svgDoc,attachPointNode){
+ attachPointNode = attachPointNode || document.body;
+
+ //in order to use this SVG document, it needs to be rendered, so we append the contents to the current document,
+ //which works well in the html context in Firefox
+ var newDocumentElement = svgDoc.documentElement.cloneNode(true)
+ attachPointNode.appendChild(newDocumentElement)
+
+ /*
+ var impl = document.implementation;
+ var svgDoc2 = impl.createDocument(commonSVG.SVG_NS, "svg", null);
+ svgDoc2.replaceChild(svgDoc.documentElement.cloneNode(true), svgDoc2.documentElement);
+ */
+ //console.log(newDocumentElement.getBBox)
+ //console.log(newDocumentElement.getBBox())
+ return newDocumentElement.ownerDocument;
+
+ } :
+ function(svgDoc){
+ //FIXME: somewhat evil, assumes batik
+
+ //copy svgDoc into SVGDOMImplementation
+ var impl = org.apache.batik.dom.svg.SVGDOMImplementation.getDOMImplementation();
+ var svgNS = org.apache.batik.dom.svg.SVGDOMImplementation.SVG_NAMESPACE_URI;
+ var svgDoc2 = impl.createDocument(svgNS, "svg", null);
+ svgDoc2.replaceChild(svgDoc2.importNode(svgDoc.documentElement,true), svgDoc2.documentElement);
+
+
+ //boot SVG and CSS DOM: http://wiki.apache.org/xmlgraphics-batik/BootSvgAndCssDom
+ var userAgent = new org.apache.batik.bridge.UserAgentAdapter();
+ var loader = new org.apache.batik.bridge.DocumentLoader(userAgent);
+ var ctx = new org.apache.batik.bridge.BridgeContext(userAgent, loader);
+ ctx.setDynamicState(org.apache.batik.bridge.BridgeContext.DYNAMIC);
+ var builder = new org.apache.batik.bridge.GVTBuilder();
+ var rootGN = builder.build(ctx, svgDoc2);
+
+ return svgDoc2;
+ };
+
+
+ var statesPredicate = "[self::s:state or self::s:parallel or self::s:final or self::s:initial or self::s:history]";
+
+ function scxmlToEntityNodeListAndLinkList(scxmlDoc,svgDoc){
+ //TODO: refactor all of this to be OO and use constructor functions and stuff
+ var root = scxmlDoc.documentElement;
+ var rootEntity = (function(scxmlNode){
+ var stateId = scxmlNode.getAttributeNS(null,"id");
+ var childrenIds = xpath.query("*" + statesPredicate + "/@id",scxmlNode).map(function(n){return n.nodeValue});
+
+ return {
+ id : stateId,
+ outConnections : [],
+ inConnections : [],
+ parent : null,
+ children : childrenIds,
+ visualObject : new graphics.BoxedGraphEntity(svgDoc.getElementById(stateId))
+ }
+ })(root);
+
+ function createStateEntity(s,graphicalClass){
+ var stateId = s.getAttributeNS(null,"id");
+ var outTransitionIds = xpath.query("s:transition/c:targets/c:target/@c:id",s).map(function(n){return n.nodeValue});;
+ var inTransitionIds = xpath.query("//s:transition/c:targets/c:target[c:targetState/text()='" + stateId + "']/@c:id",
+ scxmlDoc.documentElement).map(function(n){return n.nodeValue});
+ var parentId = s.parentNode.getAttributeNS(null,"id");
+ var childrenIds = xpath.query("*" + statesPredicate + "/@id",s).map(function(n){return n.nodeValue});
+
+ return {
+ id : stateId,
+ outConnections : outTransitionIds,
+ inConnections : inTransitionIds,
+ parent : parentId,
+ children : childrenIds,
+ visualObject : new graphicalClass(svgDoc.getElementById(stateId))
+ }
+ }
+ var states = xpath.query("//s:*[self::s:state or self::s:parallel]",scxmlDoc.documentElement);
+ var stateEntityList = states.map(function(s){return createStateEntity(s,graphics.BoxedGraphEntity)})
+
+ var initialFinalAndHistoryStateEntityList = xpath.query("//s:*[self::s:final or self::s:initial or self::s:history]",scxmlDoc.documentElement);
+ var initialStateEntityList = initialFinalAndHistoryStateEntityList.map(function(s){return createStateEntity(s,graphics.GraphEntity)})
+
+ var entityList = stateEntityList.concat(initialStateEntityList)
+
+
+ var transitionTargets = xpath.query("//s:transition/c:targets/c:target",scxmlDoc.documentElement)
+ var linkNodeList = transitionTargets.map(function(t){
+ var transitionId = t.getAttributeNS(commonSVG.SCXML_JS_NS,"id");
+ var originId = xpath.query("../../../@id",t)[0].nodeValue;
+ var targetId = xpath.query("c:targetState",t)[0].textContent;
+
+ //these attributes needed for hierarchical layout
+ var sourceAncestorOrSelfAndChildOfLCAId = xpath.query("c:sourceAncestorOrSelfAndChildOfLCA",t)[0].textContent;
+ var targetAncestorOrSelfAndChildOfLCAId = xpath.query("c:targetAncestorOrSelfAndChildOfLCA",t)[0].textContent;
+ var lcaId = xpath.query("../../c:lca",t)[0].textContent;
+
+ return {
+ id : transitionId,
+ outConnections : [targetId],
+ inConnections : [originId],
+ sourceAncestorOrSelfAndChildOfLCA : [sourceAncestorOrSelfAndChildOfLCAId],
+ lca : lcaId,
+ targetAncestorOrSelfAndChildOfLCA : [targetAncestorOrSelfAndChildOfLCAId],
+ getClosestStartAndEndConnectionPoints : function(){
+ //right now, only snap to first element of in and outConnections (no intelligent handling of hyperedges)
+ var inGraphObject = this.inConnections[0].visualObject;
+ var outGraphObject = this.outConnections[0].visualObject;
+
+ return inGraphObject.getClosestConnectorToGraphicalObject(outGraphObject,this.visualObject);
+ },
+ visualObject : new graphics.GraphLink(svgDoc.getElementById(transitionId))
+ }
+ })
+
+ var entityAndLinkList = entityList.concat(linkNodeList).concat(rootEntity);
+
+ var getNodeFromId = function(cIdOrNode){
+ return entityAndLinkList.filter(function(e){return cIdOrNode == e.id})[0] || cIdOrNode;
+
+ }
+
+ //now that all objects have been created, hook up references to other objects via in and out connections
+ //it's OK to use a new list to do this, because we're operating on objects, which is like C pointer semantics
+ entityAndLinkList.forEach(function(s){
+ s.outConnections = s.outConnections.map(getNodeFromId);
+
+ s.inConnections = s.inConnections.map(getNodeFromId);
+
+ s.parent = s.parent && getNodeFromId(s.parent);
+
+ s.children = s.children && s.children.map(getNodeFromId);
+
+ if(s.sourceAncestorOrSelfAndChildOfLCA)
+ s.sourceAncestorOrSelfAndChildOfLCA = s.sourceAncestorOrSelfAndChildOfLCA.map(getNodeFromId);
+
+ if(s.targetAncestorOrSelfAndChildOfLCA)
+ s.targetAncestorOrSelfAndChildOfLCA = s.targetAncestorOrSelfAndChildOfLCA.map(getNodeFromId);
+
+ if(s.lca)
+ s.lca = getNodeFromId(s.lca)
+
+ });
+
+ return {rootEntity : rootEntity, entityList : entityList, linkNodeList : linkNodeList};
+ }
+
+
+/*
+ function addGraphicalNodeReference(graphNodeList,svgDoc,graphicalClass){
+ //mapped based on id
+ graphNodeList.forEach(function(e){
+ e.visualObject = new graphicalClass(svgDoc.getElementById(e.id));
+ });
+ }
+ */
+
+
+ return {
+ scxmlToEntityNodeListAndLinkList : scxmlToEntityNodeListAndLinkList,
+ bootSVGDOM : bootSVGDOM
+ }
+})
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/PrepLayout.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/graphics.js
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/graphics.js?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/graphics.js (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/graphics.js Tue Aug 10 17:13:04 2010
@@ -0,0 +1,214 @@
+require.def("src/javascript/scxml/cgf/layout/graphics",
+["src/javascript/scxml/cgf/util/svg",
+ "src/javascript/scxml/cgf/util/geometry",
+ "src/javascript/scxml/cgf/util/xpath" ],
+function(commonSVG,geometry,xpath){
+
+ //constructor functions
+ function VisualObject(rootNode){
+ //a visual object. wraps a raw SVG node. also, exposes connection points
+
+ this._rawNode = rootNode; //rawNode
+ this._connectionPoints = _getConnectionPointsFromSVGNode(rootNode) //connection points
+
+ function _getConnectionPointsFromSVGNode(node){
+ return xpath.query("svg:*[@class='connectionPoint']",node);
+ }
+
+ this.getBBox = function(){
+ return this._rawNode.getBBox();
+ }
+
+ this.getBBoxInCanvasSpace = function(){
+ return commonSVG.getBBoxInCanvasSpace(this._rawNode);
+ }
+
+ this.getBBoxInEntitySpace = function(e){
+ return commonSVG.getBBoxInElementSpace(this._rawNode,e._rawNode);
+ }
+
+ this.moveTo = function(x,y){
+ var bbox = this._rawNode.getBBox();
+ var curX = bbox.x, curY = bbox.y;
+ var dx = x - curX;
+ var dy = y - curY;
+ return this.move(dx,dy);
+ }
+
+ this.getClosestConnectorToPoint = function(fx, fy, contextVisualObj){
+ /*
+ Returns the connector (cx, cy) of visualObject which makes minimum
+ the distance to (fx, fy)
+ */
+ return this._connectionPoints.reduce(function(o,c){
+ //compute distance from given point to connector
+ var bbox = commonSVG.getBBoxInElementSpace(c,contextVisualObj._rawNode);
+ var newDistance = geometry.distance(bbox.x,bbox.y,fx,fy);
+
+ return o.distance < newDistance ? o : {distance:newDistance,connector:bbox};
+ },{distance:Number.MAX_VALUE,connector:{x:0,y:0}});
+ }
+
+ this.getClosestConnectorToGraphicalObject = function(graphicalObject,contextVisualObj){
+ /*
+ for each connection point A on this object
+ find the closest connection point B on a given graphicalObject
+ */
+ return this._connectionPoints.reduce(function(o,c){
+ //compute distance from given point to connector
+ var bbox = commonSVG.getBBoxInElementSpace(c,contextVisualObj._rawNode);
+ var connPtInfo = graphicalObject.getClosestConnectorToPoint(bbox.x,bbox.y,contextVisualObj);
+
+ return o.distance < connPtInfo.distance ?
+ o :
+ {distance:connPtInfo.distance,fromConnector:bbox,toConnector:connPtInfo.connector};
+ },{distance:Number.MAX_VALUE,fromConnector:{x:0,y:0},toConnector:{x:0,y:0}});
+ }
+
+ this.setCanvasViewBox = function(r){
+ var svg = this._rawNode.ownerSVGElement;
+ svg.viewBox.baseVal.x = r.x;
+ svg.viewBox.baseVal.y = r.y;
+ svg.viewBox.baseVal.width = r.width;
+ svg.viewBox.baseVal.height = r.height;
+ }
+ }
+
+ function GraphEntity(rootNode){
+ //wraps a graph node
+
+ VisualObject.apply(this,arguments);
+
+ //Move method
+ this.move = function(dx,dy){
+ return commonSVG.translate(this._rawNode,dx,dy);
+ }
+ }
+
+ function BoxedGraphEntity(rootNode){
+ GraphEntity.apply(this,arguments);
+
+ this._boundingBoxRectElement = _getBBoxRectElement(rootNode);
+ this._boundingBoxLabelElement = _getBBoxLabelElement(rootNode);
+
+ function _getBBoxRectElement(node){
+ return xpath.query("svg:*[@class='groupBoundingRect']",node)[0];
+ }
+
+ function _getBBoxLabelElement(node){
+ return xpath.query("svg:text[@class='label']",node)[0];
+ }
+
+ this.resizeBBoxElement = function(newBBox){
+ //update connection points
+ var dx = newBBox.x - this._boundingBoxRectElement.x.baseVal.value,
+ dy = newBBox.y - this._boundingBoxRectElement.y.baseVal.value,
+ sx = newBBox.width / this._boundingBoxRectElement.width.baseVal.value,
+ sy = newBBox.height / this._boundingBoxRectElement.height.baseVal.value;
+
+ this._connectionPoints.forEach(function(pt){
+ pt.cx.baseVal.value *= sx;
+ pt.cy.baseVal.value *= sy;
+ pt.cx.baseVal.value += dx;
+ pt.cy.baseVal.value += dy;
+ })
+
+ //resize bbox
+ this._boundingBoxRectElement.width.baseVal.value = newBBox.width;
+ this._boundingBoxRectElement.height.baseVal.value = newBBox.height;
+ this._boundingBoxRectElement.x.baseVal.value = newBBox.x;
+ this._boundingBoxRectElement.y.baseVal.value = newBBox.y;
+
+ return newBBox;
+ }
+
+ this.moveLabelElementTo = function(x,y){
+ //update label
+ if(this._boundingBoxLabelElement){
+ this._boundingBoxLabelElement.x.baseVal.getItem(0).value = x;
+ this._boundingBoxLabelElement.y.baseVal.getItem(0).value = y;
+ }
+ }
+
+ this.resizeBBoxElementAndUpdateLabel = function(bbox,options){
+ options = options || {
+ labelPadding : 3
+ }
+
+ this.resizeBBoxElement(bbox);
+ this.moveLabelElementTo(bbox.x,bbox.y - options.labelPadding);
+ }
+ }
+
+ function GraphLink(rootNode){
+ //wraps a quadratic bezier curve, which has a (possibly empty) label
+
+ VisualObject.apply(this,arguments);
+
+ //label node
+ this._labelNode = _getLabelNodeFromSVGNode(rootNode);
+ this._pathNode = _getPathNodeFromSVGNode(rootNode);
+ this._startPoint = this._pathNode.pathSegList.getItem(0);
+ this._controlPoint = this._pathNode.pathSegList.getItem(1);
+
+ function _getLabelNodeFromSVGNode(node){
+ return xpath.query("svg:text[@class='label']",node)[0];
+ }
+
+ function _getPathNodeFromSVGNode(node){
+ return xpath.query("svg:path[@class='edge']",node)[0];
+ }
+
+ //Move method
+ this.move = function(dx,dy){
+ //move the control point?
+ this._controlPoint.x1 += dx
+ this._controlPoint.y1 += dy
+
+ //move the label
+ return commonSVG.translate(this._labelNode,dx,dy);
+ }
+
+
+ this.moveLabelTo = function(x,y){
+ var bbox = this._labelNode.getBBox();
+ var curX = bbox.x, curY = bbox.y;
+ var dx = x - curX;
+ var dy = y - curY;
+ return commonSVG.translate(this._labelNode,dx,dy);
+ }
+
+ //methods to adjust start, end and control point
+ this.setStartPoint = function(pt){
+ this._startPoint.x = pt.x
+ this._startPoint.y = pt.y
+ }
+
+ this.setControlPoint = function(pt){
+ this._controlPoint.x1 = pt.x
+ this._controlPoint.y1 = pt.y
+ }
+
+ this.setEndpoint = function(pt){
+ this._controlPoint.x = pt.x
+ this._controlPoint.y = pt.y
+ }
+
+ this.setCoords = function(coordsObj){
+ this.setStartPoint(coordsObj.startPoint);
+ if(coordsObj.controlPoint) this.setControlPoint(coordsObj.controlPoint);
+ this.setEndpoint(coordsObj.endPoint);
+ }
+
+ this.getCenterCoord = function(){
+ return this._labelNode.getBBox() || {x:0,y:0}; //batik will return null if the element has no size
+ }
+ }
+
+ return {
+ GraphLink : GraphLink,
+ GraphEntity : GraphEntity,
+ BoxedGraphEntity : BoxedGraphEntity
+ }
+
+});
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/graphics.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/CrossingModule.js
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/CrossingModule.js?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/CrossingModule.js (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/CrossingModule.js Tue Aug 10 17:13:04 2010
@@ -0,0 +1,234 @@
+/*
+CrossingModule.py
+
+Collection of algorithms responsible for phase II of Sugiyama-style hierarchical
+layout... crossing reduction.
+
+By Denis Dube, 2005
+*/
+
+require.def("src/javascript/scxml/cgf/layout/hierarchical/CrossingModule",
+["src/javascript/scxml/cgf/layout/hierarchical/NodeWrapper"],
+function(NodeWrapper){
+
+ function barycentricOrdering(levelDictionary, phaseOneMax, phaseTwoMax){
+ /*
+ Given a dictionary mapping of integer levels to nodes
+ Re-orders the nodes on each level to have fewer edge crossings
+ Implementation draws from:
+ CROSSING REDUCTION FOR LAYERED HIERARCHICAL GRAPH DRAWING
+ By PITCH PATARASUK
+ However, I don't perform same barycenter order reversals; for some reason
+ this yielded MORE crossings rather than less. I DO use random order
+ restarts however, which has given me good to optimal results in tests.
+ */
+
+
+ // Init variables
+ var totalLevelsInt = levelDictionary.length
+ var bestGlobalCrossings = Number.MAX_VALUE // Minimum # of crossings found so far
+ var bestLevelDictionary = null // Copy of the level dict with min cross
+ var phaseOneIterations = 0
+ var phaseTwoIterations = 0
+
+ // Initilize layer orderings
+ levelDictionary.forEach(function(level){
+ __updateOrder(level)
+ })
+
+ // Main crossing reduction loop
+ while(phaseTwoIterations < phaseTwoMax){
+
+ // Iterate over all the levels in the level dictionary
+ // If totalLevelsInt = 3, then this range yields [0, 1]
+ for(var j = 0; j < totalLevelsInt - 1; j++){
+
+ //// Down-Barycenter (Parent to child)
+ levelDictionary[j].forEach(function(node){
+ node.computeBarycenter(true)
+ });
+
+ // Sort the list in place according to barycenter values
+ levelDictionary[j].sort(function(a,b){ return a.getBarycenter() < b.getBarycenter()})
+
+ // Save the node's sorted position in it's internal parameter
+ __updateOrder(levelDictionary[j])
+
+ //// Up-Barycenter (Child to parent)
+ levelDictionary[j + 1].forEach(function(node){
+ node.computeBarycenter(false)
+ });
+
+ // Sort the list in place according to barycenter values
+ levelDictionary[j + 1].sort(function(a, b){ return a.getBarycenter() < b.getBarycenter() })
+
+ // Save the node's sorted position in it's internal parameter
+ __updateOrder(levelDictionary[j + 1])
+ }
+
+
+ // Count crossings
+ var globalCrossings = 0
+ for(var j = 0; j < totalLevelsInt - 1; j++){
+ globalCrossings += countCrossings(levelDictionary[j],
+ levelDictionary[j + 1])
+ }
+
+ // Did the last iteration of the algorithm reduce crossings?
+ if(globalCrossings < bestGlobalCrossings){
+ // Crossings reduced, reset counter since more reductions are possible
+ phaseOneIterations = 0
+ // Store a copy of levelDictionary, best ordering yet
+ bestLevelDictionary = __copyDict(levelDictionary)
+ bestGlobalCrossings = globalCrossings
+ }
+ else{
+ // Crossings not reduced, keep trying, might reduce later
+ phaseOneIterations += 1
+ if(phaseOneIterations > phaseOneMax){
+ // Reductions have ceased, try a random restart...
+ phaseOneIterations = 0
+ phaseTwoIterations += 1
+
+ // Randomize layer orderings
+ //get a list of indexes
+ //shuffle them
+ //set the order property of each node at that level, according to the random index
+ levelDictionary.forEach(function(currentLevel){
+ var indexList = range(0, currentLevel.length)
+ var shuffledList = indexList.slice()
+ fisherYates(shuffledList)
+
+ shuffledList.forEach(function(k){
+ currentLevel[indexList.pop()].setOrder(k)
+ });
+ });
+ }
+ }
+ }
+
+ // Return the levelDictionary with the least crossings
+ return bestLevelDictionary
+ }
+
+ function range(from,to,increment){
+ increment = increment || 1;
+ var toReturn = [];
+ for(var i = from; i < to; i+= increment){
+ toReturn.push(i);
+ }
+ return toReturn;
+ }
+
+ //JavaScript array shuffle implementation taken from http://sedition.com/perl/javascript-fy.html
+ function fisherYates ( myArray ) {
+ var i = myArray.length;
+ if ( i == 0 ) return false;
+ while ( --i ) {
+ var j = Math.floor( Math.random() * ( i + 1 ) );
+ var tempi = myArray[i];
+ var tempj = myArray[j];
+ myArray[i] = tempj;
+ myArray[j] = tempi;
+ }
+ }
+
+ function countCrossings(layerA, layerB){
+ /*
+ Inputs: layerA and layerB are lists of NodeWrapper objects
+ Output: // of crossings between two node layers in O(|E| log |Vsmall|)
+
+ NOTE: Most other algorithms for this are O(|E| + Number of crossings)
+ Implementation of:
+ Simple and Efficient Bilayer Cross Counting
+ Wilhelm Barth, Michael Junger, and Petra Mutzel
+ GD 2002, LNCS 2528, pp. 130-141, 2002
+ */
+ // Assumed that layerA is above layerB, so children of A are in B
+ // Now figure out which layer is smaller to improve running time a bit
+
+ var smallLayer, largeLayer, isParent2Child;
+
+ if(layerA.length < layerB.length){
+ smallLayer = layerA
+ largeLayer = layerB
+ isParent2Child = false
+ }
+ else{
+ smallLayer = layerB
+ largeLayer = layerA
+ isParent2Child = true
+ }
+
+
+ // Sort the edges and come up with a sequence of edges (integer indices)
+ var edgeSequence = []
+ largeLayer.forEach(function(node){
+ var tempList = []
+ // Get all possible nodes connected to this node
+ var targetNodeList = NodeWrapper.Source2TargetListMap[node.id]
+ targetNodeList.forEach(function(targetNode){
+ // Restrict ourselves to just those nodes that are in smallLayer
+ if(targetNode in smallLayer) tempList.append(targetNode.getOrder())
+ });
+ tempList.sort()
+ edgeSequence = edgeSequence.concat(tempList)
+ });
+
+ // Build the accumulator tree
+ var firstindex = 1
+ while(firstindex < smallLayer.length)
+ firstindex *= 2
+
+ var treesize = (2 * firstindex) - 1
+ firstindex -= 1
+ var tree = {}
+
+ for(var i = 0; i < treesize; i++){
+ tree[i] = 0
+ }
+
+ // Count the crossings
+ var crosscount = 0
+ for(var k = 0; k < edgeSequence.length; k++){
+ var index = edgeSequence[k] + firstindex
+ tree[index] += 1
+ while(index > 0){
+
+ if(index % 2)
+ crosscount += tree[index + 1]
+
+ index = (index - 1) / 2
+ tree[index] += 1
+ }
+ }
+
+ return crosscount
+ }
+
+
+
+ function __updateOrder(orderedLayer){
+ /*
+ The ordering is implicit in the node sequence in the list
+ However to do a node sort, it's handy to have each node know its order
+ This order is used only within __barycentricOrdering() except for debug
+ */
+ var i = 0
+ orderedLayer.forEach(function(node){
+ node.setOrder(i)
+ i += 1
+ });
+ }
+
+ function __copyDict(levelDictionary){
+ /* Handy method to make a real copy of the levelDictionary */
+ return levelDictionary.map(function(nodeList){
+ return nodeList.slice();
+ });
+ }
+
+ return {
+ barycentricOrdering : barycentricOrdering
+ }
+})
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/CrossingModule.js
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/HierarchicalLayout.js
URL: http://svn.apache.org/viewvc/commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/HierarchicalLayout.js?rev=984129&view=auto
==============================================================================
--- commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/HierarchicalLayout.js (added)
+++ commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/HierarchicalLayout.js Tue Aug 10 17:13:04 2010
@@ -0,0 +1,401 @@
+require.def("src/javascript/scxml/cgf/layout/hierarchical/HierarchicalLayout",
+[
+ "src/javascript/scxml/cgf/layout/hierarchical/NodeWrapper",
+ "src/javascript/scxml/cgf/layout/hierarchical/LayeringModule",
+ "src/javascript/scxml/cgf/layout/hierarchical/CrossingModule",
+ "src/javascript/scxml/cgf/layout/hierarchical/HorizontalPositioner",
+ "src/javascript/scxml/cgf/layout/LinkOptimizer"],
+function(NodeWrapper,LayeringModule,CrossingModule,HorizontalPositioner,LinkOptimizer){
+
+ var HEURISTICS_ENUM = {
+ LONGEST_PATH_LAYERING_TOP_DOWN : "LONGEST_PATH_LAYERING_TOP_DOWN",
+ LONGEST_PATH_LAYERING_BOTTOM_UP : "LONGEST_PATH_LAYERING_BOTTOM_UP",
+ MINIMUM_WIDTH_LAYERING : "MINIMUM_WIDTH_LAYERING"
+ }
+
+ function hierarchicalLayout(entityNodeList,wrappedNodeList,options){
+
+ options = options || {}
+ options['xOffset'] = options['xOffset'] || 30; // Minimum X Distance; Minimum horizontal distance between any 2 tree nodes (negative values work too)
+ options['yOffset'] = options['yOffset'] || 30; // Minimum Y Distance; Minimum vertical distance between any 2 tree nodes
+ options['addEdgeObjHeight'] = options['addEdgeObjHeight'] || true; // Add edge object height; Increment spacing between node layers with edge object drawing of maximum height between 2 given layers
+ options['Origin'] = options['Origin'] || false; // Start tree at origin?
+ options['uncrossPhase1'] = options['uncrossPhase1'] || 5; // Maximum uncrossing iterations
+ options['uncrossPhase2'] = options['uncrossPhase2'] || 15; // Maximum uncrossing random restarts
+ options['baryPlaceMax'] = options['baryPlaceMax'] || 10; // Maximum gridpositioning iterations
+ options['Spline optimization'] = options['Spline optimization'] || false; // Spline optimization
+ options['Arrow curvature'] = options['Arrow curvature'] || 0 // Arrow curvature
+ options['heuristic'] = options['heuristic'] || HEURISTICS_ENUM.LONGEST_PATH_LAYERING_TOP_DOWN
+
+ var t = new Date();
+
+ // Step 1: Get all entity nodes (semantic objects) and wrap them
+ //entityNodeList, linkNodeDict = self.__getEntityLinkTuple(selection)
+ if(entityNodeList.length == 0)
+ return
+
+ // Build a connection map (rapid access maps to children && parents)
+ wrappedNodeList.forEach(function(wrappedNode){
+ wrappedNode.buildConnectivityMaps()
+ });
+
+ // Step 2: Build a proper layered hieararchy
+ wrappedNodeList = LayeringModule.greedyCycleRemover(wrappedNodeList)
+ var layerTime = new Date();
+ var levelDictionary;
+
+ switch(options['heuristic']){
+ case HEURISTICS_ENUM.LONGEST_PATH_LAYERING_TOP_DOWN:
+ levelDictionary = LayeringModule.longestPathLayeringTopDown(wrappedNodeList)
+ levelDictionary = LayeringModule.addDummyNodes(levelDictionary, true)
+ break;
+ case HEURISTICS_ENUM.LONGEST_PATH_LAYERING_BOTTOM_UP:
+ levelDictionary = LayeringModule.longestPathLayeringBottomUp(wrappedNodeList)
+ levelDictionary = LayeringModule.addDummyNodes(levelDictionary, false)
+ break;
+ case HEURISTICS_ENUM.MINIMUM_WIDTH_LAYERING:
+ var mwl = new LayeringModule.MinimumWidthLayering(wrappedNodeList)
+ // UBW = 1..4, c = 1..2
+ levelDictionary = mwl.execute(2, 2)
+ levelDictionary = LayeringModule.addDummyNodes(levelDictionary, false)
+ break;
+ }
+
+ console.log('Layering algorithm required', new Date() - layerTime, 'milliseconds to assign each node a layer');
+
+ //return
+ console.log( 'Added dummy nodes, dumping layers:');
+
+ //debugLevelDict(levelDictionary)
+
+ // Step 3: Minimize crossings
+ levelDictionary = CrossingModule.barycentricOrdering(levelDictionary, options['uncrossPhase1'], options['uncrossPhase2'])
+
+ // Step 4: Horizontal grid positioner
+ HorizontalPositioner.priorityBarycenterPositioner(levelDictionary,options['baryPlaceMax'] )
+
+ // Step 5: Draw nodes && edges on the canvas
+ var topLeft;
+ if(entityNodeList.length != 0)
+ topLeft = __getMaxUpperLeftCoordinate(entityNodeList)
+ else
+ topLeft = [0, 0]
+
+ //TODO: hook up linkNodeDict
+ //__drawNodes(levelDictionary, linkNodeDict, topLeft)
+ __drawNodes(levelDictionary, null, topLeft)
+
+ //debugLevelDict(levelDictionary)
+
+ console.log( 'Hierarchical layout took', new Date() - t, 'milliseconds to compute')
+
+ function __getMaxUpperLeftCoordinate(entityNodeList){
+ /*
+ Returns the maximum upper left coordinate of all the nodes the layout is
+ being applied to
+ This corresponds to the minumum x && y coords of all the nodes
+ */
+ var minX = Number.MAX_VALUE
+ var minY = Number.MAX_VALUE
+ entityNodeList.forEach(function(node){
+ var bbox = node.visualObject.getBBox();
+ if(bbox.y < minY)
+ minY = bbox.y
+ if(bbox.x < minX)
+ minX = bbox.x
+ });
+ return [minX, minY]
+ }
+
+ function __drawNodes( levelDictionary, linkNodeDict, topLeft){
+ /*
+ Takes size of nodes into account to translate grid positions into actual
+ canvas coordinates
+ */
+ var setSmooth = options['Spline optimization']
+ var setCurvature = options['Arrow curvature']
+ var minOffsetY = options['yOffset']
+ var minOffsetX = options['xOffset']
+ var giveExtraSpaceForLinks = options['addEdgeObjHeight']
+
+ // Caclulate x, y offsets
+ var offsetX = 0
+ var levelInt2offsetY = {}
+ var currentLevel, levelInt;
+ for(levelInt = 0; levelInt < levelDictionary.length; levelInt++){
+ currentLevel = levelDictionary[levelInt]
+ levelInt2offsetY[levelInt] = 0
+
+ // Calculate maximum node size on a per level basis (X is for all levels)
+ // Then add minimum seperation distance between nodes
+ currentLevel.forEach(function(node){
+ // getSize returns node width, && height of the node & child link icon
+ var size = node.getSize(giveExtraSpaceForLinks)
+ var x = size[0], y = size[1];
+ offsetX = Math.max(offsetX, x)
+ levelInt2offsetY[levelInt] = Math.max(levelInt2offsetY[levelInt], y)
+ });
+ }
+
+
+ var maxOffsetX = offsetX + minOffsetX
+ var halfOffsetX = offsetX / 2
+
+ // Send nodes to their final destination, assign final pos to dummy edges
+ var x = topLeft[0], y = topLeft[1];
+ for(levelInt = 0; levelInt < levelDictionary.length; levelInt++){
+ currentLevel = levelDictionary[levelInt]
+ var longEdgeOffset = [halfOffsetX, levelInt2offsetY[levelInt] / 3]
+
+ // Move each node in the level (Dummy edges save the pos but don't move)
+ currentLevel.forEach(function(node){
+ node.moveTo(x + node.getGridPosition() * maxOffsetX, y, longEdgeOffset)
+ })
+
+ // Increment y for the next iteration
+ y += levelInt2offsetY[levelInt] + minOffsetY
+ }
+
+ // Self-looping edges (Must move these manually into position)
+ NodeWrapper.SelfLoopList.forEach(function(selfLoopedEdge){
+ var pos = selfLoopedEdge.getEdgePosition();
+ x = pos[0]
+ y = pos[1]
+ selfLoopedEdge.moveTo(x,y)
+ })
+
+ //debugger;
+ // Re-wire the links to take into account the new node positions
+ /*
+ var selectedLinks = []
+ linkNodeDict.values().forEach(function(obj){
+ selectedLinks.push(obj)
+ })
+ optimizeLinks(this.cb, setSmooth, setCurvature, selectedLinks=selectedLinks)
+ */
+ //LinkOptimizer.optimizeLinks(linkNodeList);
+
+ // Route multi-layer edges
+ //__edgeRouter()
+ }
+
+ function __edgeRouter(){
+ /*
+ Previously, edges traversing multiple layers were represented as a chain
+ of dummy nodes. Now these nodes are used as points on a continuous spline.
+ */
+ function getEndpoint(nodeTuple, pointList, direction, isReversedEdge){
+ /* Gets the nearest arrow endpoint. Handles edge reversal */
+
+ var ix,iy;
+ if((direction == 'start' && ! isReversedEdge)
+ || (direction == 'end' && isReversedEdge)){
+ var endNode = nodeTuple[0]
+ if(isReversedEdge){
+ ix = -2
+ iy = -1
+ }
+ else{
+ ix = 0
+ iy = 1
+ }
+ }
+ else{
+ endNode = nodeTuple[1]
+ if(isReversedEdge){
+ ix = 0
+ iy = 1
+ }
+ else{
+ ix = -2
+ iy = -1
+ }
+ }
+
+ // Is it connected to a named port!?!
+ if(endNode.isConnectedByNamedPort(edgeObject)){
+ handler = endNode.getConnectedByNamedPortHandler(nodeTuple[2])
+ return dc.coords(handler).slice(0,2)
+ }
+
+ // Not a named port...
+ return list(endNode.getClosestConnector2Point( endNode, pointList[ix], pointList[iy]))
+ }
+
+
+ //todo: improve method for spline arrows + add comments + optimize?
+ console.log( '----------------Dummy Edge Routing-----------------')
+ NodeWrapper.ID2LayerEdgeDict.keys().forEach(function(dummyEdge){
+
+ var dummyList = NodeWrapper.ID2LayerEdgeDict[dummyEdge]
+ var dummyNode = dummyList[0]
+ var dummyChild = dummyNode.children[0]
+ var linkFlagList = dummyNode.childNodeToLinkFlagListMap[dummyChild.id]
+
+ // Real nodes at start/end of the edge
+ var edgeSourceNode = dummyNode.parents[0]
+ edgeSourceNode = edgeSourceNode.getASGNode().visualObject
+ dummyNode = dummyList[-1]
+ var edgeTargetNode = dummyNode.children[0]
+ //print 'Dummy edge number', dummyEdge,
+ //print dummyList[0].parents.keys()[0].getName(), edgeTargetNode.getName()
+ edgeTargetNode = edgeTargetNode.getASGNode().visualObject
+ var nodeTuple = [edgeSourceNode, edgeTargetNode, null]
+
+ // Some edges are internally reversed to break cycles, when drawing
+ // this must be taken into account
+ var isReversedEdge = false
+ var edgesToRoute = []
+ linkFlagList.forEach(function(flag){
+ var linkNode = flag[0], isReversed = flag[1];
+ edgesToRoute.push(linkNode)
+ if(isReversed) isReversedEdge = true
+ });
+
+ // Get all the points the edge must pass through (sorted by layer order)
+ dummyList.sort(function(a, b){ return a.getLayer() < b.getLayer()})
+ if(isReversedEdge) dummyList.reverse()
+
+ var sortedDummyRouteList = []
+ dummyList.forEach(function(node){
+ sortedDummyRouteList =
+ sortedDummyRouteList.concat(node.getEdgePosition())
+ })
+
+ // Set the coordinates of the edge directly
+ // This is complicated by the fact that AToM3 treats edges as two
+ // segments that join poorly (for spline arrows)
+ edgesToRoute.forEach(function(edgeObject){
+ var dc = edgeObject.visualObject.dc
+ var linkObj = edgeObject.visualObject
+ var tag = linkObj.tag
+
+ if(isReversedEdge){
+ inPoint = dc.coords( tag + "2ndSeg0" ).slice(0,2)
+ outPoint = dc.coords( tag + "1stSeg0" ).slice(0,2)
+ }
+ else{
+ inPoint = dc.coords( tag + "1stSeg0" ).slice(0,2)
+ outPoint = dc.coords( tag + "2ndSeg0" ).slice(0,2)
+ }
+
+ //print 'Dummy route', sortedDummyRouteList
+ var numPoints = sortedDummyRouteList.length / 2;
+ // Add 2 extra control points for odd case (to make splines nice)
+ var center, start, end, newMid1, newMid2, centerIndex;
+ if(numPoints % 2 == 1){
+ if(numPoints == 1)
+ center = sortedDummyRouteList
+ else{
+ start = sortedDummyRouteList.slice(0,numPoints - 1)
+ end = sortedDummyRouteList.slice(0,numPoints + 1)
+ center = sortedDummyRouteList.slice(numPoints - 1,numPoints + 1)
+ }
+
+ if(! isReversedEdge){
+ newMid1 = [center[0], center[1] - 20]
+ newMid2 = [center[0], center[1] + 20]
+ }
+ else{
+ newMid2 = [center[0], center[1] - 20]
+ newMid1 = [center[0], center[1] + 20]
+ }
+
+ if(numPoints == 1)
+ sortedDummyRouteList = newMid1 + center + newMid2
+ else
+ sortedDummyRouteList = start + newMid1 + center + newMid2 + end
+ centerIndex = numPoints - 1 + 2
+ }
+
+ // Add 1 extra control point for even case (to make splines nice)
+ else{
+ start = sortedDummyRouteList.slice(0,numPoints)
+ end = sortedDummyRouteList.slice(numPoints)
+ center = [start[-2] + (end[0] - start[-2]) / 2, start[-1] + (end[1] - start[-1]) / 2]
+ sortedDummyRouteList = start + center + end
+ centerIndex = numPoints
+ }
+
+ // Now I know where the center is... so lets move the center object
+ // Is the edge object a hyperlink?
+ if(edgeObject.inConnections + edgeObject.outConnections.length > 2){
+ var fromObjs = []
+ edgeObject.inConnections.forEach(function(semObj){
+ fromObjs.push(semObj.visualObject)
+ })
+ var toObjs = []
+ edgeObject.outConnections.forEach(function(semObj){
+ toObjs.push(semObj.visualObject)
+ })
+ optimizerHyperLink(dc, linkObj, fromObjs, toObjs, 0, 0, 0, center )
+ } else {
+ linkObj.moveTo(center)
+ }
+
+ // Go through the 2 segments in the link
+ nodeTuple[2] = edgeObject
+ linkObj.connections.forEach(function(connTuple){
+ var itemHandler = connTuple[0]
+ var direction = connTuple[1]
+
+ var inPoint, outPoint, segCoords;
+ if( direction ){
+ inPoint = getEndpoint(nodeTuple, sortedDummyRouteList,
+ 'start', isReversedEdge)
+
+ segCoords = inPoint + sortedDummyRouteList.slice(0,centerIndex+2)
+ }
+ else{
+ outPoint = getEndpoint(nodeTuple, sortedDummyRouteList,
+ 'end', isReversedEdge)
+ segCoords = sortedDummyRouteList.slice(centerIndex) + outPoint
+ segCoords = __reverseCoordList(segCoords)
+ }
+
+ // Applies the changed coords to the canvas
+ dc.coords( [itemHandler] + segCoords )
+
+ // This may change the associated link drawings:
+ // move them to the new point
+ if( direction )
+ linkObj.updateDrawingsTo(inPoint[0], inPoint[1], itemHandler, segmentNumber=1)
+ else
+ linkObj.updateDrawingsTo(outPoint[0], outPoint[1], itemHandler, segmentNumber=2)
+ })
+ });
+ })
+ }
+
+
+
+ function __reverseCoordList( segCoords){
+ /*
+ Input: list of coordinates [x0, y0, x1, y1, ..., xn, yn]
+ Output: list of coordinates reversed [xn, yn, ..., x1, y1, x0, y0]
+ */
+ var reversedCoords = []
+ for(var i = segCoords.length - 1; i > 0; i-=2){
+ reversedCoords = reversedCoords.concat([segCoords[i - 1], segCoords[i]])
+ }
+ return reversedCoords
+ }
+
+
+ }
+
+ function recursivelyApplyHierarchicalLayout(graphEntity,options){
+ var nodeWrapperChildren = graphEntity.children.map(function(c){return new NodeWrapper(c,NodeWrapper.REGULAR_NODE)});
+ graphEntity.children.filter(function(c){return c.children.length})
+ .forEach(function(c){recursivelyApplyHierarchicalLayout(c,options)});
+ hierarchicalLayout(graphEntity.children,nodeWrapperChildren,options);
+ }
+
+ return {
+ recursivelyApplyHierarchicalLayout : recursivelyApplyHierarchicalLayout,
+ hierarchicalLayout : hierarchicalLayout,
+ HEURISTICS_ENUM : HEURISTICS_ENUM
+ }
+
+
+});
Propchange: commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/layout/hierarchical/HierarchicalLayout.js
------------------------------------------------------------------------------
svn:eol-style = native