view java/src/uk/ac/qmul/eecs/ccmi/gui/Edge.java @ 0:9418ab7b7f3f

Initial import
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Fri, 16 Dec 2011 17:35:51 +0000
parents
children 9e67171477bc
line wrap: on
line source
/*  
 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
	  
 Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com)
 Copyright (C) 2011  Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 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, see <http://www.gnu.org/licenses/>.
*/

package uk.ac.qmul.eecs.ccmi.gui;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.ResourceBundle;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import uk.ac.qmul.eecs.ccmi.diagrammodel.ConnectNodesException;
import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramEdge;
import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramNode;
import uk.ac.qmul.eecs.ccmi.diagrammodel.ElementChangedEvent;
import uk.ac.qmul.eecs.ccmi.gui.persistence.PersistenceManager;

/**
 * An edge in a graph. Edge objects are used in a GraphPanel to render a diagram edge visually. 
 * Edge objects are used in the tree representation of the diagram as well, as they're
 * subclasses of {@link DiagramEdge}    
 * 
 */
@SuppressWarnings("serial")
public abstract class Edge extends DiagramEdge implements GraphElement{

	public Edge(String type, String[] availableEndDescriptions, int minAttachedNodes, int maxAttachedNodes,LineStyle style){
		super(type,availableEndDescriptions);
		this.minAttachedNodes = minAttachedNodes;
		this.maxAttachedNodes = maxAttachedNodes;
		this.style = style;
		nodes = new ArrayList<Node>();
	}
   
	@Override
   public Node getNodeAt(int index){
	   return nodes.get(index);
   }
   
   @Override
   public int getNodesNum(){
	   return nodes.size();
   }
   
   public boolean removeNode(DiagramNode diagramNode){
	   Node n = (Node)diagramNode;
	   if(nodes.size() == 2)
		   throw new RuntimeException("Cannot remove a node from a two ends edge");
	   else{
		   for(InnerPoint p : points)
			   if(p.hasNeighbour(n)){
				   p.neighbours.remove(n);
				   if(p.neighbours.size() == 1)
					   removePoint(p);
				   break;
			   } 
		   return nodes.remove(n);
	   }
   }

   /**
    * Look for the node attached to this edge which lays at the minimum distance 
    * from the point passed as argument. The distance cannot be lower than the    
    * value passed as argument. 
    * 
    * @param aPoint the point the distance is measured from
    * @param distanceLimit the limit from the distance between the nodes and the point
    * @return the closest node or null if the node lays at an higher distance than distanceLimit
    */
   public Node getClosestNode(Point2D aPoint, double distanceLimit){
	   Node closestNode = null;
	   double minDist = distanceLimit;
	   
	   if(points.isEmpty()){
			Line2D line = getSegment(nodes.get(0),nodes.get(1)); 
			if(line.getP1().distance(aPoint) < minDist){
				minDist = line.getP1().distance(aPoint);
				closestNode = nodes.get(0);
			}
			if(line.getP2().distance(aPoint) < minDist){
				minDist = line.getP2().distance(aPoint);
				closestNode = nodes.get(1);
			}
			return closestNode;
		}else{
			for(InnerPoint p : points){
				for(GraphElement ge : p.getNeighbours())
					if(ge instanceof Node){
						Node n = (Node)ge;
						Direction d = new Direction(p.getBounds().getCenterX() - n.getBounds().getCenterX(),p.getBounds().getCenterY() - n.getBounds().getCenterY());
						if(n.getConnectionPoint(d).distance(aPoint) < minDist){
							minDist = n.getConnectionPoint(d).distance(aPoint);
							closestNode = n; 
						}
					}
			}
			return closestNode;
		}
   }
   
   //------------------------
   private void removePoint(InnerPoint p){
	   /* we assume at this moment p has one neighbours only */
	   InnerPoint neighbour = (InnerPoint)p.neighbours.get(0);
	   points.remove(p);
	   neighbour.neighbours.remove(p);
	   if(neighbour.neighbours.size() == 1)
		   removePoint(neighbour);
   }
   
   /**
   Draw the edge.
   @param g2 the graphics context
   */
   public abstract void draw(Graphics2D g2);

   /**
	Tests whether the edge contains a point.
	@param aPoint the point to test
	@return true if this edge contains aPoint
   */
   public boolean contains(Point2D aPoint){
	   if(points.isEmpty()){ 
		   return  fatStrokeContains (getSegment(nodes.get(0), nodes.get(1)), aPoint);
	   }
	   for(InnerPoint p : points){
		   for(GraphElement ge : p.neighbours){
			   if(fatStrokeContains(getSegment(p,ge),aPoint))
				   return true;
		   }
	   }
	   return false;
   }
   
   private boolean fatStrokeContains(Shape s, Point2D p){
	   BasicStroke fatStroke = new BasicStroke((float) (2 * MAX_DIST));
	   Shape fatPath = fatStroke.createStrokedShape(s);
	   return fatPath.contains(p);
   }
   
   protected Line2D.Double getSegment(GraphElement start, GraphElement end){
	   Rectangle2D startBounds = start.getBounds();
	   Rectangle2D endBounds = end.getBounds();
	   Direction d = new Direction(endBounds.getCenterX() - startBounds.getCenterX(), endBounds.getCenterY() - startBounds.getCenterY());
	   return new Line2D.Double(start.getConnectionPoint(d), end.getConnectionPoint(d.turn(180)));
   }
   
   public List<Point2D> getConnectionPoints(){
	   List<Point2D> list = new LinkedList<Point2D>();
	   if(points.isEmpty()){
		   Line2D line = getSegment(nodes.get(0),nodes.get(1)); 
		   list.add(line.getP1());
		   list.add(line.getP2());
	   }else{
		   for(InnerPoint p : points){
			   for(GraphElement ge : p.neighbours)
				   if(ge instanceof Node){
					   Direction d = new Direction(p.getBounds().getCenterX() - ge.getBounds().getCenterX(),p.getBounds().getCenterY() - ge.getBounds().getCenterY());
					   list.add(((Node)ge).getConnectionPoint(d));
				   }
		   }
	   }
	   return list;
   }
   
   public abstract int getStipplePattern();
	
   /**
	Connect this edge to the nodes.
	@param aStart the starting node
	@param anEnd the ending node
   */
   public void connect(List<DiagramNode> nodes) throws ConnectNodesException{
	   assert(getNodesNum() == 0);
	   /* this is to eliminate duplicates */
	   LinkedHashSet<Node> nodeSet = new LinkedHashSet<Node>();
	   for(DiagramNode n : nodes)
		   nodeSet.add((Node)n);

	   /* checks on connection consistency */
	   if((nodeSet == null)||(nodeSet.size() < minAttachedNodes))
		   throw new ConnectNodesException("You must select at least "+ minAttachedNodes + "nodes");
	   if((nodeSet.size() > maxAttachedNodes))
		   throw new ConnectNodesException("You must select at most " + maxAttachedNodes +" nodes");
	   
	   points = new ArrayList<InnerPoint>();
	   if(nodeSet.size() > 2){
		   /* there are more than three nodes. compute the central inner point         *
		    * which will connect all the nodes, as the middle points of the edge bound */
		   Rectangle2D bounds = new Rectangle2D.Double();
		   for(Node n : nodeSet){
			   bounds.add(n.getBounds());
		   }
		   InnerPoint p = new InnerPoint();
		   p.translate(new Point2D.Double(0,0), bounds.getCenterX(), bounds.getCenterY());
		   p.neighbours.addAll(nodeSet);
		   points.add(p);
	   }
	   this.nodes.addAll(nodeSet);
	   
	   if(!points.isEmpty())
			masterInnerPoint = points.get(0);
   }
   
   public void bend(Point2D p) {
	   boolean found = false;
	   if(points.isEmpty()){
		   newInnerPoint.neighbours.addAll(nodes); 
		   points.add(newInnerPoint);
		   newPointCreated = false;
	   }else if(newPointCreated){
		   /* find the segment where the new point lays */
		   for(ListIterator<InnerPoint> pItr = points.listIterator(); pItr.hasNext() && !found; ){
			   InnerPoint ePoint = pItr.next();
			   for(ListIterator<GraphElement> geItr = ePoint.neighbours.listIterator(); geItr.hasNext() && !found;){
			       /* find the neighbour of the current edge point whose line the new point lays on */
				   GraphElement ge = geItr.next();
				   if(fatStrokeContains(getSegment(ePoint, ge),downPoint)){
					   if(ge instanceof InnerPoint ){
						   /* remove current edge point from the neighbour's neighbours */
						   ((InnerPoint)ge).neighbours.remove(ePoint);
						   ((InnerPoint)ge).neighbours.add(newInnerPoint);
					   }
					   /*remove old neighbour from edgePoint neighbours */
					   geItr.remove();
					   newInnerPoint.neighbours.add(ePoint);
					   newInnerPoint.neighbours.add(ge);
					   /* add the new node to the list of EdgeNodes of this edge */
					   pItr.add(newInnerPoint);
					   geItr.add(newInnerPoint);
					   found = true;
				   }
			   }
		   }
		   newPointCreated = false;
	   }
	   newInnerPoint.translate(p, p.getX() - newInnerPoint.getBounds().getCenterX(),
			            p.getY() - newInnerPoint.getBounds().getCenterY());
	   notifyChange(new ElementChangedEvent(this,this,"bend"));
   }
   
   /*
    * this methods checks for segments of the edge which are aligned and makes a unique edge out of them
    */
   public void stopMove(){
	   for(ListIterator<InnerPoint> pItr = points.listIterator(); pItr.hasNext(); ){
		   InnerPoint ePoint = pItr.next();
		   if(ePoint.neighbours.size() > 2)
			   continue;
		   Rectangle2D startBounds = ePoint.getBounds();
		   Rectangle2D endBounds = ePoint.neighbours.get(0).getBounds();
		   Direction d1 = new Direction(endBounds.getCenterX() - startBounds.getCenterX(), endBounds.getCenterY() - startBounds.getCenterY());
		   endBounds = ePoint.neighbours.get(1).getBounds();
		   Direction d2 = new Direction(endBounds.getCenterX() - startBounds.getCenterX(), endBounds.getCenterY() - startBounds.getCenterY());
		   if(d1.isParallel(d2)){
			   InnerPoint p = null;
			   GraphElement q = null;
			   if(ePoint.neighbours.get(0) instanceof InnerPoint){
				    p = (InnerPoint)ePoint.neighbours.get(0);
			   		q = ePoint.neighbours.get(1);
			   		p.neighbours.add(q);
			   		p.neighbours.remove(ePoint);
			   }
			   if(ePoint.neighbours.get(1) instanceof InnerPoint){
				    p = (InnerPoint)ePoint.neighbours.get(1);
			   		q = ePoint.neighbours.get(0);
			   		p.neighbours.add(q);
			   		p.neighbours.remove(ePoint);
			   }
			   pItr.remove();
		   }
	   }
	   notifyChange(new ElementChangedEvent(this,this,"stop_move"));
   }
   
   public void startMove(Point2D downPoint){
	   this.downPoint = downPoint;
	   newInnerPoint = null;
	   for(InnerPoint itrPoint : points)
		   if(itrPoint.contains(downPoint)){
			   /* clicked on an already existing EdgePoint */
			   newInnerPoint = itrPoint;
			   newPointCreated = false;
		   }
	   if(newInnerPoint == null){
		   /* no point under the click, create a new one */
		   newInnerPoint = new InnerPoint();
	   	   newInnerPoint.translate(downPoint, downPoint.getX() - newInnerPoint.getBounds().getCenterX(),
	            downPoint.getY() - newInnerPoint.getBounds().getCenterY());
	   	   newPointCreated = true;
	   }
   }
   
   @Override
   public void translate(Point2D p, double dx, double dy){
	   for(InnerPoint ip : points)
		   ip.translate(p, dx, dy);
   }
  
   public Line2D getNameLine(){
		if(points.isEmpty()){ // straight line 
			return getSegment(nodes.get(0),nodes.get(1));
		}else{
			if(masterInnerPoint != null){/* multiended edge  */
				 Rectangle2D bounds = masterInnerPoint.getBounds();
				 Point2D p = new Point2D.Double(bounds.getCenterX() - 1,bounds.getCenterY());
				 Point2D q = new Point2D.Double(bounds.getCenterX() + 1,bounds.getCenterY());
				 return new Line2D.Double(p, q);
			}else{
				GraphElement ge1 = nodes.get(0);
				 GraphElement ge2 = nodes.get(1);
				 InnerPoint c1 = null;
				 InnerPoint c2 = null;
					 
				 for(InnerPoint innp : points){
					 if(innp.getNeighbours().contains(ge1)){
						 c1 = innp;
					 }
					 if(innp.getNeighbours().contains(ge2)){
						 c2 = innp;
					 }
				 }
				
				 /* we only have two nodes but the edge has been bended */
				 while((c1 != c2)&&(!c2.getNeighbours().contains(c1))){
					 if(c1.getNeighbours().get(0) == ge1){
						 ge1 = c1;
						 c1 = (InnerPoint)c1.getNeighbours().get(1);
					 }
					 else{
						 ge1 = c1;
						 c1 = (InnerPoint)c1.getNeighbours().get(0);
					 }
					 if(c2.getNeighbours().get(0) == ge2){
						 ge2 = c2;
						 c2 = (InnerPoint)c2.getNeighbours().get(1);
					 }
					 else{
						 ge2 = c2;
						 c2 = (InnerPoint)c2.getNeighbours().get(0);
					 }
				 }
				 
				 Point2D p = new Point2D.Double();
				 Point2D q = new Point2D.Double();
				 if(c1 == c2){
					 Rectangle2D bounds = c1.getBounds();
					 p.setLocation( bounds.getCenterX() - 1,bounds.getCenterY());
					 q.setLocation(  bounds.getCenterX() + 1,bounds.getCenterY());
				 }else{
					 Rectangle2D bounds = c1.getBounds();
					 p.setLocation( bounds.getCenterX(),bounds.getCenterY());
					 bounds = c2.getBounds();
					 q.setLocation(bounds.getCenterX(),bounds.getCenterY());
					 
				 }
				 return new Line2D.Double(p,q);
			}
		}
	}
	
	@Override
	public abstract Rectangle2D getBounds();
	
	public void encode(Document doc, Element parent, List<Node> nodes){
		parent.setAttribute(PersistenceManager.TYPE,getType());
		parent.setAttribute(PersistenceManager.NAME, getName());
		parent.setAttribute(PersistenceManager.ID, String.valueOf(getId()));

		int numNodes = getNodesNum(); 
		if(numNodes > 0){
			Element nodesTag = doc.createElement(PersistenceManager.NODES);
			parent.appendChild(nodesTag);
			for(int i=0; i<numNodes;i++){
				Element nodeTag = doc.createElement(PersistenceManager.NODE);
				nodeTag.setAttribute(PersistenceManager.ID, String.valueOf(getNodeAt(i).getId()));
				nodeTag.setAttribute(PersistenceManager.LABEL, getEndLabel(getNodeAt(i)));
				nodesTag.appendChild(nodeTag);
			}
		}
		 
		if(!points.isEmpty()){
			Element pointsTag = doc.createElement(PersistenceManager.POINTS);
			parent.appendChild(pointsTag);
			for(InnerPoint point : points){
				Element pointTag = doc.createElement(PersistenceManager.POINT);
				pointsTag.appendChild(pointTag);
				pointTag.setAttribute(PersistenceManager.ID, String.valueOf(-(points.indexOf(point)+1)));
				
				Element positionTag = doc.createElement(PersistenceManager.POSITION);
				pointTag.appendChild(positionTag);
				Rectangle2D bounds = point.getBounds();
				positionTag.setAttribute(PersistenceManager.X,String.valueOf(bounds.getX()));
				positionTag.setAttribute(PersistenceManager.Y,String.valueOf(bounds.getY()));
				
				Element neighboursTag = doc.createElement(PersistenceManager.NEIGHBOURS);
				pointTag.appendChild(neighboursTag);
				StringBuilder builder = new StringBuilder();
				for(GraphElement ge : point.getNeighbours()){
					if(ge instanceof Node){
						builder.append(((Node)ge).getId());
					}else{
						builder.append(-(points.indexOf(ge)+1));
					}
					builder.append(" ");
				}
				builder.deleteCharAt(builder.length()-1);
				neighboursTag.setTextContent(builder.toString());
			}
		}
	}
	
	
    public void decode(Document doc, Element edgeTag, Map<String,Node> nodesId) throws IOException{
    	setName(edgeTag.getAttribute(PersistenceManager.NAME));
    	if(getName().isEmpty())
    		throw new IOException();
    	try{
    		setId(Integer.parseInt(edgeTag.getAttribute(PersistenceManager.ID)));
    	}catch(NumberFormatException nfe){
    		throw new IOException(nfe);
    	}
    	
    	NodeList nodeList = edgeTag.getElementsByTagName(PersistenceManager.NODE);
    	List<DiagramNode> attachedNodes = new ArrayList<DiagramNode>(nodeList.getLength());
    	List<String> labels = new ArrayList<String>(nodeList.getLength());
    	for(int i=0; i<nodeList.getLength();i++){
    		String id = ((Element)nodeList.item(i)).getAttribute(PersistenceManager.ID);
    		if(!nodesId.containsKey(id))
    			throw new IOException();
    		attachedNodes.add(nodesId.get(id));
    		labels.add(((Element)nodeList.item(i)).getAttribute(PersistenceManager.LABEL));
    	}
    	
    	try {
			connect(attachedNodes);
		} catch (ConnectNodesException e) {
			throw new IOException(e);
		}
		
		for(int i=0; i < labels.size(); i++){
			setEndLabel(attachedNodes.get(i), labels.get(i));
		}
		
		Map<String, InnerPoint> pointsId = new LinkedHashMap<String, InnerPoint>();
		NodeList pointTagList = edgeTag.getElementsByTagName(PersistenceManager.POINT);
		
		for(int i=0; i<pointTagList.getLength(); i++){
			InnerPoint point = new InnerPoint();
			Element pointTag = (Element)pointTagList.item(i);
			String id = pointTag.getAttribute(PersistenceManager.ID);
			/* id of property nodes must be a negative value */
			try{
				if(Integer.parseInt(id) >= 0)
					throw new IOException();
			}catch(NumberFormatException nfe){
				throw new IOException(nfe);
			}
			
			pointsId.put(id, point);
			
			if(pointTag.getElementsByTagName(PersistenceManager.POSITION).item(0) == null)
				throw new IOException();
			Element pointPositionTag = (Element)pointTag.getElementsByTagName(PersistenceManager.POSITION).item(0);
			double dx = 0,dy = 0;
			try{
				dx = Double.parseDouble(pointPositionTag.getAttribute(PersistenceManager.X));
				dy = Double.parseDouble(pointPositionTag.getAttribute(PersistenceManager.Y));
			}catch(NumberFormatException nfe){
				throw new IOException();
			}
			point.translate(new Point2D.Double(), dx, dy);
		}
		
		/* remove the master inner point eventually created by connect */
		/* we're going to replace it with the one in the XML file      */
		points.clear();
		/* re do the cycle when all the points id have been Map-ped */
		for(int i=0; i<pointTagList.getLength(); i++){
			Element pointTag = (Element)pointTagList.item(i);
			InnerPoint point = pointsId.get(pointTag.getAttribute(PersistenceManager.ID));
			
			if(pointTag.getElementsByTagName(PersistenceManager.NEIGHBOURS).item(0) == null)
				throw new IOException();
			Element pointNeighboursTag = (Element)pointTag.getElementsByTagName(PersistenceManager.NEIGHBOURS).item(0);
			String pointNeighboursTagContent = pointNeighboursTag.getTextContent();
			String[] neighboursId = pointNeighboursTagContent.split(" ");
			
			for(String neighbourId : neighboursId){
				GraphElement ge = nodesId.get(neighbourId);
				if(ge == null) // it ain't a node
					ge = pointsId.get(neighbourId);
				if(ge == null)
					throw new IOException();
				point.neighbours.add(ge);
			}
			points.add(point);
		}
    }
    
    @Override
    public Point2D getConnectionPoint(Direction d){return null;}
    
	public int getMinAttachedNodes(){
		return minAttachedNodes;
	}
	
	public int getMaxAttachedNodes(){
		return maxAttachedNodes;
	}
	
	public LineStyle getStyle(){
		return style;
	}
    
	protected Point2D downPoint;
	private List<Node> nodes;
	
	/* list containing the vertex of the edge which are not nodes */
	protected List<InnerPoint> points;
	protected InnerPoint newInnerPoint;
	protected InnerPoint masterInnerPoint;
	protected boolean newPointCreated;
	private int minAttachedNodes;
	private int maxAttachedNodes;

	private LineStyle style;
	
	
	private static final double MAX_DIST = 3;
	private static final Color POINT_COLOR = Color.GRAY;
	public static final String NO_ARROW_STRING = ResourceBundle.getBundle(EditorFrame.class.getName()).getString("no_arrow_string");

	
	protected static class InnerPoint implements GraphElement{
	
		public InnerPoint(){
			bounds = new Rectangle2D.Double(0,0,DIM,DIM);
			neighbours = new LinkedList<GraphElement>();
		}
	
		@Override
		public void startMove(Point2D p){}
		
		@Override
		public void stopMove(){}
	
		@Override
		public void draw(Graphics2D g2){
			Color oldColor = g2.getColor();
			g2.setColor(POINT_COLOR);
			g2.fill(bounds);
			g2.setColor(oldColor);
		}

		@Override
		public boolean contains(Point2D p){
			return bounds.contains(p);
		}

		@Override
		public Point2D getConnectionPoint(Direction d){
			return new Point2D.Double(bounds.getCenterX(),bounds.getCenterY());
		}
		
		@Override
		public void translate(Point2D p, double dx, double dy){
			bounds.setFrame(bounds.getX() + dx,
				bounds.getY() + dy, 
				bounds.getWidth(), 
				bounds.getHeight());
		}
		
		@Override
		public Rectangle2D getBounds(){
			return (Rectangle2D)bounds.clone();
		}
		
		public List<GraphElement> getNeighbours(){
			return neighbours;
		}
		
		public boolean hasNeighbour(GraphElement neighbour){
			return neighbours.contains(neighbour);
		}
		
		@Override
		public String toString(){
			return "EdgePoint: "+bounds.getCenterX()+"-"+bounds.getCenterY();
		}
		
		private Rectangle2D bounds;
		private List<GraphElement> neighbours; 
		private static final int DIM = 5;
	}
	
	public static class PointRepresentation {
		public PointRepresentation(int size){
			xs = new double[size];
			ys = new double[size];
			adjMatrix = new BitSet[size];
			for(int i=0; i<size; i++){
				adjMatrix[i] = new BitSet(size); 
			}
		}
		public double xs[];
		public double ys[];
		public BitSet adjMatrix[];
		public int nodeStart; // the index of the beginning of the nodes in the adjMatrix
	}
	
	public PointRepresentation getPointRepresentation(){
		PointRepresentation pr = new PointRepresentation(points.size()+nodes.size());
		if(points.isEmpty()){ // two ended edge 
			pr.xs[0] = nodes.get(0).getBounds().getCenterX();
			pr.ys[0] = nodes.get(0).getBounds().getCenterY();
			pr.xs[1] = nodes.get(1).getBounds().getCenterX();
			pr.ys[1] = nodes.get(1).getBounds().getCenterY();
			// we only need one edge, else it would be painted twice 
			pr.adjMatrix[0].set(1);
			pr.nodeStart = 0;
		}else{
			//[ point 1, point 2, point 3, ... , point n, node, 1 node 2, ... ,  node n ]
			int pSize = points.size();
			pr.nodeStart = pSize; // the first node starts after the points
			for(int i=0; i<pSize;i++){
				  pr.xs[i] = points.get(i).getBounds().getCenterX();
				  pr.ys[i] = points.get(i).getBounds().getCenterY();
				  for(GraphElement ge : points.get(i).neighbours){
					  if(ge instanceof InnerPoint)
						  pr.adjMatrix[i].set(points.indexOf(ge));
					  else //Node
						  pr.adjMatrix[i].set(pSize+nodes.indexOf(ge));
				  }
			}
			/* set the coordinates of the nodes, no adj matrix needed as the inner points are enough */
			for(int i=0 ; i<nodes.size(); i++){
				pr.xs[pSize+i] = nodes.get(i).getBounds().getCenterX();
				pr.ys[pSize+i] = nodes.get(i).getBounds().getCenterY();
			}
		}
		return pr;
	}

}