Index: lams_build/3rdParty.userlibraries
===================================================================
diff -u -r7c06d9adee78c99dd6151735c95016f1e0b0f360 -ref201100ffd7ebdfcb18c72528f062379e4c65d3
--- lams_build/3rdParty.userlibraries (.../3rdParty.userlibraries) (revision 7c06d9adee78c99dd6151735c95016f1e0b0f360)
+++ lams_build/3rdParty.userlibraries (.../3rdParty.userlibraries) (revision ef201100ffd7ebdfcb18c72528f062379e4c65d3)
@@ -6,6 +6,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -83,6 +93,8 @@
+
+
Index: lams_build/build.xml
===================================================================
diff -u -r7c06d9adee78c99dd6151735c95016f1e0b0f360 -ref201100ffd7ebdfcb18c72528f062379e4c65d3
--- lams_build/build.xml (.../build.xml) (revision 7c06d9adee78c99dd6151735c95016f1e0b0f360)
+++ lams_build/build.xml (.../build.xml) (revision ef201100ffd7ebdfcb18c72528f062379e4c65d3)
@@ -116,6 +116,7 @@
+
@@ -133,7 +134,8 @@
-
+
+
Index: lams_build/liblist.txt
===================================================================
diff -u -r7c06d9adee78c99dd6151735c95016f1e0b0f360 -ref201100ffd7ebdfcb18c72528f062379e4c65d3
--- lams_build/liblist.txt (.../liblist.txt) (revision 7c06d9adee78c99dd6151735c95016f1e0b0f360)
+++ lams_build/liblist.txt (.../liblist.txt) (revision ef201100ffd7ebdfcb18c72528f062379e4c65d3)
@@ -13,6 +13,17 @@
saaj.jar
wsdl4j-1.5.1.jar 1.5.1 IBM JWSDL
+batik batik-anim.jar
+ batik-bridge.jar
+ batik-css.jar
+ batik-dom.jar
+ batik-ext.jar
+ batik-gvt.jar
+ batik-parser.jar
+ batik-svg-dom.jar
+ batik-util.jar
+ batik-xml.jar 1.7 Apache License 2.0 Apache toolkit for manipulating images in the Scalable Vector Graphics (SVG) format
+
cglib cglib-nodep-2.1_2.jar 2.1_2 Apache License 2.0 (old one - to be deleted soon!)
cglib_jboss404GA.jar 4.0.4.GA Apache License 2.0 JBoss Inc. JBoss
@@ -73,6 +84,9 @@
wddx wddx.jar 1.1 WDDX
+xml-commons xml-apis.jar
+ xml-apis-ext.jar 1.3 Apache License 2.0 Apache Common code and guidelines for xml projects
+
xstream jmock-2004-03-19.jar
joda-time-0.98.jar 0.98 Joda Software License 1.0 Joda.org Joda Time
stax-1.1.1-dev.jar 1.1.1-dev StAX is the reference implementation of the St...
Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java
===================================================================
diff -u -ra8d07f4579fd17f88434619258434bb777d0cd83 -ref201100ffd7ebdfcb18c72528f062379e4c65d3
--- lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java (.../ExportToolContentService.java) (revision a8d07f4579fd17f88434619258434bb777d0cd83)
+++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java (.../ExportToolContentService.java) (revision ef201100ffd7ebdfcb18c72528f062379e4c65d3)
@@ -25,6 +25,7 @@
package org.lamsfoundation.lams.learningdesign.service;
import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -64,6 +65,8 @@
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.log4j.Logger;
+import org.apache.xml.serialize.OutputFormat;
+import org.apache.xml.serialize.XMLSerializer;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
@@ -138,6 +141,7 @@
import org.lamsfoundation.lams.util.FileUtilException;
import org.lamsfoundation.lams.util.MessageService;
import org.lamsfoundation.lams.util.VersionUtil;
+import org.lamsfoundation.lams.util.svg.SVGGenerator;
import org.lamsfoundation.lams.util.zipfile.ZipFileUtil;
import org.lamsfoundation.lams.util.zipfile.ZipFileUtilException;
import org.springframework.beans.BeansException;
@@ -182,6 +186,8 @@
public static final String TOOL_FILE_NAME = "tool.xml";
public static final String TOOL_FAILED_FILE_NAME = "export_failed.xml";
+
+ public static final String SVG_IMAGE_FILE_NAME = "learning_design.svg";
private static final String ERROR_TOOL_NOT_FOUND = "error.import.matching.tool.not.found";
@@ -656,6 +662,21 @@
XStream designXml = new XStream();
designXml.toXML(ldDto, ldFile);
ldFile.close();
+
+ //generate SVG image
+ if (format != ExportToolContentService.PACKAGE_FORMAT_IMS) {
+ String svgFileName = FileUtil.getFullPath(contentDir, ExportToolContentService.SVG_IMAGE_FILE_NAME);
+ Writer svgFile = new OutputStreamWriter(new FileOutputStream(svgFileName), "UTF-8");
+ SVGGenerator svgGenerator = SVGGenerator.getInstance();
+ svgGenerator.generateSvg(ldDto);
+ OutputFormat outputFormat = new OutputFormat(svgGenerator.getSVGDocument());
+ outputFormat.setLineWidth(65);
+ outputFormat.setIndenting(true);
+ outputFormat.setIndent(2);
+ XMLSerializer serializer = new XMLSerializer(svgFile, outputFormat);
+ serializer.serialize(svgGenerator.getSVGDocument());
+ svgFile.close();
+ }
log.debug("Learning design xml export success");
@@ -700,6 +721,9 @@
} catch (IOException e) {
log.error("IOException:", e);
throw new ExportToolContentException(e);
+ } catch (JDOMException e) {
+ log.error("JDOMException:", e);
+ throw new ExportToolContentException(e);
}
}
Index: lams_common/src/java/org/lamsfoundation/lams/util/svg/ActivityTreeNode.java
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/util/svg/ActivityTreeNode.java (revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/util/svg/ActivityTreeNode.java (revision ef201100ffd7ebdfcb18c72528f062379e4c65d3)
@@ -0,0 +1,231 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+/* $Id$ */
+package org.lamsfoundation.lams.util.svg;
+
+
+import java.awt.Dimension;
+import java.awt.Point;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+
+import org.lamsfoundation.lams.learningdesign.Activity;
+import org.lamsfoundation.lams.learningdesign.dto.AuthoringActivityDTO;
+
+/**
+ * @author Andrey Balan
+ */
+public class ActivityTreeNode extends DefaultMutableTreeNode {
+
+ private static final long serialVersionUID = 1L;
+
+ public ActivityTreeNode() {
+ super();
+ }
+
+ public ActivityTreeNode(AuthoringActivityDTO activity) {
+ super(activity);
+ }
+
+ public AuthoringActivityDTO getActivity() {
+ return (AuthoringActivityDTO) userObject;
+ }
+
+ public List getChildren() {
+ Enumeration enumeration = children();
+ return Collections.list(enumeration);
+ }
+
+ /**
+ * Can be invoked only after the tree has been built already.
+ *
+ * @return
+ */
+ public AuthoringActivityDTO getParentActivity() {
+ if (parent != null) {
+ ActivityTreeNode parentNode = (ActivityTreeNode) parent;
+ return (AuthoringActivityDTO) parentNode.getUserObject();
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks whether activity is a children of an optional sequence activity
+ *
+ * @param node
+ * @return
+ */
+ public boolean isOptionalSequenceActivityChild() {
+ boolean isOptionalSequenceActivityChild = false;
+
+ AuthoringActivityDTO activity = (AuthoringActivityDTO) userObject;
+ if ((activity.getParentActivityID() != null)) {
+ DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) parent;
+ AuthoringActivityDTO parentActivity = (AuthoringActivityDTO) parentNode.getUserObject();
+ if (parentActivity.getActivityTypeID().equals(Activity.SEQUENCE_ACTIVITY_TYPE)) {
+ isOptionalSequenceActivityChild = true;
+ }
+ }
+
+ return isOptionalSequenceActivityChild;
+ }
+
+ /**
+ * Checks whether activity is a children of an optional sequence activity
+ *
+ * @param node
+ * @return
+ */
+ public String getActivityColor() {
+ String color = "";
+
+ switch (getActivity().getActivityCategoryID()) {
+ case Activity.CATEGORY_SYSTEM:
+ color = ";fill:#d0defd";
+ break;
+ case Activity.CATEGORY_COLLABORATION:
+ color = ";fill:#fffccb";
+ break;
+ case Activity.CATEGORY_ASSESSMENT:
+ color = ";fill:#ece9f7";
+ break;
+ case Activity.CATEGORY_CONTENT:
+ color = ";fill:#fdf1d3";
+ break;
+ case Activity.CATEGORY_SPLIT:
+ color = ";fill:#FFFFFF";
+ break;
+ case Activity.CATEGORY_RESPONSE:
+ color = ";fill:#e9f9c0";
+ break;
+ }
+
+ return color;
+ }
+
+ public Dimension getActivityDimension() {
+ AuthoringActivityDTO activity = (AuthoringActivityDTO) userObject;
+ int childrenSize = getChildCount();
+
+ // according to the type of activity, we need to set up difference width and height for the To and From
+ // activity to draw the transition accordingly.
+ int width;
+ int height;
+ if (activity.getActivityTypeID().equals(Activity.SYNCH_GATE_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.SCHEDULE_GATE_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.PERMISSION_GATE_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.CONDITION_GATE_ACTIVITY_TYPE)) {
+ // this is a gate activity
+ width = SVGConstants.GATE_WIDTH;
+ height = SVGConstants.GATE_HEIGHT;
+
+ } else if (activity.getActivityTypeID().equals(Activity.PARALLEL_ACTIVITY_TYPE)) {
+ // This is a parallel activity
+ // Given that for now all parallel activities are just two activities, we can hard code the width and height
+ width = SVGConstants.PARALLEL_OR_OPTIONS_ACTIVITY_WIDTH;
+ height = SVGConstants.PARALLEL_ACTIVITY_HEIGHT;
+
+ } else if (activity.getActivityTypeID().equals(Activity.CHOSEN_BRANCHING_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.GROUP_BRANCHING_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.TOOL_BRANCHING_ACTIVITY_TYPE)) {
+ // This is a branching activity
+ width = SVGConstants.BRANCHING_ACTIVITY_WIDTH;
+ height = SVGConstants.BRANCHING_ACTIVITY_HEIGHT;
+
+ } else if (activity.getActivityTypeID().equals(Activity.OPTIONS_WITH_SEQUENCES_TYPE)) {
+ // This is an optional sequence
+ width = getOptionalSequenceActivityWidth();
+ height = (57 * childrenSize) + 49;
+
+ } else if (activity.getActivityTypeID().equals(Activity.SEQUENCE_ACTIVITY_TYPE)) {
+ // This is a sequence within an optional
+ ActivityTreeNode parentNode = (ActivityTreeNode) getParent();
+ width = parentNode.getOptionalSequenceActivityWidth() -8;
+ height = SVGConstants.TOOL_HEIGHT + 3;
+
+ } else if (activity.getActivityTypeID().equals(Activity.OPTIONS_ACTIVITY_TYPE)) {
+ // This is an optional activity
+ width = SVGConstants.PARALLEL_OR_OPTIONS_ACTIVITY_WIDTH;
+ height = SVGConstants.OPTIONS_ACTIVITY_HEIGHT_MULTIPLIER * childrenSize
+ + SVGConstants.OPTIONS_ACTIVITY_HEIGHT_ADD;
+
+ } else if (activity.getActivityTypeID().equals(Activity.FLOATING_ACTIVITY_TYPE)) {
+ // This is a support activity
+ width = (SVGConstants.TOOL_WIDTH +7) * childrenSize + 11;
+ height = SVGConstants.OPTIONS_ACTIVITY_HEIGHT_MULTIPLIER + SVGConstants.OPTIONS_ACTIVITY_HEIGHT_ADD;
+
+ } else {
+ // This is a tool activity
+ width = SVGConstants.TOOL_WIDTH;
+ height = SVGConstants.TOOL_HEIGHT;
+
+ // if this activity is a children of a sequence activity, if it is, then we need to change its size
+ if (isOptionalSequenceActivityChild()) {
+ width = SVGConstants.TOOL_INSIDE_OPTIONAL_WIDTH;
+ height = 43;
+ }
+ }
+
+ return new Dimension(width, height);
+ }
+
+ /**
+ * Returns activity's left upper point
+ *
+ * @return
+ */
+ public Point getActivityCoordinates() {
+ AuthoringActivityDTO activity = getActivity();
+
+ int x = (activity.getxCoord() == null) ? 0 : activity.getxCoord();
+ int y = (activity.getyCoord() == null) ? 0 : activity.getyCoord();
+
+ return new Point(x, y);
+ }
+
+ /**
+ * Returns OptionalSequenceActivityWidth
+ *
+ * @param node node containing optional sequence activity
+ * @return
+ */
+ private int getOptionalSequenceActivityWidth() {
+ // now find out how many activities each of the subsequences have so we can calculate the width
+ int maxChildren = 0;
+ for (ActivityTreeNode childNode : getChildren()) {
+ int childrenSize = childNode.getChildCount();
+ if (childrenSize > maxChildren) {
+ maxChildren = childrenSize;
+ }
+ }
+ maxChildren = (maxChildren < 2) ? 2 : maxChildren;
+ int width = SVGConstants.TOOL_INSIDE_OPTIONAL_WIDTH * maxChildren + 20;
+
+ return width;
+ }
+
+}
Index: lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGConstants.java
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGConstants.java (revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGConstants.java (revision ef201100ffd7ebdfcb18c72528f062379e4c65d3)
@@ -0,0 +1,62 @@
+package org.lamsfoundation.lams.util.svg;
+
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+/* $Id$ */
+
+public class SVGConstants {
+
+ public static final String SVG_NAMESPACE = "http://www.w3.org/2000/svg";//SVGDOMImplementation.SVG_NAMESPACE_URI;
+ public static final String SVG_NAMESPACE_XLINK = "http://www.w3.org/1999/xlink";
+
+ // canvas dimensions
+ public static final int CANVAS_WIDTH = 1024;
+ public static final int CANVAS_HEIGHT = 768;
+
+ // gate dimensions
+ public static final int GATE_WIDTH = 30;
+ public static final int GATE_HEIGHT = 30;
+ // These are the octogon proportions so according to one point we calculate the new dimensions/proportions
+ public static final double[][] GATE_PROPORTIONS = { { 13.6, 34.4 }, { 1, 34.4 }, { -9.4, 24 }, { -9.4, 11.4 }, { 1, 1 },
+ { 13.6, 1 }, { 24, 11.4 }, { 24, 24 } };
+
+ // tool dimensions
+ public static final int TOOL_WIDTH = 125;
+ public static final int TOOL_HEIGHT = 50;
+ public static final int TOOL_INSIDE_OPTIONAL_WIDTH = 60;
+
+ public static final int PARALLEL_ACTIVITY_HEIGHT = 172;
+ public static final int OPTIONS_ACTIVITY_HEIGHT_MULTIPLIER = 63;
+ public static final int OPTIONS_ACTIVITY_HEIGHT_ADD = 53;
+ public static final int PARALLEL_OR_OPTIONS_ACTIVITY_WIDTH = 141;
+ public static final int CONTAINER_HEADER_HEIGHT = 39;
+
+ public static final int BRANCHING_ACTIVITY_HEIGHT = 100;
+ public static final int BRANCHING_ACTIVITY_WIDTH = 165;
+
+ public static final double BRANCHING_ACTIVITY_POINT = 8.5;
+ //distance between points in branching activity
+ public static final int BRANCHING_STEP = 15;
+
+
+}
Index: lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGGenerator.java
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGGenerator.java (revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGGenerator.java (revision ef201100ffd7ebdfcb18c72528f062379e4c65d3)
@@ -0,0 +1,707 @@
+package org.lamsfoundation.lams.util.svg;
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+/* $Id$ */
+
+import java.awt.geom.Point2D;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.batik.dom.svg.SVGDOMImplementation;
+import org.apache.commons.lang.StringUtils;
+import org.apache.xml.serialize.OutputFormat;
+import org.apache.xml.serialize.XMLSerializer;
+import org.jdom.JDOMException;
+import org.lamsfoundation.lams.learningdesign.Activity;
+import org.lamsfoundation.lams.learningdesign.dto.AuthoringActivityDTO;
+import org.lamsfoundation.lams.learningdesign.dto.LearningDesignDTO;
+import org.lamsfoundation.lams.learningdesign.dto.TransitionDTO;
+import org.lamsfoundation.lams.util.FileUtil;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.svg.SVGDocument;
+
+/**
+ * Generates SVG document based on exported learning design's xml file.
+ *
+ * To be able to see resulted SVG image in swing component use the following lines.
+ JSVGCanvas canvas = new JSVGCanvas();
+ JFrame f = new JFrame();
+ f.getContentPane().add(canvas);
+ canvas.setSVGDocument(svgGenerator.getSVGDocument());
+ f.pack();
+ f.setSize(CANVAS_WIDTH, CANVAS_HEIGHT);
+ f.setVisible(true);
+ *
+ * @author Andrey Balan
+ */
+public class SVGGenerator extends SVGConstants{
+
+ private SVGDocument doc;
+
+ private SVGGenerator(SVGDocument svgGDocument) {
+ doc = svgGDocument;
+ }
+
+ /**
+ * Sets up Svg root and defs.
+ */
+ public static SVGGenerator getInstance() {
+
+ // Create an SVG document.
+ DOMImplementation impl = SVGDOMImplementation.getDOMImplementation();
+ SVGDocument doc = (SVGDocument) impl.createDocument(SVG_NAMESPACE, "svg", null);
+ // Get the root element (the 'svg' element).
+ Element svgRoot = doc.getDocumentElement();
+ // Set the width and height attributes on the root 'svg' element.
+ svgRoot.setAttributeNS(null, "width", Integer.toString(CANVAS_WIDTH));
+ svgRoot.setAttributeNS(null, "height", Integer.toString(CANVAS_HEIGHT));
+ svgRoot.setAttributeNS(null, "xmlns", SVG_NAMESPACE);
+ svgRoot.setAttributeNS(null, "xmlns:xlink", SVG_NAMESPACE_XLINK);
+
+ //create arrow definition
+ Element defs = doc.createElementNS(SVG_NAMESPACE, "defs");
+ svgRoot.appendChild(defs);
+ Element marker = doc.createElementNS(SVG_NAMESPACE, "marker");
+ marker.setAttributeNS(null, "id", "Triangle");
+ marker.setAttributeNS(null, "viewBox", "0 0 10 10");
+ marker.setAttributeNS(null, "refX", "0");
+ marker.setAttributeNS(null, "refY", "5");
+ marker.setAttributeNS(null, "markerUnits", "strokeWidth");
+ marker.setAttributeNS(null, "markerWidth", "6");
+ marker.setAttributeNS(null, "markerHeight", "5");
+ marker.setAttributeNS(null, "orient", "auto");
+ defs.appendChild(marker);
+ Element path = doc.createElementNS(SVG_NAMESPACE, "path");
+ path.setAttributeNS(null, "d", "M 0 0 L 10 5 L 0 10 z");
+ marker.appendChild(path);
+
+ return new SVGGenerator(doc);
+ }
+
+ public SVGDocument getSVGDocument() {
+ return doc;
+ }
+
+ public void generateSvg(LearningDesignDTO learningDesign) throws JDOMException, IOException {
+
+ //initialize all tree nodes
+ ArrayList activities = learningDesign.getActivities();
+ HashMap allNodes = new HashMap();
+ for (AuthoringActivityDTO activity : activities) {
+ ActivityTreeNode node = new ActivityTreeNode(activity);
+ allNodes.put(activity.getActivityID(), node);
+ }
+
+ //construct activities tree
+ ActivityTreeNode root = new ActivityTreeNode();
+ for (ActivityTreeNode node : allNodes.values()) {
+ AuthoringActivityDTO activity = node.getActivity();
+ if (activity.getParentActivityID() == null) {
+ root.add(node);
+ } else {
+ Long parentId = activity.getParentActivityID();
+ ActivityTreeNode parent = allNodes.get(parentId);
+ parent.add(node);
+ }
+ }
+
+ //**************** Draw transitions********************************************************
+ Element svgRoot = doc.getDocumentElement();
+ ArrayList transitions = learningDesign.getTransitions();
+ for (TransitionDTO transition : transitions) {
+
+ ActivityTreeNode fromActivity = allNodes.get(transition.getFromActivityID());
+ ActivityTreeNode toActivity = allNodes.get(transition.getToActivityID());
+ Point2D fromIntersection = SVGTrigonometryUtils.getRectangleAndLineSegmentIntersection(fromActivity, toActivity);
+ Point2D toIntersection = SVGTrigonometryUtils.getRectangleAndLineSegmentIntersection(toActivity, fromActivity);
+
+ //skip optional sequence's childs
+ if (fromActivity.isOptionalSequenceActivityChild()) {
+ continue;
+ }
+
+ // Create the line
+ Element line = doc.createElementNS(SVG_NAMESPACE, "line");
+ line.setAttributeNS(null, "id", transition.getFromActivityID() + "_to_" + transition.getToActivityID());
+ line.setAttributeNS(null, "x1", Double.toString(fromIntersection.getX()));
+ line.setAttributeNS(null, "y1", Double.toString(fromIntersection.getY()));
+ line.setAttributeNS(null, "x2", Double.toString(toIntersection.getX()));
+ line.setAttributeNS(null, "y2", Double.toString(toIntersection.getY()));
+ line.setAttributeNS(null, "style", "stroke:#8C8FA6;stroke-width:2;opacity:1");
+ line.setAttributeNS(null, "parentID", "0");
+
+ double a = (toIntersection.getX() - fromIntersection.getX());
+ double b = (toIntersection.getY() - fromIntersection.getY());
+ double yArrowShift = 5* b/Math.sqrt(a*a + b*b);
+ double xArrowShift = 5* a/Math.sqrt(a*a + b*b);
+ // Create the arrowhead
+ Element arrowhead = doc.createElementNS(SVG_NAMESPACE, "line");
+ arrowhead.setAttributeNS(null, "id", "arrowhead_" + transition.getFromActivityID() + "_to_" + transition.getToActivityID());
+ arrowhead.setAttributeNS(null, "x1", Double.toString(fromIntersection.getX()));
+ arrowhead.setAttributeNS(null, "y1", Double.toString(fromIntersection.getY()));
+ arrowhead.setAttributeNS(null, "x2", Double.toString((fromIntersection.getX() + toIntersection.getX())/2 - xArrowShift));
+ arrowhead.setAttributeNS(null, "y2", Double.toString((fromIntersection.getY() + toIntersection.getY())/2 - yArrowShift));
+ arrowhead.setAttributeNS(null, "style", "fill:#8C8FA6;stroke:#8C8FA6;stroke-width:2;opacity:1");
+ arrowhead.setAttributeNS(null, "marker-end", "url(#Triangle)");
+ arrowhead.setAttributeNS(null, "parentID", "0");
+
+ // Attach the line to the root 'svg' element.
+ svgRoot.appendChild(line);
+ svgRoot.appendChild(arrowhead);
+ }
+
+ //**************** Draw activities ********************************************************
+ //tree traverse
+ treeTraverse(root);
+ }
+
+ /**
+ * Recursive tree traverse.
+ *
+ * @param doc
+ * @param svgRoot
+ * @param learningDesign
+ * @param node
+ */
+ private void treeTraverse(ActivityTreeNode node) {
+ AuthoringActivityDTO activity = node.getActivity();
+
+ //draw root's activity, unless it's the start root which doesn't contain activity
+ if (activity != null) {
+ createActivity(node);
+
+ //in case of branching activity don't traverse child activities
+ if (activity.getActivityTypeID().equals(Activity.CHOSEN_BRANCHING_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.GROUP_BRANCHING_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.TOOL_BRANCHING_ACTIVITY_TYPE)) {
+ return;
+ }
+ }
+
+ //traverse child subtrees
+ for (ActivityTreeNode child : node.getChildren()) {
+ treeTraverse(child);
+ }
+ }
+
+ /**
+ * Adds activity to SVG DOM.
+ *
+ * @param doc
+ * @param svgRoot
+ * @param learningDesign
+ * @param activity
+ */
+ private void createActivity(ActivityTreeNode node) {
+
+ AuthoringActivityDTO activity = node.getActivity();
+
+ // Create current activity element
+ Element g = doc.createElementNS(SVG_NAMESPACE, "g");
+ String activityId = activity.getActivityID().toString();
+ g.setAttributeNS(null, "id", activityId);
+ String parentID = (activity.getParentActivityID() == null) ? "0" : activity.getParentActivityID().toString();
+ g.setAttributeNS(null, "parentID", parentID);
+ // Attach the g element to the root 'svg' element.
+ Element svgRoot = doc.getDocumentElement();
+ svgRoot.appendChild(g);
+
+ int x = node.getActivityCoordinates().x;
+ int y = node.getActivityCoordinates().y;
+ // activities with parents (paralles, optionals, branching, etc)
+ if (activity.getParentActivityID() != null) {
+ AuthoringActivityDTO parentActivity = node.getParentActivity();
+ x += (parentActivity.getxCoord() == null) ? 0 : parentActivity.getxCoord();
+ y += (parentActivity.getyCoord() == null) ? 0 : parentActivity.getyCoord();
+ }
+
+ Integer width = node.getActivityDimension().width;
+ Integer height = node.getActivityDimension().height;
+ String text = activity.getActivityTitle();
+
+ // if this is a stop gate we need to draw an octogon instead
+ if (activity.getActivityTypeID().equals(Activity.SYNCH_GATE_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.SCHEDULE_GATE_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.PERMISSION_GATE_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.CONDITION_GATE_ACTIVITY_TYPE)) {
+ // don't care about SYSTEM_GATE_ACTIVITY_TYPE
+ x += 8;
+ y -= 2;
+
+ String finalProportions = "";
+ for (double[] proportion : GATE_PROPORTIONS) {
+ finalProportions += (x + proportion[0]) + "," + (y + proportion[1]) + " ";
+ }
+
+ Element polygon = doc.createElementNS(SVG_NAMESPACE, "polygon");
+ polygon.setAttributeNS(null, "style", "fill:red;stroke:#000000;stroke-width:0.5px");
+ polygon.setAttributeNS(null, "points", finalProportions);
+ g.appendChild(polygon);
+
+ // calculate midpoint for STOP text
+ double x1 = x + GATE_PROPORTIONS[6][0];
+ double x2 = x + GATE_PROPORTIONS[2][0];
+ double midpointX = (x1 + x2) / 2;
+
+ double y1 = y + GATE_PROPORTIONS[6][1];
+ double y2 = y + GATE_PROPORTIONS[2][1];
+ double midpointY = (y1 + y2) / 2 + 3;
+
+ createText("Gate_" + activityId, midpointX, midpointY, "0", "middle", "10", "Verdana", "fill:#FFFFFF;stroke:#FFFFFF;stroke-width:.5;", "STOP", g);
+
+ } else if (activity.getActivityTypeID().equals(Activity.PARALLEL_ACTIVITY_TYPE)) {
+ // This is a parallel activity
+
+ String style = "stroke:black;stroke-width:1;opacity:1;fill:#d0defd";
+
+ // if the parallel is grouped, show it
+ if (activity.getApplyGrouping()) {
+ createGroupingEffect("grouping-" + activityId, x, y, width, height, style, g);
+ }
+
+ //TODO may be switch to using the following operators... createRectangle(null, x, y, width, height, style, g);
+ Element parallelContainer = doc.createElementNS(SVG_NAMESPACE, "rect");
+ parallelContainer.setAttributeNS(null, "x", Integer.toString(x));
+ parallelContainer.setAttributeNS(null, "y", Integer.toString(y));
+ parallelContainer.setAttributeNS(null, "width", Integer.toString(width));
+ parallelContainer.setAttributeNS(null, "height", Integer.toString(height));
+ parallelContainer.setAttributeNS(null, "style", style);
+ g.appendChild(parallelContainer);
+
+ Element parallelHeader = doc.createElementNS(SVG_NAMESPACE, "rect");
+ parallelHeader.setAttributeNS(null, "x", Integer.toString(x +4));
+ parallelHeader.setAttributeNS(null, "y", Integer.toString(y +5));
+ parallelHeader.setAttributeNS(null, "width", Integer.toString(width -8));
+ parallelHeader.setAttributeNS(null, "height", Integer.toString(23));
+ parallelHeader.setAttributeNS(null, "style", "fill:#A9C8FD;stroke:#E1F0FD;stroke-width:2.2;opacity:1");
+ g.appendChild(parallelHeader);
+
+ if (StringUtils.isNotEmpty(text)) {
+ createText("TextElement-" + activityId, x +9, y +19, null, "start", "12", "Arial", "fill:#828990", text, g);
+ }
+
+ } else if (activity.getActivityTypeID().equals(Activity.CHOSEN_BRANCHING_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.GROUP_BRANCHING_ACTIVITY_TYPE)
+ || activity.getActivityTypeID().equals(Activity.TOOL_BRANCHING_ACTIVITY_TYPE)) {
+ // This is a branching activity
+
+ // Given that for now all parallel activities are just two activities, we can hard code the width and height
+ String style = "stroke:black;stroke-width:1;opacity:1;fill:#d0defd";
+
+ // if the parallel is grouped, show it
+ if (activity.getApplyGrouping()) {
+ createGroupingEffect("grouping-" + activityId, x, y, width, height, style, g);
+ }
+
+ Element branchingContainer = doc.createElementNS(SVG_NAMESPACE, "rect");
+ branchingContainer.setAttributeNS(null, "x", Integer.toString(x));
+ branchingContainer.setAttributeNS(null, "y", Integer.toString(y));
+ branchingContainer.setAttributeNS(null, "width", Integer.toString(width));
+ branchingContainer.setAttributeNS(null, "height", Integer.toString(height));
+ branchingContainer.setAttributeNS(null, "style", style);
+ g.appendChild(branchingContainer);
+
+ int startingPointX = x + 26;
+ int startingPointY = y + 40;
+ if (node.getChildCount() == 4) {
+ startingPointY -= 12;
+ } else if (node.getChildCount() > 4) {
+ startingPointY -= 2*12;
+ }
+
+ Element startingPoint = doc.createElementNS(SVG_NAMESPACE, "rect");
+ startingPoint.setAttributeNS(null, "x", Integer.toString(startingPointX));
+ startingPoint.setAttributeNS(null, "y", Integer.toString(startingPointY));
+ startingPoint.setAttributeNS(null, "width", Double.toString(BRANCHING_ACTIVITY_POINT + 0.5));
+ startingPoint.setAttributeNS(null, "height", Double.toString(BRANCHING_ACTIVITY_POINT + 0.5));
+ startingPoint.setAttributeNS(null, "style", "fill:#000000");
+ g.appendChild(startingPoint);
+
+ Iterator sequenceNodeIterator = node.getChildren().iterator();
+ for (int sequenceIndex = -1; sequenceNodeIterator.hasNext() && (sequenceIndex < 4); sequenceIndex++) {
+ ActivityTreeNode sequenceNode = sequenceNodeIterator.next();
+ double previousActivityPointX = startingPointX + BRANCHING_ACTIVITY_POINT/2;
+ double previousActivityPointY = startingPointY + BRANCHING_ACTIVITY_POINT/2;
+
+ // Create the lines
+ Iterator activityNodeIterator = sequenceNode.getChildren().iterator();
+ for (int activityIndex=1; activityNodeIterator.hasNext() && (activityIndex <= 6); activityIndex++, activityNodeIterator.next()) {
+ double activityPointX = startingPointX + activityIndex*BRANCHING_STEP + BRANCHING_ACTIVITY_POINT/2;
+ double activityPointY = startingPointY + sequenceIndex*BRANCHING_STEP + BRANCHING_ACTIVITY_POINT/2;
+
+ Element line = doc.createElementNS(SVG_NAMESPACE, "line");
+ line.setAttributeNS(null, "x1", Double.toString(previousActivityPointX));
+ line.setAttributeNS(null, "y1", Double.toString(previousActivityPointY));
+ line.setAttributeNS(null, "x2", Double.toString(activityPointX));
+ line.setAttributeNS(null, "y2", Double.toString(activityPointY));
+ line.setAttributeNS(null, "style", "stroke:black;stroke-width:1;");
+ g.appendChild(line);
+
+ previousActivityPointX = activityPointX;
+ previousActivityPointY = activityPointY;
+ }
+
+ //check if we need to draw line connecting last activity with endingPoint
+ if (!sequenceNode.getActivity().getStopAfterActivity()) {
+ Element line = doc.createElementNS(SVG_NAMESPACE, "line");
+ line.setAttributeNS(null, "x1", Double.toString(previousActivityPointX));
+ line.setAttributeNS(null, "y1", Double.toString(previousActivityPointY));
+ line.setAttributeNS(null, "x2", Double.toString(x + 132 + BRANCHING_ACTIVITY_POINT / 2));
+ line.setAttributeNS(null, "y2", Double.toString(startingPointY + BRANCHING_ACTIVITY_POINT / 2));
+ line.setAttributeNS(null, "style", "stroke:black;stroke-width:1;");
+ g.appendChild(line);
+
+ }
+
+ // Create activity points
+ activityNodeIterator = sequenceNode.getChildren().iterator();
+ for (int activityIndex=1; activityNodeIterator.hasNext() && (activityIndex <= 6); activityIndex++) {
+ ActivityTreeNode activityNode = activityNodeIterator.next();
+ String activityStyle = sequenceNode.getActivity().getStopAfterActivity()&&!activityNodeIterator.hasNext() ? "stroke:red" : "stroke:black";
+ activityStyle += ";stroke-width:0.8;opacity:1" + activityNode.getActivityColor();
+ double activityPointX = startingPointX + activityIndex*BRANCHING_STEP;
+ double activityPointY = startingPointY + sequenceIndex*BRANCHING_STEP;
+
+ Element activityPoint = doc.createElementNS(SVG_NAMESPACE, "rect");
+ activityPoint.setAttributeNS(null, "x", Double.toString(activityPointX));
+ activityPoint.setAttributeNS(null, "y", Double.toString(activityPointY));
+ activityPoint.setAttributeNS(null, "width", "" + BRANCHING_ACTIVITY_POINT);
+ activityPoint.setAttributeNS(null, "height", "" + BRANCHING_ACTIVITY_POINT);
+ activityPoint.setAttributeNS(null, "style", activityStyle);
+ g.appendChild(activityPoint);
+ }
+ }
+
+ Element endingPoint = doc.createElementNS(SVG_NAMESPACE, "rect");
+ endingPoint.setAttributeNS(null, "x", Integer.toString(x + 132));
+ endingPoint.setAttributeNS(null, "y", Integer.toString(startingPointY));
+ endingPoint.setAttributeNS(null, "width", Double.toString(BRANCHING_ACTIVITY_POINT + 0.5));
+ endingPoint.setAttributeNS(null, "height", Double.toString(BRANCHING_ACTIVITY_POINT + 0.5));
+ endingPoint.setAttributeNS(null, "style", "fill:#000000");
+ g.appendChild(endingPoint);
+
+ if (StringUtils.isNotEmpty(text)) {
+ createText("TextElement-" + activityId, x + BRANCHING_ACTIVITY_WIDTH/2, y +90, null, "middle", "11.4", "Verdana", null, text, g);
+ }
+
+ } else if (activity.getActivityTypeID().equals(Activity.OPTIONS_WITH_SEQUENCES_TYPE)) {
+ // This is an optional sequence
+
+ Element optionalContainer = doc.createElementNS(SVG_NAMESPACE, "rect");
+ optionalContainer.setAttributeNS(null, "x", Integer.toString(x));
+ optionalContainer.setAttributeNS(null, "y", Integer.toString(y));
+ optionalContainer.setAttributeNS(null, "width", Integer.toString(width));
+ optionalContainer.setAttributeNS(null, "height", Integer.toString(height));
+ optionalContainer.setAttributeNS(null, "style", "opacity:1;fill:#d0defd");
+ g.appendChild(optionalContainer);
+
+ Element optionalHeader = doc.createElementNS(SVG_NAMESPACE, "rect");
+ optionalHeader.setAttributeNS(null, "x", Integer.toString(x +4));
+ optionalHeader.setAttributeNS(null, "y", Integer.toString(y +5));
+ optionalHeader.setAttributeNS(null, "width", Integer.toString(width -8));
+ optionalHeader.setAttributeNS(null, "height", Integer.toString(CONTAINER_HEADER_HEIGHT));
+ optionalHeader.setAttributeNS(null, "style", "fill:#A9C8FD;stroke:#E1F0FD;stroke-width:2.2;opacity:1");
+ g.appendChild(optionalHeader);
+
+ if (StringUtils.isNotEmpty(text)) {
+ createText("TextElement-" + activityId, x +9, y +19, null, "start", "12", "Arial", "fill:#828990", text, g);
+ }
+
+ int optionalSequencesSize = node.getChildCount();
+ createText("Children-" + activityId, x +9, y +19*2+1, null, "start", "11", "Arial", "fill:#828990", optionalSequencesSize + " - Sequences", g);
+
+ } else if (activity.getActivityTypeID().equals(Activity.SEQUENCE_ACTIVITY_TYPE)) {
+ // This is a sequence within an optional
+
+ ActivityTreeNode parentNode = (ActivityTreeNode) node.getParent();
+ int indexInSiblings = parentNode.getIndex(node) % 6;
+ String color;
+ switch (indexInSiblings) {
+ case 1:
+ color = "BCD0FF";
+ break;
+ case 2:
+ color = "C7F9AE";
+ break;
+ case 3:
+ color = "FFEDC3";
+ break;
+ case 4:
+ color = "EDDDF9";
+ break;
+ case 5:
+ color = "E9E9E9";
+ break;
+ default:
+ color = "FFFFB3";
+ }
+
+ Element sequenceContainer = doc.createElementNS(SVG_NAMESPACE, "rect");
+ sequenceContainer.setAttributeNS(null, "x", Integer.toString(x));
+ sequenceContainer.setAttributeNS(null, "y", Integer.toString(y));
+ sequenceContainer.setAttributeNS(null, "width", Integer.toString(width));
+ sequenceContainer.setAttributeNS(null, "height", Integer.toString(height));
+ sequenceContainer.setAttributeNS(null, "style", "stroke:#E1F0FD;stroke-width:.4;opacity:1;fill:#" + color);
+ g.appendChild(sequenceContainer);
+
+ } else if (activity.getActivityTypeID().equals(Activity.OPTIONS_ACTIVITY_TYPE)) {
+ // This is an optional activity
+
+ int childActivitiesSize = node.getChildCount();
+
+ // Create rect
+ Element optionalContainer = doc.createElementNS(SVG_NAMESPACE, "rect");
+ optionalContainer.setAttributeNS(null, "x", Integer.toString(x));
+ optionalContainer.setAttributeNS(null, "y", Integer.toString(y));
+ optionalContainer.setAttributeNS(null, "width", Integer.toString(width));
+ optionalContainer.setAttributeNS(null, "height", Integer.toString(height));
+ optionalContainer.setAttributeNS(null, "style", "opacity:1;fill:#d0defd");
+ g.appendChild(optionalContainer);
+
+ Element optionalHeader = doc.createElementNS(SVG_NAMESPACE, "rect");
+ optionalHeader.setAttributeNS(null, "x", Integer.toString(x + 4));
+ optionalHeader.setAttributeNS(null, "y", Integer.toString(y + 5));
+ optionalHeader.setAttributeNS(null, "width", Integer.toString(width - 8));
+ optionalHeader.setAttributeNS(null, "height", Integer.toString(CONTAINER_HEADER_HEIGHT));
+ optionalHeader.setAttributeNS(null, "style", "fill:#A9C8FD;stroke:#E1F0FD;stroke-width:2.2;opacity:1");
+ g.appendChild(optionalHeader);
+
+ if (StringUtils.isNotEmpty(text)) {
+ createText("TextElement-" + activityId, x +9, y +19, null, "start", "12", "Arial", "fill:#828990", text, g);
+ }
+
+ createText("Children-" + activityId, x +9, y +19*2+1, null, "start", "11", "Arial", "fill:#828990", childActivitiesSize + " - Activities", g);
+
+ } else if (activity.getActivityTypeID().equals(Activity.FLOATING_ACTIVITY_TYPE)) {
+ // This is a support activity
+
+ Element supportContainer = doc.createElementNS(SVG_NAMESPACE, "rect");
+ supportContainer.setAttributeNS(null, "x", Integer.toString(x));
+ supportContainer.setAttributeNS(null, "y", Integer.toString(y));
+ supportContainer.setAttributeNS(null, "width", Integer.toString(width));
+ supportContainer.setAttributeNS(null, "height", Integer.toString(height));
+ supportContainer.setAttributeNS(null, "style", "opacity:1;fill:#d0defd");
+ g.appendChild(supportContainer);
+
+ Element supportHeader = doc.createElementNS(SVG_NAMESPACE, "rect");
+ supportHeader.setAttributeNS(null, "x", Integer.toString(x +4));
+ supportHeader.setAttributeNS(null, "y", Integer.toString(y +5));
+ supportHeader.setAttributeNS(null, "width", Integer.toString(width -8));
+ supportHeader.setAttributeNS(null, "height", Integer.toString(CONTAINER_HEADER_HEIGHT));
+ supportHeader.setAttributeNS(null, "style", "fill:#A9C8FD;stroke:#E1F0FD;stroke-width:2.2;opacity:1");
+ g.appendChild(supportHeader);
+
+ if (StringUtils.isNotEmpty(text)) {
+ createText("TextElement-" + activityId, x +9, y +19, null, "start", "12", "Arial", "fill:#828990", text, g);
+ }
+
+ int supportActivityChildrenSize = node.getChildCount();
+ createText("Children-" + activityId, x +9, y +19*2+1, null, "start", "11", "Arial", "fill:#828990", supportActivityChildrenSize + " - Activities", g);
+
+ } else {
+ // This is a tool activity
+
+ // if this activity is a children of a sequence activity, if it is, then we need to change its size
+ if (node.isOptionalSequenceActivityChild()) {
+ ActivityTreeNode parentNode = (ActivityTreeNode) node.getParent();
+ AuthoringActivityDTO grandParentActivity = parentNode.getParentActivity();
+ x += (grandParentActivity.getxCoord() == null) ? 0 : grandParentActivity.getxCoord();
+ y += (grandParentActivity.getyCoord() == null) ? 0 : grandParentActivity.getyCoord();
+ text = null;
+ }
+
+ String style = "stroke:black;stroke-width:0.8;opacity:1" + node.getActivityColor();
+
+ // if activity uses a grouping we need to add a second rect layer to show that it's grouped
+ if (activity.getApplyGrouping()) {
+ createGroupingEffect("grouping-" + activityId, x, y, width, height, style, g);
+ }
+
+ // Create rect
+ Element activityRectangle = doc.createElementNS(SVG_NAMESPACE, "rect");
+ activityRectangle.setAttributeNS(null, "id", "act" + activityId);
+ activityRectangle.setAttributeNS(null, "x", Integer.toString(x));
+ activityRectangle.setAttributeNS(null, "y", Integer.toString(y));
+ activityRectangle.setAttributeNS(null, "width", width.toString());
+ activityRectangle.setAttributeNS(null, "height", height.toString());
+ activityRectangle.setAttributeNS(null, "style", style);
+ g.appendChild(activityRectangle);
+
+ // Create text label
+ if (text != null) {
+ int xText = x + (width / 2);
+ int yText = y + (height / 2) + 18;
+ createText("TextElement-" + activityId, xText, yText, null, "middle", "11.4", "Verdana", null, text, g);
+ }
+
+ // Create image
+ int imageX = x + (width / 2) - 15;
+ int imageY = y + (height / 2) - 22;
+ if (node.isOptionalSequenceActivityChild()) {
+ imageX += 2;
+ imageY += 7;
+ }
+ String imagePath = activity.getLibraryActivityUIImage();
+ // if png_filename is empty then this is a grouping act:
+ String imageFileName;
+ if (StringUtils.isBlank(imagePath)) {
+ imageFileName = "icon_grouping.png";
+ } else {
+ imageFileName = FileUtil.getFileName(imagePath);
+ imageFileName = imageFileName.replaceFirst(".swf$", ".png");
+ }
+ imageFileName = "http://lamscommunity.org/lamscentral/images/acts/" + imageFileName;
+ Element imageNode = doc.createElementNS(SVG_NAMESPACE, "image");
+ imageNode.setAttributeNS(null, "id", "image-" + activityId);
+ imageNode.setAttributeNS(null, "x", Integer.toString(imageX));
+ imageNode.setAttributeNS(null, "y", Integer.toString(imageY));
+ imageNode.setAttributeNS(SVG_NAMESPACE_XLINK, "xlink:href", imageFileName);
+ imageNode.setAttributeNS(null, "width", Integer.toString(30));
+ imageNode.setAttributeNS(null, "height", Integer.toString(30));
+ g.appendChild(imageNode);
+ }
+
+ }
+
+ private void createRectangle(String id, double x, double y, Integer width, Integer height, String style, Element g) {
+
+ if (style == null) {
+ style = "";
+ }
+
+ Element rectangle = doc.createElementNS(SVG_NAMESPACE, "rect");
+ if (id != null) {
+ rectangle.setAttributeNS(null, "id", id);
+ }
+ rectangle.setAttributeNS(null, "x", Double.toString(x));
+ rectangle.setAttributeNS(null, "y", Double.toString(y));
+ rectangle.setAttributeNS(null, "width", Double.toString(width));
+ rectangle.setAttributeNS(null, "height", Double.toString(height));
+ rectangle.setAttributeNS(null, "style", style);
+ g.appendChild(rectangle);
+ }
+
+ private void createGroupingEffect(String id, double x, double y, double width, double height, String style,
+ Element g) {
+
+ Element groupingRectangle = doc.createElementNS(SVG_NAMESPACE, "rect");
+
+ groupingRectangle.setAttributeNS(null, "id", id);
+ groupingRectangle.setAttributeNS(null, "x", Double.toString(x + 4));
+ groupingRectangle.setAttributeNS(null, "y", Double.toString(y + 4));
+ groupingRectangle.setAttributeNS(null, "width", Double.toString(width));
+ groupingRectangle.setAttributeNS(null, "height",Double.toString( height));
+ groupingRectangle.setAttributeNS(null, "style", style + ";stroke:#3b3b3b;stroke-width:3");
+
+ g.appendChild(groupingRectangle);
+ }
+
+ private void createText(String id, double x, double y, String dy, String textAnchor, String fontSize,
+ String fontFamily, String style, String text, Element g) {
+
+ if (dy == null) {
+ dy = "0";
+ }
+ if (textAnchor == null) {
+ textAnchor = "start";
+ }
+ if (fontSize == null) {
+ fontSize = "11.4";
+ }
+ if (fontFamily == null) {
+ fontFamily = "Verdana";
+ }
+
+ //trim text to fit into container
+ if (text.length() > 21) {
+ text = text.substring(0, 20);
+ }
+
+ Element textNode = doc.createElementNS(SVG_NAMESPACE, "text");
+ Node textContent = doc.createTextNode(text);
+ textNode.appendChild(textContent);
+
+ textNode.setAttributeNS(null, "id", id);
+ textNode.setAttributeNS(null, "x", "" + x);
+ textNode.setAttributeNS(null, "y", "" + y);
+ textNode.setAttributeNS(null, "dy", dy);
+ textNode.setAttributeNS(null, "text-anchor", textAnchor);
+ textNode.setAttributeNS(null, "font-size", fontSize);
+ textNode.setAttributeNS(null, "font-family", fontFamily);
+ if (style != null) {
+ textNode.setAttributeNS(null, "style", style);
+ }
+
+ g.appendChild(textNode);
+ }
+
+ public static void main(String[] args) throws JDOMException, IOException {
+
+ if (args.length != 1) {
+ System.err.println("Usage: java SVGGenerator fullFilePath");
+ System.exit(1);
+ }
+
+ String fullFilePath = args[0];
+
+ // import learning design
+ LearningDesignDTO learningDesign = (LearningDesignDTO) FileUtil.getObjectFromXML(null, fullFilePath);
+
+ SVGGenerator svgGenerator = SVGGenerator.getInstance();
+ svgGenerator.generateSvg(learningDesign);
+
+// // Stream out svg document to display
+// OutputFormat format = new OutputFormat(svgGenerator.getSVGDocument());
+// format.setLineWidth(65);
+// format.setIndenting(true);
+// format.setIndent(2);
+// Writer out = new StringWriter();
+// XMLSerializer serializer = new XMLSerializer(out, format);
+// serializer.serialize(svgGenerator.getSVGDocument());
+// System.out.println(out.toString());
+
+ OutputFormat format = new OutputFormat(svgGenerator.getSVGDocument());
+ format.setLineWidth(65);
+ format.setIndenting(true);
+ format.setIndent(2);
+ // Create file
+ String svgFileName = FileUtil.getFileName(fullFilePath);
+ String fileExtension = FileUtil.getFileExtension(svgFileName);
+ svgFileName = svgFileName.replaceFirst(fileExtension + "$", "svg");
+ String svgFileFullPath = FileUtil.getFullPath(FileUtil.getFileDirectory(fullFilePath), svgFileName);
+ FileWriter fstream = new FileWriter(svgFileFullPath);
+ BufferedWriter out = new BufferedWriter(fstream);
+ XMLSerializer serializer = new XMLSerializer(out, format);
+ serializer.serialize(svgGenerator.getSVGDocument());
+ System.out.println("Creating a file " + svgFileFullPath );
+ // Close the output stream
+ out.close();
+ }
+
+}
Index: lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGTrigonometryUtils.java
===================================================================
diff -u
--- lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGTrigonometryUtils.java (revision 0)
+++ lams_common/src/java/org/lamsfoundation/lams/util/svg/SVGTrigonometryUtils.java (revision ef201100ffd7ebdfcb18c72528f062379e4c65d3)
@@ -0,0 +1,129 @@
+/****************************************************************
+ * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org)
+ * =============================================================
+ * License Information: http://lamsfoundation.org/licensing/lams/2.0/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2.0
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA
+ *
+ * http://www.gnu.org/licenses/gpl.txt
+ * ****************************************************************
+ */
+
+/* $Id$ */
+package org.lamsfoundation.lams.util.svg;
+
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+
+
+/**
+ * @author Andrey Balan
+ */
+public class SVGTrigonometryUtils {
+
+ /**
+ * @param rectangleActivity we draw rectangle based on this activity
+ * @param toActivity draw transition to this activity
+ * @return
+ */
+ public static Point2D getRectangleAndLineSegmentIntersection(ActivityTreeNode rectangleActivity, ActivityTreeNode toActivity) {
+ //rectangles dimensions
+ int rectangleX = rectangleActivity.getActivityCoordinates().x;
+ int rectangleY = rectangleActivity.getActivityCoordinates().y;
+ int width = rectangleActivity.getActivityDimension().width;
+ int height = rectangleActivity.getActivityDimension().height;
+
+ //construct rectangle's lines
+ Line2D topLine = new Line2D.Double(rectangleX, rectangleY, rectangleX + width, rectangleY);
+ Line2D rightLine = new Line2D.Double(rectangleX + width, rectangleY, rectangleX + width, rectangleY + height);
+ Line2D bottomLine = new Line2D.Double(rectangleX, rectangleY + height, rectangleX + width, rectangleY + height);
+ Line2D leftLine = new Line2D.Double(rectangleX, rectangleY, rectangleX, rectangleY + height);
+
+ //calculate MiddlePoint of the second rectangle
+ int transitionToX = toActivity.getActivityCoordinates().x + toActivity.getActivityDimension().width /2;
+ int transitionToY = toActivity.getActivityCoordinates().y + toActivity.getActivityDimension().height /2;
+
+ Line2D transitionLine = new Line2D.Double(rectangleX + width/2, rectangleY + height/2, transitionToX, transitionToY);
+
+ Point2D intersectionPoint = null;
+ if (isLineSegmentsIntersect(topLine, transitionLine)) {
+ intersectionPoint = getLinesIntersection(topLine, transitionLine);
+ } else if (isLineSegmentsIntersect(rightLine, transitionLine)) {
+ intersectionPoint = getLinesIntersection(rightLine, transitionLine);
+ } else if (isLineSegmentsIntersect(bottomLine, transitionLine)) {
+ intersectionPoint = getLinesIntersection(bottomLine, transitionLine);
+ } else if (isLineSegmentsIntersect(leftLine, transitionLine)) {
+ intersectionPoint = getLinesIntersection(leftLine, transitionLine);
+ }
+
+ return intersectionPoint;
+ }
+
+ /**
+ * Computes the intersection between first line (x1, y1)--(x2, y2) and second one (x3, y3)--(x4, y4)
+ *
+ * @return Point where the lines intersect, or null if they don't
+ */
+ private static Point2D getLinesIntersection(Line2D line1, Line2D line2) {
+ double x1 = line1.getX1();
+ double y1 = line1.getY1();
+ double x2 = line1.getX2();
+ double y2 = line1.getY2();
+ double x3 = line2.getX1();
+ double y3 = line2.getY1();
+ double x4 = line2.getX2();
+ double y4 = line2.getY2();
+
+ double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
+ if (d == 0)
+ return null;
+
+ double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
+ double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
+
+ return new Point2D.Double(xi, yi);
+ }
+
+ /** Do line segments (x1, y1)--(x2, y2) and (x3, y3)--(x4, y4) intersect? */
+ private static boolean isLineSegmentsIntersect(Line2D line1, Line2D line2) {
+ double x1 = line1.getX1();
+ double y1 = line1.getY1();
+ double x2 = line1.getX2();
+ double y2 = line1.getY2();
+ double x3 = line2.getX1();
+ double y3 = line2.getY1();
+ double x4 = line2.getX2();
+ double y4 = line2.getY2();
+
+ int d1 = computeDirection(x3, y3, x4, y4, x1, y1);
+ int d2 = computeDirection(x3, y3, x4, y4, x2, y2);
+ int d3 = computeDirection(x1, y1, x2, y2, x3, y3);
+ int d4 = computeDirection(x1, y1, x2, y2, x4, y4);
+ return (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0)) && ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0)))
+ || (d1 == 0 && isOnSegment(x3, y3, x4, y4, x1, y1)) || (d2 == 0 && isOnSegment(x3, y3, x4, y4, x2, y2))
+ || (d3 == 0 && isOnSegment(x1, y1, x2, y2, x3, y3)) || (d4 == 0 && isOnSegment(x1, y1, x2, y2, x4, y4));
+ }
+
+ private static boolean isOnSegment(double xi, double yi, double xj, double yj, double xk, double yk) {
+ return (xi <= xk || xj <= xk) && (xk <= xi || xk <= xj) && (yi <= yk || yj <= yk) && (yk <= yi || xk <= yj);
+ }
+
+ private static int computeDirection(double xi, double yi, double xj, double yj, double xk, double yk) {
+ double a = (xk - xi) * (yj - yi);
+ double b = (xj - xi) * (yk - yi);
+
+ return a < b ? -1 : a > b ? 1 : 0;
+ }
+
+}