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