]> gerrit.simantics Code Review - simantics/3d.git/blob - org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipeControlPoint.java
22c58ddb160be1a1f43303b34e63d1dd3614bc17
[simantics/3d.git] / org.simantics.plant3d / src / org / simantics / plant3d / scenegraph / controlpoint / PipeControlPoint.java
1 package org.simantics.plant3d.scenegraph.controlpoint;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.List;
7
8 import javax.vecmath.AxisAngle4d;
9 import javax.vecmath.Matrix3d;
10 import javax.vecmath.Quat4d;
11 import javax.vecmath.Tuple3d;
12 import javax.vecmath.Vector3d;
13
14 import org.simantics.g3d.math.MathTools;
15 import org.simantics.g3d.property.annotations.GetPropertyValue;
16 import org.simantics.g3d.scenegraph.G3DNode;
17 import org.simantics.plant3d.scenegraph.IP3DNode;
18 import org.simantics.plant3d.scenegraph.PipeRun;
19 import org.simantics.plant3d.scenegraph.PipelineComponent;
20
21 import vtk.vtkRenderer;
22
23
24 public class PipeControlPoint extends G3DNode implements IP3DNode {
25         
26         public enum Type{INLINE,TURN,END};
27         public enum Direction{NEXT,PREVIOUS};
28         public enum PositionType {SPLIT,NEXT,PREVIOUS,PORT}
29         
30         private PipelineComponent component;
31         
32         private Type type;
33         private boolean fixed = true;
34         private boolean rotate = false;
35         private boolean reverse = false;
36         private boolean deletable = true;
37         private boolean sub = false;
38         
39         public PipeControlPoint(PipelineComponent component) {
40                 this.component = component;
41                 if (component.getPipeRun() != null)
42                         component.getPipeRun().addChild(this);
43                 
44         }
45         
46         public PipeControlPoint(PipelineComponent component, PipeRun piperun) {
47                 this.component = component;
48                 piperun.addChild(this);
49         }
50         
51         @Override
52         public void update(vtkRenderer ren) {
53                 try {
54                         PipingRules.requestUpdate(this);
55                 } catch (Exception e) {
56                         e.printStackTrace();
57                 }
58                 
59         }
60         
61         public PipeRun getPipeRun() {
62                 return (PipeRun)getParent();
63         }
64         
65         public PipelineComponent getPipelineComponent() {
66                 return component;
67         }
68         
69         public Type getType() {
70                 return type;
71         }
72         
73         public void setType(Type type) {
74                 this.type = type;
75         }
76         
77         @GetPropertyValue(name="Fixed",tabId="Debug",value="fixed")
78         public boolean isFixed() {
79                 return fixed;
80         }
81         
82         
83         public void setFixed(boolean fixed) {
84                 this.fixed = fixed;
85         }
86         
87         @GetPropertyValue(name="Rotate",tabId="Debug",value="rotate")
88         public boolean isRotate() {
89                 return rotate;
90         }
91         
92         public void setRotate(boolean rotate) {
93                 this.rotate = rotate;
94         }
95         
96         @GetPropertyValue(name="Reverse",tabId="Debug",value="reverse")
97         public boolean isReverse() {
98                 return reverse;
99         }
100         
101         public void setReverse(boolean reverse) {
102                 this.reverse = reverse;
103         }
104         
105         public void setSub(boolean sub) {
106                 this.sub = sub;
107         }
108                 
109         @GetPropertyValue(name="Deletable",tabId="Debug",value="deletable")
110         public boolean isDeletable() {
111                 return deletable;
112         }
113         
114         public void setDeletable(boolean deletable) {
115                 this.deletable = deletable;
116         }
117         
118         public boolean isPathLegEnd() {
119                 return type != Type.INLINE;
120         }
121         
122         public boolean isEnd() {
123                 return type == Type.END;
124         }
125         
126         public boolean isTurn() {
127                 return type == Type.TURN;
128         }
129         
130         public boolean isInline() {
131                 return type == Type.INLINE;
132         }
133         
134         public boolean isDirected() {
135                 return fixed && isEnd();
136         }
137         
138         public boolean isNonDirected() {
139                 return !fixed && isEnd();
140         }
141         
142         public boolean isVariableLength() {
143                 return !fixed && isInline();
144         }
145         
146         public boolean isVariableAngle() {
147                 return !fixed && isTurn();
148         }
149         
150         public boolean isBranchEnd() {
151                 return deletable && isEnd();
152         }
153         
154         public boolean isOffset() {
155                 return offset != null;
156         }
157         
158         public boolean isDualSub() {
159                 return parent != null && sub;
160         }
161         
162         public boolean isDualInline() {
163                 return children.size() == 1 && children.get(0).isDualSub();
164         }
165         
166         public boolean isSizeChange() {
167                 if (children.size() == 0)
168                         return false;
169                 if (!isDualInline())
170                         return false;
171                 return getPipeRun() != children.get(0).getPipeRun();
172         }
173
174         
175         private PipeControlPoint next;
176         private PipeControlPoint previous;
177         
178         public PipeControlPoint getNext() {
179                 return next;
180         }
181         
182         public PipeControlPoint getPrevious() {
183                 return previous;
184         }
185         
186         public void setNext(PipeControlPoint next) {
187                 if (isEnd() && previous != null && next != null)
188                         throw new RuntimeException("End control points are allowed to have only one connection");
189 //              if (next != null && getPipeRun() == null)
190 //                      throw new RuntimeException("Cannot connect control point befor piperun has been set");
191                 this.next = next;
192                 if (component != null) {
193                         if (parent == null || sub)
194                                 component.setNext(next != null ? next.component : null);
195                         else
196                                 component.setBranch0(next != null ? next.component : null);
197                         updateSubPoint();
198                 }
199                 
200         }
201         
202         public void setPrevious(PipeControlPoint previous) {
203                 if (isEnd() && next != null && previous != null)
204                         throw new RuntimeException("End control points are allowed to have only one connection");
205 //              if (previous != null && getPipeRun() == null)
206 //                      throw new RuntimeException("Cannot connect control point befor piperun has been set");
207                 this.previous = previous;
208                 if (component != null) {
209                         if (parent == null || sub)
210                                 component.setPrevious(previous != null ? previous.component : null);
211                         else
212                                 component.setBranch0(previous != null ? previous.component : null);
213                         updateSubPoint();
214                 }
215                 
216         }
217         
218         public PipeControlPoint parent;
219         public List<PipeControlPoint> children = new ArrayList<PipeControlPoint>();
220         
221         public List<PipeControlPoint> getSubPoint() {
222                 return children;
223         }
224         
225         public PipeControlPoint getParentPoint() {
226                 return parent;
227         }
228         
229         
230         
231
232         
233         
234         
235         
236         private double length;
237         private Double turnAngle;
238         private Vector3d turnAxis;
239
240         private Double offset;
241         private Double rotationAngle;
242         private Boolean reversed;
243         
244         @GetPropertyValue(name="Length",tabId="Debug",value="length")
245         public double getLength() {
246                 return length;
247         }
248         
249         public void setLength(double l) {
250                 if (Double.isInfinite(l) || Double.isNaN(l)) {
251                         return;
252                 }
253                 if (Math.abs(this.length-l) < MathTools.NEAR_ZERO)
254                         return;
255                 this.length = l;
256                 firePropertyChanged("length");
257                 if (isDualInline())
258                         getSubPoint().get(0).setLength(l);
259         }
260         
261         @GetPropertyValue(name="Turn Angle",tabId="Debug",value="turnAngle")
262         public Double getTurnAngle() {
263                 return turnAngle;
264         }
265         
266         @GetPropertyValue(name="Turn Axis",tabId="Debug",value="turnAxis")
267         public Vector3d getTurnAxis() {
268                 return turnAxis;
269         }
270         
271         @GetPropertyValue(name="Offset",tabId="Debug",value="offset")
272         public Double getOffset() {
273                 return offset;
274         }
275         
276         @GetPropertyValue(name="Rotation Angle",tabId="Debug",value="rotationAngle")
277         public Double getRotationAngle() {
278                 return rotationAngle;
279         }
280         
281         @GetPropertyValue(name="Reversed",tabId="Debug",value="reversed")
282         public Boolean getReversed() {
283                 return reversed;
284         }
285         
286         public boolean _getReversed() {
287             if (reversed == null)
288                 return false;
289         return reversed;
290     }
291         
292         public void setTurnAngle(Double turnAngle) {
293                 if (Double.isInfinite(turnAngle) || Double.isNaN(turnAngle)) {
294                         return;
295                 }
296                 if (this.turnAngle != null && Math.abs(this.turnAngle-turnAngle) < MathTools.NEAR_ZERO)
297                         return;
298                 this.turnAngle = turnAngle;
299                 firePropertyChanged("turnAngle");
300         }
301         
302         public void setTurnAxis(Vector3d turnAxis) {
303                 this.turnAxis = turnAxis;
304                 firePropertyChanged("turnAxis");
305         }
306         
307         public void setOffset(Double offset) {
308                 if (Double.isInfinite(offset) || Double.isNaN(offset)) {
309                         return;
310                 }
311                 if (this.offset != null && Math.abs(this.offset-offset) < MathTools.NEAR_ZERO)
312                         return;
313                 this.offset = offset;
314                 firePropertyChanged("offset");
315         }
316         
317         public void setRotationAngle(Double rotationAngle) {
318                 if (Double.isInfinite(rotationAngle) || Double.isNaN(rotationAngle)) {
319                         return;
320                 }
321                 if (this.rotationAngle != null && Math.abs(this.rotationAngle-rotationAngle) < MathTools.NEAR_ZERO)
322                         return;
323                 this.rotationAngle = rotationAngle;
324                 firePropertyChanged("rotationAngle");
325         }
326         
327         public void setReversed(Boolean reversed) {
328                 this.reversed = reversed;
329                 firePropertyChanged("reversed");
330         }
331         
332         public Vector3d getSizeChangeOffsetVector(Vector3d dir) {
333                 Quat4d q;
334                 if (rotationAngle == null)
335                         q = getControlPointOrientationQuat(dir, 0.0);
336                 else
337                         q = getControlPointOrientationQuat(dir, rotationAngle);
338                 Vector3d v = new Vector3d(0.0,offset,0.0);
339         Vector3d offset = new Vector3d();
340         MathTools.rotate(q, v, offset);
341         return offset;
342         }
343         
344         public Vector3d getSizeChangeOffsetVector() {
345                 Quat4d q;
346                 if (rotationAngle == null)
347                         q = getControlPointOrientationQuat(0.0);
348                 else
349                         q = getControlPointOrientationQuat(rotationAngle);
350                 Vector3d v = new Vector3d(0.0,offset,0.0);
351         Vector3d offset = new Vector3d();
352         MathTools.rotate(q, v, offset);
353         return offset;
354         }
355         
356         @GetPropertyValue(name="Next",tabId="Debug",value="next")
357         private String getNextString() {
358                 if (next == null)
359                         return null;
360                 return next.toString();
361         }
362         
363         @GetPropertyValue(name="Previous",tabId="Debug",value="previous")
364         private String getPrevString() {
365                 if (previous == null)
366                         return null;
367                 return previous.toString();
368         }
369         
370         @GetPropertyValue(name="Sub",tabId="Debug",value="sub")
371         private String getSubString() {
372                 if (children.size() == 0)
373                         return "";
374                 return Arrays.toString(children.toArray());
375         }
376         
377         @GetPropertyValue(name="Type",tabId="Debug",value="type")
378         public String getTypeString() {
379                 return type.name();
380         }
381         
382         public Quat4d getControlPointOrientationQuat(double angle) {
383                  
384                  if (turnAxis == null) {
385                          Vector3d dir = getPathLegDirection(Direction.NEXT);
386                          if (dir.lengthSquared() > MathTools.NEAR_ZERO)
387                                  dir.normalize();
388                          return getControlPointOrientationQuat(dir, angle);
389                  } else {
390                          Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
391                          dir.negate();
392                          if (dir.lengthSquared() > MathTools.NEAR_ZERO)
393                                  dir.normalize();
394                          return getControlPointOrientationQuat(dir, turnAxis, angle);
395                  }
396         }
397          
398     public Quat4d getControlPointOrientationQuat(double angle, boolean reversed) {
399                  
400                  if (turnAxis == null) {
401                          Vector3d dir = getPathLegDirection(Direction.NEXT);
402                          if (dir.lengthSquared() > MathTools.NEAR_ZERO)
403                                  dir.normalize();
404                          Quat4d q =  getControlPointOrientationQuat(dir, angle);
405                          if (reversed) {
406                                  Quat4d q2 = new Quat4d();
407                                 q2.set(new AxisAngle4d(MathTools.Y_AXIS, Math.PI));
408                                 q.mulInverse(q2);
409                          }
410                          return q;
411                  } else {
412                          Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
413                          dir.negate();
414                          if (dir.lengthSquared() > MathTools.NEAR_ZERO)
415                                  dir.normalize();
416                          return getControlPointOrientationQuat(dir, turnAxis, angle);
417                  }
418         }
419          
420         
421         
422         public static Quat4d getControlPointOrientationQuat(Vector3d dir, double angle) {
423                         if (dir.lengthSquared() < MathTools.NEAR_ZERO)
424                                 return MathTools.getIdentityQuat();
425                 
426                 
427                         Vector3d up = new Vector3d(0.0, 1.0, 0.0);
428                         double a = up.angle(dir);
429                         if (a < 0.1 || (Math.PI - a) < 0.1) {
430                                 up.set(1.0, 0.0, 0.0);
431                         }
432                         
433
434                         return getControlPointOrientationQuat(dir, up, angle);
435             }
436          
437          public static Quat4d getControlPointOrientationQuat(Vector3d dir, Vector3d up,  double angle) {
438                         if (dir.lengthSquared() < MathTools.NEAR_ZERO)
439                                 return MathTools.getIdentityQuat();
440                 
441                 final Vector3d front = new Vector3d(1.0,0.0,0.0);
442                 
443                 Quat4d q1 = new Quat4d();
444
445
446                         Vector3d right = new Vector3d();
447
448                         right.cross(dir, up);
449                         up.cross(right, dir);
450                         right.normalize();
451                         up.normalize();
452
453                         Matrix3d m = new Matrix3d();
454                         m.m00 = dir.x;
455                         m.m10 = dir.y;
456                         m.m20 = dir.z;
457                         m.m01 = up.x;
458                         m.m11 = up.y;
459                         m.m21 = up.z;
460                         m.m02 = right.x;
461                         m.m12 = right.y;
462                         m.m22 = right.z;
463
464                         //q1.set(m); MathTools contains more stable conversion
465                         MathTools.getQuat(m, q1);
466
467 //                      if (DEBUG) System.out.println("PipingTools.getPipeComponentOrientationQuat() " + dir+ " " + up + " " + right);
468
469                         Quat4d q2 = new Quat4d();
470                         q2.set(new AxisAngle4d(front, angle));
471                         q1.mul(q2);
472                         return q1;
473             }
474         
475         public Vector3d getDirection(Direction direction) {
476             if (isDirected())
477                 return getDirectedControlPointDirection();
478             if (isTurn() && isFixed()) {
479                 if (direction == Direction.NEXT) {
480                     if (previous != null) {
481                     PipeControlPoint pcp = this;
482                     Vector3d dir = new Vector3d();
483                     dir.sub(pcp.getWorldPosition(),previous.getWorldPosition());
484                     if (dir.lengthSquared() > MathTools.NEAR_ZERO)
485                          dir.normalize();
486                     Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
487                     AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
488                     Quat4d q2 = MathTools.getQuat(aa);
489                     Vector3d v = new Vector3d(1.,0.,0.);
490                     Vector3d offset = new Vector3d();
491                     MathTools.rotate(q2, v, offset);
492                     MathTools.rotate(q, offset, dir);
493                     return dir;
494                     }
495                 } else {
496                     if (next != null) {
497                     PipeControlPoint pcp = this;
498                     Vector3d dir = new Vector3d();
499                     dir.sub(next.getWorldPosition(),pcp.getWorldPosition());
500                     if (dir.lengthSquared() > MathTools.NEAR_ZERO)
501                          dir.normalize();
502                     Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
503                     AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
504                     Quat4d q2 = MathTools.getQuat(aa);
505                     Vector3d v = new Vector3d(1.,0.,0.);
506                     Vector3d offset = new Vector3d();
507                     MathTools.rotate(q2, v, offset);
508                     MathTools.rotate(q, offset, dir);
509                     return dir;
510                     }
511                 }
512             }
513             return null;
514         }
515         
516         public void insert(PipeControlPoint previous, PipeControlPoint next) {
517                 // inserting an offsetpoint is error, 
518                 if (isDualSub())
519                         throw new RuntimeException("Dual sub points cannot be inserted.");
520                 // size change control point cannot be inserted this way, because it ends PipeRun
521                 if (isSizeChange())
522                         throw new RuntimeException("Size change points cannot be inserted.");
523                 PipeRun piperun = previous.getPipeRun();
524                 // and just to make sure that control point structure is not corrupted
525                 if (getPipeRun() != null) {
526                         if (piperun != getPipeRun() || piperun != next.getPipeRun())
527                                 throw new RuntimeException("All controls points must be located on the same pipe run");
528                 } else {
529                         piperun.addChild(this);
530                 }
531                 
532                 // insert new BranchControlPoint between straight's control points
533                 PipeControlPoint previousNext = previous.getNext();
534                 PipeControlPoint previousPrevious = previous.getPrevious();
535                 
536                 PipeControlPoint offsetCP = null;
537                 if (isOffset()) {
538                         offsetCP = getSubPoint().get(0);
539                 }
540                 if (previousNext != null && previousNext == next) {
541                         if (previous.isDualInline()) {
542                                 throw new RuntimeException();
543                         }
544                         if (next.isDualSub()) {
545                                 throw new RuntimeException();
546                         }
547                         previous.setNext(this);
548                         this.setPrevious(previous);
549                         if (previous.isDualSub()) {
550                                 previous.getParentPoint().setNext(this);
551                         }
552                         this.setNext(next);
553                         
554                         if (offsetCP == null) {
555                                 next.setPrevious(this);
556                         } else {
557                                 next.setPrevious(offsetCP);
558                                 offsetCP.setNext(next);
559                                 offsetCP.setPrevious(previous);
560                         }
561                         
562                         if (next.isDualInline()) {
563                                 next.getSubPoint().get(0).setPrevious(this);
564                         }
565                 } else if (previousPrevious != null && previousPrevious == next) {
566                          // control point were given in reverse order 
567                         if (next.isDualInline())
568                                 throw new RuntimeException();
569                         if (previous.isDualSub())
570                                 throw new RuntimeException();
571                         
572                         this.setNext(previous);
573                         if (offsetCP == null) {
574                                 previous.setNext(this);
575                         } else {
576                                 previous.setPrevious(offsetCP);
577                                 offsetCP.setNext(previous);
578                                 offsetCP.setPrevious(next);
579                         }
580                         if (previous.isDualInline()) {
581                                 previous.getSubPoint().get(0).setPrevious(this);
582                         }
583                         this.setPrevious(next);
584                         next.setNext(this);
585                         if (next.isDualSub()) {
586                                 next.getParentPoint().setNext(this);
587                         }
588                         
589                 } else {
590                         throw new RuntimeException();
591                 }       
592                 
593                 PipingRules.validate(piperun);
594         }
595         
596         
597         
598         public void insert(PipeControlPoint pcp, Direction direction) {
599                 if (isDualSub())
600                         throw new RuntimeException();
601                 if (direction == Direction.NEXT) {
602                         // if direction is next, user must have given OffsetPoint
603                         if (pcp.isDualInline())
604                                 throw new RuntimeException();
605                         // basic next/prev links
606                         pcp.setNext(this);
607                         this.setPrevious(pcp);
608                         // and last take care of sizechange / offset points
609                         if (pcp.isDualSub()) {
610                                 pcp.getParentPoint().setNext(this);
611                         }
612                         if (isDualInline()) {
613                                 getSubPoint().get(0).setPrevious(this);
614                         }
615                 } else {
616                         // if direction is previous, user must have given sizechange
617                         if (pcp.isDualSub())
618                                 throw new RuntimeException();
619                         // previous direction is more complicated, since if newCP is SizeChangeControlPoint,
620                         // we must link pcp to newCP's OffsetPoint
621                         PipeControlPoint nocp = null;
622                         if (isDualInline()) {
623                                 nocp = getSubPoint().get(0);
624                                 nocp.setNext(pcp);
625                         }
626                         if (nocp == null) {
627                                 pcp.setPrevious(this);
628                         } else {
629                                 pcp.setPrevious(nocp);
630                         }
631                         this.setNext(pcp);
632                         if (pcp.isDualInline()) {
633                                 PipeControlPoint ocp = pcp.getSubPoint().get(0);
634                                 if (nocp == null)
635                                         ocp.setPrevious(this);
636                                 else
637                                         ocp.setPrevious(nocp);
638                         }
639                         
640                 }
641                 PipingRules.validate(getPipeRun());
642         }
643         
644         public Vector3d getDirectedControlPointDirection() {
645                 assert (isDirected());
646                 Vector3d dir = new Vector3d();
647                 MathTools.rotate(getWorldOrientation(), new Vector3d(1.0, 0.0, 0.0), dir);
648                 dir.normalize();
649                 return dir;
650         }
651         
652         public Vector3d getPathLegDirection(Direction direction) {
653                 if (direction == Direction.NEXT) {
654                         if (next != null) {
655                                 PipeControlPoint pcp = this;
656                                 if (pcp.isDualInline()) {
657                                         pcp = pcp.getSubPoint().get(0);
658                                 }
659                                 Vector3d v = new Vector3d();
660                                 v.sub(next.getWorldPosition(),pcp.getWorldPosition());
661                                 return v;
662                         } else {
663                                 if (previous == null) {
664                                         if (!isDirected())
665                                                 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point");
666                                         return getDirectedControlPointDirection();
667                                         
668                                 } else {
669                                     if (isVariableAngle())
670                             throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
671                                         if (isInline()) {
672                                                 PipeControlPoint pcp = this;
673                                                 if (pcp.isDualSub()) {
674                                                         pcp = pcp.getParentPoint();
675                                                 }
676                                                 Vector3d v = new Vector3d();
677                                                 v.sub(pcp.getWorldPosition(),previous.getWorldPosition());
678                                                 return v;
679                                         } else if (isDirected()) {
680                                                 return getDirectedControlPointDirection();
681                                         } else if (isEnd()) {
682                                                 Vector3d v = new Vector3d();
683                                                 v.sub(getWorldPosition(),previous.getWorldPosition());
684                                                 return v;
685                                         } else if (isTurn() && isFixed() && !_getReversed()) {
686                                            return getDirection(Direction.NEXT);
687                                         }
688                                         throw new RuntimeException("Missing implementation");
689                                 }
690                         }
691                 } else {
692                         if (previous != null) {
693                                 PipeControlPoint pcp = this;
694                                 if (isDualSub()) 
695                                         pcp = getParentPoint();
696                                 Vector3d v = new Vector3d();
697                                 v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
698                                 return v;
699                         } else {
700                                 if (next == null)  {
701                                         if (!isDirected())
702                                                 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point");
703                                         Vector3d v = getDirectedControlPointDirection();
704                                         v.negate();
705                                         return v;
706                                 } else {
707                                     if (isVariableAngle())
708                             throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point");
709                         if (isInline()) {
710                                                 PipeControlPoint pcp = this;
711                                                 if (pcp.isDualInline()) {
712                                                         pcp = pcp.getSubPoint().get(0);
713                                                 }
714                                                 Vector3d v = new Vector3d();
715                                                 v.sub(pcp.getWorldPosition(),next.getWorldPosition());
716                                                 return v;
717                                         } else if (isDirected()) {
718                                                 Vector3d v = getDirectedControlPointDirection();
719                                                 v.negate();
720                                                 return v;
721                                         } else if (isEnd()) {
722                                                 Vector3d v = new Vector3d();
723                                                 v.sub(getWorldPosition(),next.getWorldPosition());
724                                                 return v;
725                                         } else if (isTurn() && isFixed() && _getReversed()) {
726                                             return getDirection(Direction.PREVIOUS);
727                     }
728                                         throw new RuntimeException("Missing implementation");
729                                 }
730                         }
731                 }
732         }
733         
734         public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2) {
735                 assert (isInline());
736                 
737                 Vector3d pos = getWorldPosition();
738                 Vector3d dir = getPathLegDirection(Direction.NEXT);
739                 dir.normalize();
740                 dir.scale(length * 0.5);
741                 p1.set(pos);
742                 p2.set(pos);
743                 p1.sub(dir);
744                 p2.add(dir);
745         }
746         
747         public void getControlPointEnds(Tuple3d p1, Tuple3d p2) {
748                 Vector3d pos = getWorldPosition();
749                 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
750                 dir1.normalize();
751                 Vector3d dir2 = getPathLegDirection(Direction.NEXT);
752                 dir2.normalize();
753                 if (isInline()) {
754                         dir1.scale(length * 0.5);
755                         dir2.scale(length * 0.5);
756                 } else {
757                         dir1.scale(length);
758                         dir2.scale(length);
759                 }
760                 p1.set(pos);
761                 p2.set(pos);
762                 p1.add(dir1);
763                 p2.add(dir2);
764         }
765         
766         public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2, Vector3d dir) {
767                 assert (isInline());
768                 
769                 Vector3d pos = getWorldPosition();
770                 dir.set(getPathLegDirection(Direction.NEXT));
771                 dir.normalize();
772                 dir.scale(length * 0.5);
773                 p1.set(pos);
774                 p2.set(pos);
775                 p1.sub(dir);
776                 p2.add(dir);
777         }
778         
779         public void getInlineControlPointEnds(Tuple3d center, Tuple3d p1, Tuple3d p2, Vector3d dir) {
780                 assert (isInline());
781                 
782                 Vector3d pos = getWorldPosition();
783                 center.set(pos);
784                 dir.set(getPathLegDirection(Direction.NEXT));
785                 dir.normalize();
786                 dir.scale(length * 0.5);
787                 p1.set(pos);
788                 p2.set(pos);
789                 p1.sub(dir);
790                 p2.add(dir);
791         }
792         
793         public double getInlineLength() {
794                 if (type == Type.TURN)
795                         return length;
796                 else if (type == Type.INLINE)
797                         return length * 0.5;
798                 return 0;
799         }
800         
801         public Vector3d getRealPosition(PositionType type) {
802                 Vector3d pos = getWorldPosition();
803                 switch (type) {
804                 case NEXT: {
805                         Vector3d dir = getPathLegDirection(Direction.NEXT);
806                         double length = getInlineLength();
807                         dir.normalize();
808                         dir.scale(length);
809                         pos.add(dir);
810                         break;
811                 }
812                 case PREVIOUS: {
813                         Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
814                         double length = getInlineLength();
815                         dir.normalize();
816                         dir.scale(length);
817                         pos.add(dir);
818                         break;
819                 }
820                 case PORT:
821                         // IEntity portDir = pcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasDirection);
822                         // TODO : how we calculated needed space for a port; does it has an offset from control point's position or not?
823                         break;
824                 case SPLIT:
825                         // do nothing
826                         break;
827                         
828                 }
829                 return pos;
830         }
831         
832         public void getInlineMovement(Tuple3d start, Tuple3d end) {
833                 // FIXME : check type of neighbor components and allow movement on top of variable length components,
834                 //         find proper range for movement (pcp's position is not)
835                 PipeControlPoint p = previous.getPrevious();
836                 PipeControlPoint n = next.getNext();
837                 start.set(p.getWorldPosition());
838                 end.set(n.getWorldPosition());
839         }
840         
841         public PipeControlPoint findNextEnd() {
842                 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
843         return findNextEnd( t);
844         }
845         
846         public PipeControlPoint findPreviousEnd() {
847                 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
848         return findPreviousEnd(t);
849         }
850         
851         public PipeControlPoint findNextEnd(List<PipeControlPoint> nextList) {
852                  while (true) {
853             PipeControlPoint pcp = null;
854             PipeControlPoint p = null;
855             if (nextList.size() == 0)
856                 p = this;
857                 
858             else
859                 p = nextList.get(nextList.size() - 1);
860
861             pcp = p.getNext();
862             if (pcp == null) {
863                 pcp = p;
864                 if (nextList.size() > 0)
865                         nextList.remove(nextList.size() - 1);
866 //              if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
867                 return pcp;
868                 //break;
869             }
870             if (pcp.isPathLegEnd()) {
871                 //if (DEBUG) System.out.println(" " + pcp.getResource());
872                 return pcp;
873             } else {
874                 nextList.add(pcp);
875                // if (DEBUG) System.out.print(" " + pcp.getResource());
876             }
877         }
878         }
879         
880         public PipeControlPoint findPreviousEnd(List<PipeControlPoint> prevList) {
881                 while (true) {
882                         PipeControlPoint pcp = null;
883                         PipeControlPoint p = null;
884                         if (prevList.size() == 0)
885                                 p = this;
886
887                         else
888                                 p = prevList.get(prevList.size() - 1);
889
890                         pcp = p.getPrevious();
891                         if (pcp == null) {
892                                 pcp = p;
893                                 if (prevList.size() > 0)
894                                         prevList.remove(prevList.size() - 1);
895 //                              if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
896                                 return pcp;
897                         }
898                         if (pcp.isPathLegEnd()) {
899 //                              if (DEBUG)      System.out.println(" " + pcp.getResource());
900                                 return pcp;
901                         } else {
902                                 prevList.add(pcp);
903 //                              if (DEBUG)System.out.print(" " + pcp.getResource());
904                         }
905                 }
906         }
907         
908         public void _remove() {
909                 if (component == null && next == null && previous == null)
910                         return;
911                 if (isDualInline() || isDualSub()) {
912                         removeDualPoint();
913                         return;
914                 }
915                 PipeRun pipeRun = getPipeRun();
916                 if (pipeRun == null)
917                         return;
918                 
919                 PipeControlPoint additionalRemove = null;
920                 if (!PipingRules.isEnabled()) {
921                         setPrevious(null);
922                         setNext(null);
923                 } else {
924
925                         PipeControlPoint currentPrev = previous;
926                         PipeControlPoint currentNext = next;
927                         if (currentNext == null && currentPrev == null) {
928                                 removeComponent();
929                                 pipeRun.remChild(this);
930                                 return;
931                         }
932                         if (currentNext != null && currentPrev != null) {
933                                 boolean link = true;
934                                 if (currentNext.isBranchEnd()) {
935                                         link = false;
936 //                                      currentNext.setPrevious(null);
937 //                                      currentNext.setNext(null);
938                                         currentNext.remove();
939                                         currentNext = null;
940                                         setNext(null);
941                                 }
942                                 if (currentPrev.isBranchEnd()) {
943                                         link = false;
944 //                                      currentPrev.setPrevious(null);
945 //                                      currentPrev.setNext(null);
946                                         currentPrev.remove();
947                                         currentPrev = null;
948                                         setPrevious(null);
949                                 }
950                                 if (link && currentPrev.isDirected() && currentNext.isDirected()) {
951                                         link = false;
952                                 }
953                                 if (currentNext == null) {
954                                         // Nothing to do
955                                 } else if (currentNext.isDualInline()) {
956                                         PipeControlPoint sccp = currentNext;
957                                         PipeControlPoint ocp = sccp.getSubPoint().get(0);
958                                         if (ocp == null) {
959                                                 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
960                                         }
961                                         if (link) {
962                                                 sccp.setPrevious(currentPrev);
963                                                 ocp.setPrevious(currentPrev);
964                                         } else {
965                                                 sccp.setPrevious(null);
966                                                 ocp.setPrevious(null);
967                                         }
968                                         setNext(null);
969                                 } else if (currentNext.isDualSub()) {
970                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
971                                 } else if (currentNext.previous == this) {
972                                         if (link) {
973                                                 currentNext.setPrevious(currentPrev);
974                                         } else {
975                                                 currentNext.setPrevious(null);
976                                         }
977                                         setNext(null);
978                                 } else {
979                                         throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
980                                 }
981                                 if (currentPrev == null) {
982                                         // Nothing to do
983                                 } else if (currentPrev.isDualInline()) {
984                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
985                                 } else if (currentPrev.isDualSub()) {
986                                         PipeControlPoint ocp = currentPrev;
987                                         PipeControlPoint sccp = ocp.getParentPoint();
988                                         if (sccp == null)
989                                                 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
990                                         if (link) {
991                                                 ocp.setNext(currentNext);
992                                                 sccp.setNext(currentNext);
993                                         } else {
994                                                 ocp.setNext(null);
995                                                 sccp.setNext(null);
996                                         }
997                                         setPrevious(null);
998                                 } else if (currentPrev.next == this) {
999                                         if (link)
1000                                                 currentPrev.setNext(currentNext);
1001                                         else
1002                                                 currentPrev.setNext(null);
1003                                         
1004                                         setPrevious(null);
1005                                 } else {
1006                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged");
1007                                 }
1008                                 if (link) {
1009                                         if (currentNext.isVariableLength() && currentPrev.isVariableLength()) {
1010                                                 // we have to join them into single variable length component.
1011                                                 additionalRemove = currentPrev;
1012                                                 //currentPrev.remove();
1013                                         }
1014                                 } else {
1015                                         // FIXME : pipe run must be split into two parts, since the control point structure is no more continuous. 
1016                                 }
1017                         } else if (next != null) {
1018                                 if (next.isDualInline()) {
1019                                         PipeControlPoint sccp = next;
1020                                         PipeControlPoint ocp = sccp.getSubPoint().get(0);
1021                                         if (ocp == null) {
1022                                                 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1023                                         }
1024                                         sccp.setPrevious(null);
1025                                         ocp.setPrevious(null);
1026                                 } else if (next.isDualSub()) {
1027                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1028                                 } else if (next.previous == this) {
1029                                         next.setPrevious(null);
1030                                 } else {
1031                                         throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1032                                 }
1033                                 setNext(null);
1034                         } else {  //(previous != null)
1035                                 if(previous.isDualInline()) {
1036                                         throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1037                                 } else if (previous.isDualSub()) {
1038                                         PipeControlPoint ocp = previous;
1039                                         PipeControlPoint sccp = ocp.getParentPoint();
1040                                         if (sccp == null) {
1041                                                 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1042                                         }
1043                                         ocp.setNext(null);
1044                                         sccp.setNext(null);
1045                                 } else if (previous.next == this) {
1046                                         previous.setNext(null);
1047                                 } else {
1048                                         throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1049                                 }
1050                                 setPrevious(null);
1051                         }
1052                         if (children.size() > 0 ) {
1053                                 removeSubPoints();
1054                         } else if (parent!= null) {
1055                                 removeParentPoint();
1056                         }
1057                         
1058                 }
1059                 
1060                 removeComponent();
1061                 pipeRun.remChild(this);
1062                 checkRemove(pipeRun);
1063                 if (PipingRules.isEnabled() && pipeRun.getParent() != null && pipeRun.getControlPoints().size() > 0)
1064                         PipingRules.validate(pipeRun);
1065                 if (additionalRemove != null)
1066                         additionalRemove.remove();
1067         }
1068         
1069         public void remove() {
1070                 PipeControlPoint currentPrev = previous;
1071                 PipeControlPoint currentNext = next;
1072                 _remove();
1073                 try {
1074                         if (currentNext != null)
1075                                 PipingRules.requestUpdate(currentNext);
1076                         if (currentPrev != null)
1077                                 PipingRules.requestUpdate(currentPrev);
1078                 } catch (Exception e) {
1079                         e.printStackTrace();
1080                 }
1081         }
1082         
1083         private void checkRemove(PipeRun pipeRun) {
1084                 Collection<PipeControlPoint> points = pipeRun.getControlPoints();
1085                 if (points.size() == 0) {
1086                         pipeRun.remove();
1087                 } else if (points.size() == 1) {
1088                         PipeControlPoint pcp = points.iterator().next();
1089                         if (pcp.isDeletable())
1090                                 pcp._remove();
1091                 }
1092         }
1093         
1094         private void removeDualPoint() {
1095                 if (previous != null)
1096                         previous.setNext(null);
1097                 if (next != null)
1098                         next.setPrevious(null);
1099                 PipeControlPoint ocp;
1100                 PipeControlPoint sccp;
1101                 if (isDualInline()) {
1102                         sccp = this;
1103                         ocp = getSubPoint().get(0);
1104                 } else {
1105                         ocp = this;
1106                         sccp = getParentPoint();
1107                 }
1108                 PipeRun p1 = ocp.getPipeRun();
1109                 PipeRun p2 = sccp.getPipeRun();
1110                 
1111                 ocp.removeComponent();
1112                 sccp.removeComponent();
1113                 
1114                 p1.remChild(ocp);
1115                 p2.remChild(sccp);
1116                 
1117                 // TODO : now we assume that this is size change, and we do
1118                 if (ocp.next != null)
1119                         ocp.next.setPrevious(null);
1120                 if (ocp.previous != null)
1121                         ocp.previous.setNext(null);
1122                 if (sccp.next != null)
1123                         sccp.next.setPrevious(null);
1124                 if (sccp.previous != null)
1125                         sccp.previous.setNext(null);
1126                 ocp.setNext(null);
1127                 ocp.setPrevious(null);
1128                 sccp.setNext(null);
1129                 sccp.setPrevious(null);
1130                 
1131                 checkRemove(p1);
1132                 checkRemove(p2);
1133         }
1134         
1135         private void removeSubPoints() {
1136                 for (PipeControlPoint p : children) {
1137                         // TODO : this may affect delete routine, since classification of the point changes.
1138                         p.parent = null;
1139                         p._remove();
1140                 }
1141                 children.clear();
1142         }
1143         
1144         private void removeParentPoint() {
1145                 throw new RuntimeException("Child points cannot be removed directly");
1146         }
1147         
1148         private void removeComponent() {
1149                 if (component == null)
1150                         return;
1151                 PipelineComponent next = component.getNext();
1152                 PipelineComponent prev = component.getNext();
1153                 if (next != null) {
1154                         if (next.getNext() == component)
1155                                 next.setNext(null);
1156                         else if (next.getPrevious() == component)
1157                                 next.setPrevious(null);
1158                 }
1159                 if (prev != null) {
1160                         if (prev.getNext() == component)
1161                                 prev.setNext(null);
1162                         else if (prev.getPrevious() == component)
1163                                 prev.setPrevious(null);
1164                 }
1165                 PipelineComponent comp = component;
1166                 component = null;
1167                 
1168                 comp.remove();
1169         }
1170         
1171         @Override
1172         public void setOrientation(Quat4d orientation) {
1173                 if (MathTools.equals(orientation, getOrientation()))
1174                         return;
1175                 super.setOrientation(orientation);
1176                 if (getParentPoint() == null && component != null)
1177                         component._setWorldOrientation(getWorldOrientation());
1178                 updateSubPoint();
1179         }
1180         
1181         @Override
1182         public void setPosition(Vector3d position) {
1183                 if (MathTools.equals(position, getPosition()))
1184                         return;
1185                 if (Double.isNaN(position.x) || Double.isNaN(position.y) || Double.isNaN(position.z))
1186                         throw new IllegalArgumentException("NaN is not supported");
1187                 super.setPosition(position);
1188                 if (getParentPoint() == null && component != null)
1189                         component._setWorldPosition(getWorldPosition());
1190                 updateSubPoint();
1191         }
1192         
1193         private void updateSubPoint() {
1194                 if (isOffset()) {
1195                         if (next == null && previous == null) {
1196                                 for (PipeControlPoint sub : getSubPoint()) {
1197                                         sub.setWorldPosition(getWorldPosition());
1198                                         sub.setWorldOrientation(getWorldOrientation());
1199                                 }
1200                                 return;
1201                         }
1202                         for (PipeControlPoint sub : getSubPoint()) {
1203                                 Vector3d wp = getWorldPosition();
1204                                 wp.add(getSizeChangeOffsetVector());
1205                                 sub.setWorldPosition(wp);
1206                                 sub.setWorldOrientation(getWorldOrientation());
1207                         }
1208                 } else {
1209                         for (PipeControlPoint sub : getSubPoint()) {
1210                                 sub.setWorldPosition(getWorldPosition());
1211                                 sub.setWorldOrientation(getWorldOrientation());
1212                         }
1213                 }
1214         }
1215
1216         
1217         public void _setWorldPosition(Vector3d position) {
1218                 Vector3d localPos = getLocalPosition(position);
1219                 super.setPosition(localPos);
1220                 updateSubPoint();
1221         }
1222         
1223         public void _setWorldOrientation(Quat4d orientation) {
1224                 Quat4d localOr = getLocalOrientation(orientation);
1225                 super.setOrientation(localOr);
1226                 updateSubPoint();
1227         }
1228         
1229         @Override
1230         public String toString() {
1231                 return getClass().getName() + "@" + Integer.toHexString(hashCode());
1232         }
1233
1234 }