1 package org.simantics.plant3d.scenegraph.controlpoint;
3 import java.util.ArrayList;
4 import java.util.Collection;
7 import javax.vecmath.Point3d;
8 import javax.vecmath.Quat4d;
9 import javax.vecmath.Vector3d;
11 import org.simantics.g3d.math.MathTools;
12 import org.simantics.plant3d.scenegraph.InlineComponent;
13 import org.simantics.plant3d.scenegraph.P3DRootNode;
14 import org.simantics.plant3d.scenegraph.PipeRun;
15 import org.simantics.plant3d.scenegraph.PipelineComponent;
16 import org.simantics.plant3d.scenegraph.TurnComponent;
17 import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction;
18 import org.simantics.plant3d.utils.ComponentUtils;
19 import org.simantics.utils.datastructures.Pair;
20 import org.simantics.utils.ui.ErrorLogger;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
24 public class PipingRules {
25 private static final Logger LOGGER = LoggerFactory.getLogger(PipingRules.class);
27 private static final boolean DUMMY = false;
29 private static double MIN_TURN_ANGLE = 0.001; // Threshold for removing turn components.
30 private static double ALLOWED_OFFSET = 0.001; // Allowed offset for directed path legs
31 private static double MIN_INLINE_LENGTH = 0.0005; // Minimum length of inline components, when component removal is not allowed.
33 private static final int REMOVE_NONE = 0;
34 private static final int REMOVE_START = 1;
35 private static final int REMOVE_END = 2;
36 private static final int REMOVE_BOTH = 3;
39 // PathLeg iteration indicator. NEXT_S > NEXT > NONE PREV_S > PREV > NONE
40 private enum PathLegUpdateType {
41 NONE, // Only current path leg needs to be updated (for example, inline comp was moved)
42 PREV, // Current and previous path leg need to be updated
43 NEXT, // Current and next path leg need to be updated
44 PREV_S, // Current and previous two path legs need to be updated (turn was moved, which affect other path leg end turns, and thus following path legs
45 NEXT_S // Current and next two path legs need to be updated
48 private static boolean enabled = true; //
49 private static boolean updating = false;
50 private static boolean allowInsertRemove = true;
51 private static boolean triedIR = false;
54 private static List<PipeControlPoint> requestUpdates = new ArrayList<PipeControlPoint>();
55 private static List<PipeControlPoint> currentUpdates = new ArrayList<PipeControlPoint>();
57 private static Object updateMutex = new Object();
58 private static Object ruleMutex = new Object();
60 public static void requestUpdate(PipeControlPoint pcp) {
61 if (!PipingRules.enabled)
63 if (LOGGER.isTraceEnabled()) LOGGER.trace("PipingRules request " + pcp);
64 synchronized (updateMutex) {
65 if (!requestUpdates.contains(pcp))
66 requestUpdates.add(pcp);
70 public static boolean update() throws Exception {
71 if (!PipingRules.enabled)
74 List<PipeControlPoint> temp;
75 synchronized(updateMutex) {
76 if (requestUpdates.size() == 0)
79 temp = new ArrayList<PipeControlPoint>(requestUpdates.size());
80 temp.addAll(requestUpdates);
81 requestUpdates.clear();
83 synchronized (ruleMutex) {
84 currentUpdates.clear();
85 currentUpdates.addAll(temp);
86 // TODO : we should remove already processed control points from currentUpdates after each _positionUpdate call.
87 for (PipeControlPoint pcp : currentUpdates)
88 _positionUpdate(pcp, true);
89 currentUpdates.clear();
91 synchronized(updateMutex) {
92 requestUpdates.removeAll(temp);
98 public static boolean positionUpdate(PipeControlPoint pcp) throws Exception {
100 return positionUpdate(pcp, true);
103 public static boolean positionUpdate(PipeControlPoint pcp, boolean allowIR) throws Exception {
104 synchronized (ruleMutex) {
105 currentUpdates.add(pcp);
106 boolean b = _positionUpdate(pcp, allowIR);
107 currentUpdates.clear();
113 private static boolean _positionUpdate(PipeControlPoint pcp, boolean allowIR) throws Exception {
114 if (updating || !enabled)
116 if (pcp.getPipeRun() == null)
119 if (LOGGER.isTraceEnabled()) LOGGER.trace("PipingRules " + pcp);
121 allowInsertRemove = allowIR;
123 validate(pcp.getPipeRun());
124 if (pcp.getParentPoint() != null)
125 pcp = pcp.getParentPoint();
126 if (pcp.asPathLegEnd()) {
127 updatePathLegEndControlPoint(pcp); // FIXME: Rules won't work properly, if they are not run twice.
128 //updatePathLegEndControlPoint(pcp);
130 updateInlineControlPoint(pcp);
131 //updateInlineControlPoint(pcp);
133 validate(pcp.getPipeRun());
134 if (!allowInsertRemove)
139 // LOGGER.trace("PipingRules done " + pcp);
143 public static void setEnabled(boolean enabled) {
144 PipingRules.enabled = enabled;
146 synchronized (ruleMutex) {
147 currentUpdates.clear();
152 public static boolean isEnabled() {
156 public static class ExpandIterInfo {
157 // these two are turn control points
158 private PipeControlPoint start;
159 private PipeControlPoint end;
162 public ExpandIterInfo() {
166 public ExpandIterInfo(PipeControlPoint tcp, int type) {
167 if (type == REMOVE_START)
174 public ExpandIterInfo(PipeControlPoint start, PipeControlPoint end) {
177 this.type = REMOVE_BOTH;
180 public PipeControlPoint getEnd() {
184 public void setEnd(PipeControlPoint end) {
188 public PipeControlPoint getStart() {
192 public void setStart(PipeControlPoint start) {
196 public int getType() {
200 public void setType(int type) {
206 private static void updatePathLegEndControlPoint(PipeControlPoint pcp) throws Exception {
207 if (LOGGER.isTraceEnabled())
208 LOGGER.trace("PipingRules.updatePathLegEndControlPoint() " + pcp);
209 if (pcp.getNext() != null) {
210 updatePathLegNext(pcp, pcp, PathLegUpdateType.NEXT_S);
212 if (pcp.getPrevious() != null) {
213 updatePathLegPrev(pcp, pcp, PathLegUpdateType.PREV_S);
218 private static void updateInlineControlPoint(PipeControlPoint pcp) throws Exception {
219 if (LOGGER.isTraceEnabled())
220 LOGGER.trace("PipingRules.updateInlineControlPoint() " + pcp);
221 PipeControlPoint start = pcp.findPreviousEnd();
222 updatePathLegNext(start, pcp, PathLegUpdateType.NONE);
224 if (pcp.isOffset()) {
225 // Adjusting the rotation angle of an offset component may change variable angle turns
226 PipeControlPoint end = pcp.findNextEnd();
227 if (end.isVariableAngle()) {
228 updatePathLegNext(end, end, PathLegUpdateType.NONE);
230 if (start.isVariableAngle()) {
231 updatePathLegPrev(start, start, PathLegUpdateType.NONE);
236 private static PipeControlPoint insertElbow(PipeControlPoint pcp1, PipeControlPoint pcp2, Vector3d pos) throws Exception{
237 if (LOGGER.isTraceEnabled())
238 LOGGER.trace("PipingRules.insertElbow() " + pcp1 + " " + pcp2 + " " + pos);
239 if (pcp1.getNext() == pcp2 && pcp2.getPrevious() == pcp1) {
241 } else if (pcp1.getNext() == pcp2 && pcp1.isDualInline() && pcp2.getPrevious() == pcp1.getDualSub()) {
242 pcp1 = pcp1.getDualSub();
243 } else if (pcp1.getPrevious() == pcp2 && pcp2.getNext() == pcp1) {
244 PipeControlPoint t = pcp1;
247 } else if (pcp2.isDualInline() && pcp1.getPrevious() == pcp2.getDualSub() && pcp2.getNext() == pcp1) {
248 PipeControlPoint t = pcp1;
249 pcp1 = pcp2.getDualSub();
252 throw new RuntimeException();
254 TurnComponent elbow = ComponentUtils.createTurn((P3DRootNode)pcp1.getRootNode());
255 PipeControlPoint pcp = elbow.getControlPoint();
256 if (pcp1.isDualInline())
257 pcp1 = pcp1.getDualSub();
258 String name = pcp1.getPipeRun().getUniqueName("Elbow");
260 pcp1.getPipeRun().addChild(elbow);
262 pcp.insert(pcp1, pcp2);
264 pcp.setWorldPosition(pos);
265 validate(pcp.getPipeRun());
269 private static PipeControlPoint insertStraight(PipeControlPoint pcp1, PipeControlPoint pcp2, Vector3d pos, double length) throws Exception {
270 if (LOGGER.isTraceEnabled())
271 LOGGER.trace("PipingRules.insertStraight() " + pcp1 + " " + pcp2 + " " + pos);
272 if (pcp1.getNext() == pcp2 && pcp2.getPrevious() == pcp1) {
274 } else if (pcp1.getNext() == pcp2 && pcp1.isDualInline() && pcp2.getPrevious() == pcp1.getDualSub()) {
275 pcp1 = pcp1.getDualSub();
276 } else if (pcp1.getPrevious() == pcp2 && pcp2.getNext() == pcp1) {
277 PipeControlPoint t = pcp1;
280 } else if (pcp2.isDualInline() && pcp1.getPrevious() == pcp2.getDualSub() && pcp2.getNext() == pcp1) {
281 PipeControlPoint t = pcp1;
282 pcp1 = pcp2.getDualSub();
285 throw new RuntimeException();
287 InlineComponent component = ComponentUtils.createStraight((P3DRootNode)pcp1.getRootNode());
288 PipeControlPoint scp = component.getControlPoint();
289 if (pcp1.isDualInline())
290 pcp1 = pcp1.getDualSub();
291 String name = pcp1.getPipeRun().getUniqueName("Pipe");
292 component.setName(name);
293 pcp1.getPipeRun().addChild(component);
295 scp.insert(pcp1, pcp2);
297 scp.setWorldPosition(pos);
298 Vector3d dir = new Vector3d();
299 dir.sub(pcp2.getWorldPosition(), pcp1.getWorldPosition());
300 scp.orientToDirection(dir);
301 scp.setLength(length);
302 validate(scp.getPipeRun());
306 private static PipeControlPoint insertStraight(PipeControlPoint pcp, Direction direction , Vector3d pos, double length) throws Exception {
307 if (LOGGER.isTraceEnabled())
308 LOGGER.trace("PipingRules.insertStraight() " + pcp + " " + direction + " " + pos);
310 InlineComponent component = ComponentUtils.createStraight((P3DRootNode)pcp.getRootNode());
311 PipeControlPoint scp = component.getControlPoint();
312 if (pcp.isDualInline() && direction == Direction.NEXT)
313 pcp = pcp.getDualSub();
314 String name = pcp.getPipeRun().getUniqueName("Pipe");
315 component.setName(name);
316 pcp.getPipeRun().addChild(component);
318 scp.insert(pcp,direction);
320 scp.setWorldPosition(pos);
321 scp.setLength(length);
322 validate(scp.getPipeRun());
326 private static void updatePathLegNext(PipeControlPoint start, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
327 UpdateStruct2 us = createUS(start, Direction.NEXT, 0, new ArrayList<ExpandIterInfo>(), updated);
329 LOGGER.trace("Null update struct " + start);
332 updatePathLeg(us, lengthChange);
335 private static void updatePathLegPrev(PipeControlPoint start, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
336 UpdateStruct2 us = createUS(start, Direction.PREVIOUS, 0, new ArrayList<ExpandIterInfo>(), updated);
338 LOGGER.trace("Null update struct " + start);
341 updatePathLeg(us, lengthChange);
344 private static class UpdateStruct2 {
345 public PipeControlPoint start;
346 public Vector3d startPoint;
347 public ArrayList<PipeControlPoint> list;
348 public PipeControlPoint end;
349 public Vector3d endPoint;
351 public Vector3d offset;
352 public boolean hasOffsets;
354 public boolean reversed;
355 public ArrayList<ExpandIterInfo> toRemove;
356 public PipeControlPoint updated;
358 public UpdateStruct2(PipeControlPoint start, Vector3d startPoint, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d endPoint, Vector3d dir, Vector3d offset, boolean hasOffsets, int iter, boolean reversed, ArrayList<ExpandIterInfo> toRemove, PipeControlPoint updated) {
359 if (start == null || end == null)
360 throw new NullPointerException();
362 this.startPoint = startPoint;
365 this.endPoint = endPoint;
367 this.offset = offset;
368 this.hasOffsets = hasOffsets;
370 this.reversed = reversed;
371 this.toRemove = toRemove;
372 this.updated = updated;
374 if (!MathTools.isValid(startPoint) ||
375 !MathTools.isValid(endPoint) ||
376 !MathTools.isValid(dir)) {
377 throw new RuntimeException();
381 public String toString() {
382 return start + " " + end+ " " + dir + " " + hasOffsets + " " + offset + " " + iter + " " + toRemove.size();
388 * Calculate offset based on a given fixed component direction.
390 * The desired component direction is provided as an input to this method,
391 * unlike the direction vector that is calculated by calculateOffset.
393 * The returned offset vector is always perpendicular to the given direction
396 * @param startPoint Start point of leg
397 * @param endPoint End point of leg
398 * @param start Starting component of leg
399 * @param list Inline components between start and end
400 * @param end Ending component of leg
401 * @param dir Direction at which the offset is calculated
402 * @param offset A vector object to receive the offset vector values
403 * @return True if offsets are present
405 public static boolean calculateDirectedOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d dir, Vector3d offset) {
406 return calculateOffset(startPoint, endPoint, start, list, end, dir, offset, true);
410 * Calculate offset and direction vectors for a path leg so that the given chain
411 * of components starts and ends at the given coordinates
413 * The returned direction and offset vectors are perpendicular to each other.
415 * @param startPoint Start point of the leg
416 * @param endPoint End point of the leg
417 * @param start Starting component of the leg
418 * @param list Inline components between start and end
419 * @param end Ending component of the leg
420 * @param dir A vector object to receive the component direction vector
421 * @param offset A vector object to receive the offset vector
422 * @return True if offsets are present
424 public static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d dir, Vector3d offset) {
425 return calculateOffset(startPoint, endPoint, start, list, end, dir, offset, false);
428 private static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d dir, Vector3d offset, boolean directed) {
429 List<PipeControlPoint> offsets = getOffsetPoints(start, list);
430 if (offsets.size() == 0) {
431 setZeroOffset(startPoint, endPoint, dir, offset);
434 Vector3d sp = new Vector3d(startPoint);
435 Point3d ep = new Point3d(endPoint);
441 double l = dir.lengthSquared();
442 if (l > MathTools.NEAR_ZERO)
443 dir.scale(1.0/Math.sqrt(l));
448 offset.set(0.0, 0.0, 0.0);
450 for (PipeControlPoint icp : offsets) {
451 Vector3d v = icp.getSizeChangeOffsetVector(dir);
458 Point3d nep = new Point3d(endPoint);
460 if (nep.distance(ep) < 0.0000000001 || iter <= 0) {
467 l = dir.lengthSquared();
468 if (l > MathTools.NEAR_ZERO)
469 dir.scale(1.0/Math.sqrt(l));
472 if (LOGGER.isTraceEnabled())
473 LOGGER.trace("calcOffset s:"+ startPoint + " e:" + endPoint + " d:" + dir + " o:"+offset) ;
479 public static void setZeroOffset(Vector3d startPoint, Vector3d endPoint, Vector3d dir, Vector3d offset) {
482 double l = dir.lengthSquared();
483 if (l > MathTools.NEAR_ZERO)
484 dir.scale(1.0/Math.sqrt(l));
485 offset.set(0.0, 0.0, 0.0);
488 public static List<PipeControlPoint> getOffsetPoints(PipeControlPoint start, ArrayList<PipeControlPoint> list) {
489 List<PipeControlPoint> offsets = new ArrayList<PipeControlPoint>(list.size());
490 // Only start offset affects the calculation
491 if (start.isOffset())
493 for (PipeControlPoint icp : list) {
494 if (icp.isOffset()) {
496 } else if (icp.isDualSub())
497 ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
502 private static UpdateStruct2 createUS(PipeControlPoint start, Direction direction, int iter, ArrayList<ExpandIterInfo> toRemove, PipeControlPoint updated) {
503 ArrayList<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
504 PipeControlPoint end = null;
505 if (direction == Direction.NEXT) {
506 end = start.findNextEnd(list);
508 ArrayList<PipeControlPoint> prevList = new ArrayList<PipeControlPoint>();
509 PipeControlPoint tend = start.findPreviousEnd(prevList);
510 for (PipeControlPoint icp : prevList) {
511 if (icp.isDualSub()) {
512 list.add(0, icp.getParentPoint());
522 boolean hasOffsets = false;
523 Vector3d offset = new Vector3d();
524 Vector3d startPoint = start.getWorldPosition();
525 Vector3d endPoint = end.getWorldPosition();
526 Vector3d dir = new Vector3d();
527 hasOffsets = calculateOffset(startPoint, endPoint, start, list, end, dir, offset);
528 return new UpdateStruct2(start, startPoint, list, end, endPoint, dir, offset, hasOffsets, iter, direction == Direction.PREVIOUS, toRemove, updated);
531 private static Vector3d pathLegDirection(PipeControlPoint start) {
532 ArrayList<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
533 PipeControlPoint end = start.findNextEnd(list);
535 return start.getDirection(Direction.NEXT);
538 Vector3d offset = new Vector3d();
539 Vector3d startPoint = start.getWorldPosition();
540 Vector3d endPoint = end.getWorldPosition();
541 Vector3d dir = new Vector3d();
542 calculateOffset(startPoint, endPoint, start, list, end, dir, offset);
546 private static boolean asDirected(PipeControlPoint pcp, Direction direction) {
547 if (pcp.isDirected())
549 if (pcp.asFixedAngle()) {
550 if (!pcp._getReversed())
551 return direction == Direction.NEXT;
553 return direction == Direction.PREVIOUS;
558 private static Vector3d direction(PipeControlPoint pcp, Direction direction) {
559 return pcp.getDirection(direction);
562 private static void updatePathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
565 if (lengthChange == PathLegUpdateType.NONE) {
569 updatePathLeg(u, lengthChange, rs, re);
572 private static void updatePathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange, boolean rs, boolean re) throws Exception {
574 if (asDirected(u.start, Direction.NEXT))
576 if (asDirected(u.end, Direction.PREVIOUS))
579 setErrorForce(u.start, null);
581 setErrorForce(u.end, null);
582 for (PipeControlPoint pcp : u.list)
583 setErrorForce(pcp, null);
586 updateFreePathLeg(u, lengthChange);
589 updateDirectedPathLeg(u, lengthChange);
592 updateDualDirectedPathLeg(u, lengthChange);
598 private static void updateFreePathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
599 if (LOGGER.isTraceEnabled())
600 LOGGER.trace("PipingRules.updateFreePipeRun " + u + " " + lengthChange);
601 checkExpandPathLeg(u, lengthChange);
602 if (u.start.isInline() || u.end.isInline() || u.start.asFixedAngle()|| u.end.asFixedAngle())
603 processPathLeg(u, true, false);
606 private static void updateInlineControlPoints(UpdateStruct2 u, boolean checkSizes) throws Exception{
607 if (LOGGER.isTraceEnabled())
608 LOGGER.trace("PipingRules.updateInlineControlPoints() " + u);
610 Vector3d start = new Vector3d(u.startPoint);
611 Vector3d end = new Vector3d(u.endPoint);
614 // create offsets for leg ends.
615 if (u.start.isTurn())
616 MathTools.mad(start, u.dir, u.start.getInlineLength());
618 MathTools.mad(end, u.dir, -u.end.getInlineLength());
621 boolean recalcline = false;
623 Vector3d sp = new Vector3d(start);
624 Vector3d ep = new Vector3d(end);
627 if (u.start.isOffset()) {
628 Vector3d offset = u.start.getSizeChangeOffsetVector(u.dir);
629 updateOffsetPoint(u.start, offset);
634 for (PipeControlPoint icp : u.list) {
635 updateInlineControlPoint(icp, sp, ep, u.dir);
636 if (icp.isOffset()) {
637 // TODO : offset vector is already calculated and should be cached
638 Vector3d offset = icp.getSizeChangeOffsetVector(u.dir);
639 updateOffsetPoint(icp, offset);
648 // Collect all path leg points for updating variable length components. This list will also contain leg ends (usually turns)
649 ArrayList<PipeControlPoint> pathLegPoints = new ArrayList<>();
650 // Collect all fixed length components with their offsets.
651 ArrayList<Pair<PipeControlPoint,Vector3d>> fixedLengthPoints = new ArrayList<>();
653 pathLegPoints.add(u.start);
654 fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(u.start, new Vector3d()));
655 Vector3d off = new Vector3d();
656 for (PipeControlPoint icp : u.list) {
657 pathLegPoints.add(icp);
658 updateBranchControlPointBranches(icp);
659 if (icp.isOffset()) {
660 fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(icp, new Vector3d(off)));
661 Vector3d offset = icp.getSizeChangeOffsetVector(u.dir);
663 } else if (!icp.isVariableLength()) {
664 fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(icp, new Vector3d(off)));
667 pathLegPoints.add(u.end);
668 fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(u.end, new Vector3d(off)));
670 sp = new Vector3d(start);
671 ep = new Vector3d(end);
674 updateFixedLengths(fixedLengthPoints, sp, ep, u.dir);
676 for (int i = 0; i < pathLegPoints.size(); i++) {
677 PipeControlPoint icp = pathLegPoints.get(i);
679 PipeControlPoint prev = i > 0 ? pathLegPoints.get(i - 1) : null;
680 PipeControlPoint next = i < pathLegPoints.size() - 1 ? pathLegPoints.get(i + 1) : null;
682 if (prev != null && prev.isDualInline())
683 prev = prev.getDualSub();
685 if (icp.isVariableLength()) {
686 if (prev != null && next != null) {
687 recalcline = recalcline | updateVariableLength(icp, prev, next);
690 // this is variable length component at the end of the piperun.
691 // the problem is that we want to keep unconnected end of the component in the same
692 // place, but center of the component must be moved.
693 updateVariableLengthEnd(icp, prev != null ? prev : next);
695 } else if (prev != null && !prev.isVariableLength()) {
696 // If this and previous control point are not variable length pcps,
697 // we'll have to check if there is no empty space between them.
698 // I there is, we'll have to create new variable length component between them.
699 recalcline = recalcline | possibleVaribleLengthInsert(icp, prev);
701 if (icp.isOffset()) {
702 // TODO : offset vector is already calculated and should be cached
703 Vector3d offset = icp.getSizeChangeOffsetVector(u.dir);
711 u.start.findNextEnd(u.list);
714 sp = new Vector3d(u.startPoint);
715 ep = new Vector3d(u.endPoint);
717 double pathLegLength = MathTools.distance(sp, ep);
718 double availableLength = pathLegLength;
719 if (u.start.isTurn())
720 availableLength -= u.start.getInlineLength();
722 availableLength -= u.end.getInlineLength();
723 for (PipeControlPoint pcp : u.list) {
724 if (!pcp.isVariableLength())
725 availableLength-= pcp.getLength();
727 if (availableLength < 0.0) {
728 setError(u.start, "Not enough available space");
729 setError(u.end, "Not enough available space");
730 for (PipeControlPoint pcp : u.list)
731 setError(pcp, "Not enough available space");
733 // LOGGER.trace(u.start.getPipelineComponent().toString() + " " + pathLegLength + " " + availableLength + " " + u.end.getPipelineComponent().toString() + " " + u.start.getInlineLength() + " " + u.end.getInlineLength());
737 private enum Gap{ATTACHED,OVERLAP,SPACE};
739 private static class GapObj {
743 Pair<PipeControlPoint,Vector3d> pcp1;
744 Pair<PipeControlPoint,Vector3d> pcp2;
747 private static void updateFixedLengths(List<Pair<PipeControlPoint,Vector3d>> fixedLengthPoints, Vector3d s, Vector3d e, Vector3d dir) {
748 double totalLength = MathTools.distance(s, e);
749 double reservedLength = 0.0;
750 List<Double> distances = new ArrayList<>(fixedLengthPoints.size());
752 for (int i = 1; i < fixedLengthPoints.size()-1; i++) {
753 Pair<PipeControlPoint,Vector3d> pcp = fixedLengthPoints.get(i);
754 reservedLength += pcp.first.getLength();
755 Vector3d p = pcp.first.getWorldPosition();
757 double d= MathTools.distance(s, p);
760 distances.add(totalLength);
762 if (totalLength >= reservedLength) {
763 // There is enough space for all fixed length components.
764 List<GapObj> gaps = new ArrayList<>(fixedLengthPoints.size()-1);
766 // Analyze gaps between components
767 for (int i = 0; i < fixedLengthPoints.size()-1; i++) {
768 Pair<PipeControlPoint,Vector3d> pcp1 = fixedLengthPoints.get(i);
769 Pair<PipeControlPoint,Vector3d> pcp2 = fixedLengthPoints.get(i+1);
770 double d1 = distances.get(i);
771 double d2 = distances.get(i+1);
772 double ld1 = i == 0 ? 0.0 :pcp1.first.getInlineLength();
773 double ld2 = i == fixedLengthPoints.size()-2 ? 0.0 : pcp2.first.getInlineLength();
775 double e1 = d1 + ld1; // End of comp1
776 double s2 = d2 - ld2; // Start of comp2
777 double diff =s2 - e1;
778 GapObj obj = new GapObj();
782 if (diff < -MIN_INLINE_LENGTH) {
783 obj.gap = Gap.OVERLAP;
785 } else if (diff > MIN_INLINE_LENGTH) {
788 obj.gap = Gap.ATTACHED;
792 // If there are no overlaps, there is nothing to do.
795 // Get rid of overlapping components by using closest available free spaces.
796 for (int i = 0; i < gaps.size(); i++) {
797 GapObj gapObj = gaps.get(i);
798 if (gapObj.gap != Gap.OVERLAP)
800 double curr = gapObj.d;
802 while (d < gaps.size() && curr < -MIN_INLINE_LENGTH) {
803 GapObj next = i+d < gaps.size() ? gaps.get(i+d) : null;
804 GapObj prev = i-d >= 0 ? gaps.get(i-d) : null;
805 if (next != null && next.gap == Gap.SPACE) {
806 double move = Math.min(-curr, next.d);
809 if (next.d < MIN_INLINE_LENGTH)
810 next.gap = Gap.ATTACHED;
811 Vector3d mv = new Vector3d(dir);
814 for (int j = i ; j < i+d; j++) {
815 Pair<PipeControlPoint,Vector3d> pcp = gaps.get(j).pcp2;
816 Vector3d p = new Vector3d(pcp.first.getWorldPosition());
818 pcp.first.setWorldPosition(p);
821 else if (prev != null && prev.gap == Gap.SPACE) {
822 double move = Math.min(-curr, prev.d);
825 if (prev.d < MIN_INLINE_LENGTH)
826 prev.gap = Gap.ATTACHED;
827 Vector3d mv = new Vector3d(dir);
830 for (int j = i ; j > i-d; j--) {
831 Pair<PipeControlPoint,Vector3d> pcp = gaps.get(j).pcp1;
832 Vector3d p = new Vector3d(pcp.first.getWorldPosition());
834 pcp.first.setWorldPosition(p);
843 for (int i = 1; i < fixedLengthPoints.size()-1; i++) {
844 Pair<PipeControlPoint,Vector3d> prev = i == 0 ? null : fixedLengthPoints.get(i-1);
845 Pair<PipeControlPoint,Vector3d> curr = fixedLengthPoints.get(i);
846 Pair<PipeControlPoint,Vector3d> next = i == fixedLengthPoints.size() -1 ? null : fixedLengthPoints.get(i+1);
847 updateFixedLength(curr, prev, next, s,e, dir);
852 private static void updateFixedLength(Pair<PipeControlPoint,Vector3d> icp, Pair<PipeControlPoint,Vector3d> prev, Pair<PipeControlPoint,Vector3d> next, Vector3d s, Vector3d e, Vector3d dir) {
854 checkOverlap(prev, icp, dir,true);
857 checkOverlap(icp, next, dir,true);
860 private static boolean checkOverlap(Pair<PipeControlPoint,Vector3d> icp, Pair<PipeControlPoint,Vector3d> icp2, Vector3d dir, boolean se) {
861 Vector3d p1 = icp.first.getWorldPosition();
862 Vector3d p2 = icp2.first.getWorldPosition();
865 double u[] = new double[1];
866 MathTools.closestPointOnStraight(p2, p1, dir, u);
871 MathTools.mad(p2, dir, MIN_INLINE_LENGTH);
872 icp2.first.setWorldPosition(p2);
874 double d = MathTools.distance(p1, p2);
875 double r = icp.first.getInlineLength() + icp2.first.getInlineLength();
877 if ((d-r) < - MIN_INLINE_LENGTH) {
879 setError(icp.first, "Overlapping");
880 setError(icp2.first, "Overlapping");
888 * Overrides current error of a component
892 private static void setErrorForce(PipeControlPoint pcp, String error) {
893 PipelineComponent comp = pcp.getPipelineComponent();
896 comp.setError(error);
900 * Sets error for a component, if there is no existing error.
904 private static void setError(PipeControlPoint pcp, String error) {
905 PipelineComponent comp = pcp.getPipelineComponent();
908 if (comp.getError() != null)
910 comp.setError(error);
913 private static boolean updateVariableLength(PipeControlPoint icp, PipeControlPoint prev, PipeControlPoint next) {
914 Vector3d prevPos = prev.getWorldPosition();
915 Vector3d nextPos = next.getWorldPosition();
917 Vector3d dir = new Vector3d(nextPos);
919 double l = dir.length(); // distance between control points
920 double l2prev = prev.getInlineLength(); // distance taken by components
921 double l2next = next.getInlineLength();
922 double l2 = l2prev + l2next;
923 double length = l - l2; // true length of the variable length component
924 if (length >= MIN_INLINE_LENGTH) { // check if there is enough space for variable length component.
927 dir.scale(length * 0.5 + l2prev); // calculate center position of the component
929 icp.setWorldPosition(dir);
930 icp.setLength(length);
933 // components leave no space to the component and it must be removed
934 if (icp.isDeletable()) {
935 if (!allowInsertRemove) {
936 icp.setLength(MIN_INLINE_LENGTH);
937 setError(icp, "Not enough available space");
941 if (LOGGER.isTraceEnabled())
942 LOGGER.trace("PipingRules.updateVariableLength removing " + icp);
946 icp.setLength(MIN_INLINE_LENGTH);
947 icp.getPipelineComponent().setError("Not enough available space");
953 private static boolean possibleVaribleLengthInsert(PipeControlPoint icp, PipeControlPoint prev) throws Exception{
954 Vector3d currentPos = icp.getWorldPosition();
955 Vector3d prevPos = prev.getWorldPosition();
956 Vector3d dir = new Vector3d(currentPos);
958 double l = dir.lengthSquared();
959 double l2prev = prev.getInlineLength();
960 double l2next = icp.getInlineLength();
961 double l2 = l2prev + l2next;
962 double l2s = l2 * l2;
963 double diff = l - l2s;
964 if (diff >= MIN_INLINE_LENGTH) {
965 if (allowInsertRemove) {
967 double length = Math.sqrt(l) - l2; // true length of the variable length component
968 dir.scale(length * 0.5 + l2prev); // calculate center position of the component
970 insertStraight(prev, icp, dir, length);
979 private static void updateVariableLengthEnd(PipeControlPoint icp, PipeControlPoint prev) {
980 Vector3d currentPos = icp.getWorldPosition();
981 Vector3d prevPos = prev.getWorldPosition();
983 Vector3d dir = new Vector3d();
984 dir.sub(currentPos, prevPos);
987 synchronized (ruleMutex) {
988 simple = currentUpdates.contains(icp);
992 // Update based on position -> adjust length
993 double currentLength = (dir.length() - prev.getInlineLength()) * 2.0;
994 icp.setLength(currentLength);
996 // Update based on neighbour movement -> adjust length and position, so that free end stays in place.
997 double currentLength = icp.getLength();
998 if (currentLength < MathTools.NEAR_ZERO) {
999 currentLength = (dir.length() - prev.getInlineLength()) * 2.0;
1002 if (dir.lengthSquared() > MathTools.NEAR_ZERO)
1004 Point3d endPos = new Point3d(dir);
1005 endPos.scale(currentLength * 0.5);
1006 endPos.add(currentPos); // this is the free end of the component
1008 double offset = prev.getInlineLength();
1009 Point3d beginPos = new Point3d(dir);
1010 beginPos.scale(offset);
1011 beginPos.add(prevPos); // this is the connected end of the component
1013 double l = beginPos.distance(endPos);
1015 if (Double.isNaN(l))
1016 LOGGER.debug("Length for " + icp + " is NaN");
1019 beginPos.add(dir); // center position
1021 if (LOGGER.isTraceEnabled())
1022 LOGGER.trace("PipingRules.updateInlineControlPoints() setting variable length to " + l);
1025 icp.setWorldPosition(new Vector3d(beginPos));
1030 * Recalculates offset vector based on current direction, and calls checkExpandPathLeg
1035 private static void ppNoOffset(UpdateStruct2 u, boolean updateEnds) throws Exception {
1036 if (LOGGER.isTraceEnabled())
1037 LOGGER.trace("PipingRules.ppNoOffset() " + u);
1038 Vector3d offset = new Vector3d();
1040 for (PipeControlPoint icp : u.list) {
1041 if (icp.isOffset()) {
1042 offset.add(icp.getSizeChangeOffsetVector(u.dir));
1043 } else if (icp.isDualSub())
1044 ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
1048 checkExpandPathLeg(u, PathLegUpdateType.NONE, updateEnds);
1051 private static void ppNoDir(PipeControlPoint start, Vector3d startPoint, ArrayList<PipeControlPoint> list, PipeControlPoint end, Vector3d endPoint, boolean hasOffsets, int iter, boolean reversed, ArrayList<ExpandIterInfo> toRemove, PipeControlPoint updated) throws Exception {
1052 if (LOGGER.isTraceEnabled())
1053 LOGGER.trace("PipingRules.ppNoDir() " + start + " " + end + " " + iter + " " + toRemove.size());
1054 // FIXME : extra loop (dir should be calculated here)
1055 Vector3d dir = new Vector3d();
1056 Vector3d offset = new Vector3d();
1057 hasOffsets = calculateOffset(startPoint, endPoint, start, list, end, dir, offset);
1058 ppNoOffset(new UpdateStruct2(start, startPoint, list, end, endPoint, dir, null, hasOffsets, iter, reversed, toRemove, updated),true);
1061 private static void checkExpandPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
1062 checkExpandPathLeg(u, lengthChange, u.updated.isInline() && u.updated.isOffset());
1065 private static void checkExpandPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange, boolean updateEnds) throws Exception {
1066 if (LOGGER.isTraceEnabled())
1067 LOGGER.trace("PipingRules.checkExpandPathLeg() " + u + " " + lengthChange);
1068 if (lengthChange != PathLegUpdateType.NONE) {
1069 // FIXME : turns cannot be checked before inline cps are updated,
1070 // since their position affects calculation of turns
1071 processPathLeg(u, updateEnds, false);
1072 int type = checkTurns(u, lengthChange);
1073 if (type == REMOVE_NONE) {
1074 processPathLeg(u, updateEnds, true);
1076 expandPathLeg(u, type);
1079 processPathLeg(u, updateEnds, true);
1083 private static void updateDirectedPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
1084 if (LOGGER.isTraceEnabled())
1085 LOGGER.trace("PipingRules.updateDirectedPipeRun() " + u + " " + lengthChange);
1086 PipeControlPoint dcp;
1087 PipeControlPoint other;
1088 boolean canMoveOther = false;
1089 boolean dcpStart = false;
1090 boolean inlineEnd = false;
1092 if (asDirected(u.start, Direction.NEXT)) {
1095 position = u.startPoint;
1098 canMoveOther = true;
1099 inlineEnd = u.end.isInline();
1104 position = u.endPoint;
1106 canMoveOther = true;
1107 inlineEnd = u.start.isInline();
1110 Vector3d directedDirection = direction(dcp, dcpStart ? Direction.NEXT : Direction.PREVIOUS);
1111 if (directedDirection == null) {
1112 //updateTurnControlPointTurn(dcp, dcp.getPrevious(), dcp.getNext());
1113 updateTurnControlPointTurn(dcp, null, null);
1114 directedDirection = direction(dcp, dcpStart ? Direction.NEXT : Direction.PREVIOUS);
1115 if (directedDirection == null) {
1120 Point3d otherPosition = new Point3d(dcpStart ? u.endPoint : u.startPoint);
1122 Vector3d dir = dcp.getDirection(dcpStart ? Direction.NEXT : Direction.PREVIOUS);
1126 Vector3d offset = new Vector3d();
1127 calculateDirectedOffset(u.startPoint, u.endPoint, u.start, u.list, u.end, dir, offset);
1132 otherPosition.sub(offset);
1134 otherPosition.add(offset);
1137 double mu[] = new double[2];
1140 Vector3d t = new Vector3d();
1142 closest = MathTools.closestPointOnStraight(otherPosition, position, directedDirection, mu);
1143 t.sub(closest, otherPosition);
1145 double distance = t.length();
1146 boolean aligned = (distance < ALLOWED_OFFSET);
1147 double requiredSpace = 0.0;
1148 if (other.isVariableAngle()) {
1149 requiredSpace = spaceForTurn(other, dcp);
1151 if (mu[0] < requiredSpace) {
1152 // At the moment, if next component is directly behind the nozzle, we must force moving the other component.
1153 // Trying to solve the situation by adding new turn creates infinite loop...
1155 canMoveOther = true;
1158 //if (u.start.isInline() || u.end.isInline() || u.start.asFixedAngle() || u.end.asFixedAngle())
1159 // processPathLeg(u, true, false);
1160 checkExpandPathLeg(u, lengthChange, inlineEnd || u.start.isInline() || u.end.isInline() || u.start.asFixedAngle() || u.end.asFixedAngle());
1165 PipeControlPoint nextToMoved;
1167 if (u.list.size() > 0)
1169 nextToMoved = u.list.get(0);
1171 nextToMoved = u.list.get(u.list.size() - 1);
1173 nextToMoved = u.end;
1175 nextToMoved = u.start;
1176 if (other.isVariableAngle()) {
1178 // TODO calculate needed space from next run end.
1179 if (mu[0] < requiredSpace) {
1181 closest.set(u.startPoint);
1183 closest.set(u.endPoint);
1185 Vector3d v = new Vector3d(directedDirection);
1186 v.scale(requiredSpace);
1191 if (LOGGER.isTraceEnabled())
1192 LOGGER.trace("PipingRules.updateDirectedPipeRun() moved end " + other + " to " + closest);
1194 // Not aligned - we need to recalculate the offset to reflect new end points.
1197 offset = new Vector3d();
1198 Vector3d newDir = new Vector3d();
1199 calculateDirectedOffset(position, closest, u.start, u.list, u.end, newDir, offset);
1200 closest.add(offset);
1202 offset = new Vector3d();
1205 other.setWorldPosition(closest);
1208 checkExpandPathLeg(new UpdateStruct2(u.start, u.startPoint, u.list, u.end, new Vector3d(closest), directedDirection, offset, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated), PathLegUpdateType.NONE, true);
1209 if (u.end.getNext() != null)
1210 updatePathLegNext(u.end, u.updated, PathLegUpdateType.NEXT);
1212 checkExpandPathLeg(new UpdateStruct2(u.start, new Vector3d(closest), u.list, u.end, u.endPoint, directedDirection, offset, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated), PathLegUpdateType.NONE, true);
1213 if (u.start.getPrevious() != null)
1214 updatePathLegPrev(u.start, u.updated, PathLegUpdateType.PREV);
1217 // TODO : calculate needed space from next run end.
1218 if (allowInsertRemove)
1219 insertElbowUpdate(u, dcp, nextToMoved, dcpStart, position, directedDirection);
1223 } else if (other.isNonDirected() && other.getParentPoint() != null) {
1224 // FIXME : this code was for updating branches
1225 Vector3d bintersect = new Vector3d();
1226 PipeControlPoint bcp = other.getParentPoint();
1227 if (bcp != null && canMoveOther) {
1228 Point3d bstart = new Point3d();
1229 Point3d bend = new Point3d();
1230 Vector3d bdir = new Vector3d();
1231 bcp.getInlineControlPointEnds(bstart, bend, bdir);
1232 Vector3d nintersect = new Vector3d();
1234 MathTools.intersectStraightStraight(position, directedDirection, bend, bdir, nintersect, bintersect, mu);
1235 Vector3d dist = new Vector3d(nintersect);
1236 dist.sub(bintersect);
1237 canMoveOther = mu[1] > 0.0 && mu[1] < 1.0 && dist.lengthSquared() < 0.01;
1239 // TODO : endControlPoints are undirected: calculcate
1240 // correct position for it
1241 throw new UnsupportedOperationException("not implemented");
1244 if (LOGGER.isTraceEnabled())
1245 LOGGER.trace("PipingRules.updateDirectedPipeRun() moved end " + other + " to " + bintersect);
1246 // is required branch position is in possible range
1247 bcp.setWorldPosition(bintersect);
1249 checkExpandPathLeg(new UpdateStruct2(u.start, u.startPoint, u.list, u.end, new Vector3d(bintersect), directedDirection, u.offset, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated), lengthChange);
1251 checkExpandPathLeg(new UpdateStruct2(u.start, new Vector3d(bintersect), u.list, u.end, u.endPoint, directedDirection, u.offset, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated), lengthChange);
1254 // branch cannot be moved into right position, new turn
1255 // / elbow must be inserted
1256 if (allowInsertRemove)
1257 insertElbowUpdate(u, dcp, nextToMoved, dcpStart, position, directedDirection);
1262 } else { // assume that control point cannot be moved, but can
1264 if (allowInsertRemove)
1265 insertElbowUpdate(u, dcp, nextToMoved, dcpStart, position, directedDirection);
1273 private static void updateDualDirectedPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
1274 if (LOGGER.isTraceEnabled())
1275 LOGGER.trace("PipingRules.updateDualDirectedPipeRun() " + u + " " + lengthChange);
1277 PipeControlPoint dcp1 = u.start;
1278 PipeControlPoint dcp2 = u.end;
1279 Point3d position1 = new Point3d(u.startPoint);
1280 Point3d position2 = new Point3d(u.endPoint);
1282 Vector3d dir = u.start.getDirection(Direction.NEXT), offset = new Vector3d();
1283 calculateDirectedOffset(new Vector3d(position1), new Vector3d(position2), u.start, u.list, u.end, dir, offset);
1285 Point3d position1offset = new Point3d(position1);
1286 position1offset.add(offset);
1287 Point3d position2offset = new Point3d(position2);
1288 position2offset.sub(offset);
1289 Vector3d dir1 = direction(dcp1, Direction.NEXT);
1290 Vector3d dir2 = direction(dcp2, Direction.PREVIOUS);
1292 Vector3d p1 = MathTools.closestPointOnStraight(position1, position2offset, dir2);
1293 Vector3d p2 = MathTools.closestPointOnStraight(position2, position1offset, dir1);
1294 double d1 = position1.distance(new Point3d(p1));
1295 double d2 = position2.distance(new Point3d(p2));
1297 boolean aligned = (d1 < ALLOWED_OFFSET && d2 < ALLOWED_OFFSET);
1303 } else if (allowInsertRemove){
1304 PipeControlPoint dcp;
1305 PipeControlPoint next;
1308 if (u.list.size() > 0)
1309 next = u.list.get(0);
1314 if (u.list.size() > 0)
1315 next = u.list.get(u.list.size() - 1);
1320 p1 = dcp.getWorldPosition();
1321 Vector3d v = new Vector3d();
1327 // Reserve space for 90 deg elbow
1328 double off = dcp1.getPipeRun().getTurnRadius();
1333 p2 = MathTools.closestPointOnStraight(new Point3d(p1), position2offset, dir2);
1335 p2 = MathTools.closestPointOnStraight(new Point3d(p1), position1offset, dir1);
1337 // By default, the elbows are placed next to each other, by using 90 deg angles.
1338 // If the distance between elbows is not enough, we must move the other elbow (and create more shallow angle elbows)
1339 if (MathTools.distance(p1, p2) < off*2.05) {
1343 PipeControlPoint tcp1 = insertElbow(dcp, next, p1);
1344 PipeControlPoint tcp2 = insertElbow(tcp1, next, p2);
1346 if (LOGGER.isTraceEnabled())
1347 LOGGER.trace("PipingRules.updateDualDirectedPipeRun() created two turns " + tcp1 + " " + tcp2);
1350 Vector3d dd = new Vector3d(p2);
1353 updatePathLegNext(u.start, u.updated, PathLegUpdateType.NONE);
1354 updatePathLegNext(tcp1, u.updated, PathLegUpdateType.NONE);
1356 updatePathLegNext(tcp2, u.updated, PathLegUpdateType.NONE);
1358 updatePathLegPrev(tcp2, u.updated, PathLegUpdateType.NONE);
1360 Vector3d dd = new Vector3d(p1);
1363 updatePathLegNext(tcp1, u.updated, PathLegUpdateType.NONE);
1364 updatePathLegNext(tcp2, u.updated, PathLegUpdateType.NONE);
1366 updatePathLegNext(u.start, u.updated, PathLegUpdateType.NONE);
1368 updatePathLegPrev(u.start, u.updated, PathLegUpdateType.NONE);
1377 private static double spaceForTurn(PipeControlPoint tcp, PipeControlPoint dcp) {
1378 // TODO : if the path legs contain offset, using just positions of opposite path leg ends is not enough.
1379 // TODO : current iterative way for calculating required space may return longer length that is required.
1380 double tr = ((TurnComponent)tcp.getPipelineComponent()).getTurnRadius();
1382 return tr; // space for 90 deg
1383 PipeControlPoint ne = tcp.findNextEnd();
1384 PipeControlPoint pe = tcp.findPreviousEnd();
1385 PipeControlPoint other = null;
1391 return tr; // space for 90 deg
1393 return tr; // space for 90 deg
1394 Vector3d dir = dcp.getDirectedControlPointDirection();
1397 dir2 = pathLegDirection(tcp);
1399 dir2 = pathLegDirection(pe);
1403 double d = dir.dot(dir2);
1405 return 0.0; // point following turn is directly in the front of the nozzle.
1406 else if (d < -0.9999)
1407 return tr*2.0; // point following turn is directly behind the nozzle, in theory, we should return Double.Inf...
1411 Vector3d tp0 = tcp.getPosition();
1413 Vector3d dp = dcp.getWorldPosition();
1415 Vector3d tp = new Vector3d(dir);
1416 tp.scaleAdd(curr, dp);
1417 tcp._setPosition(tp); // no firing of listeners here
1419 dir2 = pathLegDirection(tcp);
1421 dir2 = pathLegDirection(pe);
1425 double a = dir.angle(dir2);
1427 // other is directly between dcp and tcp, a zero angle turn should do
1428 if (Math.PI - a <= MathTools.NEAR_ZERO)
1431 double R = tr * Math.tan(a * 0.5);
1439 tcp._setPosition(tp0); // return the original value
1444 private static void insertElbowUpdate(UpdateStruct2 u, PipeControlPoint dcp, PipeControlPoint next, boolean dcpStart, Vector3d position, Vector3d directedDirection) throws Exception{
1447 // Vector3d closest = new Vector3d(position);
1448 // closest.add(directedDirection);
1450 PipeControlPoint tcp = null;
1451 Vector3d closest = new Vector3d(directedDirection);
1452 closest.scaleAdd(dcp.getPipeRun().getTurnRadius(), position);
1454 tcp = insertElbow(dcp, next, closest);
1456 tcp = insertElbow(next, dcp, closest);
1459 double s = spaceForTurn(tcp,dcp);
1460 Vector3d p = new Vector3d(directedDirection);
1461 p.scaleAdd(s, position);
1465 if (LOGGER.isTraceEnabled())
1466 LOGGER.trace("PipingRules.updateDirectedPipeRun() inserted " + tcp);
1469 // update pipe run from new turn to other end
1470 ppNoDir(tcp, new Vector3d(closest), u.list, u.end, u.endPoint, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated);
1471 // update pipe run from directed to new turn
1472 processPathLeg(new UpdateStruct2(u.start, u.startPoint, new ArrayList<PipeControlPoint>(), tcp, new Vector3d(closest), directedDirection, new Vector3d(), false, 0, false, new ArrayList<ExpandIterInfo>(), u.updated));
1474 // update pipe run from other end to new turn
1475 ppNoDir(u.start, u.startPoint, u.list, tcp, new Vector3d(closest), u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated);
1476 // update pipe run from new turn to directed
1477 processPathLeg(new UpdateStruct2(tcp, new Vector3d(closest), new ArrayList<PipeControlPoint>(), u.end, u.endPoint, directedDirection, new Vector3d(), false, 0, false, new ArrayList<ExpandIterInfo>(), u.updated));
1482 * Checks if turns can be removed (turn angle near zero)
1484 private static int checkTurns(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
1485 if (LOGGER.isTraceEnabled())
1486 LOGGER.trace("PipingRules.checkTurns() " + u.start + " " + u.end);
1487 boolean startRemoved = false;
1488 boolean endRemoved = false;
1489 if (u.start.isVariableAngle()) {
1490 // this won't work properly if inline control points are not updated
1491 PipeControlPoint startPrev = u.start.getPrevious();
1492 if (startPrev != null) {
1493 double a = updateTurnControlPointTurn(u.start, null, u.dir);
1494 if (a < MIN_TURN_ANGLE && u.start.isDeletable())
1495 startRemoved = true;
1496 else if (lengthChange == PathLegUpdateType.PREV || lengthChange == PathLegUpdateType.PREV_S) {
1497 PathLegUpdateType type;
1498 if (lengthChange == PathLegUpdateType.PREV_S)
1499 type = PathLegUpdateType.PREV;
1501 type = PathLegUpdateType.NONE;
1502 updatePathLegPrev(u.start, u.start, type);
1506 if (u.end.isVariableAngle()) {
1508 PipeControlPoint endNext = u.end.getNext();
1509 if (endNext != null) {
1510 // TODO: u.end, u.dir, null
1511 double a = updateTurnControlPointTurn(u.end, null, null);
1512 if (a < MIN_TURN_ANGLE && u.end.isDeletable())
1514 else if (lengthChange == PathLegUpdateType.NEXT || lengthChange == PathLegUpdateType.NEXT_S) {
1515 PathLegUpdateType type;
1516 if (lengthChange == PathLegUpdateType.NEXT_S)
1517 type = PathLegUpdateType.NEXT;
1519 type = PathLegUpdateType.NONE;
1520 updatePathLegNext(u.end, u.end, type);
1524 if (LOGGER.isTraceEnabled())
1525 LOGGER.trace("PipingRules.checkTurns() res " + startRemoved + " " + endRemoved);
1526 if (!startRemoved && !endRemoved)
1528 if (startRemoved && endRemoved)
1531 return REMOVE_START;
1536 * Expands piperun search over turns that are going to be removed
1539 private static void expandPathLeg(UpdateStruct2 u, int type) throws Exception {
1540 if (LOGGER.isTraceEnabled())
1541 LOGGER.trace("PipingRules.expandPipeline " + u.start + " " + u.end);
1542 ArrayList<PipeControlPoint> newList = new ArrayList<PipeControlPoint>();
1545 throw new RuntimeException("Error in piping rules");
1547 u.toRemove.add(new ExpandIterInfo(u.start, REMOVE_START));
1548 u.start = u.start.findPreviousEnd();
1549 u.startPoint = u.start.getPosition();
1550 u.start.findNextEnd(newList);
1551 newList.addAll(u.list);
1555 u.toRemove.add(new ExpandIterInfo(u.end, REMOVE_END));
1556 u.end = u.end.findNextEnd(newList);
1557 u.endPoint = u.end.getPosition();
1558 u.list.addAll(newList);
1561 u.toRemove.add(new ExpandIterInfo(u.start, u.end));
1562 u.start = u.start.findPreviousEnd();
1563 u.startPoint = u.start.getPosition();
1564 u.start.findNextEnd(newList);
1565 newList.addAll(u.list);
1567 newList = new ArrayList<PipeControlPoint>();
1568 u.end = u.end.findNextEnd(newList);
1569 u.endPoint = u.end.getPosition();
1570 u.list.addAll(newList);
1573 throw new RuntimeException("Error in piping rules");
1576 u.offset = new Vector3d();
1579 for (PipeControlPoint icp : u.list) {
1580 if (icp.isOffset()) {
1581 u.offset.add(icp.getSizeChangeOffsetVector(u.dir));
1582 } else if (icp.isDualSub())
1583 ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
1586 if (LOGGER.isTraceEnabled())
1587 LOGGER.trace("PipingRules.expandPipeline expanded " + u.start + " " + u.end);
1589 updatePathLeg(u, PathLegUpdateType.NONE);
1593 * reverts one iteration of turn removing back)
1595 private static void backIter(UpdateStruct2 u) throws Exception {
1597 if (LOGGER.isTraceEnabled())
1598 LOGGER.trace("PipingRules.backIter" + u.start + " " + u.end);
1600 throw new RuntimeException("Error in piping rules");
1601 ExpandIterInfo info = u.toRemove.get(u.toRemove.size() - 1);
1602 u.toRemove.remove(u.toRemove.size() - 1);
1603 if (info.getType() == REMOVE_START || info.getType() == REMOVE_BOTH) {
1604 while (u.list.size() > 0) {
1605 PipeControlPoint icp = u.list.get(0);
1606 if (icp.getPrevious().equals(info.getStart()))
1610 u.start = info.getStart();
1612 if (info.getType() == REMOVE_END || info.getType() == REMOVE_BOTH) {
1613 while (u.list.size() > 0) {
1614 PipeControlPoint icp = u.list.get(u.list.size() - 1);
1615 if (icp.getNext().equals(info.getEnd()))
1619 u.end = info.getEnd();
1621 u.offset = new Vector3d();
1624 for (PipeControlPoint icp : u.list) {
1625 if (icp.isOffset()) {
1626 u.offset.add(icp.getSizeChangeOffsetVector(u.dir));
1627 } else if (icp.isDualSub())
1628 ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
1636 * Processes pipe run (removes necessary turns and updates run ends)
1638 // private static void processPathLeg(PipeControlPoint start, Point3d
1639 // startPoint,ArrayList<InlineControlPoint> list, PipeControlPoint
1640 // end,Point3d endPoint, Vector3d dir,Vector3d offset, boolean
1641 // hasOffsets,int iter, boolean reversed, ArrayList<ExpandIterInfo>
1642 // toRemove) throws TransactionException {
1644 private static void processPathLeg(UpdateStruct2 u) throws Exception {
1645 if (LOGGER.isTraceEnabled())
1646 LOGGER.trace("PipingRules.processPathLeg " + u.start + " " + u.end);
1647 processPathLeg(u, true, true);
1650 private static void processPathLeg(UpdateStruct2 u, boolean updateEnds, boolean updateInline) throws Exception {
1651 if (LOGGER.isTraceEnabled())
1652 LOGGER.trace("PipingRules.processPathLeg " + (updateEnds ? "ends " : "") + (updateInline ? "inline " : "") + u.start + " " + u.end);
1654 if (u.toRemove.size() > 0) {
1655 for (ExpandIterInfo info : u.toRemove) {
1656 if (info.getStart() != null) {
1657 if (LOGGER.isTraceEnabled())
1658 LOGGER.trace("PipingRules.processPathLeg removing start " + info.getStart());
1659 info.getStart()._remove();
1661 if (info.getEnd() != null) {
1662 if (LOGGER.isTraceEnabled())
1663 LOGGER.trace("PipingRules.processPathLeg removing end " + info.getEnd());
1664 info.getEnd()._remove();
1667 // ControlPointTools.removeControlPoint may remove more than one CP;
1668 // we must populate inline CP list again.
1670 u.start.findNextEnd( u.list);
1672 // FIXME : inline CPs are update twice because their positions must be
1673 // updated before and after ends.
1674 updateInlineControlPoints(u, false);
1677 if (u.start.isTurn()) {
1678 //updateTurnControlPointTurn(u.start, u.start.getPrevious(), u.start.getNext());
1679 updateTurnControlPointTurn(u.start, null, null);
1680 // updatePathLegPrev(u.start, u.start, PathLegUpdateType.NONE);
1681 } else if (u.start.isEnd()) {
1682 updateEndComponentControlPoint(u.start, u.dir);
1683 } else if (u.start.isInline()) {
1684 u.start.orientToDirection(u.dir);
1686 if (u.end.isTurn()) {
1687 //updateTurnControlPointTurn(u.end, u.end.getPrevious(), u.end.getNext());
1688 updateTurnControlPointTurn(u.end, null, null);
1689 // updatePathLegNext(u.end, u.end, PathLegUpdateType.NONE);
1690 } else if (u.end.isEnd()) {
1691 updateEndComponentControlPoint(u.end, u.dir);
1692 } else if (u.end.isInline()) {
1693 u.end.orientToDirection(u.dir);
1697 if (u.start.isEnd()) {
1698 updateEndComponentControlPoint(u.start, u.dir);
1700 if (u.end.isEnd()) {
1701 updateEndComponentControlPoint(u.end, u.dir);
1705 updateInlineControlPoints(u, true);
1710 * Processes pipe run and recalculates offset
1712 // private static void processPathLeg(PipeControlPoint start, Point3d
1713 // startPoint,ArrayList<InlineControlPoint> list, PipeControlPoint
1714 // end,Point3d endPoint, Vector3d dir, boolean hasOffsets,int iter, boolean
1715 // reversed, ArrayList<ExpandIterInfo> toRemove) throws TransactionException
1717 @SuppressWarnings("unused")
1718 private static void processPathLegNoOffset(UpdateStruct2 u) throws Exception {
1719 if (LOGGER.isTraceEnabled())
1720 LOGGER.trace("PipingRules.processPathLeg " + u.start + " " + u.end);
1721 Vector3d offset = new Vector3d();
1724 for (PipeControlPoint icp : u.list) {
1725 if (icp.isOffset()) {
1726 offset.add(icp.getSizeChangeOffsetVector(u.dir));
1727 } else if (icp.isDualSub()) {
1728 ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
1735 private static void updateOffsetPoint(PipeControlPoint sccp, Vector3d offset) {
1736 Vector3d world = sccp.getWorldPosition();
1738 PipeControlPoint ocp = sccp.getDualSub();
1739 ocp.setWorldPosition(world);
1743 * Updates InlineControlPoints position when straight pipe's end(s) have
1751 private static void updateInlineControlPoint(PipeControlPoint icp, Vector3d prev, Vector3d next, Vector3d dir) {
1752 if (LOGGER.isTraceEnabled())
1753 LOGGER.trace("PipingRules.updateInlineControlPoint() " + icp);
1755 Vector3d inlinePoint = icp.getWorldPosition();
1756 Vector3d prevPoint = new Vector3d(prev);
1757 Vector3d nextPoint = new Vector3d(next);
1758 if (!icp.isVariableLength()) {
1759 // Reserve space for fixed length components.
1760 MathTools.mad(prevPoint, dir, icp.getInlineLength());
1761 MathTools.mad(nextPoint, dir, -icp.getInlineLength());
1762 if (MathTools.distance(prevPoint, nextPoint) < ALLOWED_OFFSET) {
1767 boolean canCalc = MathTools.distance(prevPoint, nextPoint) > ALLOWED_OFFSET;
1768 if (LOGGER.isTraceEnabled())
1769 System.out.print("InlineControlPoint update " + icp + " " + inlinePoint + " " + prevPoint + " " + nextPoint);
1770 Vector3d newInlinePoint = null;
1772 boolean branchUpdate = false;
1773 PipeControlPoint becp = null;
1774 for (PipeControlPoint pcp : icp.getChildPoints())
1775 if (pcp.isNonDirected()) {
1776 branchUpdate = true;
1781 if (DUMMY || !branchUpdate) {
1782 newInlinePoint = MathTools.closestPointOnEdge(new Vector3d(inlinePoint), prevPoint, nextPoint);
1786 // FIXME : can only handle one branch
1787 PipeControlPoint p = null;
1788 if (becp.getNext() != null) {
1789 p = becp.findNextEnd();
1790 } else if (becp.getPrevious() != null) {
1791 p = becp.findPreviousEnd();
1794 newInlinePoint = MathTools.closestPointOnEdge(new Vector3d(inlinePoint), prevPoint, nextPoint);
1795 } else if (canCalc){
1796 Vector3d branchLegEnd = p.getWorldPosition();
1797 Vector3d dir2 = new Vector3d(inlinePoint);
1798 dir2.sub(branchLegEnd);
1799 Vector3d dir1 = new Vector3d(nextPoint);
1800 dir1.sub(prevPoint);
1801 newInlinePoint = new Vector3d();
1802 double mu[] = new double[2];
1803 MathTools.intersectStraightStraight(new Vector3d(prevPoint), dir1, new Vector3d(branchLegEnd), dir2, newInlinePoint, new Vector3d(), mu);
1804 if (LOGGER.isTraceEnabled())
1805 LOGGER.trace(Double.toString(mu[0]));
1806 // FIXME : reserve space
1808 newInlinePoint = new Vector3d(prevPoint);
1809 } else if (mu[0] > 1.0) {
1810 newInlinePoint = new Vector3d(nextPoint);
1815 // prevPoint == nextPoint
1816 newInlinePoint = new Vector3d(prevPoint);
1818 if (LOGGER.isTraceEnabled())
1819 LOGGER.trace(" " + newInlinePoint);
1821 icp.setWorldPosition(newInlinePoint);
1822 icp.orientToDirection(dir);
1826 * Updates InlineControlPoints position when straight pipe's end(s) have
1834 private static void updateEndComponentControlPoint(PipeControlPoint ecp, Vector3d dir) throws Exception {
1835 if (LOGGER.isTraceEnabled())
1836 LOGGER.trace("PipingRules.updateEndComponentControlPoint() " + ecp);
1838 if (!ecp.isFixed()) // prevent overriding nozzle orientations..
1839 ecp.orientToDirection(dir);
1841 for (PipeControlPoint pcp : ecp.getChildPoints()) {
1842 // TODO update position
1843 updatePathLegEndControlPoint(pcp);
1848 * Updates all branches when branch's position has been changed
1852 private static void updateBranchControlPointBranches(PipeControlPoint bcp) throws Exception {
1853 if (LOGGER.isTraceEnabled())
1854 LOGGER.trace("PipingRules.updateBranchControlPointBranches() " + bcp);
1855 if (bcp.isDualInline())
1857 Collection<PipeControlPoint> branches = bcp.getChildPoints();
1858 if (branches.size() == 0) {
1859 if (LOGGER.isTraceEnabled())
1860 LOGGER.trace("No Branches found");
1864 for (PipeControlPoint pcp : branches) {
1865 updatePathLegEndControlPoint(pcp);
1869 private static double updateTurnControlPointTurn(PipeControlPoint tcp, Vector3d prev, Vector3d next) {
1871 UpdateStruct2 us = createUS(tcp, Direction.NEXT, 0, new ArrayList<PipingRules.ExpandIterInfo>(), tcp);
1876 UpdateStruct2 us = createUS(tcp, Direction.PREVIOUS, 0, new ArrayList<PipingRules.ExpandIterInfo>(), tcp);
1882 if (!tcp.asFixedAngle()) {
1885 if (next == null || prev == null) {
1886 if (tcp.getTurnAngle() != null)
1887 return tcp.getTurnAngle();
1888 return Math.PI; // FIXME : argh
1891 final boolean isDegenerate = prev.lengthSquared() < MathTools.NEAR_ZERO || next.lengthSquared() < MathTools.NEAR_ZERO;
1892 double turnAngle = isDegenerate ? 0.0 : prev.angle(next);
1894 Vector3d turnAxis = new Vector3d();
1895 turnAxis.cross(prev, next);
1896 if (turnAxis.lengthSquared() > MathTools.NEAR_ZERO) {
1897 double elbowRadius = ((TurnComponent)tcp.getPipelineComponent()).getTurnRadius();
1898 double R = elbowRadius * Math.tan(turnAngle * 0.5);
1900 turnAxis.normalize();
1901 tcp.setTurnAngle(turnAngle);
1902 tcp.setLength(R);// setComponentOffsetValue(R);
1903 tcp.setTurnAxis(turnAxis);
1904 // tcp.setPosition(tcp.getPosition());
1907 tcp.setTurnAngle(0.0);
1909 tcp.setTurnAxis(new Vector3d(MathTools.Y_AXIS));
1912 tcp.orientToDirection(prev);
1914 if (LOGGER.isTraceEnabled())
1915 LOGGER.trace("PipingTools.updateTurnControlPointTurn " + prev + " " + next + " " + turnAngle + " " + turnAxis);
1919 if (prev != null && next != null) {
1921 } else if (prev == null) {
1922 if (!tcp._getReversed())
1923 tcp.setReversed(true);
1924 } else if (next == null) {
1925 if (tcp._getReversed())
1926 tcp.setReversed(false);
1929 Vector3d dir = null;
1930 if (!tcp._getReversed()) {
1937 return Math.PI; // FIXME : argh
1940 Quat4d q = tcp.getControlPointOrientationQuat(dir, tcp.getRotationAngle() != null ? tcp.getRotationAngle() : 0.0);
1941 Vector3d v = new Vector3d();
1942 MathTools.rotate(q, MathTools.Y_AXIS,v);
1944 tcp.setWorldOrientation(q);
1945 if (tcp.getTurnAngle() != null)
1946 return tcp.getTurnAngle();
1947 return Math.PI; // FIXME : argh
1953 public static List<PipeControlPoint> getControlPoints(PipeRun pipeRun) {
1954 List<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
1955 if (pipeRun.getControlPoints().size() == 0)
1957 PipeControlPoint pcp = pipeRun.getControlPoints().iterator().next();
1958 while (pcp.getPrevious() != null) {
1959 PipeControlPoint prev = pcp.getPrevious();
1960 if (prev.getPipeRun() != pipeRun && prev.getPipeRun() != null) { // bypass possible corruption
1965 if (pcp.isDualSub()) {
1966 pcp = pcp.getParentPoint();
1969 while (pcp.getNext() != null) {
1970 pcp = pcp.getNext();
1971 if (pcp.getPipeRun() != pipeRun)
1978 public static void reverse(PipeRun pipeRun) {
1981 List<PipeControlPoint> points = getControlPoints(pipeRun);
1982 PipeControlPoint pcp = points.get(0);
1983 if (pcp.isSizeChange() && pcp.getChildPoints().size() > 0) {
1984 PipeRun pr = pcp.getPipeRun();
1992 List<PipeRun> all = new ArrayList<PipeRun>();
1993 List<List<PipeControlPoint>> pcps = new ArrayList<List<PipeControlPoint>>();
1996 List<PipeControlPoint> points = getControlPoints(pipeRun);
1998 PipeControlPoint pcp = points.get(points.size()-1);
1999 if (pcp.getChildPoints().size() > 0) {
2000 PipeRun pipeRun2 = pcp.getChildPoints().get(0).getPipeRun();
2001 if (pipeRun == pipeRun2)
2009 for (int i = 0 ; i < all.size(); i++) {
2010 List<PipeControlPoint> list = pcps.get(i);
2013 for (int i = 0 ; i < all.size(); i++) {
2014 boolean last = i == all.size() - 1;
2015 List<PipeControlPoint> list = pcps.get(i);
2018 List<PipeControlPoint> list2 = pcps.get(i+1);
2019 PipeControlPoint prev = list.get(list.size()-1);
2020 PipeControlPoint next = list2.get(0);
2022 // Reverse the component on the boundary.
2023 InlineComponent ic = (InlineComponent)prev.getPipelineComponent();
2024 PipeRun r1 = ic.getPipeRun();
2025 PipeRun r2 = ic.getAlternativePipeRun();
2026 if (r1 == null || r2 == null)
2027 throw new RuntimeException("Components on PipeRun changes should refer to bot PipeRuns");
2031 ic.setAlternativePipeRun(r1);
2033 throw new RuntimeException("PipeRun changes should contain shared control points");
2041 private static void _reverse(List<PipeControlPoint> list) {
2042 if (list.size() <= 1)
2043 return; // nothing to do.
2045 for (int i = 0 ; i < list.size(); i++) {
2046 boolean first = i == 0;
2047 boolean last = i == list.size() - 1;
2048 PipeControlPoint current = list.get(i);
2049 PipeControlPoint currentSub = null;
2050 if (current.isDualInline())
2051 currentSub = current.getDualSub();
2053 PipeControlPoint next = list.get(i+1);
2054 if (next.isDualInline())
2055 next = next.getDualSub();
2056 if (current.getNext() == next)
2057 current.setNext(null);
2058 current.setPrevious(next);
2059 if (currentSub != null) {
2060 if (currentSub.getNext() == next)
2061 currentSub.setNext(null);
2062 currentSub.setPrevious(next);
2065 PipeControlPoint prev = list.get(i-1);
2067 if (current.getPrevious() == prev)
2068 current.setPrevious(null);
2069 current.setNext(prev);
2071 if (currentSub != null) {
2072 if (currentSub.getPrevious() == prev)
2073 currentSub.setPrevious(null);
2074 currentSub.setNext(prev);
2077 PipeControlPoint prev = list.get(i-1);
2078 PipeControlPoint next = list.get(i+1);
2079 if (next.isDualInline())
2080 next = next.getDualSub();
2083 current.setPrevious(next);
2084 current.setNext(prev);
2086 if (currentSub != null) {
2087 currentSub.setPrevious(next);
2088 currentSub.setNext(prev);
2092 //if (current.isTurn() && current.isFixed()) {
2093 if (current.asFixedAngle()) {
2094 current.setReversed(!current._getReversed());
2096 if (current.isInline() && current.isReverse()) {
2097 current.setReversed(!current._getReversed());
2104 public static void validate(PipeRun pipeRun) {
2105 if (pipeRun == null)
2107 synchronized (ruleMutex) {
2108 Collection<PipeControlPoint> pcps = pipeRun.getControlPoints();
2110 //LOGGER.trace("Validate " + pipeRun.getName());
2111 for (PipeControlPoint pcp : pcps) {
2112 if (pcp.getParentPoint() == null || pcp.getParentPoint().getPipeRun() != pipeRun)
2115 List<PipeControlPoint> runPcps = getControlPoints(pipeRun);
2116 if (runPcps.size() != count) {
2117 LOGGER.debug("Run " + pipeRun.getName() + " contains unconnected control points, found " + runPcps.size() + " connected, " + pcps.size() + " total.");
2118 for (PipeControlPoint pcp : pcps) {
2119 if (!runPcps.contains(pcp)) {
2120 LOGGER.debug("Unconnected " + pcp + " " + pcp.getPipelineComponent());
2124 for (PipeControlPoint pcp : pcps) {
2125 if (pcp.getPipeRun() == null) {
2126 LOGGER.debug("PipeRun ref missing " + pcp + " " + pcp.getPipelineComponent());
2128 if (!pcp.isDirected() && pcp.getNext() == null && pcp.getPrevious() == null)
2129 LOGGER.debug("Orphan undirected " + pcp + " " + pcp.getPipelineComponent());
2131 for (PipeControlPoint pcp : pcps) {
2132 if (pcp.getParentPoint() == null) {
2133 PipeControlPoint sub = null;
2134 if (pcp.isDualInline())
2135 sub = pcp.getDualSub();
2136 PipeControlPoint next = pcp.getNext();
2137 PipeControlPoint prev = pcp.getPrevious();
2139 if (!(next.getPrevious() == pcp || next.getPrevious() == sub)) {
2140 LOGGER.debug("Inconsistency between " + pcp + " -> " +next );
2144 PipeControlPoint prevParent = null;
2145 if (prev.isDualSub()) {
2146 prevParent = prev.getParentPoint();
2147 } else if (prev.isDualInline()) {
2148 LOGGER.debug("Inconsistency between " + pcp + " <-- " +prev );
2150 if (!(prev.getNext() == pcp && (prevParent == null || prevParent.getNext() == pcp))) {
2151 LOGGER.debug("Inconsistency between " + pcp + " <-- " +prev );
2159 public static void splitVariableLengthComponent(PipelineComponent newComponent, InlineComponent splittingComponent, boolean assignPos) throws Exception{
2160 assert(!splittingComponent.getControlPoint().isFixedLength());
2161 assert(!(newComponent instanceof InlineComponent && !newComponent.getControlPoint().isFixedLength()));
2162 PipeControlPoint newCP = newComponent.getControlPoint();
2163 PipeControlPoint splittingCP = splittingComponent.getControlPoint();
2164 PipeControlPoint nextCP = splittingCP.getNext();
2165 PipeControlPoint prevCP = splittingCP.getPrevious();
2167 /* there are many different cases to insert new component when
2168 it splits existing VariableLengthinlineComponent.
2170 1. VariableLengthComponet is connected from both sides:
2171 - insert new component between VariableLength component and component connected to it
2172 - insert new VariableLengthComponent between inserted component and component selected in previous step
2174 2. VariableLengthComponent is connected from one side
2175 - Use previous case or:
2176 - Insert new component to empty end
2177 - Insert new VariableLength component to inserted components empty end
2179 3. VariableLength is not connected to any component.
2180 - Should not be possible, at least in current implementation.
2181 - Could be done using second case
2185 if (nextCP == null && prevCP == null) {
2186 // this should not be possible
2187 throw new RuntimeException("VariableLengthComponent " + splittingComponent + " is not connected to anything.");
2189 double newLength = newComponent.getControlPoint().getLength();
2192 Point3d next = new Point3d();
2193 Point3d prev = new Point3d();
2194 splittingCP.getInlineControlPointEnds(prev, next);
2196 Vector3d newPos = null;
2198 newPos = new Vector3d(prev);
2199 Vector3d dir = new Vector3d(next);
2203 newComponent.setWorldPosition(newPos);
2205 newPos = newComponent.getWorldPosition();
2210 Vector3d dir = new Vector3d(next);
2213 dir.scale(newLength * 0.5);
2214 Point3d vn = new Point3d(newPos);
2215 Point3d vp = new Point3d(newPos);
2218 double ln = vn.distance(next);
2219 double lp = vp.distance(prev);
2220 vp.interpolate(prev, 0.5);
2221 vn.interpolate(next, 0.5);
2224 if (nextCP == null) {
2225 newCP.insert(splittingCP, Direction.NEXT);
2226 insertStraight(newCP, Direction.NEXT, new Vector3d(vn), ln);
2227 splittingCP.setWorldPosition(new Vector3d(vp));
2228 // ControlPointTools.setWorldPosition(splittingCP, vp);
2229 // splittingCP.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, lp);
2230 } else if (prevCP == null) {
2231 newCP.insert(splittingCP, Direction.PREVIOUS);
2232 insertStraight(newCP, Direction.PREVIOUS, new Vector3d(vp), lp);
2233 splittingCP.setWorldPosition(new Vector3d(vn));
2234 // splittingCP.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, ln);
2236 newCP.insert(splittingCP, nextCP);
2237 insertStraight(newCP, nextCP, new Vector3d(vn), ln);
2238 splittingCP.setWorldPosition(new Vector3d(vp));
2239 // splittingCP.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, lp);
2241 positionUpdate(newCP);
2245 public static void addSizeChange(boolean reversed, PipeRun pipeRun, PipeRun other, InlineComponent reducer, PipeControlPoint previous, PipeControlPoint next) {
2246 PipeControlPoint pcp = reducer.getControlPoint();
2247 PipeControlPoint ocp = pcp.getDualSub();
2249 String name = pipeRun.getUniqueName("Reducer");
2250 reducer.setName(name);
2251 pipeRun.addChild(reducer);
2252 other.addChild(ocp);
2253 reducer.setAlternativePipeRun(other);
2255 previous.setNext(pcp);
2256 pcp.setPrevious(previous);
2257 ocp.setPrevious(previous);
2261 next.setPrevious(ocp);
2264 String name = other.getUniqueName("Reducer");
2265 reducer.setName(name);
2266 other.addChild(reducer);
2267 pipeRun.addChild(ocp);
2268 reducer.setAlternativePipeRun(pipeRun);
2272 pcp.setPrevious(next);
2273 ocp.setPrevious(next);
2275 pcp.setNext(previous);
2276 ocp.setNext(previous);
2277 previous.setPrevious(ocp);