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 isMod = false; // Can user modify fixed value manually
40 private boolean isRotate = false; // rotates around path leg axis.
41 private boolean isReverse = false; // definition direction can be swapped
42 private boolean isDeletable = true; // can be removed by rules
43 private boolean isSizeChange = false; // changes size of the pipe. The next control point / component is on different PipeRun
44 private boolean isSub = false; // child point for offset / size change
46 public PipeControlPoint(PipelineComponent component) {
47 this.component = component;
48 if (component.getPipeRun() != null)
49 component.getPipeRun().addChild(this);
53 public PipeControlPoint(PipelineComponent component, PipeRun piperun) {
54 this.component = component;
55 piperun.addChild(this);
59 public void update(vtkRenderer ren) {
61 PipingRules.requestUpdate(this);
62 } catch (Exception e) {
68 public PipeRun getPipeRun() {
69 return (PipeRun)getParent();
72 public PipelineComponent getPipelineComponent() {
76 public PointType getType() {
80 public void setType(PointType type) {
84 @GetPropertyValue(name="Fixed",tabId="Debug",value="fixed")
85 public boolean isFixed() {
89 public void setFixed(boolean fixed) {
93 @GetPropertyValue(name="Mod",tabId="Debug",value="mod")
94 public boolean isMod() {
98 public void setMod(boolean isMod) {
102 @GetPropertyValue(name="Rotate",tabId="Debug",value="rotate")
103 public boolean isRotate() {
107 public void setRotate(boolean rotate) {
108 this.isRotate = rotate;
111 @GetPropertyValue(name="Reverse",tabId="Debug",value="reverse")
112 public boolean isReverse() {
116 public void setReverse(boolean reverse) {
117 this.isReverse = reverse;
120 public void setSub(boolean sub) {
124 @GetPropertyValue(name="Deletable",tabId="Debug",value="deletable")
125 public boolean isDeletable() {
129 public void setDeletable(boolean deletable) {
130 this.isDeletable = deletable;
133 public boolean isPathLegEnd() {
134 return type != PointType.INLINE;
137 public boolean isEnd() {
138 return type == PointType.END;
141 public boolean isTurn() {
142 return type == PointType.TURN;
145 public boolean isInline() {
146 return type == PointType.INLINE;
150 * True for end components, if control point defines absolute position direction, which rules cannot modify.
151 * This is typical for nozzles.
154 public boolean isDirected() {
155 return isFixed && isEnd();
159 * True for end components, if control is opposite to directed, and rules can modify position and orientation.
160 * This is typical for caps, and other end components.
163 public boolean isNonDirected() {
164 return !isFixed && isEnd();
167 public boolean isVariableLength() {
168 return !isFixed && isInline();
172 * Fixed length in-line component is such that piping rules cannot modify the length.
175 public boolean isFixedLength() {
176 return isFixed && isInline();
179 public boolean isVariableAngle() {
180 return !isFixed && isTurn();
184 * Fixed angle turn component is such that piping rules cannot modify the angle.
187 public boolean isFixedAngle() {
188 return isFixed && isTurn();
192 * Does the turn behave like fixed angle?
193 * For variable angle turns, the turn angle is defined by connected components, and without them, we must handle the component as fixed angle.
196 public boolean asFixedAngle() {
197 return isTurn() && (isFixed || next == null || previous == null);
200 public boolean isBranchEnd() {
201 return isDeletable && isEnd();
204 public boolean isOffset() {
205 return offset != null;
208 public boolean isDualSub() {
209 return parent != null && isSub;
212 public boolean isDualInline() {
213 return children.size() == 1 && children.get(0).isDualSub();
216 public boolean isAxial() {
217 return isInline() && !isDualInline();
220 public boolean isSizeChange() {
222 // if (children.size() == 0)
224 // if (!isDualInline())
226 // return getPipeRun() != children.get(0).getPipeRun();
229 public void setSizeChange(boolean isSizeChange) {
230 this.isSizeChange = isSizeChange;
234 private PipeControlPoint next;
235 private PipeControlPoint previous;
237 public PipeControlPoint getNext() {
241 public PipeControlPoint getPrevious() {
245 public void setNext(PipeControlPoint next) {
247 getParentPoint().setNext(next);
250 if (next != null && next.isDualSub())
252 if (_setNext(next)) {
253 for (PipeControlPoint pcp : children) {
261 public void setPrevious(PipeControlPoint prev) {
263 getParentPoint().setPrevious(prev);
266 if (prev != null && prev.isDualInline())
267 prev = prev.children.get(0);
268 if (_setPrevious(prev)) {
269 for (PipeControlPoint pcp : children) {
271 pcp._setPrevious(prev);
277 protected boolean _setNext(PipeControlPoint next) {
278 if (isEnd() && previous != null && next != null)
279 throw new RuntimeException("End control points are allowed to have only one connection");
281 throw new RuntimeException("Cannot connect to self");
282 if (this.next == next)
284 if (DEBUG) System.out.println(this + " next " + next);
285 if (next == null && isVariableAngle() && previous != null && !isRemoved()) {
286 convertVariableAngleToFixed(Direction.NEXT);
289 if (component != null) {
290 if (parent == null || isSub)
291 component.setNext(next != null ? next.component : null);
293 component.setBranch0(next != null ? next.component : null);
299 protected boolean _setPrevious(PipeControlPoint previous) {
300 if (isEnd() && next != null && previous != null)
301 throw new RuntimeException("End control points are allowed to have only one connection");
302 if (previous == this)
303 throw new RuntimeException("Cannot connect to self");
304 if (this.previous == previous)
306 if (DEBUG) System.out.println(this + " previous " + previous);
307 if (previous == null && isVariableAngle() && next != null && !isRemoved()) {
308 convertVariableAngleToFixed(Direction.PREVIOUS);
310 this.previous = previous;
311 if (component != null) {
312 if (parent == null || isSub)
313 component.setPrevious(previous != null ? previous.component : null);
315 component.setBranch0(previous != null ? previous.component : null);
321 private void convertVariableAngleToFixed(Direction direction) {
322 // We are removing reference, which transforms variable angle to fixed angle.
323 // Since fixed angle is defined differently, we need to calculate fixed angle parameters based on current data
324 // We need to calculate turnAngle and rotationAngle
325 Vector3d dirOut = getPathLegDirection(direction == Direction.NEXT ? Direction.NEXT : Direction.PREVIOUS);
326 Vector3d dir = getPathLegDirection(direction == Direction.NEXT ? Direction.PREVIOUS : Direction.NEXT);
330 double angle = dir.angle(dirOut);
331 //super._setNext(null);
332 if (direction == Direction.NEXT)
336 setRotationAngle(0.0);
337 setReversed(direction == Direction.NEXT ? false : true);
338 Vector3d dirOutN = getPathLegDirection(direction == Direction.NEXT ? Direction.NEXT : Direction.PREVIOUS);
340 AxisAngle4d aa = new AxisAngle4d();
341 if (MathTools.createRotation(dirOutN, dirOut, dir, aa)) {
342 setRotationAngle(aa.angle);
347 public PipeControlPoint parent;
348 public List<PipeControlPoint> children = new ArrayList<PipeControlPoint>();
350 public List<PipeControlPoint> getChildPoints() {
354 public PipeControlPoint getParentPoint() {
359 private double length;
360 private Double turnAngle;
361 private Vector3d turnAxis;
363 private Double offset;
364 private Double rotationAngle;
365 private Boolean reversed;
367 @GetPropertyValue(name="Length",tabId="Debug",value="length")
368 public double getLength() {
372 public void setLength(double l) {
373 if (Double.isInfinite(l) || Double.isNaN(l)) {
376 if (Math.abs(this.length-l) < MathTools.NEAR_ZERO)
379 firePropertyChanged("length");
381 getDualSub().setLength(l);
384 @GetPropertyValue(name="Turn Angle",tabId="Debug",value="turnAngle")
385 public Double getTurnAngle() {
389 @GetPropertyValue(name="Turn Axis",tabId="Debug",value="turnAxis")
390 public Vector3d getTurnAxis() {
394 @GetPropertyValue(name="Offset",tabId="Debug",value="offset")
395 public Double getOffset() {
399 @GetPropertyValue(name="Rotation Angle",tabId="Debug",value="rotationAngle")
400 public Double getRotationAngle() {
402 return rotationAngle;
406 @GetPropertyValue(name="Reversed",tabId="Debug",value="reversed")
407 public Boolean getReversed() {
411 public boolean _getReversed() {
412 if (reversed == null)
417 public void setTurnAngle(Double turnAngle) {
418 if (turnAngle == null || Double.isInfinite(turnAngle) || Double.isNaN(turnAngle)) {
421 if (this.turnAngle != null && Math.abs(this.turnAngle-turnAngle) < MathTools.NEAR_ZERO)
423 this.turnAngle = turnAngle;
424 firePropertyChanged("turnAngle");
427 public void setTurnAxis(Vector3d turnAxis) {
428 if (this.turnAxis != null && MathTools.equals(turnAxis, this.turnAxis))
430 this.turnAxis = turnAxis;
431 firePropertyChanged("turnAxis");
434 public void setOffset(Double offset) {
435 if (Double.isInfinite(offset) || Double.isNaN(offset)) {
438 if (this.offset != null && Math.abs(this.offset-offset) < MathTools.NEAR_ZERO)
440 this.offset = offset;
441 firePropertyChanged("offset");
444 public void setRotationAngle(Double rotationAngle) {
445 if (Double.isInfinite(rotationAngle) || Double.isNaN(rotationAngle)) {
448 if (this.rotationAngle != null && Math.abs(this.rotationAngle-rotationAngle) < MathTools.NEAR_ZERO)
450 this.rotationAngle = rotationAngle;
451 firePropertyChanged("rotationAngle");
454 public void setReversed(Boolean reversed) {
455 this.reversed = reversed;
456 firePropertyChanged("reversed");
459 public Vector3d getSizeChangeOffsetVector(Vector3d dir) {
461 if (rotationAngle == null)
462 q = getControlPointOrientationQuat(dir, 0.0);
464 q = getControlPointOrientationQuat(dir, rotationAngle);
465 Vector3d v = new Vector3d(0.0,offset,0.0);
466 Vector3d offset = new Vector3d();
467 MathTools.rotate(q, v, offset);
471 public Vector3d getSizeChangeOffsetVector() {
473 if (rotationAngle == null)
474 q = getControlPointOrientationQuat(0.0);
476 q = getControlPointOrientationQuat(rotationAngle);
477 Vector3d v = new Vector3d(0.0,offset,0.0);
478 Vector3d offset = new Vector3d();
479 MathTools.rotate(q, v, offset);
483 @GetPropertyValue(name="Next",tabId="Debug",value="next")
484 private String getNextString() {
487 return next.toString();
490 @GetPropertyValue(name="Previous",tabId="Debug",value="previous")
491 private String getPrevString() {
492 if (previous == null)
494 return previous.toString();
497 @GetPropertyValue(name="Sub",tabId="Debug",value="sub")
498 private String getSubString() {
499 if (children.size() == 0)
501 return Arrays.toString(children.toArray());
504 @GetPropertyValue(name="Type",tabId="Debug",value="type")
505 public String getTypeString() {
509 public Quat4d getControlPointOrientationQuat(double angle) {
511 if (turnAxis == null) {
512 Vector3d dir = getPathLegDirection(Direction.NEXT);
513 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
515 return getControlPointOrientationQuat(dir, angle);
517 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
519 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
521 return getControlPointOrientationQuat(dir, turnAxis, angle);
525 public Quat4d getControlPointOrientationQuat(double angle, boolean reversed) {
527 if (turnAxis == null) {
528 Vector3d dir = getPathLegDirection(Direction.NEXT);
529 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
531 Quat4d q = getControlPointOrientationQuat(dir, angle);
533 Quat4d q2 = new Quat4d();
534 q2.set(new AxisAngle4d(MathTools.Y_AXIS, Math.PI));
539 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
541 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
543 return getControlPointOrientationQuat(dir, turnAxis, angle);
549 public static Quat4d getControlPointOrientationQuat(Vector3d dir, double angle) {
550 if (dir.lengthSquared() < MathTools.NEAR_ZERO)
551 return MathTools.getIdentityQuat();
554 Vector3d up = new Vector3d(0.0, 1.0, 0.0);
555 double a = up.angle(dir);
556 if (a < 0.1 || (Math.PI - a) < 0.1) {
557 up.set(1.0, 0.0, 0.0);
561 return getControlPointOrientationQuat(dir, up, angle);
564 public static Quat4d getControlPointOrientationQuat(Vector3d dir, Vector3d up, double angle) {
565 if (dir.lengthSquared() < MathTools.NEAR_ZERO)
566 return MathTools.getIdentityQuat();
568 final Vector3d front = new Vector3d(1.0,0.0,0.0);
570 Quat4d q1 = new Quat4d();
573 Vector3d right = new Vector3d();
575 right.cross(dir, up);
576 up.cross(right, dir);
580 Matrix3d m = new Matrix3d();
591 //q1.set(m); MathTools contains more stable conversion
592 MathTools.getQuat(m, q1);
594 // if (DEBUG) System.out.println("PipingTools.getPipeComponentOrientationQuat() " + dir+ " " + up + " " + right);
596 Quat4d q2 = new Quat4d();
597 q2.set(new AxisAngle4d(front, angle));
602 public void insert(PipeControlPoint previous, PipeControlPoint next) {
603 // inserting an offsetpoint is error,
605 throw new RuntimeException("Dual sub points cannot be inserted.");
606 // size change control point cannot be inserted this way, because it ends PipeRun
608 throw new RuntimeException("Size change points cannot be inserted.");
609 PipeRun piperun = previous.getPipeRun();
610 // and just to make sure that control point structure is not corrupted
611 if (getPipeRun() != null) {
612 if (piperun != getPipeRun() || piperun != next.getPipeRun())
613 throw new RuntimeException("All controls points must be located on the same pipe run");
615 piperun.addChild(this);
618 // insert new BranchControlPoint between straight's control points
619 PipeControlPoint previousNext = previous.getNext();
620 PipeControlPoint previousPrevious = previous.getPrevious();
622 PipeControlPoint offsetCP = null;
624 offsetCP = getDualSub();
626 if (previousNext != null && previousNext == next) {
627 if (previous.isDualInline()) {
628 throw new RuntimeException();
630 if (next.isDualSub()) {
631 throw new RuntimeException();
633 previous.setNext(this);
634 this.setPrevious(previous);
635 if (previous.isDualSub()) {
636 previous.getParentPoint().setNext(this);
640 if (offsetCP == null) {
641 next.setPrevious(this);
643 next.setPrevious(offsetCP);
644 offsetCP.setNext(next);
645 offsetCP.setPrevious(previous);
648 if (next.isDualInline()) {
649 next.getDualSub().setPrevious(this);
651 } else if (previousPrevious != null && previousPrevious == next) {
652 // control point were given in reverse order
653 if (next.isDualInline())
654 throw new RuntimeException();
655 if (previous.isDualSub())
656 throw new RuntimeException();
658 this.setNext(previous);
659 if (offsetCP == null) {
660 previous.setNext(this);
662 previous.setPrevious(offsetCP);
663 offsetCP.setNext(previous);
664 offsetCP.setPrevious(next);
666 if (previous.isDualInline()) {
667 previous.getDualSub().setPrevious(this);
669 this.setPrevious(next);
671 if (next.isDualSub()) {
672 next.getParentPoint().setNext(this);
676 throw new RuntimeException();
679 PipingRules.validate(piperun);
684 public void insert(PipeControlPoint pcp, Direction direction) {
686 throw new RuntimeException();
687 if (direction == Direction.NEXT) {
688 // if direction is next, user must have given OffsetPoint
689 if (pcp.isDualInline())
690 throw new RuntimeException();
691 // basic next/prev links
693 this.setPrevious(pcp);
694 // and last take care of sizechange / offset points
695 if (pcp.isDualSub()) {
696 pcp.getParentPoint().setNext(this);
698 if (isDualInline()) {
699 getDualSub().setPrevious(this);
702 // if direction is previous, user must have given sizechange
704 throw new RuntimeException();
705 // previous direction is more complicated, since if newCP is SizeChangeControlPoint,
706 // we must link pcp to newCP's OffsetPoint
707 PipeControlPoint nocp = null;
708 if (isDualInline()) {
713 pcp.setPrevious(this);
715 pcp.setPrevious(nocp);
718 if (pcp.isDualInline()) {
719 PipeControlPoint ocp = pcp.getDualSub();
721 ocp.setPrevious(this);
723 ocp.setPrevious(nocp);
727 PipingRules.validate(getPipeRun());
730 public Vector3d getDirectedControlPointDirection() {
731 assert (isDirected());
732 Vector3d dir = new Vector3d();
733 MathTools.rotate(getWorldOrientation(), new Vector3d(1.0, 0.0, 0.0), dir);
738 public Vector3d getDirection(Direction direction) {
740 return getDirectedControlPointDirection();
741 if (isTurn() && asFixedAngle()) {
742 if (direction == Direction.NEXT) {
743 if (previous != null) {
744 PipeControlPoint pcp = this;
745 Vector3d dir = new Vector3d();
746 dir.sub(pcp.getWorldPosition(),previous.getWorldPosition());
747 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
751 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
752 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
753 Quat4d q2 = MathTools.getQuat(aa);
754 Vector3d v = new Vector3d(1.,0.,0.);
755 Vector3d offset = new Vector3d();
756 MathTools.rotate(q2, v, offset);
757 MathTools.rotate(q, offset, dir);
762 PipeControlPoint pcp = this;
763 Vector3d dir = new Vector3d();
764 dir.sub(next.getWorldPosition(),pcp.getWorldPosition());
765 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
769 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
770 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
771 Quat4d q2 = MathTools.getQuat(aa);
772 Vector3d v = new Vector3d(1.,0.,0.);
773 Vector3d offset = new Vector3d();
774 MathTools.rotate(q2, v, offset);
775 MathTools.rotate(q, offset, dir);
783 public Vector3d getPathLegDirection(Direction direction) {
784 if (direction == Direction.NEXT) {
786 PipeControlPoint pcp = this;
787 if (pcp.isDualInline()) {
788 pcp = pcp.getDualSub();
790 Vector3d v = new Vector3d();
791 v.sub(next.getWorldPosition(),pcp.getWorldPosition());
794 if (previous == null) {
796 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
797 return getDirectedControlPointDirection();
800 if (isVariableAngle() && !asFixedAngle())
801 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
803 PipeControlPoint pcp = this;
804 if (pcp.isDualSub()) {
805 pcp = pcp.getParentPoint();
807 Vector3d v = new Vector3d();
808 v.sub(pcp.getWorldPosition(),previous.getWorldPosition());
810 } else if (isDirected()) {
811 return getDirectedControlPointDirection();
812 } else if (isEnd()) {
813 Vector3d v = new Vector3d();
814 v.sub(getWorldPosition(),previous.getWorldPosition());
816 } else if (isTurn() && asFixedAngle() && !_getReversed()) {
817 return getDirection(Direction.NEXT);
819 throw new RuntimeException("Missing implementation " + this);
823 if (previous != null) {
824 PipeControlPoint pcp = this;
826 pcp = getParentPoint();
827 Vector3d v = new Vector3d();
828 v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
833 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
834 Vector3d v = getDirectedControlPointDirection();
838 if (isVariableAngle() && !asFixedAngle())
839 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
841 PipeControlPoint pcp = this;
842 if (pcp.isDualInline()) {
843 pcp = pcp.getDualSub();
845 Vector3d v = new Vector3d();
846 v.sub(pcp.getWorldPosition(),next.getWorldPosition());
848 } else if (isDirected()) {
849 Vector3d v = getDirectedControlPointDirection();
852 } else if (isEnd()) {
853 Vector3d v = new Vector3d();
854 v.sub(getWorldPosition(),next.getWorldPosition());
856 } else if (isTurn() && asFixedAngle() && _getReversed()) {
857 return getDirection(Direction.PREVIOUS);
859 throw new RuntimeException("Missing implementation " + this);
865 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2) {
868 PipeControlPoint sub = isAxial() ? this : getDualSub();
869 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
870 Vector3d dir = sub.getPathLegDirection(Direction.NEXT);
873 dir.scale(length * 0.5);
880 public void getControlPointEnds(Tuple3d p1, Tuple3d p2) {
881 PipeControlPoint sub = isAxial() || isDirected() || isTurn() ? this : getChildPoints().get(0);
882 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
884 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
886 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
889 dir1.scale(length * 0.5);
890 dir2.scale(length * 0.5);
901 public void getEndDirections(Tuple3d v1, Tuple3d v2) {
902 PipeControlPoint sub = isAxial() ? this : getDualSub();
904 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
906 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
912 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2, Vector3d dir) {
915 Vector3d pos = getWorldPosition();
916 dir.set(getPathLegDirection(Direction.NEXT));
918 dir.scale(length * 0.5);
925 public void getInlineControlPointEnds(Tuple3d center, Tuple3d p1, Tuple3d p2, Vector3d dir) {
928 Vector3d pos = getWorldPosition();
930 dir.set(getPathLegDirection(Direction.NEXT));
932 dir.scale(length * 0.5);
939 public double getInlineLength() {
940 if (type == PointType.TURN)
942 else if (type == PointType.INLINE)
947 public Vector3d getRealPosition(PositionType type) {
948 Vector3d pos = getWorldPosition();
951 Vector3d dir = getPathLegDirection(Direction.NEXT);
952 double length = getInlineLength();
959 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
960 double length = getInlineLength();
967 // IEntity portDir = pcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasDirection);
968 // TODO : how we calculated needed space for a port; does it has an offset from control point's position or not?
978 public void getInlineMovement(Tuple3d start, Tuple3d end) {
979 // FIXME : check type of neighbor components and allow movement on top of variable length components,
980 // find proper range for movement (pcp's position is not)
981 PipeControlPoint p = previous.getPrevious();
982 PipeControlPoint n = next.getNext();
983 start.set(p.getWorldPosition());
984 end.set(n.getWorldPosition());
987 public PipeControlPoint findNextEnd() {
988 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
989 return findNextEnd( t);
992 public PipeControlPoint findPreviousEnd() {
993 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
994 return findPreviousEnd(t);
997 public PipeControlPoint findNextEnd(List<PipeControlPoint> nextList) {
999 PipeControlPoint pcp = null;
1000 PipeControlPoint p = null;
1001 if (nextList.size() == 0)
1005 p = nextList.get(nextList.size() - 1);
1010 if (nextList.size() > 0)
1011 nextList.remove(nextList.size() - 1);
1012 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
1016 if (pcp.isPathLegEnd()) {
1017 //if (DEBUG) System.out.println(" " + pcp.getResource());
1021 // if (DEBUG) System.out.print(" " + pcp.getResource());
1026 public PipeControlPoint findPreviousEnd(List<PipeControlPoint> prevList) {
1028 PipeControlPoint pcp = null;
1029 PipeControlPoint p = null;
1030 if (prevList.size() == 0)
1034 p = prevList.get(prevList.size() - 1);
1036 pcp = p.getPrevious();
1039 if (prevList.size() > 0)
1040 prevList.remove(prevList.size() - 1);
1041 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
1044 if (pcp.isPathLegEnd()) {
1045 // if (DEBUG) System.out.println(" " + pcp.getResource());
1049 // if (DEBUG)System.out.print(" " + pcp.getResource());
1054 public void _remove() {
1059 public PipeControlPoint getDualSub() {
1061 return getChildPoints().get(0);
1063 throw new IllegalStateException("Current control point is not dual inline");
1067 public void _remove(boolean renconnect) {
1068 if (component == null && next == null && previous == null)
1070 if (DEBUG) System.out.println(this + " Remove " + renconnect);
1072 if (getParentPoint() != null) {
1073 getParentPoint()._remove(renconnect);
1076 PipeRun pipeRun = getPipeRun();
1077 if (pipeRun == null)
1080 PipeControlPoint additionalRemove = null;
1081 if (!PipingRules.isEnabled()) {
1087 PipeControlPoint currentPrev = previous;
1088 PipeControlPoint currentNext = next;
1089 if (currentNext == null && currentPrev == null) {
1091 pipeRun.remChild(this);
1092 checkRemove(pipeRun);
1095 if (currentNext != null && currentPrev != null) {
1096 boolean link = renconnect;
1097 if (currentNext.isBranchEnd()) {
1099 currentNext.remove();
1103 if (currentPrev.isBranchEnd()) {
1105 currentPrev.remove();
1110 if (currentPrev.isDirected() && currentNext.isDirected())
1112 else if (this.isDualInline()) {
1114 } else if (this.isDualSub()) {
1115 throw new RuntimeException("_remove() is called for parent point, somehow got to child point. " + this);
1118 if (currentNext == null) {
1120 } else if (currentNext.isDualInline()) {
1121 PipeControlPoint sccp = currentNext;
1122 PipeControlPoint ocp = currentNext.getDualSub();
1124 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1127 sccp.setPrevious(currentPrev);
1128 //ocp.setPrevious(currentPrev);
1129 assert(ocp.getPrevious() == currentPrev);
1131 sccp.setPrevious(null);
1132 //ocp.setPrevious(null);
1133 assert(ocp.getPrevious() == null);
1136 } else if (currentNext.isDualSub()) {
1137 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1138 } else if (currentNext.previous == this) {
1140 currentNext.setPrevious(currentPrev);
1142 currentNext.setPrevious(null);
1145 } else if (isDualInline()) {
1146 if (currentNext.previous != getDualSub()) {
1147 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1150 currentNext.setPrevious(currentPrev);
1152 currentNext.setPrevious(null);
1156 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1158 if (currentPrev == null) {
1160 } else if (currentPrev.isDualInline()) {
1161 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1162 } else if (currentPrev.isDualSub()) {
1163 PipeControlPoint ocp = currentPrev;
1164 PipeControlPoint sccp = currentPrev.getParentPoint();
1166 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1168 //ocp.setNext(currentNext);
1169 sccp.setNext(currentNext);
1170 assert(ocp.getNext() == currentNext);
1172 //ocp.setNext(null);
1174 assert(ocp.getNext() == null);
1177 } else if (currentPrev.next == this) {
1179 currentPrev.setNext(currentNext);
1181 currentPrev.setNext(null);
1185 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged");
1188 if (currentNext.isVariableLength() && currentPrev.isVariableLength()) {
1189 // we have to join them into single variable length component.
1190 additionalRemove = currentPrev;
1191 // combine lengths and set the location of remaining control point to the center.
1192 Point3d ps = new Point3d();
1193 Point3d pe = new Point3d();
1194 Point3d ns = new Point3d();
1195 Point3d ne = new Point3d();
1196 currentPrev.getInlineControlPointEnds(ps, pe);
1197 currentNext.getInlineControlPointEnds(ns, ne);
1198 double l = currentPrev.getLength() + currentNext.getLength();
1199 Vector3d cp = new Vector3d();
1202 currentNext.setLength(l);
1203 currentNext.setWorldPosition(cp);
1206 // FIXME : pipe run must be split into two parts, since the control point structure is no more continuous.
1208 } else if (currentNext != null) {
1209 if (currentNext.isDualInline()) {
1210 PipeControlPoint sccp = currentNext;
1211 PipeControlPoint ocp = getDualSub();
1213 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1215 sccp.setPrevious(null);
1216 assert(ocp.getPrevious() == null);
1217 //ocp.setPrevious(null);
1218 } else if (currentNext.isDualSub()) {
1219 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1220 } else if (currentNext.previous == this) {
1221 currentNext.setPrevious(null);
1222 } else if (isDualInline()) {
1223 if (currentNext.previous != getDualSub()) {
1224 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1226 currentNext.setPrevious(null);
1228 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1231 } else { //(previous != null)
1232 if(currentPrev.isDualInline()) {
1233 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1234 } else if (currentPrev.isDualSub()) {
1235 PipeControlPoint ocp = currentPrev;
1236 PipeControlPoint sccp = currentPrev.getParentPoint();
1238 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1241 assert(ocp.getNext() == null);
1242 } else if (currentPrev.next == this) {
1243 currentPrev.setNext(null);
1245 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1249 if (children.size() > 0 ) {
1251 } else if (parent!= null) {
1252 removeParentPoint();
1258 pipeRun.remChild(this);
1259 checkRemove(pipeRun);
1260 if (PipingRules.isEnabled() && pipeRun.getParent() != null && pipeRun.getControlPoints().size() > 0)
1261 PipingRules.validate(pipeRun);
1262 if (additionalRemove != null)
1263 additionalRemove.remove();
1267 * Removes control point and attempts to reconnect next/prev
1269 * If this point is size change (PipeRuns are different on both sides), then reconnection cannot be made.
1271 public void remove() {
1272 PipeControlPoint currentPrev = previous;
1273 PipeControlPoint currentNext = next;
1276 if (currentNext != null)
1277 if (!currentNext.checkRemove())
1278 PipingRules.requestUpdate(currentNext);
1279 if (currentPrev != null)
1280 if (!currentPrev.checkRemove())
1281 PipingRules.requestUpdate(currentPrev);
1282 } catch (Exception e) {
1283 e.printStackTrace();
1289 * Removes control point without attempting to reconnect next/prev.
1290 * This usually leads to creation of another PipeRun for the control points after this point.
1292 public void removeAndSplit() {
1293 PipeControlPoint currentPrev = previous;
1294 PipeControlPoint currentNext = next;
1296 if (next != null && previous != null) {
1297 P3DRootNode root = (P3DRootNode)getPipelineComponent().getRootNode();
1298 PipeRun nextPipeRun = new PipeRun();
1299 nextPipeRun.setName(root.getUniqueName("PipeRun"));
1300 root.addChild(nextPipeRun);
1302 PipeRun previousRun = previous.getPipeRun();
1303 nextPipeRun.setPipeDiameter(previousRun.getPipeDiameter());
1304 nextPipeRun.setTurnRadiusArray(previousRun.getTurnRadiusArray());
1306 PipelineComponent n = next.getPipelineComponent();
1308 if (! (n instanceof Nozzle)) {
1310 nextPipeRun.addChild(n);
1312 n.setPipeRun(nextPipeRun);
1318 if (currentNext != null)
1319 if (!currentNext.checkRemove())
1320 PipingRules.requestUpdate(currentNext);
1321 if (currentPrev != null)
1322 if (!currentPrev.checkRemove())
1323 PipingRules.requestUpdate(currentPrev);
1324 } catch (Exception e) {
1325 e.printStackTrace();
1330 * This is called when adjacent control point is removed.
1332 * This call should remove the give point, if the point cannot exist alone.
1333 * At the moment there is one such case: branch.
1337 protected boolean checkRemove() {
1338 if (getParentPoint() != null) {
1339 return getParentPoint().checkRemove();
1341 if (getPipelineComponent() == null)
1342 return true; // already removed
1343 if (getPipelineComponent().getType().equals("Plant3D.URIs.Builtin_BranchSplitComponent")) {
1344 if (getChildPoints().get(0).getNext() == null && getChildPoints().get(0).getPrevious() == null) {
1349 return checkRemove(getPipeRun());
1353 private boolean checkRemove(PipeRun pipeRun) {
1354 Collection<PipeControlPoint> points = pipeRun.getControlPoints();
1355 if (points.size() == 0) {
1358 } else if (points.size() == 1) {
1359 PipeControlPoint pcp = points.iterator().next();
1360 if (pcp.isDeletable() && pcp.getNext() == null && pcp.getPrevious() == null) {
1361 pcp._remove(); // This call will recursively call also this method...
1364 } else if (points.size() == 2) {
1370 private void removeSubPoints() {
1371 for (PipeControlPoint p : children) {
1375 PipeControlPoint currentNext = p.getNext();
1376 PipeControlPoint currentPrev = p.getPrevious();
1378 p._setPrevious(null);
1379 PipeRun run = p.getPipeRun();
1384 if (currentNext != null)
1385 if (!currentNext.checkRemove())
1386 PipingRules.requestUpdate(currentNext);
1387 if (currentPrev != null)
1388 if (!currentPrev.checkRemove())
1389 PipingRules.requestUpdate(currentPrev);
1395 private void removeParentPoint() {
1396 throw new RuntimeException("Child points cannot be removed directly");
1399 public boolean isRemoved() {
1400 return component == null;
1403 private void removeComponent() {
1404 if (component == null)
1406 PipelineComponent next = component.getNext();
1407 PipelineComponent prev = component.getPrevious();
1408 PipelineComponent br0 = component.getBranch0();
1409 component.setNext(null);
1410 component.setPrevious(null);
1411 component.setBranch0(null);
1413 if (next.getNext() == component)
1415 else if (next.getPrevious() == component)
1416 next.setPrevious(null);
1417 else if (next.getBranch0() == component)
1418 next.setBranch0(null);
1421 if (prev.getNext() == component)
1423 else if (prev.getPrevious() == component)
1424 prev.setPrevious(null);
1425 else if (prev.getBranch0() == component)
1426 prev.setBranch0(null);
1429 if (br0.getNext() == component)
1431 else if (br0.getPrevious() == component)
1432 br0.setPrevious(null);
1433 else if (br0.getBranch0() == component)
1434 br0.setBranch0(null);
1436 PipelineComponent comp = component;
1443 public void setOrientation(Quat4d orientation) {
1444 if (MathTools.equals(orientation, getOrientation()))
1446 super.setOrientation(orientation);
1447 if (getParentPoint() == null && component != null)
1448 component._setWorldOrientation(getWorldOrientation());
1453 public void setPosition(Vector3d position) {
1454 if (MathTools.equals(position, getPosition()))
1456 if (Double.isNaN(position.x) || Double.isNaN(position.y) || Double.isNaN(position.z))
1457 throw new IllegalArgumentException("NaN is not supported");
1458 super.setPosition(position);
1459 if (getParentPoint() == null && component != null)
1460 component._setWorldPosition(getWorldPosition());
1464 private void updateSubPoint() {
1466 if (next == null && previous == null) {
1467 for (PipeControlPoint sub : getChildPoints()) {
1468 sub.setWorldPosition(getWorldPosition());
1469 sub.setWorldOrientation(getWorldOrientation());
1473 for (PipeControlPoint sub : getChildPoints()) {
1474 Vector3d wp = getWorldPosition();
1475 wp.add(getSizeChangeOffsetVector());
1476 sub.setWorldPosition(wp);
1477 sub.setWorldOrientation(getWorldOrientation());
1480 for (PipeControlPoint sub : getChildPoints()) {
1481 sub.setWorldPosition(getWorldPosition());
1482 sub.setWorldOrientation(getWorldOrientation());
1488 public void _setWorldPosition(Vector3d position) {
1489 Vector3d localPos = getLocalPosition(position);
1490 super.setPosition(localPos);
1494 public void _setWorldOrientation(Quat4d orientation) {
1495 Quat4d localOr = getLocalOrientation(orientation);
1496 super.setOrientation(localOr);
1501 public String toString() {
1502 return getClass().getName() + "@" + Integer.toHexString(hashCode());