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