1 package org.simantics.plant3d.scenegraph.controlpoint;
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
8 import javax.vecmath.AxisAngle4d;
9 import javax.vecmath.Matrix3d;
10 import javax.vecmath.Point3d;
11 import javax.vecmath.Quat4d;
12 import javax.vecmath.Tuple3d;
13 import javax.vecmath.Vector3d;
15 import org.simantics.g3d.math.MathTools;
16 import org.simantics.g3d.property.annotations.GetPropertyValue;
17 import org.simantics.g3d.scenegraph.G3DNode;
18 import org.simantics.plant3d.scenegraph.IP3DNode;
19 import org.simantics.plant3d.scenegraph.Nozzle;
20 import org.simantics.plant3d.scenegraph.P3DRootNode;
21 import org.simantics.plant3d.scenegraph.PipeRun;
22 import org.simantics.plant3d.scenegraph.PipelineComponent;
24 import vtk.vtkRenderer;
27 public class PipeControlPoint extends G3DNode implements IP3DNode {
29 private static boolean DEBUG = false;
31 public enum PointType{INLINE,TURN,END};
32 public enum Direction{NEXT,PREVIOUS};
33 public enum PositionType {SPLIT,NEXT,PREVIOUS,PORT}
35 private PipelineComponent component;
37 private PointType type;
38 private boolean isFixed = true; // In-line: fixed-length Turn: fixed-angle
39 private boolean isRotate = false; // rotates around path leg axis.
40 private boolean isReverse = false; // definition direction can be swapped
41 private boolean isDeletable = true; // can be removed by rules
42 private boolean isSizeChange = false; // changes size of the pipe. The next control point / component is on different PipeRun
43 private boolean isSub = false; // child point
45 public PipeControlPoint(PipelineComponent component) {
46 this.component = component;
47 if (component.getPipeRun() != null)
48 component.getPipeRun().addChild(this);
52 public PipeControlPoint(PipelineComponent component, PipeRun piperun) {
53 this.component = component;
54 piperun.addChild(this);
58 public void update(vtkRenderer ren) {
60 PipingRules.requestUpdate(this);
61 } catch (Exception e) {
67 public PipeRun getPipeRun() {
68 return (PipeRun)getParent();
71 public PipelineComponent getPipelineComponent() {
75 public PointType getType() {
79 public void setType(PointType type) {
83 @GetPropertyValue(name="Fixed",tabId="Debug",value="fixed")
84 public boolean isFixed() {
88 public void setFixed(boolean fixed) {
92 @GetPropertyValue(name="Rotate",tabId="Debug",value="rotate")
93 public boolean isRotate() {
97 public void setRotate(boolean rotate) {
98 this.isRotate = rotate;
101 @GetPropertyValue(name="Reverse",tabId="Debug",value="reverse")
102 public boolean isReverse() {
106 public void setReverse(boolean reverse) {
107 this.isReverse = reverse;
110 public void setSub(boolean sub) {
114 @GetPropertyValue(name="Deletable",tabId="Debug",value="deletable")
115 public boolean isDeletable() {
119 public void setDeletable(boolean deletable) {
120 this.isDeletable = deletable;
123 public boolean isPathLegEnd() {
124 return type != PointType.INLINE;
127 public boolean isEnd() {
128 return type == PointType.END;
131 public boolean isTurn() {
132 return type == PointType.TURN;
135 public boolean isInline() {
136 return type == PointType.INLINE;
140 * True for end components, if control point defines absolute position direction, which rules cannot modify.
141 * This is typical for nozzles.
144 public boolean isDirected() {
145 return isFixed && isEnd();
149 * True for end components, if control is opposite to directed, and rules can modify position and orientation.
150 * This is typical for caps, and other end components.
153 public boolean isNonDirected() {
154 return !isFixed && isEnd();
157 public boolean isVariableLength() {
158 return !isFixed && isInline();
162 * Fixed length in-line component is such that piping rules cannot modify the length.
165 public boolean isFixedLength() {
166 return isFixed && isInline();
169 public boolean isVariableAngle() {
170 return !isFixed && isTurn();
174 * Fixed angle turn component is such that piping rules cannot modify the angle.
177 public boolean isFixedAngle() {
178 return isFixed && isTurn();
182 * Does the turn behave like fixed angle?
183 * For variable angle turns, the turn angle is defined by connected components, and without them, we must handle the component as fixed angle.
186 public boolean asFixedAngle() {
187 return isTurn() && (isFixed || next == null || previous == null);
190 public boolean isBranchEnd() {
191 return isDeletable && isEnd();
194 public boolean isOffset() {
195 return offset != null;
198 public boolean isDualSub() {
199 return parent != null && isSub;
202 public boolean isDualInline() {
203 return children.size() == 1 && children.get(0).isDualSub();
206 public boolean isAxial() {
207 return isInline() && !isDualInline();
210 public boolean isSizeChange() {
212 // if (children.size() == 0)
214 // if (!isDualInline())
216 // return getPipeRun() != children.get(0).getPipeRun();
219 public void setSizeChange(boolean isSizeChange) {
220 this.isSizeChange = isSizeChange;
224 private PipeControlPoint next;
225 private PipeControlPoint previous;
227 public PipeControlPoint getNext() {
231 public PipeControlPoint getPrevious() {
235 public void setNext(PipeControlPoint next) {
236 if (isEnd() && previous != null && next != null)
237 throw new RuntimeException("End control points are allowed to have only one connection");
239 throw new RuntimeException("Cannot connect to self");
240 if (this.next == next)
242 if (DEBUG) System.out.println(this + " next " + next);
243 if (next == null && isVariableAngle() && previous != null && !isRemoved()) {
244 convertVariableAngleToFixed(Direction.NEXT);
247 if (component != null) {
248 if (parent == null || isSub)
249 component.setNext(next != null ? next.component : null);
251 component.setBranch0(next != null ? next.component : null);
256 public void setPrevious(PipeControlPoint previous) {
257 if (isEnd() && next != null && previous != null)
258 throw new RuntimeException("End control points are allowed to have only one connection");
259 if (previous == this)
260 throw new RuntimeException("Cannot connect to self");
261 if (this.previous == previous)
263 if (DEBUG) System.out.println(this + " previous " + previous);
264 if (previous == null && isVariableAngle() && next != null && !isRemoved()) {
265 convertVariableAngleToFixed(Direction.PREVIOUS);
267 this.previous = previous;
268 if (component != null) {
269 if (parent == null || isSub)
270 component.setPrevious(previous != null ? previous.component : null);
272 component.setBranch0(previous != null ? previous.component : null);
277 private void convertVariableAngleToFixed(Direction direction) {
278 // We are removing reference, which transforms variable angle to fixed angle.
279 // Since fixed angle is defined differently, we need to calculate fixed angle parameters based on current data
280 // We need to calculate turnAngle and rotationAngle
281 Vector3d dirOut = getPathLegDirection(direction == Direction.NEXT ? Direction.NEXT : Direction.PREVIOUS);
282 Vector3d dir = getPathLegDirection(direction == Direction.NEXT ? Direction.PREVIOUS : Direction.NEXT);
286 double angle = dir.angle(dirOut);
287 //super._setNext(null);
288 if (direction == Direction.NEXT)
292 setRotationAngle(0.0);
293 setReversed(direction == Direction.NEXT ? false : true);
294 Vector3d dirOutN = getPathLegDirection(direction == Direction.NEXT ? Direction.NEXT : Direction.PREVIOUS);
296 AxisAngle4d aa = new AxisAngle4d();
297 if (MathTools.createRotation(dirOutN, dirOut, dir, aa)) {
298 setRotationAngle(aa.angle);
303 public PipeControlPoint parent;
304 public List<PipeControlPoint> children = new ArrayList<PipeControlPoint>();
306 public List<PipeControlPoint> getSubPoint() {
310 public PipeControlPoint getParentPoint() {
315 private double length;
316 private Double turnAngle;
317 private Vector3d turnAxis;
319 private Double offset;
320 private Double rotationAngle;
321 private Boolean reversed;
323 @GetPropertyValue(name="Length",tabId="Debug",value="length")
324 public double getLength() {
328 public void setLength(double l) {
329 if (Double.isInfinite(l) || Double.isNaN(l)) {
332 if (Math.abs(this.length-l) < MathTools.NEAR_ZERO)
335 firePropertyChanged("length");
337 getSubPoint().get(0).setLength(l);
340 @GetPropertyValue(name="Turn Angle",tabId="Debug",value="turnAngle")
341 public Double getTurnAngle() {
345 @GetPropertyValue(name="Turn Axis",tabId="Debug",value="turnAxis")
346 public Vector3d getTurnAxis() {
350 @GetPropertyValue(name="Offset",tabId="Debug",value="offset")
351 public Double getOffset() {
355 @GetPropertyValue(name="Rotation Angle",tabId="Debug",value="rotationAngle")
356 public Double getRotationAngle() {
358 return rotationAngle;
362 @GetPropertyValue(name="Reversed",tabId="Debug",value="reversed")
363 public Boolean getReversed() {
367 public boolean _getReversed() {
368 if (reversed == null)
373 public void setTurnAngle(Double turnAngle) {
374 if (turnAngle == null || Double.isInfinite(turnAngle) || Double.isNaN(turnAngle)) {
377 if (this.turnAngle != null && Math.abs(this.turnAngle-turnAngle) < MathTools.NEAR_ZERO)
379 this.turnAngle = turnAngle;
380 firePropertyChanged("turnAngle");
383 public void setTurnAxis(Vector3d turnAxis) {
384 if (this.turnAxis != null && MathTools.equals(turnAxis, this.turnAxis))
386 this.turnAxis = turnAxis;
387 firePropertyChanged("turnAxis");
390 public void setOffset(Double offset) {
391 if (Double.isInfinite(offset) || Double.isNaN(offset)) {
394 if (this.offset != null && Math.abs(this.offset-offset) < MathTools.NEAR_ZERO)
396 this.offset = offset;
397 firePropertyChanged("offset");
400 public void setRotationAngle(Double rotationAngle) {
401 if (Double.isInfinite(rotationAngle) || Double.isNaN(rotationAngle)) {
404 if (this.rotationAngle != null && Math.abs(this.rotationAngle-rotationAngle) < MathTools.NEAR_ZERO)
406 this.rotationAngle = rotationAngle;
407 firePropertyChanged("rotationAngle");
410 public void setReversed(Boolean reversed) {
411 this.reversed = reversed;
412 firePropertyChanged("reversed");
415 public Vector3d getSizeChangeOffsetVector(Vector3d dir) {
417 if (rotationAngle == null)
418 q = getControlPointOrientationQuat(dir, 0.0);
420 q = getControlPointOrientationQuat(dir, rotationAngle);
421 Vector3d v = new Vector3d(0.0,offset,0.0);
422 Vector3d offset = new Vector3d();
423 MathTools.rotate(q, v, offset);
427 public Vector3d getSizeChangeOffsetVector() {
429 if (rotationAngle == null)
430 q = getControlPointOrientationQuat(0.0);
432 q = getControlPointOrientationQuat(rotationAngle);
433 Vector3d v = new Vector3d(0.0,offset,0.0);
434 Vector3d offset = new Vector3d();
435 MathTools.rotate(q, v, offset);
439 @GetPropertyValue(name="Next",tabId="Debug",value="next")
440 private String getNextString() {
443 return next.toString();
446 @GetPropertyValue(name="Previous",tabId="Debug",value="previous")
447 private String getPrevString() {
448 if (previous == null)
450 return previous.toString();
453 @GetPropertyValue(name="Sub",tabId="Debug",value="sub")
454 private String getSubString() {
455 if (children.size() == 0)
457 return Arrays.toString(children.toArray());
460 @GetPropertyValue(name="Type",tabId="Debug",value="type")
461 public String getTypeString() {
465 public Quat4d getControlPointOrientationQuat(double angle) {
467 if (turnAxis == null) {
468 Vector3d dir = getPathLegDirection(Direction.NEXT);
469 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
471 return getControlPointOrientationQuat(dir, angle);
473 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
475 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
477 return getControlPointOrientationQuat(dir, turnAxis, angle);
481 public Quat4d getControlPointOrientationQuat(double angle, boolean reversed) {
483 if (turnAxis == null) {
484 Vector3d dir = getPathLegDirection(Direction.NEXT);
485 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
487 Quat4d q = getControlPointOrientationQuat(dir, angle);
489 Quat4d q2 = new Quat4d();
490 q2.set(new AxisAngle4d(MathTools.Y_AXIS, Math.PI));
495 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
497 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
499 return getControlPointOrientationQuat(dir, turnAxis, angle);
505 public static Quat4d getControlPointOrientationQuat(Vector3d dir, double angle) {
506 if (dir.lengthSquared() < MathTools.NEAR_ZERO)
507 return MathTools.getIdentityQuat();
510 Vector3d up = new Vector3d(0.0, 1.0, 0.0);
511 double a = up.angle(dir);
512 if (a < 0.1 || (Math.PI - a) < 0.1) {
513 up.set(1.0, 0.0, 0.0);
517 return getControlPointOrientationQuat(dir, up, angle);
520 public static Quat4d getControlPointOrientationQuat(Vector3d dir, Vector3d up, double angle) {
521 if (dir.lengthSquared() < MathTools.NEAR_ZERO)
522 return MathTools.getIdentityQuat();
524 final Vector3d front = new Vector3d(1.0,0.0,0.0);
526 Quat4d q1 = new Quat4d();
529 Vector3d right = new Vector3d();
531 right.cross(dir, up);
532 up.cross(right, dir);
536 Matrix3d m = new Matrix3d();
547 //q1.set(m); MathTools contains more stable conversion
548 MathTools.getQuat(m, q1);
550 // if (DEBUG) System.out.println("PipingTools.getPipeComponentOrientationQuat() " + dir+ " " + up + " " + right);
552 Quat4d q2 = new Quat4d();
553 q2.set(new AxisAngle4d(front, angle));
558 public void insert(PipeControlPoint previous, PipeControlPoint next) {
559 // inserting an offsetpoint is error,
561 throw new RuntimeException("Dual sub points cannot be inserted.");
562 // size change control point cannot be inserted this way, because it ends PipeRun
564 throw new RuntimeException("Size change points cannot be inserted.");
565 PipeRun piperun = previous.getPipeRun();
566 // and just to make sure that control point structure is not corrupted
567 if (getPipeRun() != null) {
568 if (piperun != getPipeRun() || piperun != next.getPipeRun())
569 throw new RuntimeException("All controls points must be located on the same pipe run");
571 piperun.addChild(this);
574 // insert new BranchControlPoint between straight's control points
575 PipeControlPoint previousNext = previous.getNext();
576 PipeControlPoint previousPrevious = previous.getPrevious();
578 PipeControlPoint offsetCP = null;
580 offsetCP = getSubPoint().get(0);
582 if (previousNext != null && previousNext == next) {
583 if (previous.isDualInline()) {
584 throw new RuntimeException();
586 if (next.isDualSub()) {
587 throw new RuntimeException();
589 previous.setNext(this);
590 this.setPrevious(previous);
591 if (previous.isDualSub()) {
592 previous.getParentPoint().setNext(this);
596 if (offsetCP == null) {
597 next.setPrevious(this);
599 next.setPrevious(offsetCP);
600 offsetCP.setNext(next);
601 offsetCP.setPrevious(previous);
604 if (next.isDualInline()) {
605 next.getSubPoint().get(0).setPrevious(this);
607 } else if (previousPrevious != null && previousPrevious == next) {
608 // control point were given in reverse order
609 if (next.isDualInline())
610 throw new RuntimeException();
611 if (previous.isDualSub())
612 throw new RuntimeException();
614 this.setNext(previous);
615 if (offsetCP == null) {
616 previous.setNext(this);
618 previous.setPrevious(offsetCP);
619 offsetCP.setNext(previous);
620 offsetCP.setPrevious(next);
622 if (previous.isDualInline()) {
623 previous.getSubPoint().get(0).setPrevious(this);
625 this.setPrevious(next);
627 if (next.isDualSub()) {
628 next.getParentPoint().setNext(this);
632 throw new RuntimeException();
635 PipingRules.validate(piperun);
640 public void insert(PipeControlPoint pcp, Direction direction) {
642 throw new RuntimeException();
643 if (direction == Direction.NEXT) {
644 // if direction is next, user must have given OffsetPoint
645 if (pcp.isDualInline())
646 throw new RuntimeException();
647 // basic next/prev links
649 this.setPrevious(pcp);
650 // and last take care of sizechange / offset points
651 if (pcp.isDualSub()) {
652 pcp.getParentPoint().setNext(this);
654 if (isDualInline()) {
655 getSubPoint().get(0).setPrevious(this);
658 // if direction is previous, user must have given sizechange
660 throw new RuntimeException();
661 // previous direction is more complicated, since if newCP is SizeChangeControlPoint,
662 // we must link pcp to newCP's OffsetPoint
663 PipeControlPoint nocp = null;
664 if (isDualInline()) {
665 nocp = getSubPoint().get(0);
669 pcp.setPrevious(this);
671 pcp.setPrevious(nocp);
674 if (pcp.isDualInline()) {
675 PipeControlPoint ocp = pcp.getSubPoint().get(0);
677 ocp.setPrevious(this);
679 ocp.setPrevious(nocp);
683 PipingRules.validate(getPipeRun());
686 public Vector3d getDirectedControlPointDirection() {
687 assert (isDirected());
688 Vector3d dir = new Vector3d();
689 MathTools.rotate(getWorldOrientation(), new Vector3d(1.0, 0.0, 0.0), dir);
694 public Vector3d getDirection(Direction direction) {
696 return getDirectedControlPointDirection();
697 if (isTurn() && asFixedAngle()) {
698 if (direction == Direction.NEXT) {
699 if (previous != null) {
700 PipeControlPoint pcp = this;
701 Vector3d dir = new Vector3d();
702 dir.sub(pcp.getWorldPosition(),previous.getWorldPosition());
703 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
707 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
708 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
709 Quat4d q2 = MathTools.getQuat(aa);
710 Vector3d v = new Vector3d(1.,0.,0.);
711 Vector3d offset = new Vector3d();
712 MathTools.rotate(q2, v, offset);
713 MathTools.rotate(q, offset, dir);
718 PipeControlPoint pcp = this;
719 Vector3d dir = new Vector3d();
720 dir.sub(next.getWorldPosition(),pcp.getWorldPosition());
721 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
725 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
726 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
727 Quat4d q2 = MathTools.getQuat(aa);
728 Vector3d v = new Vector3d(1.,0.,0.);
729 Vector3d offset = new Vector3d();
730 MathTools.rotate(q2, v, offset);
731 MathTools.rotate(q, offset, dir);
739 public Vector3d getPathLegDirection(Direction direction) {
740 if (direction == Direction.NEXT) {
742 PipeControlPoint pcp = this;
743 if (pcp.isDualInline()) {
744 pcp = pcp.getSubPoint().get(0);
746 Vector3d v = new Vector3d();
747 v.sub(next.getWorldPosition(),pcp.getWorldPosition());
750 if (previous == null) {
752 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
753 return getDirectedControlPointDirection();
756 if (isVariableAngle() && !asFixedAngle())
757 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
759 PipeControlPoint pcp = this;
760 if (pcp.isDualSub()) {
761 pcp = pcp.getParentPoint();
763 Vector3d v = new Vector3d();
764 v.sub(pcp.getWorldPosition(),previous.getWorldPosition());
766 } else if (isDirected()) {
767 return getDirectedControlPointDirection();
768 } else if (isEnd()) {
769 Vector3d v = new Vector3d();
770 v.sub(getWorldPosition(),previous.getWorldPosition());
772 } else if (isTurn() && asFixedAngle() && !_getReversed()) {
773 return getDirection(Direction.NEXT);
775 throw new RuntimeException("Missing implementation " + this);
779 if (previous != null) {
780 PipeControlPoint pcp = this;
782 pcp = getParentPoint();
783 Vector3d v = new Vector3d();
784 v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
789 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
790 Vector3d v = getDirectedControlPointDirection();
794 if (isVariableAngle() && !asFixedAngle())
795 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
797 PipeControlPoint pcp = this;
798 if (pcp.isDualInline()) {
799 pcp = pcp.getSubPoint().get(0);
801 Vector3d v = new Vector3d();
802 v.sub(pcp.getWorldPosition(),next.getWorldPosition());
804 } else if (isDirected()) {
805 Vector3d v = getDirectedControlPointDirection();
808 } else if (isEnd()) {
809 Vector3d v = new Vector3d();
810 v.sub(getWorldPosition(),next.getWorldPosition());
812 } else if (isTurn() && asFixedAngle() && _getReversed()) {
813 return getDirection(Direction.PREVIOUS);
815 throw new RuntimeException("Missing implementation " + this);
821 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2) {
824 PipeControlPoint sub = isAxial() ? this : getSubPoint().get(0);
825 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
826 Vector3d dir = sub.getPathLegDirection(Direction.NEXT);
829 dir.scale(length * 0.5);
836 public void getControlPointEnds(Tuple3d p1, Tuple3d p2) {
837 PipeControlPoint sub = isAxial() || isDirected() || isTurn() ? this : getSubPoint().get(0);
838 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
840 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
842 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
845 dir1.scale(length * 0.5);
846 dir2.scale(length * 0.5);
857 public void getEndDirections(Tuple3d v1, Tuple3d v2) {
858 PipeControlPoint sub = isAxial() ? this : getSubPoint().get(0);
860 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
862 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
868 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2, Vector3d dir) {
871 Vector3d pos = getWorldPosition();
872 dir.set(getPathLegDirection(Direction.NEXT));
874 dir.scale(length * 0.5);
881 public void getInlineControlPointEnds(Tuple3d center, Tuple3d p1, Tuple3d p2, Vector3d dir) {
884 Vector3d pos = getWorldPosition();
886 dir.set(getPathLegDirection(Direction.NEXT));
888 dir.scale(length * 0.5);
895 public double getInlineLength() {
896 if (type == PointType.TURN)
898 else if (type == PointType.INLINE)
903 public Vector3d getRealPosition(PositionType type) {
904 Vector3d pos = getWorldPosition();
907 Vector3d dir = getPathLegDirection(Direction.NEXT);
908 double length = getInlineLength();
915 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
916 double length = getInlineLength();
923 // IEntity portDir = pcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasDirection);
924 // TODO : how we calculated needed space for a port; does it has an offset from control point's position or not?
934 public void getInlineMovement(Tuple3d start, Tuple3d end) {
935 // FIXME : check type of neighbor components and allow movement on top of variable length components,
936 // find proper range for movement (pcp's position is not)
937 PipeControlPoint p = previous.getPrevious();
938 PipeControlPoint n = next.getNext();
939 start.set(p.getWorldPosition());
940 end.set(n.getWorldPosition());
943 public PipeControlPoint findNextEnd() {
944 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
945 return findNextEnd( t);
948 public PipeControlPoint findPreviousEnd() {
949 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
950 return findPreviousEnd(t);
953 public PipeControlPoint findNextEnd(List<PipeControlPoint> nextList) {
955 PipeControlPoint pcp = null;
956 PipeControlPoint p = null;
957 if (nextList.size() == 0)
961 p = nextList.get(nextList.size() - 1);
966 if (nextList.size() > 0)
967 nextList.remove(nextList.size() - 1);
968 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
972 if (pcp.isPathLegEnd()) {
973 //if (DEBUG) System.out.println(" " + pcp.getResource());
977 // if (DEBUG) System.out.print(" " + pcp.getResource());
982 public PipeControlPoint findPreviousEnd(List<PipeControlPoint> prevList) {
984 PipeControlPoint pcp = null;
985 PipeControlPoint p = null;
986 if (prevList.size() == 0)
990 p = prevList.get(prevList.size() - 1);
992 pcp = p.getPrevious();
995 if (prevList.size() > 0)
996 prevList.remove(prevList.size() - 1);
997 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
1000 if (pcp.isPathLegEnd()) {
1001 // if (DEBUG) System.out.println(" " + pcp.getResource());
1005 // if (DEBUG)System.out.print(" " + pcp.getResource());
1010 public void _remove() {
1014 public void _remove(boolean renconnect) {
1015 if (component == null && next == null && previous == null)
1017 if (DEBUG) System.out.println(this + " Remove " + renconnect);
1018 if (isDualInline() || isDualSub()) {
1022 if (getParentPoint() != null) {
1023 getParentPoint()._remove(renconnect);
1025 PipeRun pipeRun = getPipeRun();
1026 if (pipeRun == null)
1029 PipeControlPoint additionalRemove = null;
1030 if (!PipingRules.isEnabled()) {
1036 PipeControlPoint currentPrev = previous;
1037 PipeControlPoint currentNext = next;
1038 if (currentNext == null && currentPrev == null) {
1040 pipeRun.remChild(this);
1043 if (currentNext != null && currentPrev != null) {
1044 boolean link = renconnect;
1045 if (currentNext.isBranchEnd()) {
1047 // currentNext.setPrevious(null);
1048 // currentNext.setNext(null);
1049 currentNext.remove();
1053 if (currentPrev.isBranchEnd()) {
1055 // currentPrev.setPrevious(null);
1056 // currentPrev.setNext(null);
1057 currentPrev.remove();
1061 if (link && currentPrev.isDirected() && currentNext.isDirected()) {
1064 if (currentNext == null) {
1066 } else if (currentNext.isDualInline()) {
1067 PipeControlPoint sccp = currentNext;
1068 PipeControlPoint ocp = sccp.getSubPoint().get(0);
1070 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1073 sccp.setPrevious(currentPrev);
1074 ocp.setPrevious(currentPrev);
1076 sccp.setPrevious(null);
1077 ocp.setPrevious(null);
1080 } else if (currentNext.isDualSub()) {
1081 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1082 } else if (currentNext.previous == this) {
1084 currentNext.setPrevious(currentPrev);
1086 currentNext.setPrevious(null);
1090 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1092 if (currentPrev == null) {
1094 } else if (currentPrev.isDualInline()) {
1095 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1096 } else if (currentPrev.isDualSub()) {
1097 PipeControlPoint ocp = currentPrev;
1098 PipeControlPoint sccp = ocp.getParentPoint();
1100 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1102 ocp.setNext(currentNext);
1103 sccp.setNext(currentNext);
1109 } else if (currentPrev.next == this) {
1111 currentPrev.setNext(currentNext);
1113 currentPrev.setNext(null);
1117 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged");
1120 if (currentNext.isVariableLength() && currentPrev.isVariableLength()) {
1121 // we have to join them into single variable length component.
1122 additionalRemove = currentPrev;
1123 // combine lengths and set the location of remaining control point to the center.
1124 Point3d ps = new Point3d();
1125 Point3d pe = new Point3d();
1126 Point3d ns = new Point3d();
1127 Point3d ne = new Point3d();
1128 currentPrev.getInlineControlPointEnds(ps, pe);
1129 currentNext.getInlineControlPointEnds(ns, ne);
1130 double l = currentPrev.getLength() + currentNext.getLength();
1131 Vector3d cp = new Vector3d();
1134 currentNext.setLength(l);
1135 currentNext.setWorldPosition(cp);
1138 // FIXME : pipe run must be split into two parts, since the control point structure is no more continuous.
1140 } else if (next != null) {
1141 if (next.isDualInline()) {
1142 PipeControlPoint sccp = next;
1143 PipeControlPoint ocp = sccp.getSubPoint().get(0);
1145 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1147 sccp.setPrevious(null);
1148 ocp.setPrevious(null);
1149 } else if (next.isDualSub()) {
1150 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1151 } else if (next.previous == this) {
1152 next.setPrevious(null);
1154 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1157 } else { //(previous != null)
1158 if(previous.isDualInline()) {
1159 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1160 } else if (previous.isDualSub()) {
1161 PipeControlPoint ocp = previous;
1162 PipeControlPoint sccp = ocp.getParentPoint();
1164 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1168 } else if (previous.next == this) {
1169 previous.setNext(null);
1171 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1175 if (children.size() > 0 ) {
1177 } else if (parent!= null) {
1178 removeParentPoint();
1184 pipeRun.remChild(this);
1185 checkRemove(pipeRun);
1186 if (PipingRules.isEnabled() && pipeRun.getParent() != null && pipeRun.getControlPoints().size() > 0)
1187 PipingRules.validate(pipeRun);
1188 if (additionalRemove != null)
1189 additionalRemove.remove();
1193 * Removes control point and attempts to reconnect next/prev
1195 * If this point is size change (PipeRuns are different on both sides), then reconnection cannot be made.
1197 public void remove() {
1198 PipeControlPoint currentPrev = previous;
1199 PipeControlPoint currentNext = next;
1202 if (currentNext != null)
1203 if (!currentNext.checkRemove())
1204 PipingRules.requestUpdate(currentNext);
1205 if (currentPrev != null)
1206 if (!currentPrev.checkRemove())
1207 PipingRules.requestUpdate(currentPrev);
1208 } catch (Exception e) {
1209 e.printStackTrace();
1215 * Removes control point without attempting to reconnect next/prev.
1216 * This usually leads to creation of another PipeRun for the control points after this point.
1218 public void removeAndSplit() {
1219 PipeControlPoint currentPrev = previous;
1220 PipeControlPoint currentNext = next;
1222 if (next != null && previous != null) {
1223 P3DRootNode root = (P3DRootNode)getPipelineComponent().getRootNode();
1224 PipeRun nextPipeRun = new PipeRun();
1225 nextPipeRun.setName(root.getUniqueName("PipeRun"));
1226 root.addChild(nextPipeRun);
1228 PipeRun previousRun = previous.getPipeRun();
1229 nextPipeRun.setPipeDiameter(previousRun.getPipeDiameter());
1230 nextPipeRun.setTurnRadiusArray(previousRun.getTurnRadiusArray());
1232 PipelineComponent n = next.getPipelineComponent();
1234 if (! (n instanceof Nozzle)) {
1236 nextPipeRun.addChild(n);
1238 n.setPipeRun(nextPipeRun);
1244 if (currentNext != null)
1245 if (!currentNext.checkRemove())
1246 PipingRules.requestUpdate(currentNext);
1247 if (currentPrev != null)
1248 if (!currentPrev.checkRemove())
1249 PipingRules.requestUpdate(currentPrev);
1250 } catch (Exception e) {
1251 e.printStackTrace();
1256 * This is called when adjacent control point is removed.
1258 * This call should remove the give point, if the point cannot exist alone.
1259 * At the moment there is one such case: branch.
1263 protected boolean checkRemove() {
1264 if (getParentPoint() != null) {
1265 return getParentPoint().checkRemove();
1267 if (getPipelineComponent() == null)
1268 return true; // already removed
1269 if (getPipelineComponent().getType().equals("Plant3D.URIs.Builtin_BranchSplitComponent")) {
1270 if (getSubPoint().get(0).getNext() == null && getSubPoint().get(0).getPrevious() == null) {
1279 private void checkRemove(PipeRun pipeRun) {
1280 Collection<PipeControlPoint> points = pipeRun.getControlPoints();
1281 if (points.size() == 0) {
1283 } else if (points.size() == 1) {
1284 PipeControlPoint pcp = points.iterator().next();
1285 if (pcp.isDeletable())
1290 private void removeDualPoint() {
1291 if (previous != null)
1292 previous.setNext(null);
1294 next.setPrevious(null);
1295 PipeControlPoint ocp;
1296 PipeControlPoint sccp;
1297 if (isDualInline()) {
1299 ocp = getSubPoint().get(0);
1302 sccp = getParentPoint();
1304 PipeRun p1 = ocp.getPipeRun();
1305 PipeRun p2 = sccp.getPipeRun();
1307 ocp.removeComponent();
1308 sccp.removeComponent();
1315 // TODO : now we assume that this is size change, and we do
1316 if (ocp.next != null)
1317 ocp.next.setPrevious(null);
1318 if (ocp.previous != null)
1319 ocp.previous.setNext(null);
1320 if (sccp.next != null)
1321 sccp.next.setPrevious(null);
1322 if (sccp.previous != null)
1323 sccp.previous.setNext(null);
1325 ocp.setPrevious(null);
1327 sccp.setPrevious(null);
1335 private void removeSubPoints() {
1336 for (PipeControlPoint p : children) {
1337 // TODO : this may affect delete routine, since classification of the point changes.
1344 private void removeParentPoint() {
1345 throw new RuntimeException("Child points cannot be removed directly");
1348 public boolean isRemoved() {
1349 return component == null;
1352 private void removeComponent() {
1353 if (component == null)
1355 PipelineComponent next = component.getNext();
1356 PipelineComponent prev = component.getPrevious();
1357 PipelineComponent br0 = component.getBranch0();
1358 component.setNext(null);
1359 component.setPrevious(null);
1360 component.setBranch0(null);
1362 if (next.getNext() == component)
1364 else if (next.getPrevious() == component)
1365 next.setPrevious(null);
1366 else if (next.getBranch0() == component)
1367 next.setBranch0(null);
1370 if (prev.getNext() == component)
1372 else if (prev.getPrevious() == component)
1373 prev.setPrevious(null);
1374 else if (prev.getBranch0() == component)
1375 prev.setBranch0(null);
1378 if (br0.getNext() == component)
1380 else if (br0.getPrevious() == component)
1381 br0.setPrevious(null);
1382 else if (br0.getBranch0() == component)
1383 br0.setBranch0(null);
1385 PipelineComponent comp = component;
1392 public void setOrientation(Quat4d orientation) {
1393 if (MathTools.equals(orientation, getOrientation()))
1395 super.setOrientation(orientation);
1396 if (getParentPoint() == null && component != null)
1397 component._setWorldOrientation(getWorldOrientation());
1402 public void setPosition(Vector3d position) {
1403 if (MathTools.equals(position, getPosition()))
1405 if (Double.isNaN(position.x) || Double.isNaN(position.y) || Double.isNaN(position.z))
1406 throw new IllegalArgumentException("NaN is not supported");
1407 super.setPosition(position);
1408 if (getParentPoint() == null && component != null)
1409 component._setWorldPosition(getWorldPosition());
1413 private void updateSubPoint() {
1415 if (next == null && previous == null) {
1416 for (PipeControlPoint sub : getSubPoint()) {
1417 sub.setWorldPosition(getWorldPosition());
1418 sub.setWorldOrientation(getWorldOrientation());
1422 for (PipeControlPoint sub : getSubPoint()) {
1423 Vector3d wp = getWorldPosition();
1424 wp.add(getSizeChangeOffsetVector());
1425 sub.setWorldPosition(wp);
1426 sub.setWorldOrientation(getWorldOrientation());
1429 for (PipeControlPoint sub : getSubPoint()) {
1430 sub.setWorldPosition(getWorldPosition());
1431 sub.setWorldOrientation(getWorldOrientation());
1437 public void _setWorldPosition(Vector3d position) {
1438 Vector3d localPos = getLocalPosition(position);
1439 super.setPosition(localPos);
1443 public void _setWorldOrientation(Quat4d orientation) {
1444 Quat4d localOr = getLocalOrientation(orientation);
1445 super.setOrientation(localOr);
1450 public String toString() {
1451 return getClass().getName() + "@" + Integer.toHexString(hashCode());