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