Commits

Anonymous committed 910aa01 Draft

changed Node to AMINode and added README

Comments (0)

Files changed (11)

-Installing.
+==Overview==
+AMI2 is a general approach to Open Content Mining of STM documents. It is designed to extract semantic 
+information from both textual and diagrammatic content (if present as graphical primitives, not bitmaps)
+
+==Installing==
 AMI2 is a Java/maven project using a number of third-party and PMR libraries. The latest version is at:
 https://bitbucket.org/petermr/ami2
 You will need mercurial to download/sync the code.
 hg clone https://petermr@bitbucket.org/petermr/ami2
 which will download the latest version into a new directory ami2 (unxedr the current dir)
 
-Running from commandline
+==Libraries==
+AMI2 depends on the following PMRGroup libraries:
+https://petermr@bitbucket.org/wwmm/euclid-testutils
+https://petermr@bitbucket.org/wwmm/euclid
+https://petermr@bitbucket.org/wwmm/cmlxom
+https://petermr@bitbucket.org/wwmm/jumbo-testutils
+https://petermr@bitbucket.org/wwmm/jumbo6
+https://petermr@bitbucket.org/petermr/svg
+https://petermr@bitbucket.org/petermr/html
+In alpha-phase the safest approach is to clone these and for each:
+    cd foo
+    mvn clean install
+(At a later stage we shall include these in the wwmm repo at http://wwmm.ch.cam.ac.uk)
+The install should generate entries in your maven repo e.g:
+  .m2\repository\org\xml-cml
+with components similar to (* may be absent at this stage):
+  16/06/2012  15:04    <DIR>          cifxom*
+  10/10/2012  06:35    <DIR>          cmlxom
+  10/10/2012  06:35    <DIR>          euclid
+  15/06/2012  13:18    <DIR>          euclid-testutil
+  16/06/2012  18:40    <DIR>          html
+  22/06/2012  16:43    <DIR>          jc*
+  14/10/2012  01:29    <DIR>          jtmt1*
+  15/06/2012  13:56    <DIR>          jumbo
+  10/10/2012  06:35    <DIR>          jumbo-testutil
+  24/06/2012  14:06    <DIR>          svg
 
-current 2012-10-04
-C:\Users\pm286\workspace\ami2>
-mvn -e exec:java -Dexec.mainClass="org.xmlcml.graphics.pdf2svg.PDF2XMLConverter" 
--Dexec.args="-c src/main/resources/org/xmlcml/graphics/styles/bmc/basic.xml -i src/test/resources/test"
 
-This will run the commands in bmcDirectory.xml against the PDF files in the directory pdfs/bmcevolbio/2
+==Running from commandline==
+At a later stage we expect to have a jar-file installed but with the project changing daily 
+you need to run from the ami2 directory:
+  cd ami2
+and run PDF2XMLConverter with arguments: 
+  mvn -e exec:java -Dexec.mainClass="org.xmlcml.graphics.pdf2svg.PDF2XMLConverter" 
+    -Dexec.args="-c src/main/resources/org/xmlcml/graphics/styles/basic.xml -i src/test/resources/test"
 
+-c commandfile (the commands to execute, here "basic.xml").
+-i The directory containing the PDFs to convert.
+Note that maven "home" is where the pom.xml is, so src/ is relative to this or you can use absolute
+filenames
 
+==Operation and output==
+The process involves 2 steps:
+ * convert PDF to SVG with no normalization. output is in ./raw relative to -i directory. 
+    One page ({n}page.svg) is generated for each PDF page.n starts at ZERO (we shall probably 
+    change this to ONE)
+ * convert ./raw/{n}page.svg to ./out/{n}start.svg and ./out/{n}end.svg. The exact filenames depend
+    on the write statements in the commandfile. 
+    
+Note:
+ * each page is converted separately. A thread is launched with a timeout (ca 15 secs) in case the 
+   conversion hangs or takes too long. If the thread fails to complete, no *end*.svg is written.
+ * images are discarded from the output to save space and processing time. This can/will be changed later
+ * the conversion is highly controllable by the commandfile. 
 
+==Control==
+The commandfile is designed for precise control but will normally be generic at some level. Where possible
+the specific input (e.g. directory to convert) should be at commandline and the generic material
+(e.g. page normalization) at controlfile. The controlfile is designed to be as generic as possible 
+but it may be necessary to specify stylefiles for different document types (e.g. publishers). In general
+the commandline overrides the controlfile and allows for the input of values for symbolic variables.
+

src/main/java/org/xmlcml/graphics/cml/AMIEdge.java

+package org.xmlcml.graphics.cml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.xmlcml.euclid.Angle;
+import org.xmlcml.euclid.Line2;
+import org.xmlcml.euclid.Real2;
+import org.xmlcml.euclid.Vector2;
+import org.xmlcml.graphics.cml.AMINode.NodeType;
+import org.xmlcml.graphics.svg.SVGLine;
+
+import com.google.common.collect.Multimap;
+
+public class AMIEdge {
+	private final static Logger LOG = Logger.getLogger(AMIEdge.class);
+
+	private static Double EPS = 0.001;
+	private List<SVGLine> lineList;
+	// slowest index manages 01/ end of line; fastest is number of lines in edge
+	private List<List<Real2>> endPointsList;
+	private ArrayList<AMINode> nodeList;
+	private String id;
+	private AMIGraph graph;
+
+	AMIEdge(AMIGraph graph, SVGLine line) {
+		this.add(line);
+		ensureNodeList();
+		this.graph = graph;
+	}
+	
+	public void add(SVGLine line) {
+		ensureLineList();
+		if (!lineList.contains(line)) {
+			if (lineList.size() > 0) {
+				makeSameDirectionAsLine0(line);
+			}
+			lineList.add(line);
+		}
+	}
+	
+	public String getId() {
+		if (id == null) {
+			setId("e_"+lineList.get(0).getId());
+		}
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+
+	private void makeSameDirectionAsLine0(SVGLine line) {
+		SVGLine line0 = lineList.get(0);
+		Angle angle = line0.getEuclidLine().getAngleMadeWith(line.getEuclidLine());
+		double cosAngle = angle.cos();
+		if (cosAngle < 0) {
+			line0.getEuclidLine().flipCoordinates();
+		}
+	}
+	
+	private void ensureLineList() {
+		if (lineList == null) {
+			lineList = new ArrayList<SVGLine>();
+		}
+	}
+
+	public boolean isTramLine(SVGLine nextLine, double maxAbsSin, double maxMidPointDistanceFactor) {
+		boolean isTramLine = false;
+		SVGLine thisLine = lineList.get(0);
+		Angle angle = thisLine.getEuclidLine().getAngleMadeWith(nextLine.getEuclidLine());
+		double absSin = Math.abs(angle.sin());
+		LOG.trace("abs"+absSin);
+		if (absSin < maxAbsSin) {
+			Real2 thisMidPoint = thisLine.getEuclidLine().getMidPoint();
+			Double thisLength = thisLine.getLength();
+			Real2 nextMidPoint = nextLine.getEuclidLine().getMidPoint();
+			double interLine = thisMidPoint.getDistance(nextMidPoint);
+			LOG.trace("absSin "+absSin+" interline "+interLine +" factor "+maxMidPointDistanceFactor*thisLength);
+			isTramLine = interLine < maxMidPointDistanceFactor*thisLength;
+		}
+		return isTramLine;
+	}
+
+	public AMINode createNodeIfEdgeMeets(AMIEdge nextEdge, List<AMINode> nodeList, double deltaXY) {
+		AMINode node = null;
+		for (int i = 0; i < 2; i++) {
+			List<Real2> thisPoints = this.getEndPoints(i);
+			for (Real2 thisPoint : thisPoints) {
+				for (int j = 0; j < 2; j++) {
+					List<Real2> nextPoints = nextEdge.getEndPoints(j);
+					for (Real2 nextPoint : nextPoints) {
+						if (thisPoint.getDistance(nextPoint) < deltaXY) {
+							//we've found a join
+							LOG.debug("looking "+thisPoint+" "+deltaXY);
+							node = graph.lookupNode(nodeList, thisPoint, deltaXY);
+							if (node == null) {
+								node = AMINode.createAndAddNewNode(nodeList, thisPoint);
+							} else {
+								LOG.debug("existing node: "+node);
+							}
+							mutuallyRegisterEdgesAndNodes(nextEdge, i, j, node);
+						}
+					}
+				}
+			}
+		}
+		LOG.trace("nodeList "+nodeList.size());
+		return node;
+	}
+
+	private void mutuallyRegisterEdgesAndNodes(AMIEdge nextEdge, int iend, int jend, AMINode node) {
+		this.addNodeToEdgeAndEdgeToNode(iend, node);
+		nextEdge.addNodeToEdgeAndEdgeToNode(jend, node);
+		LOG.debug("added Node "+node);
+	}
+
+	public void addNodeToEdgeAndEdgeToNode(int jend, AMINode node) {
+		this.addNode(node, jend);
+		node.add(this, jend);
+	}
+
+	private void addNode(AMINode node, int iend) {
+		ensureNodeList();
+		this.nodeList.set(iend, node);
+		node.add(this, iend);
+	}
+
+	List<Real2> getEndPoints(int iend) {
+		ensureEndPointList();
+		return endPointsList.get(iend);
+	}
+
+	public AMINode getNode(int i) {
+		return nodeList.get(i);
+	}
+
+	public Double getLength() {
+		return (lineList.size() == 0) ? null : lineList.get(0).getLength();
+		
+	}
+
+	private void ensureEndPointList() {
+		if (endPointsList == null) {
+			endPointsList = new ArrayList<List<Real2>>();
+			endPointsList.add(new ArrayList<Real2>());
+			endPointsList.add(new ArrayList<Real2>());
+
+			for (int iend = 0; iend < 2; iend++) {
+				for (SVGLine line : lineList) {
+					endPointsList.get(iend).add(line.getXY(iend));
+				}
+				// add midpoint of two tramlines
+				if (lineList.size() == 2) {
+					Real2 midPoint = lineList.get(0).getXY(iend).getMidPoint(lineList.get(1).getXY(iend));
+					endPointsList.get(iend).add(midPoint);
+				}
+			}
+		}
+	}
+
+	private void ensureNodeList() {
+		if (nodeList == null) {
+			nodeList = new ArrayList<AMINode>();
+			nodeList.add(null);
+			nodeList.add(null);
+		}
+	}
+
+	/**
+	private List<SVGLine> lineList;
+	private List<List<Real2>> endPointsList;
+	private ArrayList<Node> nodeList;
+	 */
+	public String toString() {
+		ensureNodeList();
+		String s = ""+id+" ";
+		s += nodeList.get(0)+" "+lineList.size()+" "+nodeList.get(1);
+		return s;
+	}
+
+	public void addMissingNodesByExtension(Double length) {
+		Real2 projected0 = addMissingNodeByExtension(0, length);
+		Real2 projected1 = addMissingNodeByExtension(1, length);
+		if (projected0 != null) {
+			AMINode newNode = new AMINode(projected0, NodeType.EXTENDED_EDGE);
+			nodeList.set(0, newNode);
+		}
+		if (projected1 != null) {
+			AMINode newNode = new AMINode(projected1, NodeType.EXTENDED_EDGE);
+			nodeList.set(1, newNode);
+		}
+
+	}
+
+	public Real2 addMissingNodeByExtension(int nodeSerial, Double extensionLength) {
+		AMINode node = nodeList.get(nodeSerial);
+		AMINode otherNode = nodeList.get(1 - nodeSerial);
+		Real2 projected = null;
+		if (node == null) {
+			SVGLine line = lineList.get(0);
+			Line2 line2 = line.getEuclidLine();
+			Real2 point = this.getEndPoints(nodeSerial).get(0);
+			int serial = line2.getSerial(point, EPS);
+			Double lineLength = line.getLength();
+			if (otherNode != null) {
+				projected = line2.createPointOnLine(extensionLength, serial);
+			} else {
+				// compute from midPoint
+				Real2 midPoint = line2.getMidPoint();
+				Vector2 vector = new Vector2(line2.getVector().getUnitVector().multiplyBy(extensionLength / 2.));
+				if (serial == 1) {
+					vector.negative();
+				}
+				projected = midPoint.plus(vector);
+			}
+		}
+		return projected;
+	}
+}

src/main/java/org/xmlcml/graphics/cml/AMIGraph.java

+package org.xmlcml.graphics.cml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.xmlcml.euclid.Real2;
+import org.xmlcml.euclid.RealArray;
+import org.xmlcml.graphics.svg.LinePrimitive;
+import org.xmlcml.graphics.svg.SVGLine;
+
+public class AMIGraph {
+	private static Logger LOG = Logger.getLogger(AMIGraph.class);
+
+	// these are fairly crude
+	private static final double MAX_INTERLINE_SIN = 0.08;
+	private static final double MAX_INTERLINE_DIST_FACTOR = 0.4;
+	public static final double NODE_SPREAD = 0.5;
+
+	private List<AMIEdge> edgeList;
+	private List<AMINode> nodeList;
+
+	private List<SVGLine> linePrimitives;
+	
+	public AMIGraph() {
+		
+	}
+
+	public AMINode lookupNode(List<AMINode> nodeList, Real2 thisPoint, double deltaXY) {
+		AMINode node = null;
+		for (AMINode node1 : nodeList) {
+			if (node1.getXY().getDistance(thisPoint) < deltaXY) {
+				node = node1;
+				break; // brutal - take first match
+			}
+		}
+		return node;
+	}
+
+	public AMIEdge createEdge(SVGLine line) {
+		AMIEdge edge = new AMIEdge(this, line);
+		return edge;
+	}
+	
+	private void normalizeMultipleEdges() {
+		ensureEdgeList();
+		List<SVGLine> lines = linePrimitives;
+		for (int i = 0; i < lines.size(); i++) {
+			SVGLine line = lines.get(i);
+			LOG.trace("line "+i);
+			if (line != null) {
+				AMIEdge existingEdge = null;
+				for (AMIEdge edge : edgeList) {
+					if (edge.isTramLine(line, MAX_INTERLINE_SIN, MAX_INTERLINE_DIST_FACTOR)) {
+						edge.add(line);
+						existingEdge = edge;
+						LOG.debug("multipleEdge "+edge);
+						break;
+					}
+				}
+				if (existingEdge == null) {
+					AMIEdge newEdge = this.createEdge(line);
+					LOG.trace("newEdge"+newEdge);
+					lines.set(i,  null);
+					edgeList.add(newEdge);
+				}
+			}
+		}
+	}
+	
+	private void ensureNodeList() {
+		if (nodeList == null) {
+			nodeList = new ArrayList<AMINode>();
+		}
+	}
+
+	private void ensureEdgeList() {
+		if (edgeList == null) {
+			edgeList = new ArrayList<AMIEdge>();
+		}
+	}
+
+	public List<AMIEdge> createEdges(List<SVGLine> linePrimitives) {
+		if (edgeList == null) {
+			this.linePrimitives = linePrimitives;
+			normalizeMultipleEdges();
+		}
+		return edgeList;
+	}
+
+	public List<AMIEdge> getEdges() {
+		createEdges(linePrimitives);
+		return edgeList;
+	}
+
+	public List<AMINode> getNodes() {
+		return nodeList;
+	}
+
+	public void createIntersectionNodes(double delta) {
+		ensureNodeList();
+		createEdges(linePrimitives);
+		for (int i = 0; i < edgeList.size()-1; i++) {
+			AMIEdge edgei = edgeList.get(i);
+			LOG.trace("i "+i+" edgeList "+edgeList.get(i).getId()+"/"+nodeList.size());
+			for (int j = i+1; j < edgeList.size(); j++) {
+				LOG.trace("....j "+j);
+				AMIEdge edgej = edgeList.get(j);
+				AMINode nodeij = edgei.createNodeIfEdgeMeets(edgej, nodeList, delta);
+			}
+		}
+	}
+
+	public List<AMIEdge> getEdgesWithNodes(int count) {
+		List<AMIEdge> edgesWithNodeCount = new ArrayList<AMIEdge>();
+		List<AMIEdge> edges = getEdges();
+		for (AMIEdge edge : edges) {
+			if (count == 0) {
+				if (edge.getNode(0) == null && edge.getNode(1) == null) {
+					edgesWithNodeCount.add(edge);
+				}
+			} else if (count == 2) {
+				if (edge.getNode(0) != null && edge.getNode(1) != null) {
+					edgesWithNodeCount.add(edge);
+				}
+			} else if (count == 1) {
+				if ((edge.getNode(0) != null && edge.getNode(1) == null) ||
+				    (edge.getNode(0) == null && edge.getNode(1) != null)) {
+					edgesWithNodeCount.add(edge);
+				}
+			}
+		}
+		return edgesWithNodeCount;
+	}
+
+	public List<AMIEdge> createListOfNullNodeEdges() {
+		List<AMIEdge> nullNodeEdges = new ArrayList<AMIEdge>();
+		for (AMIEdge nullNodeEdge : this.getEdgesWithNodes(0)) {
+			nullNodeEdges.add(nullNodeEdge);
+		}
+		for (AMIEdge nullNodeEdge : this.getEdgesWithNodes(1)) {
+			nullNodeEdges.add(nullNodeEdge);
+		}
+		return nullNodeEdges;
+	}
+
+	public Double getAverageEdgeLengthBetweenNodes() {
+		RealArray realArray = new RealArray();
+		for (AMIEdge edge : this.getEdgesWithNodes(2)) {
+			Double length = edge.getLength();
+			if (length != null) {
+				realArray.addElement(length);
+			}
+		}
+		return realArray.size() == 0 ? null : realArray.getMean();
+	}
+}

src/main/java/org/xmlcml/graphics/cml/AMINode.java

+package org.xmlcml.graphics.cml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.xmlcml.euclid.Real2;
+import org.xmlcml.graphics.cml.AMINode.NodeType;
+
+public class AMINode {
+
+	public enum NodeType {
+		EXTENDED_EDGE, 
+		LINE_END;
+	}
+	
+	private Real2 xy;
+	private List<AMIEdge> edgeList;
+	private String id;
+	private NodeType nodeType;
+
+	public AMINode(Real2 xy, NodeType nodeType) {
+		this.xy = xy;
+		this.nodeType = nodeType;
+	}
+
+	public Real2 getXY() {
+		return xy;
+	}
+
+	public void add(AMIEdge edge, int iend) {
+		enableEdgeList();
+		edgeList.add(edge);
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+	
+	private void enableEdgeList() {
+		if (edgeList == null) {
+			edgeList = new ArrayList<AMIEdge>();
+		}
+	}
+	
+	public String toString() {
+		String s = "node: "+id+" "+ xy;
+		return s;
+	}
+
+	public void setType(NodeType nodeType) {
+		this.nodeType = nodeType;
+	}
+
+	public static AMINode createAndAddNewNode(List<AMINode> nodeList, Real2 thisPoint) {
+		AMINode node = new AMINode(thisPoint, NodeType.LINE_END);
+		node.setId("node"+nodeList.size());
+		nodeList.add(node);
+		return node;
+	}
+
+}

src/main/java/org/xmlcml/graphics/cml/CMLAnalyzer.java

 package org.xmlcml.graphics.cml;
 
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 
 import nu.xom.Attribute;
 import org.apache.log4j.Logger;
 import org.xmlcml.cml.element.CMLFormula;
 import org.xmlcml.euclid.Real2;
-import org.xmlcml.euclid.RealArray;
 import org.xmlcml.graphics.control.page.PageAnalyzer;
 import org.xmlcml.graphics.pdf2svg.AbstractSVGAnalyzer;
 import org.xmlcml.graphics.svg.SVGElement;
 	private static final String ELEMENT_COUNT = "elementCount";
 	private static final String ELEMENT = "element";
 	private static final String FORMULA = "formula";
-	// these are fairly crude
-	private static final double MAX_INTERLINE_SIN = 0.08;
-	private static final double MAX_INTERLINE_DIST_FACTOR = 0.4;
-	public static final double NODE_SPREAD = 0.5;
 
 	private SVGG g;
 	private List<SVGLine> linePrimitives;
 	private List<SVGText> textPrimitives;
 	private ListMultimap<String, Real2> textCentreMap;
 	private RGroupManager rGroupManager;
-	private List<Edge> edgeList;
-	private List<Node> nodeList;
 
 	public CMLAnalyzer() {
 		super(new PageAnalyzer());
 
 	}
 
-	public List<Edge> createEdges() {
-		if (edgeList == null) {
-			getLinePrimitives();
-			normalizeMultipleEdges();
-		}
-		return edgeList;
+	public AMIGraph createGraphWithLinePrimitives() {
+		AMIGraph graph = new AMIGraph();
+		graph.createEdges(linePrimitives);
+		return graph;
 	}
 
-	public List<Edge> getEdges() {
-		createEdges();
-		return edgeList;
-	}
 
-	public List<Node> getNodes() {
-		return nodeList;
-	}
-
-	private void normalizeMultipleEdges() {
-		ensureEdgeList();
-		List<SVGLine> lines = linePrimitives;
-		for (int i = 0; i < lines.size(); i++) {
-			SVGLine line = lines.get(i);
-			LOG.trace("line "+i);
-			if (line != null) {
-				Edge existingEdge = null;
-				for (Edge edge : edgeList) {
-					if (edge.isTramLine(line, MAX_INTERLINE_SIN, MAX_INTERLINE_DIST_FACTOR)) {
-						edge.add(line);
-						existingEdge = edge;
-						LOG.debug("multipleEdge "+edge);
-						break;
-					}
-				}
-				if (existingEdge == null) {
-					Edge newEdge = new Edge(line);
-					LOG.trace("newEdge"+newEdge);
-					lines.set(i,  null);
-					edgeList.add(newEdge);
-				}
-			}
-		}
-	}
-
-	public void createIntersectionNodes(double delta) {
-		ensureNodeList();
-		createEdges();
-		for (int i = 0; i < edgeList.size()-1; i++) {
-			Edge edgei = edgeList.get(i);
-			LOG.trace("i "+i+" edgeList "+edgeList.get(i).getId()+"/"+nodeList.size());
-			for (int j = i+1; j < edgeList.size(); j++) {
-				LOG.trace("....j "+j);
-				Edge edgej = edgeList.get(j);
-				Node nodeij = edgei.createNodeIfEdgeMeets(edgej, nodeList, delta);
-			}
-		}
-	}
-
-	private void ensureNodeList() {
-		if (nodeList == null) {
-			nodeList = new ArrayList<Node>();
-		}
-	}
-
-	private void ensureEdgeList() {
-		if (edgeList == null) {
-			edgeList = new ArrayList<Edge>();
-		}
-	}
-
-//	public void createIntersectionNodes() {
-//
-//	}
-
-	public List<Edge> getEdgesWithNodes(int count) {
-		List<Edge> edgesWithNodeCount = new ArrayList<Edge>();
-		List<Edge> edges = getEdges();
-		for (Edge edge : edges) {
-			if (count == 0) {
-				if (edge.getNode(0) == null && edge.getNode(1) == null) {
-					edgesWithNodeCount.add(edge);
-				}
-			} else if (count == 2) {
-				if (edge.getNode(0) != null && edge.getNode(1) != null) {
-					edgesWithNodeCount.add(edge);
-				}
-			} else if (count == 1) {
-				if ((edge.getNode(0) != null && edge.getNode(1) == null) ||
-				    (edge.getNode(0) == null && edge.getNode(1) != null)) {
-					edgesWithNodeCount.add(edge);
-				}
-			}
-		}
-		return edgesWithNodeCount;
-	}
-
-	public List<Edge> createListOfNullNodeEdges() {
-		List<Edge> nullNodeEdges = new ArrayList<Edge>();
-		for (Edge nullNodeEdge : this.getEdgesWithNodes(0)) {
-			nullNodeEdges.add(nullNodeEdge);
-		}
-		for (Edge nullNodeEdge : this.getEdgesWithNodes(1)) {
-			nullNodeEdges.add(nullNodeEdge);
-		}
-		return nullNodeEdges;
-	}
-
-	public Double getAverageEdgeLength() {
-		RealArray realArray = new RealArray();
-		for (Edge edge : this.getEdgesWithNodes(2)) {
-			Double length = edge.getLength();
-			if (length != null) {
-				realArray.addElement(length);
-			}
-		}
-		return realArray.size() == 0 ? null : realArray.getMean();
-	}
 
 }

src/main/java/org/xmlcml/graphics/cml/Edge.java

-package org.xmlcml.graphics.cml;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.xmlcml.euclid.Angle;
-import org.xmlcml.euclid.Line2;
-import org.xmlcml.euclid.Real2;
-import org.xmlcml.euclid.Vector2;
-import org.xmlcml.graphics.cml.Node.NodeType;
-import org.xmlcml.graphics.svg.SVGLine;
-
-import com.google.common.collect.Multimap;
-
-public class Edge {
-	private final static Logger LOG = Logger.getLogger(Edge.class);
-
-	private static Double EPS = 0.001;
-	private List<SVGLine> lineList;
-	// slowest index manages 01/ end of line; fastest is number of lines in edge
-	private List<List<Real2>> endPointsList;
-	private ArrayList<Node> nodeList;
-	private String id;
-
-	public Edge(SVGLine line) {
-		this.add(line);
-		ensureNodeList();
-	}
-	
-	public void add(SVGLine line) {
-		ensureLineList();
-		if (!lineList.contains(line)) {
-			if (lineList.size() > 0) {
-				makeSameDirectionAsLine0(line);
-			}
-			lineList.add(line);
-		}
-	}
-	
-	public String getId() {
-		if (id == null) {
-			setId("e_"+lineList.get(0).getId());
-		}
-		return id;
-	}
-
-	public void setId(String id) {
-		this.id = id;
-	}
-
-
-	private void makeSameDirectionAsLine0(SVGLine line) {
-		SVGLine line0 = lineList.get(0);
-		Angle angle = line0.getEuclidLine().getAngleMadeWith(line.getEuclidLine());
-		double cosAngle = angle.cos();
-		if (cosAngle < 0) {
-			line0.getEuclidLine().flipCoordinates();
-		}
-	}
-	
-	private void ensureLineList() {
-		if (lineList == null) {
-			lineList = new ArrayList<SVGLine>();
-		}
-	}
-
-	public boolean isTramLine(SVGLine nextLine, double maxAbsSin, double maxMidPointDistanceFactor) {
-		boolean isTramLine = false;
-		SVGLine thisLine = lineList.get(0);
-		Angle angle = thisLine.getEuclidLine().getAngleMadeWith(nextLine.getEuclidLine());
-		double absSin = Math.abs(angle.sin());
-		LOG.trace("abs"+absSin);
-		if (absSin < maxAbsSin) {
-			Real2 thisMidPoint = thisLine.getEuclidLine().getMidPoint();
-			Double thisLength = thisLine.getLength();
-			Real2 nextMidPoint = nextLine.getEuclidLine().getMidPoint();
-			double interLine = thisMidPoint.getDistance(nextMidPoint);
-			LOG.trace("absSin "+absSin+" interline "+interLine +" factor "+maxMidPointDistanceFactor*thisLength);
-			isTramLine = interLine < maxMidPointDistanceFactor*thisLength;
-		}
-		return isTramLine;
-	}
-
-	public Node createNodeIfEdgeMeets(Edge nextEdge, List<Node> nodeList, double deltaXY) {
-		Node node = null;
-		for (int i = 0; i < 2; i++) {
-			List<Real2> thisPoints = this.getEndPoints(i);
-			for (Real2 thisPoint : thisPoints) {
-				for (int j = 0; j < 2; j++) {
-					List<Real2> nextPoints = nextEdge.getEndPoints(j);
-					for (Real2 nextPoint : nextPoints) {
-						if (thisPoint.getDistance(nextPoint) < deltaXY) {
-							//we've found a join
-							LOG.debug("looking "+thisPoint+" "+deltaXY);
-							node = Node.lookupNode(nodeList, thisPoint, deltaXY);
-							if (node == null) {
-								node = Node.createAndAddNewNode(nodeList, thisPoint);
-							} else {
-								LOG.debug("existing node: "+node);
-							}
-							mutuallyRegisterEdgesAndNodes(nextEdge, i, j, node);
-						}
-					}
-				}
-			}
-		}
-		LOG.trace("nodeList "+nodeList.size());
-		return node;
-	}
-
-	private void mutuallyRegisterEdgesAndNodes(Edge nextEdge, int iend, int jend, Node node) {
-		this.addNodeToEdgeAndEdgeToNode(iend, node);
-		nextEdge.addNodeToEdgeAndEdgeToNode(jend, node);
-		LOG.debug("added Node "+node);
-	}
-
-	public void addNodeToEdgeAndEdgeToNode(int jend, Node node) {
-		this.addNode(node, jend);
-		node.add(this, jend);
-	}
-
-	private void addNode(Node node, int iend) {
-		ensureNodeList();
-		this.nodeList.set(iend, node);
-		node.add(this, iend);
-	}
-
-	List<Real2> getEndPoints(int iend) {
-		ensureEndPointList();
-		return endPointsList.get(iend);
-	}
-
-	public Node getNode(int i) {
-		return nodeList.get(i);
-	}
-
-	public Double getLength() {
-		return (lineList.size() == 0) ? null : lineList.get(0).getLength();
-		
-	}
-
-	private void ensureEndPointList() {
-		if (endPointsList == null) {
-			endPointsList = new ArrayList<List<Real2>>();
-			endPointsList.add(new ArrayList<Real2>());
-			endPointsList.add(new ArrayList<Real2>());
-
-			for (int iend = 0; iend < 2; iend++) {
-				for (SVGLine line : lineList) {
-					endPointsList.get(iend).add(line.getXY(iend));
-				}
-				// add midpoint of two tramlines
-				if (lineList.size() == 2) {
-					Real2 midPoint = lineList.get(0).getXY(iend).getMidPoint(lineList.get(1).getXY(iend));
-					endPointsList.get(iend).add(midPoint);
-				}
-			}
-		}
-	}
-
-	private void ensureNodeList() {
-		if (nodeList == null) {
-			nodeList = new ArrayList<Node>();
-			nodeList.add(null);
-			nodeList.add(null);
-		}
-	}
-
-	/**
-	private List<SVGLine> lineList;
-	private List<List<Real2>> endPointsList;
-	private ArrayList<Node> nodeList;
-	 */
-	public String toString() {
-		ensureNodeList();
-		String s = ""+id+" ";
-		s += nodeList.get(0)+" "+lineList.size()+" "+nodeList.get(1);
-		return s;
-	}
-
-	public void addMissingNodesByExtension(Double length) {
-		addMissingNodeByExtension(0, length);
-		addMissingNodeByExtension(1, length);
-	}
-
-	public Node addMissingNodeByExtension(int nodeSerial, Double length) {
-		Node node = nodeList.get(nodeSerial);
-		Node otherNode = nodeList.get(1 - nodeSerial);
-		Node newNode = null;
-		Real2 projected = null;
-		if (node == null) {
-			SVGLine line = lineList.get(0);
-			Line2 line2 = line.getEuclidLine();
-			Real2 point = this.getEndPoints(nodeSerial).get(0);
-			int serial = line2.getSerial(point, EPS);
-			if (otherNode != null) {
-				if (serial == 0) {
-					projected = line2.createPointOnLine(length);
-				} else if (serial == 1) {
-					projected = line2.createPointOnLine(length);
-				}
-			} else {
-				// compute from midPoint
-				Real2 midPoint = line2.getMidPoint();
-				Vector2 vector = new Vector2(line2.getVector().getUnitVector().multiplyBy(length / 2.));
-				if (serial == 0) {
-					projected = midPoint.subtract(vector);
-				} else if (serial == 1) {
-					projected = midPoint.plus(vector);
-				}
-			}
-			if (projected != null) {
-				newNode = new Node(projected, NodeType.EXTENDED_EDGE);
-				nodeList.set(nodeSerial, newNode);
-			}
-		}
-		return newNode;
-	}
-}

src/main/java/org/xmlcml/graphics/cml/Node.java

-package org.xmlcml.graphics.cml;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.xmlcml.euclid.Real2;
-import org.xmlcml.graphics.cml.Node.NodeType;
-
-public class Node {
-
-	public enum NodeType {
-		EXTENDED_EDGE, 
-		LINE_END;
-	}
-	
-	private Real2 xy;
-	private List<Edge> edgeList;
-	private String id;
-	private NodeType nodeType;
-
-	public Node(Real2 xy, NodeType nodeType) {
-		this.xy = xy;
-		this.nodeType = nodeType;
-	}
-
-	public Real2 getXY() {
-		return xy;
-	}
-
-	public void add(Edge edge, int iend) {
-		enableEdgeList();
-		edgeList.add(edge);
-	}
-
-	public void setId(String id) {
-		this.id = id;
-	}
-	
-	public static Node lookupNode(List<Node> nodeList, Real2 thisPoint, double deltaXY) {
-		Node node = null;
-		for (Node node1 : nodeList) {
-			if (node1.getXY().getDistance(thisPoint) < deltaXY) {
-				node = node1;
-				break; // brutal - take first match
-			}
-		}
-		return node;
-	}
-
-	private void enableEdgeList() {
-		if (edgeList == null) {
-			edgeList = new ArrayList<Edge>();
-		}
-	}
-	
-	public String toString() {
-		String s = "node: "+id+" "+ xy;
-		return s;
-	}
-
-	public void setType(NodeType nodeType) {
-		this.nodeType = nodeType;
-	}
-
-	public static Node createAndAddNewNode(List<Node> nodeList, Real2 thisPoint) {
-		Node node = new Node(thisPoint, NodeType.LINE_END);
-		node.setId("node"+nodeList.size());
-		nodeList.add(node);
-		return node;
-	}
-
-}

src/test/java/org/xmlcml/graphics/cml/AMIEdgeTest.java

+package org.xmlcml.graphics.cml;
+
+import java.util.ArrayList;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.junit.Assert;
+import org.junit.Test;
+import org.xmlcml.euclid.Real2;
+import org.xmlcml.euclid.test.Real2Test;
+import org.xmlcml.graphics.svg.SVGLine;
+
+public class AMIEdgeTest {
+
+	public final static Logger LOG = Logger.getLogger(AMIEdgeTest.class);
+	@Test
+	public void testConstructor() {
+		AMIGraph graph = new AMIGraph();
+		SVGLine line = new SVGLine(new Real2(0.,0.), new Real2(1., 2.));
+		AMIEdge edge = graph.createEdge(line);
+		edge.setId("e00");
+		Assert.assertEquals("no nodes", "e00 null 1 null", edge.toString());
+	}
+	
+	@Test
+	public void testConstructor1() {
+		AMIGraph graph = new AMIGraph();
+		List<AMINode> nodeList = new ArrayList<AMINode>();
+		Real2 point = new Real2(1.,2.);
+		SVGLine line = new SVGLine(point, new Real2(3., 5.));
+		AMIEdge edge = graph.createEdge(line);
+		edge.setId("e10");
+		AMINode newNode = AMINode.createAndAddNewNode(nodeList, point);
+		edge.addNodeToEdgeAndEdgeToNode(0, newNode);
+		Assert.assertEquals("no nodes", "e10 node: node0 (1.0,2.0) 1 null", edge.toString());
+	}
+	
+	@Test
+	public void testConstructor2() {
+		AMIGraph graph = new AMIGraph();
+		List<AMINode> nodeList = new ArrayList<AMINode>();
+		Real2 point0 = new Real2(1.,2.);
+		Real2 point1 = new Real2(3., 5.);
+		SVGLine line = new SVGLine(point0, point1);
+		AMIEdge edge = new AMIEdge(graph, line);
+		edge.setId("e11");
+		AMINode newNode = AMINode.createAndAddNewNode(nodeList, point0);
+		edge.addNodeToEdgeAndEdgeToNode(0, newNode);
+		AMINode newNode1 = AMINode.createAndAddNewNode(nodeList, point1);
+		edge.addNodeToEdgeAndEdgeToNode(1, newNode1);
+		Assert.assertEquals("no nodes", "e11 node: node0 (1.0,2.0) 1 node: node1 (3.0,5.0)", edge.toString());
+	}
+	
+	@Test
+	public void testAddNodes() {
+		AMIGraph graph = new AMIGraph();
+		Real2 point0 = new Real2(0.,0.);
+		Real2 point1 = new Real2(3., 4.);
+		SVGLine line = new SVGLine(point0, point1);
+		AMIEdge edge = new AMIEdge(graph, line);
+		edge.setId("e00");
+		edge.addMissingNodesByExtension(10.);
+		
+		AMINode node0 = edge.getNode(0);
+		Real2 real20 = node0.getXY();
+		Real2Test.assertEquals("node0", new Real2(4.5, 6.0), real20,  0.01);
+		
+		AMINode node1 = edge.getNode(1);
+		Real2 real21 = node1.getXY();
+		Real2Test.assertEquals("node1", new Real2(-1.5, -2.), real21,  0.01);
+	}
+	
+	@Test
+	public void testAddNodes1() {
+		AMIGraph graph = new AMIGraph();
+		List<AMINode> nodeList = new ArrayList<AMINode>();
+		Real2 point0 = new Real2(1.,2.);
+		Real2 point1 = new Real2(3., 5.);
+		SVGLine line = new SVGLine(point0, point1);
+		AMIEdge edge = new AMIEdge(graph, line);
+		edge.setId("e10");
+		AMINode node0 = AMINode.createAndAddNewNode(nodeList, point0);
+		edge.addNodeToEdgeAndEdgeToNode(0, node0);
+		edge.addMissingNodesByExtension(10.);
+		AMINode node1 = edge.getNode(1);
+		Real2 real21 = node1.getXY();
+		LOG.debug("node xy"+real21+ " "+real21.getDistance(point0)+" "+real21.getDistance(point1));
+//		Assert.assertEquals("no nodes", "e10 node: node0 (1.0,2.0) 1 node: null (4.547001962252291,7.320502943378436)", edge.toString());
+	}
+	
+	@Test
+	public void testAddNodes2() {
+		AMIGraph graph = new AMIGraph();
+		List<AMINode> nodeList = new ArrayList<AMINode>();
+		Real2 point0 = new Real2(1.,2.);
+		Real2 point1 = new Real2(3., 5.);
+		SVGLine line = new SVGLine(point0, point1);
+		AMIEdge edge = new AMIEdge(graph, line);
+		edge.setId("e11");
+		AMINode newNode = AMINode.createAndAddNewNode(nodeList, point0);
+		edge.addNodeToEdgeAndEdgeToNode(0, newNode);
+		AMINode newNode1 = AMINode.createAndAddNewNode(nodeList, point1);
+		edge.addNodeToEdgeAndEdgeToNode(1, newNode1);
+		edge.addMissingNodesByExtension(10.);
+		Assert.assertEquals("no nodes", "e11 node: node0 (1.0,2.0) 1 node: node1 (3.0,5.0)", edge.toString());
+	}
+	
+}

src/test/java/org/xmlcml/graphics/cml/AMIGraphTest.java

+package org.xmlcml.graphics.cml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.xmlcml.euclid.Real2;
+import org.xmlcml.graphics.svg.SVGLine;
+
+public class AMIGraphTest {
+
+	
+	List<Real2> pointList;
+	List<SVGLine> isopentane;
+	List<SVGLine> methylisopropylamine;
+	
+	@Before
+	public void setup() {
+		pointList = new ArrayList<Real2>();
+		pointList.add(new Real2(0., 0.));
+		pointList.add(new Real2(5., 10.));
+		pointList.add(new Real2(5., -10.));
+		pointList.add(new Real2(-15., 0.));
+		pointList.add(new Real2(-20., 10.));
+		pointList.add(new Real2(-12., 0.));
+		pointList.add(new Real2(-16., 2.));
+		SVGLine line01 = new SVGLine(pointList.get(0), pointList.get(1));
+		SVGLine line02 = new SVGLine(pointList.get(0), pointList.get(2));
+		SVGLine line03 = new SVGLine(pointList.get(0), pointList.get(3));
+		SVGLine line03a = new SVGLine(pointList.get(0), pointList.get(5));
+		SVGLine line34 = new SVGLine(pointList.get(3), pointList.get(4));
+		SVGLine line34a = new SVGLine(pointList.get(3), pointList.get(6));
+		line01.setId("l01");
+		line02.setId("l02");
+		line03.setId("l03");
+		line03a.setId("l03a");
+		line34.setId("l34");
+		line34a.setId("l3a");
+		
+		isopentane = new ArrayList<SVGLine>();
+		isopentane.add(line01);
+		isopentane.add(line02);
+		isopentane.add(line03);
+		isopentane.add(line34);
+		
+		methylisopropylamine = new ArrayList<SVGLine>();
+		methylisopropylamine.add(line01);		
+		methylisopropylamine.add(line02);		
+		methylisopropylamine.add(line03a);		
+		methylisopropylamine.add(line34a);		
+	}
+	
+	@Test
+	public void testGetEdges() {
+		AMIGraph graph = new AMIGraph();
+		graph.createEdges(isopentane);
+		List<AMIEdge> edges = graph.getEdges();
+		Assert.assertEquals("edges", 4, edges.size());
+	}
+
+	@Test
+	public void testCreateIntersectionNodes() {
+		AMIGraph graph = new AMIGraph();
+		graph.createEdges(isopentane);
+		graph.createIntersectionNodes(AMIGraph.NODE_SPREAD);
+		List<AMINode> nodeList = graph.getNodes();
+		Assert.assertEquals("nodes", 2, graph.getNodes().size());
+		int i = 0;
+		for (AMINode node : nodeList) {
+			System.out.println(" "+i+++" "+node);
+		}
+		i = 0;
+		Assert.assertEquals("edges", 4, graph.getEdges().size());
+		List<AMIEdge> edgeList = graph.getEdges();
+		for (AMIEdge edge : edgeList) {
+			System.out.println(" "+i+++" "+edge);
+		}
+		Assert.assertEquals("edges with no nodes", 0, graph.getEdgesWithNodes(0).size());
+		Assert.assertEquals("edges with one node", 3, graph.getEdgesWithNodes(1).size());
+		Assert.assertEquals("edges with one node", 1, graph.getEdgesWithNodes(2).size());
+	}
+
+	@Test
+	public void testgetEdgeEndpoints() {
+		AMIGraph graph = new AMIGraph();
+		graph.createEdges(isopentane);
+		List<AMIEdge> edges = graph.getEdges();
+		Assert.assertEquals("edges", 4, edges.size());
+		for (AMIEdge edge : edges) {
+			System.out.println("\n==== "+edge.getId()+" ===");
+			for (Real2 point : edge.getEndPoints(0)) {
+				System.out.println(point);
+			}
+			System.out.println("----------");
+			for (Real2 point : edge.getEndPoints(1)) {
+				System.out.println(point);
+			}
+		}
+	}
+
+	@Test
+	public void testGetMeanEdgeLength() {
+		AMIGraph graph = new AMIGraph();
+		graph.createEdges(isopentane);
+		List<AMIEdge> edges = graph.getEdges();
+		graph.createIntersectionNodes(AMIGraph.NODE_SPREAD);
+		Assert.assertEquals("mean", 15.00, graph.getAverageEdgeLengthBetweenNodes(), 0.01);
+	}
+
+	@Test
+	public void testAddNodesByExtension() {
+		AMIGraph graph = new AMIGraph();
+		graph.createEdges(methylisopropylamine);
+		List<AMIEdge> edges = graph.getEdges();
+		graph.createIntersectionNodes(AMIGraph.NODE_SPREAD);
+		List<AMIEdge> nullNodeEdges = graph.createListOfNullNodeEdges();
+		Double length = graph.getAverageEdgeLengthBetweenNodes();
+		for (AMIEdge edge : nullNodeEdges) {
+			edge.addMissingNodesByExtension(length);
+		}
+	}
+
+}

src/test/java/org/xmlcml/graphics/cml/CMLAnalyzerTest.java

 import java.util.Map;
 
 import junit.framework.Assert;
+
 import nu.xom.Element;
 
 import org.junit.Ignore;
 import org.xmlcml.graphics.svg.SVGG;
 import org.xmlcml.graphics.svg.SVGUtil;
 
-import com.google.common.collect.Multimap;
 
 public class CMLAnalyzerTest {
 	@Test
-	public void testRemoveGHierarchies() throws IOException {
-		Element element = CMLUtil.parseQuietlyToDocument(Util.getResource("org/xmlcml/graphics/cml/molecule.svg").openStream()).getRootElement();
-		SVGElement svgg = SVGElement.readAndCreateSVG(element);
-		List<SVGElement> leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:*[count(*)=0]");
-		Assert.assertEquals("before leafs", 36, leafs.size());
-		CMLAnalyzer.removeGHierarchies((SVGG) svgg.getChildElements().get(0));
-		leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:*[count(*)=0]");
-		Assert.assertEquals("after leafs", 36, leafs.size());
-		leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:line");
-		Assert.assertEquals("lines", 25, leafs.size());
-		leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:polyline");
-		Assert.assertEquals("polylines", 0, leafs.size());
-		leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:polygon");
-		Assert.assertEquals("polygons", 3, leafs.size());
-		leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:text");
-		Assert.assertEquals("text", 8, leafs.size());
-	}
-	
-	@Test
 	public void testRGroupManager() throws IOException {
 		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
 		RGroupManager rGroupManager = cmlAnalyzer.ensureRGroupManager();
 	}
 	
 	@Test
-	public void testGetTextPrimitives() throws IOException {
+	public void tesGetPolygonPrimitives() throws IOException {
 		SVGG g = readG();
 		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
 		cmlAnalyzer.readAndTidyHierarchies(g);
+		Assert.assertEquals("polygons", 3, cmlAnalyzer.getPolygonPrimitives().size());
+	}
+
+	@Test
+	public void testGetLinePrimitives() {
+		SVGG g = CMLAnalyzerTest.readG();
+		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
+		cmlAnalyzer.readAndTidyHierarchies(g);
+		Assert.assertEquals("lines", 25, cmlAnalyzer.getLinePrimitives().size());
+	}
+
+	@Test
+	public void testGetTextPrimitives() {
+		SVGG g = CMLAnalyzerTest.readG();
+		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
+		cmlAnalyzer.readAndTidyHierarchies(g);
 		Assert.assertEquals("texts", 8, cmlAnalyzer.getTextPrimitives().size());
 	}
 
 	@Test
-	public void testGetLinePrimitives() throws IOException {
-		SVGG g = readG();
-		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
-		cmlAnalyzer.readAndTidyHierarchies(g);
-		Assert.assertEquals("lines", 25, cmlAnalyzer.getLinePrimitives().size());
+	public void testRemoveGHierarchies() {
+		Element element = null;
+		try {
+			element = CMLUtil.parseQuietlyToDocument(Util.getResource("org/xmlcml/graphics/cml/molecule.svg").openStream()).getRootElement();
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+		SVGElement svgg = SVGElement.readAndCreateSVG(element);
+		List<SVGElement> leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:*[count(*)=0]");
+		Assert.assertEquals("before leafs", 36, leafs.size());
+		CMLAnalyzer.removeGHierarchies((SVGG) svgg.getChildElements().get(0));
+		leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:*[count(*)=0]");
+		Assert.assertEquals("after leafs", 36, leafs.size());
+		leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:line");
+		Assert.assertEquals("lines", 25, leafs.size());
+		leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:polyline");
+		Assert.assertEquals("polylines", 0, leafs.size());
+		leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:polygon");
+		Assert.assertEquals("polygons", 3, leafs.size());
+		leafs = SVGUtil.getQuerySVGElements(svgg, ".//svg:text");
+		Assert.assertEquals("text", 8, leafs.size());
 	}
 
-	@Test
-	public void tesGetPolygonPrimitives() throws IOException {
-		SVGG g = readG();
-		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
-		cmlAnalyzer.readAndTidyHierarchies(g);
-		Assert.assertEquals("polygons", 3, cmlAnalyzer.getPolygonPrimitives().size());
-	}
 	
-	@Test
-	public void testGetEdges() throws IOException {
-		SVGG g = readG();
-		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
-		cmlAnalyzer.readAndTidyHierarchies(g);
-		cmlAnalyzer.createEdges();
-		Assert.assertEquals("edges", 21, cmlAnalyzer.getEdges().size());
-	}
-
-	@Test
-	public void testgetEdgeEndpoints() throws IOException {
-		SVGG g = readG();
-		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
-		cmlAnalyzer.readAndTidyHierarchies(g);
-		List<Edge> edges = cmlAnalyzer.getEdges();
-		Assert.assertEquals("edges", 21, edges.size());
-		for (Edge edge : edges) {
-			System.out.println("\n==== "+edge.getId()+" ===");
-			for (Real2 point : edge.getEndPoints(0)) {
-				System.out.println(point);
-			}
-			System.out.println("----------");
-			for (Real2 point : edge.getEndPoints(1)) {
-				System.out.println(point);
-			}
+	public static SVGG readG() {
+		SVGG g;
+		try {
+			Element element = CMLUtil.parseQuietlyToDocument(
+					Util.getResource("org/xmlcml/graphics/cml/molecule.svg").openStream()).getRootElement();
+			SVGElement svgg = SVGElement.readAndCreateSVG(element);
+			g = (SVGG) svgg.getChildElements().get(0);
+		} catch (IOException e) {
+			throw new RuntimeException(e);
 		}
-	}
-
-	@Test
-	public void testCreateIntersectionNodes() throws IOException {
-		SVGG g = readG();
-		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
-		cmlAnalyzer.readAndTidyHierarchies(g);
-		cmlAnalyzer.createIntersectionNodes(CMLAnalyzer.NODE_SPREAD);
-		List<Node> nodeList = cmlAnalyzer.getNodes();
-		Assert.assertEquals("nodes", 13, cmlAnalyzer.getNodes().size());
-		int i = 0;
-		for (Node node : nodeList) {
-			System.out.println(" "+i+++" "+node);
-		}
-		i = 0;
-		Assert.assertEquals("edges", 21, cmlAnalyzer.getEdges().size());
-		List<Edge> edgeList = cmlAnalyzer.getEdges();
-		for (Edge edge : edgeList) {
-			System.out.println(" "+i+++" "+edge);
-		}
-		Assert.assertEquals("edges with no nodes", 0, cmlAnalyzer.getEdgesWithNodes(0).size());
-		Assert.assertEquals("edges with one node", 10, cmlAnalyzer.getEdgesWithNodes(1).size());
-		Assert.assertEquals("edges with one node", 11, cmlAnalyzer.getEdgesWithNodes(2).size());
-	}
-
-	@Test
-	public void testGetMeanEdgeLength() throws IOException {
-		SVGG g = readG();
-		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
-		cmlAnalyzer.readAndTidyHierarchies(g);
-		cmlAnalyzer.createIntersectionNodes(CMLAnalyzer.NODE_SPREAD);
-		Assert.assertEquals("mean", 12.65, cmlAnalyzer.getAverageEdgeLength(), 0.01);
-	}
-
-	@Test
-	public void testAddTextNodes() throws IOException {
-		SVGG g = readG();
-		CMLAnalyzer cmlAnalyzer = new CMLAnalyzer();
-		cmlAnalyzer.readAndTidyHierarchies(g);
-		cmlAnalyzer.createIntersectionNodes(CMLAnalyzer.NODE_SPREAD);
-		List<Edge> nullNodeEdges = cmlAnalyzer.createListOfNullNodeEdges();
-		Double length = cmlAnalyzer.getAverageEdgeLength();
-		Multimap<String, Real2> textCentres = cmlAnalyzer.findTextCentres();
-		for (Edge edge : nullNodeEdges) {
-			edge.addMissingNodesByExtension(length);
-		}
-	}
-
-	private SVGG readG() throws IOException {
-		Element element = CMLUtil.parseQuietlyToDocument(
-				Util.getResource("org/xmlcml/graphics/cml/molecule.svg").openStream()).getRootElement();
-		SVGElement svgg = SVGElement.readAndCreateSVG(element);
-		SVGG g = (SVGG) svgg.getChildElements().get(0);
 		return g;
 	}
 

src/test/java/org/xmlcml/graphics/cml/EdgeTest.java

-package org.xmlcml.graphics.cml;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.junit.Assert;
-import org.junit.Test;
-import org.xmlcml.euclid.Real2;
-import org.xmlcml.graphics.svg.SVGLine;
-
-public class EdgeTest {
-
-	public final static Logger LOG = Logger.getLogger(EdgeTest.class);
-	@Test
-	public void testConstructor() {
-		SVGLine line = new SVGLine(new Real2(0.,0.), new Real2(1., 2.));
-		Edge edge = new Edge(line);
-		edge.setId("e00");
-		Assert.assertEquals("no nodes", "e00 null 1 null", edge.toString());
-	}
-	
-	@Test
-	public void testConstructor1() {
-		List<Node> nodeList = new ArrayList<Node>();
-		Real2 point = new Real2(1.,2.);
-		SVGLine line = new SVGLine(point, new Real2(3., 5.));
-		Edge edge = new Edge(line);
-		edge.setId("e10");
-		Node newNode = Node.createAndAddNewNode(nodeList, point);
-		edge.addNodeToEdgeAndEdgeToNode(0, newNode);
-		Assert.assertEquals("no nodes", "e10 node: node0 (1.0,2.0) 1 null", edge.toString());
-	}
-	
-	@Test
-	public void testConstructor2() {
-		List<Node> nodeList = new ArrayList<Node>();
-		Real2 point0 = new Real2(1.,2.);
-		Real2 point1 = new Real2(3., 5.);
-		SVGLine line = new SVGLine(point0, point1);
-		Edge edge = new Edge(line);
-		edge.setId("e11");
-		Node newNode = Node.createAndAddNewNode(nodeList, point0);
-		edge.addNodeToEdgeAndEdgeToNode(0, newNode);
-		Node newNode1 = Node.createAndAddNewNode(nodeList, point1);
-		edge.addNodeToEdgeAndEdgeToNode(1, newNode1);
-		Assert.assertEquals("no nodes", "e11 node: node0 (1.0,2.0) 1 node: node1 (3.0,5.0)", edge.toString());
-	}
-	
-	@Test
-	public void testAddNodes() {
-		Real2 point0 = new Real2(0.,0.);
-		Real2 point1 = new Real2(1., 2.);
-		SVGLine line = new SVGLine(point0, point1);
-		Edge edge = new Edge(line);
-		edge.setId("e00");
-		edge.addMissingNodesByExtension(10.);
-		Node node0 = edge.getNode(0);
-		Real2 real20 = node0.getXY();
-		LOG.debug("node xy"+real20+ " "+real20.getDistance(point0)+" "+real20.getDistance(point1));
-		
-		Node node1 = edge.getNode(1);
-		Real2 real21 = node1.getXY();
-		LOG.debug("node xy"+real21+ " "+real21.getDistance(point1)+" "+real21.getDistance(point0));
-		LOG.debug("node dist "+real21.getDistance(real20));
-//		Assert.assertEquals("no nodes", "e00 node: null (-1.7360679774997898,-3.4721359549995796) 1 node: null (3.472135954999579,6.944271909999158)", edge.toString());
-	}
-	
-	@Test
-	public void testAddNodes1() {
-		List<Node> nodeList = new ArrayList<Node>();
-		Real2 point0 = new Real2(1.,2.);
-		Real2 point1 = new Real2(3., 5.);
-		SVGLine line = new SVGLine(point0, point1);
-		Edge edge = new Edge(line);
-		edge.setId("e10");
-		Node node0 = Node.createAndAddNewNode(nodeList, point0);
-		edge.addNodeToEdgeAndEdgeToNode(0, node0);
-		edge.addMissingNodesByExtension(10.);
-		Node node1 = edge.getNode(1);
-		Real2 real21 = node1.getXY();
-		LOG.debug("node xy"+real21+ " "+real21.getDistance(point0)+" "+real21.getDistance(point1));
-//		Assert.assertEquals("no nodes", "e10 node: node0 (1.0,2.0) 1 node: null (4.547001962252291,7.320502943378436)", edge.toString());
-	}
-	
-	@Test
-	public void testAddNodes2() {
-		List<Node> nodeList = new ArrayList<Node>();
-		Real2 point0 = new Real2(1.,2.);
-		Real2 point1 = new Real2(3., 5.);
-		SVGLine line = new SVGLine(point0, point1);
-		Edge edge = new Edge(line);
-		edge.setId("e11");
-		Node newNode = Node.createAndAddNewNode(nodeList, point0);
-		edge.addNodeToEdgeAndEdgeToNode(0, newNode);
-		Node newNode1 = Node.createAndAddNewNode(nodeList, point1);
-		edge.addNodeToEdgeAndEdgeToNode(1, newNode1);
-		edge.addMissingNodesByExtension(10.);
-		Assert.assertEquals("no nodes", "e11 node: node0 (1.0,2.0) 1 node: node1 (3.0,5.0)", edge.toString());
-	}
-	
-}