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);
244 if (component != null) {
245 if (parent == null || isSub)
246 component.setNext(next != null ? next.component : null);
248 component.setBranch0(next != null ? next.component : null);
254 public void setPrevious(PipeControlPoint previous) {
255 if (isEnd() && next != null && previous != null)
256 throw new RuntimeException("End control points are allowed to have only one connection");
257 if (previous == this)
258 throw new RuntimeException("Cannot connect to self");
259 if (this.previous == previous)
261 if (DEBUG) System.out.println(this + " previous " + previous);
262 this.previous = previous;
263 if (component != null) {
264 if (parent == null || isSub)
265 component.setPrevious(previous != null ? previous.component : null);
267 component.setBranch0(previous != null ? previous.component : null);
273 public PipeControlPoint parent;
274 public List<PipeControlPoint> children = new ArrayList<PipeControlPoint>();
276 public List<PipeControlPoint> getSubPoint() {
280 public PipeControlPoint getParentPoint() {
285 private double length;
286 private Double turnAngle;
287 private Vector3d turnAxis;
289 private Double offset;
290 private Double rotationAngle;
291 private Boolean reversed;
293 @GetPropertyValue(name="Length",tabId="Debug",value="length")
294 public double getLength() {
298 public void setLength(double l) {
299 if (Double.isInfinite(l) || Double.isNaN(l)) {
302 if (Math.abs(this.length-l) < MathTools.NEAR_ZERO)
305 firePropertyChanged("length");
307 getSubPoint().get(0).setLength(l);
310 @GetPropertyValue(name="Turn Angle",tabId="Debug",value="turnAngle")
311 public Double getTurnAngle() {
315 @GetPropertyValue(name="Turn Axis",tabId="Debug",value="turnAxis")
316 public Vector3d getTurnAxis() {
320 @GetPropertyValue(name="Offset",tabId="Debug",value="offset")
321 public Double getOffset() {
325 @GetPropertyValue(name="Rotation Angle",tabId="Debug",value="rotationAngle")
326 public Double getRotationAngle() {
327 return rotationAngle;
330 @GetPropertyValue(name="Reversed",tabId="Debug",value="reversed")
331 public Boolean getReversed() {
335 public boolean _getReversed() {
336 if (reversed == null)
341 public void setTurnAngle(Double turnAngle) {
342 if (turnAngle == null || Double.isInfinite(turnAngle) || Double.isNaN(turnAngle)) {
345 if (this.turnAngle != null && Math.abs(this.turnAngle-turnAngle) < MathTools.NEAR_ZERO)
347 this.turnAngle = turnAngle;
348 firePropertyChanged("turnAngle");
351 public void setTurnAxis(Vector3d turnAxis) {
352 if (this.turnAxis != null && MathTools.equals(turnAxis, this.turnAxis))
354 this.turnAxis = turnAxis;
355 firePropertyChanged("turnAxis");
358 public void setOffset(Double offset) {
359 if (Double.isInfinite(offset) || Double.isNaN(offset)) {
362 if (this.offset != null && Math.abs(this.offset-offset) < MathTools.NEAR_ZERO)
364 this.offset = offset;
365 firePropertyChanged("offset");
368 public void setRotationAngle(Double rotationAngle) {
369 if (Double.isInfinite(rotationAngle) || Double.isNaN(rotationAngle)) {
372 if (this.rotationAngle != null && Math.abs(this.rotationAngle-rotationAngle) < MathTools.NEAR_ZERO)
374 this.rotationAngle = rotationAngle;
375 firePropertyChanged("rotationAngle");
378 public void setReversed(Boolean reversed) {
379 this.reversed = reversed;
380 firePropertyChanged("reversed");
383 public Vector3d getSizeChangeOffsetVector(Vector3d dir) {
385 if (rotationAngle == null)
386 q = getControlPointOrientationQuat(dir, 0.0);
388 q = getControlPointOrientationQuat(dir, rotationAngle);
389 Vector3d v = new Vector3d(0.0,offset,0.0);
390 Vector3d offset = new Vector3d();
391 MathTools.rotate(q, v, offset);
395 public Vector3d getSizeChangeOffsetVector() {
397 if (rotationAngle == null)
398 q = getControlPointOrientationQuat(0.0);
400 q = getControlPointOrientationQuat(rotationAngle);
401 Vector3d v = new Vector3d(0.0,offset,0.0);
402 Vector3d offset = new Vector3d();
403 MathTools.rotate(q, v, offset);
407 @GetPropertyValue(name="Next",tabId="Debug",value="next")
408 private String getNextString() {
411 return next.toString();
414 @GetPropertyValue(name="Previous",tabId="Debug",value="previous")
415 private String getPrevString() {
416 if (previous == null)
418 return previous.toString();
421 @GetPropertyValue(name="Sub",tabId="Debug",value="sub")
422 private String getSubString() {
423 if (children.size() == 0)
425 return Arrays.toString(children.toArray());
428 @GetPropertyValue(name="Type",tabId="Debug",value="type")
429 public String getTypeString() {
433 public Quat4d getControlPointOrientationQuat(double angle) {
435 if (turnAxis == null) {
436 Vector3d dir = getPathLegDirection(Direction.NEXT);
437 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
439 return getControlPointOrientationQuat(dir, angle);
441 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
443 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
445 return getControlPointOrientationQuat(dir, turnAxis, angle);
449 public Quat4d getControlPointOrientationQuat(double angle, boolean reversed) {
451 if (turnAxis == null) {
452 Vector3d dir = getPathLegDirection(Direction.NEXT);
453 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
455 Quat4d q = getControlPointOrientationQuat(dir, angle);
457 Quat4d q2 = new Quat4d();
458 q2.set(new AxisAngle4d(MathTools.Y_AXIS, Math.PI));
463 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
465 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
467 return getControlPointOrientationQuat(dir, turnAxis, angle);
473 public static Quat4d getControlPointOrientationQuat(Vector3d dir, double angle) {
474 if (dir.lengthSquared() < MathTools.NEAR_ZERO)
475 return MathTools.getIdentityQuat();
478 Vector3d up = new Vector3d(0.0, 1.0, 0.0);
479 double a = up.angle(dir);
480 if (a < 0.1 || (Math.PI - a) < 0.1) {
481 up.set(1.0, 0.0, 0.0);
485 return getControlPointOrientationQuat(dir, up, angle);
488 public static Quat4d getControlPointOrientationQuat(Vector3d dir, Vector3d up, double angle) {
489 if (dir.lengthSquared() < MathTools.NEAR_ZERO)
490 return MathTools.getIdentityQuat();
492 final Vector3d front = new Vector3d(1.0,0.0,0.0);
494 Quat4d q1 = new Quat4d();
497 Vector3d right = new Vector3d();
499 right.cross(dir, up);
500 up.cross(right, dir);
504 Matrix3d m = new Matrix3d();
515 //q1.set(m); MathTools contains more stable conversion
516 MathTools.getQuat(m, q1);
518 // if (DEBUG) System.out.println("PipingTools.getPipeComponentOrientationQuat() " + dir+ " " + up + " " + right);
520 Quat4d q2 = new Quat4d();
521 q2.set(new AxisAngle4d(front, angle));
526 public void insert(PipeControlPoint previous, PipeControlPoint next) {
527 // inserting an offsetpoint is error,
529 throw new RuntimeException("Dual sub points cannot be inserted.");
530 // size change control point cannot be inserted this way, because it ends PipeRun
532 throw new RuntimeException("Size change points cannot be inserted.");
533 PipeRun piperun = previous.getPipeRun();
534 // and just to make sure that control point structure is not corrupted
535 if (getPipeRun() != null) {
536 if (piperun != getPipeRun() || piperun != next.getPipeRun())
537 throw new RuntimeException("All controls points must be located on the same pipe run");
539 piperun.addChild(this);
542 // insert new BranchControlPoint between straight's control points
543 PipeControlPoint previousNext = previous.getNext();
544 PipeControlPoint previousPrevious = previous.getPrevious();
546 PipeControlPoint offsetCP = null;
548 offsetCP = getSubPoint().get(0);
550 if (previousNext != null && previousNext == next) {
551 if (previous.isDualInline()) {
552 throw new RuntimeException();
554 if (next.isDualSub()) {
555 throw new RuntimeException();
557 previous.setNext(this);
558 this.setPrevious(previous);
559 if (previous.isDualSub()) {
560 previous.getParentPoint().setNext(this);
564 if (offsetCP == null) {
565 next.setPrevious(this);
567 next.setPrevious(offsetCP);
568 offsetCP.setNext(next);
569 offsetCP.setPrevious(previous);
572 if (next.isDualInline()) {
573 next.getSubPoint().get(0).setPrevious(this);
575 } else if (previousPrevious != null && previousPrevious == next) {
576 // control point were given in reverse order
577 if (next.isDualInline())
578 throw new RuntimeException();
579 if (previous.isDualSub())
580 throw new RuntimeException();
582 this.setNext(previous);
583 if (offsetCP == null) {
584 previous.setNext(this);
586 previous.setPrevious(offsetCP);
587 offsetCP.setNext(previous);
588 offsetCP.setPrevious(next);
590 if (previous.isDualInline()) {
591 previous.getSubPoint().get(0).setPrevious(this);
593 this.setPrevious(next);
595 if (next.isDualSub()) {
596 next.getParentPoint().setNext(this);
600 throw new RuntimeException();
603 PipingRules.validate(piperun);
608 public void insert(PipeControlPoint pcp, Direction direction) {
610 throw new RuntimeException();
611 if (direction == Direction.NEXT) {
612 // if direction is next, user must have given OffsetPoint
613 if (pcp.isDualInline())
614 throw new RuntimeException();
615 // basic next/prev links
617 this.setPrevious(pcp);
618 // and last take care of sizechange / offset points
619 if (pcp.isDualSub()) {
620 pcp.getParentPoint().setNext(this);
622 if (isDualInline()) {
623 getSubPoint().get(0).setPrevious(this);
626 // if direction is previous, user must have given sizechange
628 throw new RuntimeException();
629 // previous direction is more complicated, since if newCP is SizeChangeControlPoint,
630 // we must link pcp to newCP's OffsetPoint
631 PipeControlPoint nocp = null;
632 if (isDualInline()) {
633 nocp = getSubPoint().get(0);
637 pcp.setPrevious(this);
639 pcp.setPrevious(nocp);
642 if (pcp.isDualInline()) {
643 PipeControlPoint ocp = pcp.getSubPoint().get(0);
645 ocp.setPrevious(this);
647 ocp.setPrevious(nocp);
651 PipingRules.validate(getPipeRun());
654 public Vector3d getDirectedControlPointDirection() {
655 assert (isDirected());
656 Vector3d dir = new Vector3d();
657 MathTools.rotate(getWorldOrientation(), new Vector3d(1.0, 0.0, 0.0), dir);
662 public Vector3d getDirection(Direction direction) {
664 return getDirectedControlPointDirection();
665 if (isTurn() && asFixedAngle()) {
666 if (direction == Direction.NEXT) {
667 if (previous != null) {
668 PipeControlPoint pcp = this;
669 Vector3d dir = new Vector3d();
670 dir.sub(pcp.getWorldPosition(),previous.getWorldPosition());
671 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
675 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
676 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
677 Quat4d q2 = MathTools.getQuat(aa);
678 Vector3d v = new Vector3d(1.,0.,0.);
679 Vector3d offset = new Vector3d();
680 MathTools.rotate(q2, v, offset);
681 MathTools.rotate(q, offset, dir);
686 PipeControlPoint pcp = this;
687 Vector3d dir = new Vector3d();
688 dir.sub(next.getWorldPosition(),pcp.getWorldPosition());
689 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
693 Quat4d q = getControlPointOrientationQuat(dir, pcp.getRotationAngle() != null ? pcp.getRotationAngle() : 0.0);
694 AxisAngle4d aa = new AxisAngle4d(MathTools.Y_AXIS,pcp.getTurnAngle() == null ? 0.0 : pcp.getTurnAngle());
695 Quat4d q2 = MathTools.getQuat(aa);
696 Vector3d v = new Vector3d(1.,0.,0.);
697 Vector3d offset = new Vector3d();
698 MathTools.rotate(q2, v, offset);
699 MathTools.rotate(q, offset, dir);
707 public Vector3d getPathLegDirection(Direction direction) {
708 if (direction == Direction.NEXT) {
710 PipeControlPoint pcp = this;
711 if (pcp.isDualInline()) {
712 pcp = pcp.getSubPoint().get(0);
714 Vector3d v = new Vector3d();
715 v.sub(next.getWorldPosition(),pcp.getWorldPosition());
718 if (previous == null) {
720 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
721 return getDirectedControlPointDirection();
724 if (isVariableAngle() && !asFixedAngle())
725 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
727 PipeControlPoint pcp = this;
728 if (pcp.isDualSub()) {
729 pcp = pcp.getParentPoint();
731 Vector3d v = new Vector3d();
732 v.sub(pcp.getWorldPosition(),previous.getWorldPosition());
734 } else if (isDirected()) {
735 return getDirectedControlPointDirection();
736 } else if (isEnd()) {
737 Vector3d v = new Vector3d();
738 v.sub(getWorldPosition(),previous.getWorldPosition());
740 } else if (isTurn() && asFixedAngle() && !_getReversed()) {
741 return getDirection(Direction.NEXT);
743 throw new RuntimeException("Missing implementation " + this);
747 if (previous != null) {
748 PipeControlPoint pcp = this;
750 pcp = getParentPoint();
751 Vector3d v = new Vector3d();
752 v.sub(previous.getWorldPosition(),pcp.getWorldPosition());
757 throw new RuntimeException("Cannot calculate path leg direction for unconnected control point " + this);
758 Vector3d v = getDirectedControlPointDirection();
762 if (isVariableAngle() && !asFixedAngle())
763 throw new RuntimeException("Cannot calculate path leg direction for unconnected variable angle control point " + this);
765 PipeControlPoint pcp = this;
766 if (pcp.isDualInline()) {
767 pcp = pcp.getSubPoint().get(0);
769 Vector3d v = new Vector3d();
770 v.sub(pcp.getWorldPosition(),next.getWorldPosition());
772 } else if (isDirected()) {
773 Vector3d v = getDirectedControlPointDirection();
776 } else if (isEnd()) {
777 Vector3d v = new Vector3d();
778 v.sub(getWorldPosition(),next.getWorldPosition());
780 } else if (isTurn() && asFixedAngle() && _getReversed()) {
781 return getDirection(Direction.PREVIOUS);
783 throw new RuntimeException("Missing implementation " + this);
789 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2) {
792 PipeControlPoint sub = isAxial() ? this : getSubPoint().get(0);
793 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
794 Vector3d dir = sub.getPathLegDirection(Direction.NEXT);
797 dir.scale(length * 0.5);
804 public void getControlPointEnds(Tuple3d p1, Tuple3d p2) {
805 PipeControlPoint sub = isAxial() || isDirected() || isTurn() ? this : getSubPoint().get(0);
806 Vector3d pos = getWorldPosition(), pos2 = sub == this ? pos : sub.getWorldPosition();
808 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
810 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
813 dir1.scale(length * 0.5);
814 dir2.scale(length * 0.5);
825 public void getEndDirections(Tuple3d v1, Tuple3d v2) {
826 PipeControlPoint sub = isAxial() ? this : getSubPoint().get(0);
828 Vector3d dir1 = getPathLegDirection(Direction.PREVIOUS);
830 Vector3d dir2 = sub.getPathLegDirection(Direction.NEXT);
836 public void getInlineControlPointEnds(Tuple3d p1, Tuple3d p2, Vector3d dir) {
839 Vector3d pos = getWorldPosition();
840 dir.set(getPathLegDirection(Direction.NEXT));
842 dir.scale(length * 0.5);
849 public void getInlineControlPointEnds(Tuple3d center, Tuple3d p1, Tuple3d p2, Vector3d dir) {
852 Vector3d pos = getWorldPosition();
854 dir.set(getPathLegDirection(Direction.NEXT));
856 dir.scale(length * 0.5);
863 public double getInlineLength() {
864 if (type == PointType.TURN)
866 else if (type == PointType.INLINE)
871 public Vector3d getRealPosition(PositionType type) {
872 Vector3d pos = getWorldPosition();
875 Vector3d dir = getPathLegDirection(Direction.NEXT);
876 double length = getInlineLength();
883 Vector3d dir = getPathLegDirection(Direction.PREVIOUS);
884 double length = getInlineLength();
891 // IEntity portDir = pcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasDirection);
892 // TODO : how we calculated needed space for a port; does it has an offset from control point's position or not?
902 public void getInlineMovement(Tuple3d start, Tuple3d end) {
903 // FIXME : check type of neighbor components and allow movement on top of variable length components,
904 // find proper range for movement (pcp's position is not)
905 PipeControlPoint p = previous.getPrevious();
906 PipeControlPoint n = next.getNext();
907 start.set(p.getWorldPosition());
908 end.set(n.getWorldPosition());
911 public PipeControlPoint findNextEnd() {
912 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
913 return findNextEnd( t);
916 public PipeControlPoint findPreviousEnd() {
917 ArrayList<PipeControlPoint> t = new ArrayList<PipeControlPoint>();
918 return findPreviousEnd(t);
921 public PipeControlPoint findNextEnd(List<PipeControlPoint> nextList) {
923 PipeControlPoint pcp = null;
924 PipeControlPoint p = null;
925 if (nextList.size() == 0)
929 p = nextList.get(nextList.size() - 1);
934 if (nextList.size() > 0)
935 nextList.remove(nextList.size() - 1);
936 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
940 if (pcp.isPathLegEnd()) {
941 //if (DEBUG) System.out.println(" " + pcp.getResource());
945 // if (DEBUG) System.out.print(" " + pcp.getResource());
950 public PipeControlPoint findPreviousEnd(List<PipeControlPoint> prevList) {
952 PipeControlPoint pcp = null;
953 PipeControlPoint p = null;
954 if (prevList.size() == 0)
958 p = prevList.get(prevList.size() - 1);
960 pcp = p.getPrevious();
963 if (prevList.size() > 0)
964 prevList.remove(prevList.size() - 1);
965 // if (DEBUG) System.out.println(" " + pcp.getResource() + " not full");
968 if (pcp.isPathLegEnd()) {
969 // if (DEBUG) System.out.println(" " + pcp.getResource());
973 // if (DEBUG)System.out.print(" " + pcp.getResource());
978 public void _remove() {
982 public void _remove(boolean renconnect) {
983 if (component == null && next == null && previous == null)
985 if (DEBUG) System.out.println(this + " Remove " + renconnect);
986 if (isDualInline() || isDualSub()) {
990 if (getParentPoint() != null) {
991 getParentPoint()._remove(renconnect);
993 PipeRun pipeRun = getPipeRun();
997 PipeControlPoint additionalRemove = null;
998 if (!PipingRules.isEnabled()) {
1003 PipeControlPoint currentPrev = previous;
1004 PipeControlPoint currentNext = next;
1005 if (currentNext == null && currentPrev == null) {
1007 pipeRun.remChild(this);
1010 if (currentNext != null && currentPrev != null) {
1011 boolean link = renconnect;
1012 if (currentNext.isBranchEnd()) {
1014 // currentNext.setPrevious(null);
1015 // currentNext.setNext(null);
1016 currentNext.remove();
1020 if (currentPrev.isBranchEnd()) {
1022 // currentPrev.setPrevious(null);
1023 // currentPrev.setNext(null);
1024 currentPrev.remove();
1028 if (link && currentPrev.isDirected() && currentNext.isDirected()) {
1031 if (currentNext == null) {
1033 } else if (currentNext.isDualInline()) {
1034 PipeControlPoint sccp = currentNext;
1035 PipeControlPoint ocp = sccp.getSubPoint().get(0);
1037 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1040 sccp.setPrevious(currentPrev);
1041 ocp.setPrevious(currentPrev);
1043 sccp.setPrevious(null);
1044 ocp.setPrevious(null);
1047 } else if (currentNext.isDualSub()) {
1048 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1049 } else if (currentNext.previous == this) {
1051 currentNext.setPrevious(currentPrev);
1053 currentNext.setPrevious(null);
1057 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1059 if (currentPrev == null) {
1061 } else if (currentPrev.isDualInline()) {
1062 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1063 } else if (currentPrev.isDualSub()) {
1064 PipeControlPoint ocp = currentPrev;
1065 PipeControlPoint sccp = ocp.getParentPoint();
1067 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1069 ocp.setNext(currentNext);
1070 sccp.setNext(currentNext);
1076 } else if (currentPrev.next == this) {
1078 currentPrev.setNext(currentNext);
1080 currentPrev.setNext(null);
1084 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged");
1087 if (currentNext.isVariableLength() && currentPrev.isVariableLength()) {
1088 // we have to join them into single variable length component.
1089 additionalRemove = currentPrev;
1090 // combine lengths and set the location of remaining control point to the center.
1091 Point3d ps = new Point3d();
1092 Point3d pe = new Point3d();
1093 Point3d ns = new Point3d();
1094 Point3d ne = new Point3d();
1095 currentPrev.getInlineControlPointEnds(ps, pe);
1096 currentNext.getInlineControlPointEnds(ns, ne);
1097 double l = currentPrev.getLength() + currentNext.getLength();
1098 Vector3d cp = new Vector3d();
1101 currentNext.setLength(l);
1102 currentNext.setWorldPosition(cp);
1105 // FIXME : pipe run must be split into two parts, since the control point structure is no more continuous.
1107 } else if (next != null) {
1108 if (next.isDualInline()) {
1109 PipeControlPoint sccp = next;
1110 PipeControlPoint ocp = sccp.getSubPoint().get(0);
1112 throw new RuntimeException("Removing PipeControlPoint " + this+ " structure damaged, no offset control point");
1114 sccp.setPrevious(null);
1115 ocp.setPrevious(null);
1116 } else if (next.isDualSub()) {
1117 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, next control point is offset control point");
1118 } else if (next.previous == this) {
1119 next.setPrevious(null);
1121 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1124 } else { //(previous != null)
1125 if(previous.isDualInline()) {
1126 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, previous control point is size change control point");
1127 } else if (previous.isDualSub()) {
1128 PipeControlPoint ocp = previous;
1129 PipeControlPoint sccp = ocp.getParentPoint();
1131 throw new RuntimeException("Removing PipeControlPoint " + this + " structure damaged, no size change control point");
1135 } else if (previous.next == this) {
1136 previous.setNext(null);
1138 throw new RuntimeException("Removing PipeControlPoint "+ this+ " structure damaged");
1142 if (children.size() > 0 ) {
1144 } else if (parent!= null) {
1145 removeParentPoint();
1151 pipeRun.remChild(this);
1152 checkRemove(pipeRun);
1153 if (PipingRules.isEnabled() && pipeRun.getParent() != null && pipeRun.getControlPoints().size() > 0)
1154 PipingRules.validate(pipeRun);
1155 if (additionalRemove != null)
1156 additionalRemove.remove();
1160 * Removes control point and attempts to reconnect next/prev
1162 * If this point is size change (PipeRuns are different on both sides), then reconnection cannot be made.
1164 public void remove() {
1165 PipeControlPoint currentPrev = previous;
1166 PipeControlPoint currentNext = next;
1169 if (currentNext != null)
1170 if (!currentNext.checkRemove())
1171 PipingRules.requestUpdate(currentNext);
1172 if (currentPrev != null)
1173 if (!currentPrev.checkRemove())
1174 PipingRules.requestUpdate(currentPrev);
1175 } catch (Exception e) {
1176 e.printStackTrace();
1182 * Removes control point without attempting to reconnect next/prev.
1183 * This usually leads to creation of another PipeRun for the control points after this point.
1185 public void removeAndSplit() {
1186 PipeControlPoint currentPrev = previous;
1187 PipeControlPoint currentNext = next;
1189 if (next != null && previous != null) {
1190 P3DRootNode root = (P3DRootNode)getPipelineComponent().getRootNode();
1191 PipeRun nextPipeRun = new PipeRun();
1192 nextPipeRun.setName(root.getUniqueName("PipeRun"));
1193 root.addChild(nextPipeRun);
1195 PipeRun previousRun = previous.getPipeRun();
1196 nextPipeRun.setPipeDiameter(previousRun.getPipeDiameter());
1197 nextPipeRun.setTurnRadiusArray(previousRun.getTurnRadiusArray());
1199 PipelineComponent n = next.getPipelineComponent();
1201 if (! (n instanceof Nozzle)) {
1203 nextPipeRun.addChild(n);
1205 n.setPipeRun(nextPipeRun);
1211 if (currentNext != null)
1212 if (!currentNext.checkRemove())
1213 PipingRules.requestUpdate(currentNext);
1214 if (currentPrev != null)
1215 if (!currentPrev.checkRemove())
1216 PipingRules.requestUpdate(currentPrev);
1217 } catch (Exception e) {
1218 e.printStackTrace();
1223 * This is called when adjacent control point is removed.
1225 * This call should remove the give point, if the point cannot exist alone.
1226 * At the moment there is one such case: branch.
1230 protected boolean checkRemove() {
1231 if (getParentPoint() != null) {
1232 return getParentPoint().checkRemove();
1234 if (getPipelineComponent() == null)
1235 return true; // already removed
1236 if (getPipelineComponent().getType().equals("Plant3D.URIs.Builtin_BranchSplitComponent")) {
1237 if (getSubPoint().get(0).getNext() == null && getSubPoint().get(0).getPrevious() == null) {
1246 private void checkRemove(PipeRun pipeRun) {
1247 Collection<PipeControlPoint> points = pipeRun.getControlPoints();
1248 if (points.size() == 0) {
1250 } else if (points.size() == 1) {
1251 PipeControlPoint pcp = points.iterator().next();
1252 if (pcp.isDeletable())
1257 private void removeDualPoint() {
1258 if (previous != null)
1259 previous.setNext(null);
1261 next.setPrevious(null);
1262 PipeControlPoint ocp;
1263 PipeControlPoint sccp;
1264 if (isDualInline()) {
1266 ocp = getSubPoint().get(0);
1269 sccp = getParentPoint();
1271 PipeRun p1 = ocp.getPipeRun();
1272 PipeRun p2 = sccp.getPipeRun();
1274 ocp.removeComponent();
1275 sccp.removeComponent();
1282 // TODO : now we assume that this is size change, and we do
1283 if (ocp.next != null)
1284 ocp.next.setPrevious(null);
1285 if (ocp.previous != null)
1286 ocp.previous.setNext(null);
1287 if (sccp.next != null)
1288 sccp.next.setPrevious(null);
1289 if (sccp.previous != null)
1290 sccp.previous.setNext(null);
1292 ocp.setPrevious(null);
1294 sccp.setPrevious(null);
1302 private void removeSubPoints() {
1303 for (PipeControlPoint p : children) {
1304 // TODO : this may affect delete routine, since classification of the point changes.
1311 private void removeParentPoint() {
1312 throw new RuntimeException("Child points cannot be removed directly");
1315 private void removeComponent() {
1316 if (component == null)
1318 PipelineComponent next = component.getNext();
1319 PipelineComponent prev = component.getPrevious();
1320 PipelineComponent br0 = component.getBranch0();
1321 component.setNext(null);
1322 component.setPrevious(null);
1323 component.setBranch0(null);
1325 if (next.getNext() == component)
1327 else if (next.getPrevious() == component)
1328 next.setPrevious(null);
1329 else if (next.getBranch0() == component)
1330 next.setBranch0(null);
1333 if (prev.getNext() == component)
1335 else if (prev.getPrevious() == component)
1336 prev.setPrevious(null);
1337 else if (prev.getBranch0() == component)
1338 prev.setBranch0(null);
1341 if (br0.getNext() == component)
1343 else if (br0.getPrevious() == component)
1344 br0.setPrevious(null);
1345 else if (br0.getBranch0() == component)
1346 br0.setBranch0(null);
1348 PipelineComponent comp = component;
1355 public void setOrientation(Quat4d orientation) {
1356 if (MathTools.equals(orientation, getOrientation()))
1358 super.setOrientation(orientation);
1359 if (getParentPoint() == null && component != null)
1360 component._setWorldOrientation(getWorldOrientation());
1365 public void setPosition(Vector3d position) {
1366 if (MathTools.equals(position, getPosition()))
1368 if (Double.isNaN(position.x) || Double.isNaN(position.y) || Double.isNaN(position.z))
1369 throw new IllegalArgumentException("NaN is not supported");
1370 super.setPosition(position);
1371 if (getParentPoint() == null && component != null)
1372 component._setWorldPosition(getWorldPosition());
1376 private void updateSubPoint() {
1378 if (next == null && previous == null) {
1379 for (PipeControlPoint sub : getSubPoint()) {
1380 sub.setWorldPosition(getWorldPosition());
1381 sub.setWorldOrientation(getWorldOrientation());
1385 for (PipeControlPoint sub : getSubPoint()) {
1386 Vector3d wp = getWorldPosition();
1387 wp.add(getSizeChangeOffsetVector());
1388 sub.setWorldPosition(wp);
1389 sub.setWorldOrientation(getWorldOrientation());
1392 for (PipeControlPoint sub : getSubPoint()) {
1393 sub.setWorldPosition(getWorldPosition());
1394 sub.setWorldOrientation(getWorldOrientation());
1400 public void _setWorldPosition(Vector3d position) {
1401 Vector3d localPos = getLocalPosition(position);
1402 super.setPosition(localPos);
1406 public void _setWorldOrientation(Quat4d orientation) {
1407 Quat4d localOr = getLocalOrientation(orientation);
1408 super.setOrientation(localOr);
1413 public String toString() {
1414 return getClass().getName() + "@" + Integer.toHexString(hashCode());