X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.plant3d%2Fsrc%2Forg%2Fsimantics%2Fplant3d%2Fscenegraph%2Fcontrolpoint%2FPipingRules.java;h=e930b8d6d07094035a188933141c4519085c5382;hb=47edf45b081e0b06aa8839a39098edba737b162f;hp=688e50f9f6485824a6f51efb4abfcf86e85eaf80;hpb=3f17b6e42935927f12683fc26ecd5808bf66cde6;p=simantics%2F3d.git diff --git a/org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java b/org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java index 688e50f9..e930b8d6 100644 --- a/org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java +++ b/org.simantics.plant3d/src/org/simantics/plant3d/scenegraph/controlpoint/PipingRules.java @@ -16,6 +16,7 @@ import org.simantics.plant3d.scenegraph.PipelineComponent; import org.simantics.plant3d.scenegraph.TurnComponent; import org.simantics.plant3d.scenegraph.controlpoint.PipeControlPoint.Direction; import org.simantics.plant3d.utils.ComponentUtils; +import org.simantics.utils.datastructures.Pair; import org.simantics.utils.ui.ErrorLogger; public class PipingRules { @@ -24,7 +25,7 @@ public class PipingRules { private static double MIN_TURN_ANGLE = 0.001; // Threshold for removing turn components. private static double ALLOWED_OFFSET = 0.001; // Allowed offset for directed path legs - private static double MIN_INLINE_LENGTH = 0.001; // Minimum length of inline components, when component removal is not allowed. + private static double MIN_INLINE_LENGTH = 0.0005; // Minimum length of inline components, when component removal is not allowed. private static final int REMOVE_NONE = 0; private static final int REMOVE_START = 1; @@ -67,11 +68,12 @@ public class PipingRules { if (!PipingRules.enabled) return false; - if (requestUpdates.size() == 0) - return false; - - List temp = new ArrayList(requestUpdates.size()); - synchronized(updateMutex) { + List temp; + synchronized(updateMutex) { + if (requestUpdates.size() == 0) + return false; + + temp = new ArrayList(requestUpdates.size()); temp.addAll(requestUpdates); requestUpdates.clear(); } @@ -116,6 +118,8 @@ public class PipingRules { allowInsertRemove = allowIR; triedIR = false; validate(pcp.getPipeRun()); + if (pcp.getParentPoint() != null) + pcp = pcp.getParentPoint(); if (pcp.asPathLegEnd()) { updatePathLegEndControlPoint(pcp); // FIXME: Rules won't work properly, if they are not run twice. //updatePathLegEndControlPoint(pcp); @@ -135,8 +139,11 @@ public class PipingRules { public static void setEnabled(boolean enabled) { PipingRules.enabled = enabled; - if(!enabled) - currentUpdates.clear(); + if(!enabled) { + synchronized (ruleMutex) { + currentUpdates.clear(); + } + } } public static boolean isEnabled() { @@ -210,6 +217,17 @@ public class PipingRules { System.out.println("PipingRules.updateInlineControlPoint() " + pcp); PipeControlPoint start = pcp.findPreviousEnd(); updatePathLegNext(start, pcp, PathLegUpdateType.NONE); + + if (pcp.isOffset()) { + // Adjusting the rotation angle of an offset component may change variable angle turns + PipeControlPoint end = pcp.findNextEnd(); + if (end.isVariableAngle()) { + updatePathLegNext(end, end, PathLegUpdateType.NONE); + } + if (start.isVariableAngle()) { + updatePathLegPrev(start, start, PathLegUpdateType.NONE); + } + } } private static PipeControlPoint insertElbow(PipeControlPoint pcp1, PipeControlPoint pcp2, Vector3d pos) throws Exception{ @@ -274,6 +292,9 @@ public class PipingRules { scp.insert(pcp1, pcp2); scp.setWorldPosition(pos); + Vector3d dir = new Vector3d(); + dir.sub(pcp2.getWorldPosition(), pcp1.getWorldPosition()); + scp.orientToDirection(dir); scp.setLength(length); validate(scp.getPipeRun()); return scp; @@ -360,34 +381,66 @@ public class PipingRules { } - @SuppressWarnings("unused") - private static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, ArrayList list, Vector3d dir, Vector3d offset) { - boolean hasOffsets = false; - List offsets = new ArrayList(list.size()); - for (PipeControlPoint icp : list) { - if (icp.isOffset()) { - offsets.add(icp); - } else if (icp.isDualSub()) - ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!")); - } + /** + * Calculate offset based on a given fixed component direction. + * + * The desired component direction is provided as an input to this method, + * unlike the direction vector that is calculated by calculateOffset. + * + * The returned offset vector is always perpendicular to the given direction + * vector. + * + * @param startPoint Start point of leg + * @param endPoint End point of leg + * @param start Starting component of leg + * @param list Inline components between start and end + * @param end Ending component of leg + * @param dir Direction at which the offset is calculated + * @param offset A vector object to receive the offset vector values + * @return True if offsets are present + */ + public static boolean calculateDirectedOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList list, PipeControlPoint end, Vector3d dir, Vector3d offset) { + return calculateOffset(startPoint, endPoint, start, list, end, dir, offset, true); + } + + /** + * Calculate offset and direction vectors for a path leg so that the given chain + * of components starts and ends at the given coordinates + * + * The returned direction and offset vectors are perpendicular to each other. + * + * @param startPoint Start point of the leg + * @param endPoint End point of the leg + * @param start Starting component of the leg + * @param list Inline components between start and end + * @param end Ending component of the leg + * @param dir A vector object to receive the component direction vector + * @param offset A vector object to receive the offset vector + * @return True if offsets are present + */ + public static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList list, PipeControlPoint end, Vector3d dir, Vector3d offset) { + return calculateOffset(startPoint, endPoint, start, list, end, dir, offset, false); + } + + private static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, PipeControlPoint start, ArrayList list, PipeControlPoint end, Vector3d dir, Vector3d offset, boolean directed) { + List offsets = getOffsetPoints(start, list); if (offsets.size() == 0) { - dir.set(endPoint); - dir.sub(startPoint); - double l = dir.lengthSquared(); - if (l > MathTools.NEAR_ZERO) - dir.scale(1.0/Math.sqrt(l)); - offset.set(0.0, 0.0, 0.0); + setZeroOffset(startPoint, endPoint, dir, offset); return false; } else { Vector3d sp = new Vector3d(startPoint); Point3d ep = new Point3d(endPoint); - dir.set(ep); - dir.sub(sp); + if (!directed) { + dir.set(ep); + dir.sub(sp); + } + double l = dir.lengthSquared(); if (l > MathTools.NEAR_ZERO) dir.scale(1.0/Math.sqrt(l)); + int iter = 100; - while (iter >= 0) { + while (true) { iter--; offset.set(0.0, 0.0, 0.0); @@ -395,11 +448,16 @@ public class PipingRules { Vector3d v = icp.getSizeChangeOffsetVector(dir); offset.add(v); } + + if (directed) + break; + Point3d nep = new Point3d(endPoint); nep.sub(offset); - if (nep.distance(ep) < 0.0000000001) { + if (nep.distance(ep) < 0.0000000001 || iter <= 0) { break; - } + } + ep = nep; dir.set(ep); dir.sub(sp); @@ -407,14 +465,37 @@ public class PipingRules { if (l > MathTools.NEAR_ZERO) dir.scale(1.0/Math.sqrt(l)); } - hasOffsets = true; + + if (DEBUG) + System.out.println("calcOffset s:"+ startPoint + " e:" + endPoint + " d:" + dir + " o:"+offset) ; + + return true; } - - if (DEBUG && hasOffsets) - System.out.println("calcOffset s:"+ startPoint + " e:" + endPoint + " d:" + dir + " o:"+offset) ; - return hasOffsets; } + public static void setZeroOffset(Vector3d startPoint, Vector3d endPoint, Vector3d dir, Vector3d offset) { + dir.set(endPoint); + dir.sub(startPoint); + double l = dir.lengthSquared(); + if (l > MathTools.NEAR_ZERO) + dir.scale(1.0/Math.sqrt(l)); + offset.set(0.0, 0.0, 0.0); + } + + public static List getOffsetPoints(PipeControlPoint start, ArrayList list) { + List offsets = new ArrayList(list.size()); + // Only start offset affects the calculation + if (start.isOffset()) + offsets.add(start); + for (PipeControlPoint icp : list) { + if (icp.isOffset()) { + offsets.add(icp); + } else if (icp.isDualSub()) + ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!")); + } + return offsets; + } + private static UpdateStruct2 createUS(PipeControlPoint start, Direction direction, int iter, ArrayList toRemove, PipeControlPoint updated) { ArrayList list = new ArrayList(); PipeControlPoint end = null; @@ -440,10 +521,25 @@ public class PipingRules { Vector3d startPoint = start.getWorldPosition(); Vector3d endPoint = end.getWorldPosition(); Vector3d dir = new Vector3d(); - hasOffsets = calculateOffset(startPoint, endPoint, list, dir, offset); + hasOffsets = calculateOffset(startPoint, endPoint, start, list, end, dir, offset); return new UpdateStruct2(start, startPoint, list, end, endPoint, dir, offset, hasOffsets, iter, direction == Direction.PREVIOUS, toRemove, updated); } + private static Vector3d pathLegDirection(PipeControlPoint start) { + ArrayList list = new ArrayList(); + PipeControlPoint end = start.findNextEnd(list); + if (start == end) { + return start.getDirection(Direction.NEXT); + } + + Vector3d offset = new Vector3d(); + Vector3d startPoint = start.getWorldPosition(); + Vector3d endPoint = end.getWorldPosition(); + Vector3d dir = new Vector3d(); + calculateOffset(startPoint, endPoint, start, list, end, dir, offset); + return dir; + } + private static boolean asDirected(PipeControlPoint pcp, Direction direction) { if (pcp.isDirected()) return true; @@ -477,11 +573,11 @@ public class PipingRules { if (asDirected(u.end, Direction.PREVIOUS)) directed++; if (rs) - u.start.getPipelineComponent().setError(null); + setErrorForce(u.start, null); if (re) - u.end.getPipelineComponent().setError(null); + setErrorForce(u.end, null); for (PipeControlPoint pcp : u.list) - pcp.getPipelineComponent().setError(null); + setErrorForce(pcp, null); switch (directed) { case 0: updateFreePathLeg(u, lengthChange); @@ -520,149 +616,102 @@ public class PipingRules { } boolean recalcline = false; - if (!u.hasOffsets) { - - - for (PipeControlPoint icp : u.list) { - updateInlineControlPoint(icp, start, end, u.dir); - - if (icp.isOffset()) { - // TODO : offset vector is already calculated and should be cached - Vector3d off = icp.getSizeChangeOffsetVector(u.dir); - updateOffsetPoint(icp, off); - } - } - if (!checkSizes) - return; - ArrayList pathLegPoints = new ArrayList(); - ArrayList fixedLengthPoints = new ArrayList(); - pathLegPoints.add(u.start); - - for (PipeControlPoint icp : u.list) { - // updateInlineControlPoint(icp, u.startPoint, - // u.endPoint,u.dir); - updateBranchControlPointBranches(icp); - pathLegPoints.add(icp); - if (!icp.isVariableLength()) - fixedLengthPoints.add(icp); + Vector3d sp = new Vector3d(start); + Vector3d ep = new Vector3d(end); + ep.sub(u.offset); + + if (u.start.isOffset()) { + Vector3d offset = u.start.getSizeChangeOffsetVector(u.dir); + updateOffsetPoint(u.start, offset); + sp.add(offset); + ep.add(offset); + } + + for (PipeControlPoint icp : u.list) { + updateInlineControlPoint(icp, sp, ep, u.dir); + if (icp.isOffset()) { + // TODO : offset vector is already calculated and should be cached + Vector3d offset = icp.getSizeChangeOffsetVector(u.dir); + updateOffsetPoint(icp, offset); + sp.add(offset); + ep.add(offset); } - pathLegPoints.add(u.end); + } + + if (!checkSizes) + return; + + // Collect all path leg points for updating variable length components. This list will also contain leg ends (usually turns) + ArrayList pathLegPoints = new ArrayList<>(); + // Collect all fixed length components with their offsets. + ArrayList> fixedLengthPoints = new ArrayList<>(); + + pathLegPoints.add(u.start); + fixedLengthPoints.add(new Pair(u.start, new Vector3d())); + Vector3d off = new Vector3d(); + for (PipeControlPoint icp : u.list) { + pathLegPoints.add(icp); + updateBranchControlPointBranches(icp); + if (icp.isOffset()) { + fixedLengthPoints.add(new Pair(icp, new Vector3d(off))); + Vector3d offset = icp.getSizeChangeOffsetVector(u.dir); + off.add(offset); + } else if (!icp.isVariableLength()) { + fixedLengthPoints.add(new Pair(icp, new Vector3d(off))); + } + } + pathLegPoints.add(u.end); + fixedLengthPoints.add(new Pair(u.end, new Vector3d(off))); + + sp = new Vector3d(start); + ep = new Vector3d(end); + ep.sub(u.offset); + + updateFixedLengths(fixedLengthPoints, sp, ep, u.dir); + + for (int i = 0; i < pathLegPoints.size(); i++) { + PipeControlPoint icp = pathLegPoints.get(i); - // updateInlineControlPoint keeps components between path leg ends, but does not ensure that fixed length components do no overlap each other + PipeControlPoint prev = i > 0 ? pathLegPoints.get(i - 1) : null; + PipeControlPoint next = i < pathLegPoints.size() - 1 ? pathLegPoints.get(i + 1) : null; - for (int i = 0; i < fixedLengthPoints.size(); i++) { - PipeControlPoint prev = i == 0 ? null : fixedLengthPoints.get(i-1); - PipeControlPoint curr = fixedLengthPoints.get(i); - PipeControlPoint next = i == fixedLengthPoints.size() -1 ? null : fixedLengthPoints.get(i+1); - updateFixedLength(curr, prev, next, start,end, u.dir); - } - - for (int i = 0; i < pathLegPoints.size(); i++) { - PipeControlPoint icp = pathLegPoints.get(i); - - PipeControlPoint prev = i > 0 ? pathLegPoints.get(i - 1) : null; - PipeControlPoint next = i < pathLegPoints.size() - 1 ? pathLegPoints.get(i + 1) : null; - - if (icp.isVariableLength()) { - if (prev != null && next != null) { - - recalcline = recalcline | updateVariableLength(icp, prev, next); + if (prev != null && prev.isDualInline()) + prev = prev.getDualSub(); - } else { - // this is variable length component at the end of the - // piperun. - // the problem is that we want to keep unconnected end - // of the component in the same - // place, but center of the component must be moved. - updateVariableLengthEnd(icp, prev != null ? prev : next); - } - - - } else if (prev != null && !prev.isVariableLength()) { - // If this and previous control point are not variable - // length pcps, we'll have to check if there is no empty - // space between them. - // I there is, we'll have to create new variable length - // component between them. - recalcline = recalcline | possibleVaribleLengthInsert(icp, prev); - } - } - } else { // with offset - Vector3d sp = new Vector3d(start); - Vector3d ep = new Vector3d(end); - ep.sub(u.offset); - - ArrayList pathLegPoints = new ArrayList(); - pathLegPoints.add(u.start); + if (icp.isVariableLength()) { + if (prev != null && next != null) { + recalcline = recalcline | updateVariableLength(icp, prev, next); - for (PipeControlPoint icp : u.list) { - updateInlineControlPoint(icp, sp, ep, u.dir); - updateBranchControlPointBranches(icp); - pathLegPoints.add(icp); - if (icp.isOffset()) { - // TODO : offset vector is already calculated and should be - // cached - Vector3d offset = icp.getSizeChangeOffsetVector(u.dir); - updateOffsetPoint(icp, offset); - sp.add(offset); - ep.add(offset); + } else { + // this is variable length component at the end of the piperun. + // the problem is that we want to keep unconnected end of the component in the same + // place, but center of the component must be moved. + updateVariableLengthEnd(icp, prev != null ? prev : next); } + } else if (prev != null && !prev.isVariableLength()) { + // If this and previous control point are not variable length pcps, + // we'll have to check if there is no empty space between them. + // I there is, we'll have to create new variable length component between them. + recalcline = recalcline | possibleVaribleLengthInsert(icp, prev); } - pathLegPoints.add(u.end); - - if (!checkSizes) - return; - - sp = new Vector3d(u.startPoint); - ep = new Vector3d(u.endPoint); - ep.sub(u.offset); - - for (int i = 0; i < pathLegPoints.size(); i++) { - PipeControlPoint icp = pathLegPoints.get(i); - - PipeControlPoint prev = i > 0 ? pathLegPoints.get(i - 1) : null; - PipeControlPoint next = i < pathLegPoints.size() - 1 ? pathLegPoints.get(i + 1) : null; - - if (prev != null && prev.isDualInline()) - prev = prev.getDualSub(); - - - if (icp.isVariableLength()) { - if (prev != null && next != null) { - recalcline = recalcline | updateVariableLength(icp, prev, next); - - } else { - // this is variable length component at the end of the - // piperun. - // the problem is that we want to keep unconnected end - // of the component in the same - // place, but center of the component must be moved. - updateVariableLengthEnd(icp, prev != null ? prev : next); - } - } else if (prev != null && !prev.isVariableLength()) { - // If this and previous control point are not variable - // length pcps, we'll have to check if there is no empty - // space between them. - // I there is, we'll have to create new variable length - // component between them. - recalcline = recalcline | possibleVaribleLengthInsert(icp, prev); - } - if (icp.isOffset()) { - // TODO : offset vector is already calculated and should be - // cached - Vector3d offset = icp.getSizeChangeOffsetVector(u.dir); - sp.add(offset); - ep.add(offset); - } + if (icp.isOffset()) { + // TODO : offset vector is already calculated and should be cached + Vector3d offset = icp.getSizeChangeOffsetVector(u.dir); + sp.add(offset); + ep.add(offset); } } + if (recalcline) { u.list.clear(); u.start.findNextEnd(u.list); } if (checkSizes) { - double pathLegLength = MathTools.distance(u.startPoint, u.endPoint); + sp = new Vector3d(u.startPoint); + ep = new Vector3d(u.endPoint); + ep.sub(u.offset); + double pathLegLength = MathTools.distance(sp, ep); double availableLength = pathLegLength; if (u.start.isTurn()) availableLength -= u.start.getInlineLength(); @@ -673,32 +722,189 @@ public class PipingRules { availableLength-= pcp.getLength(); } if (availableLength < 0.0) { - u.start.getPipelineComponent().setError("Not enough available space"); - u.end.getPipelineComponent().setError("Not enough available space"); + setError(u.start, "Not enough available space"); + setError(u.end, "Not enough available space"); for (PipeControlPoint pcp : u.list) - pcp.getPipelineComponent().setError("Not enough available space"); + setError(pcp, "Not enough available space"); } // System.out.println(u.start.getPipelineComponent().toString() + " " + pathLegLength + " " + availableLength + " " + u.end.getPipelineComponent().toString() + " " + u.start.getInlineLength() + " " + u.end.getInlineLength()); } } - private static void updateFixedLength(PipeControlPoint icp, PipeControlPoint prev, PipeControlPoint next, Vector3d s, Vector3d e, Vector3d dir) { + private enum Gap{ATTACHED,OVERLAP,SPACE}; + + private static class GapObj { + Gap gap; + double d; + + Pair pcp1; + Pair pcp2; + } + + private static void updateFixedLengths(List> fixedLengthPoints, Vector3d s, Vector3d e, Vector3d dir) { + double totalLength = MathTools.distance(s, e); + double reservedLength = 0.0; + List distances = new ArrayList<>(fixedLengthPoints.size()); + distances.add(0.0); + for (int i = 1; i < fixedLengthPoints.size()-1; i++) { + Pair pcp = fixedLengthPoints.get(i); + reservedLength += pcp.first.getLength(); + Vector3d p = pcp.first.getWorldPosition(); + p.sub(pcp.second); + double d= MathTools.distance(s, p); + distances.add(d); + } + distances.add(totalLength); + + if (totalLength >= reservedLength) { + // There is enough space for all fixed length components. + List gaps = new ArrayList<>(fixedLengthPoints.size()-1); + int overlaps = 0; + // Analyze gaps between components + for (int i = 0; i < fixedLengthPoints.size()-1; i++) { + Pair pcp1 = fixedLengthPoints.get(i); + Pair pcp2 = fixedLengthPoints.get(i+1); + double d1 = distances.get(i); + double d2 = distances.get(i+1); + double ld1 = i == 0 ? 0.0 :pcp1.first.getInlineLength(); + double ld2 = i == fixedLengthPoints.size()-2 ? 0.0 : pcp2.first.getInlineLength(); + + double e1 = d1 + ld1; // End of comp1 + double s2 = d2 - ld2; // Start of comp2 + double diff =s2 - e1; + GapObj obj = new GapObj(); + obj.pcp1 = pcp1; + obj.pcp2 = pcp2; + obj.d = diff; + if (diff < -MIN_INLINE_LENGTH) { + obj.gap = Gap.OVERLAP; + overlaps++; + } else if (diff > MIN_INLINE_LENGTH) { + obj.gap = Gap.SPACE; + } else { + obj.gap = Gap.ATTACHED; + } + gaps.add(obj); + } + // If there are no overlaps, there is nothing to do. + if (overlaps == 0) + return; + // Get rid of overlapping components by using closest available free spaces. + for (int i = 0; i < gaps.size(); i++) { + GapObj gapObj = gaps.get(i); + if (gapObj.gap != Gap.OVERLAP) + continue; + double curr = gapObj.d; + int d = 1; + while (d < gaps.size() && curr < -MIN_INLINE_LENGTH) { + GapObj next = i+d < gaps.size() ? gaps.get(i+d) : null; + GapObj prev = i-d >= 0 ? gaps.get(i-d) : null; + if (next != null && next.gap == Gap.SPACE) { + double move = Math.min(-curr, next.d); + curr+= move; + next.d -= move; + if (next.d < MIN_INLINE_LENGTH) + next.gap = Gap.ATTACHED; + Vector3d mv = new Vector3d(dir); + mv.normalize(); + mv.scale(move); + for (int j = i ; j < i+d; j++) { + Pair pcp = gaps.get(j).pcp2; + Vector3d p = new Vector3d(pcp.first.getWorldPosition()); + p.add(mv); + pcp.first.setWorldPosition(p); + } + } + else if (prev != null && prev.gap == Gap.SPACE) { + double move = Math.min(-curr, prev.d); + curr+= move; + prev.d -= move; + if (prev.d < MIN_INLINE_LENGTH) + prev.gap = Gap.ATTACHED; + Vector3d mv = new Vector3d(dir); + mv.normalize(); + mv.scale(-move); + for (int j = i ; j > i-d; j--) { + Pair pcp = gaps.get(j).pcp1; + Vector3d p = new Vector3d(pcp.first.getWorldPosition()); + p.add(mv); + pcp.first.setWorldPosition(p); + } + } + else { + d++; + } + } + } + } else { + for (int i = 1; i < fixedLengthPoints.size()-1; i++) { + Pair prev = i == 0 ? null : fixedLengthPoints.get(i-1); + Pair curr = fixedLengthPoints.get(i); + Pair next = i == fixedLengthPoints.size() -1 ? null : fixedLengthPoints.get(i+1); + updateFixedLength(curr, prev, next, s,e, dir); + } + } + } + + private static void updateFixedLength(Pair icp, Pair prev, Pair next, Vector3d s, Vector3d e, Vector3d dir) { if (prev != null) { - checkOverlap(icp, prev); + checkOverlap(prev, icp, dir,true); } if (next != null) - checkOverlap(icp, next); + checkOverlap(icp, next, dir,true); } - private static void checkOverlap(PipeControlPoint icp, PipeControlPoint prev) { - double d = MathTools.distance(prev.getWorldPosition(), icp.getWorldPosition()); - double r = icp.getInlineLength() + prev.getInlineLength(); - if (d < r) { - if (icp.getPipelineComponent().getError() == null) - icp.getPipelineComponent().setError("Overlapping"); - if (prev.getPipelineComponent().getError() == null) - prev.getPipelineComponent().setError("Overlapping"); + private static boolean checkOverlap(Pair icp, Pair icp2, Vector3d dir, boolean se) { + Vector3d p1 = icp.first.getWorldPosition(); + Vector3d p2 = icp2.first.getWorldPosition(); + p1.add(icp.second); + p2.add(icp2.second); + double u[] = new double[1]; + MathTools.closestPointOnStraight(p2, p1, dir, u); + if (u[0] < 0.0) { + p2.set(p1); + p2.sub(icp.second); + p2.add(icp2.second); + MathTools.mad(p2, dir, MIN_INLINE_LENGTH); + icp2.first.setWorldPosition(p2); + } + double d = MathTools.distance(p1, p2); + double r = icp.first.getInlineLength() + icp2.first.getInlineLength(); + + if ((d-r) < - MIN_INLINE_LENGTH) { + if (se) { + setError(icp.first, "Overlapping"); + setError(icp2.first, "Overlapping"); + } + return true; } + return false; + } + + /** + * Overrides current error of a component + * @param pcp + * @param error + */ + private static void setErrorForce(PipeControlPoint pcp, String error) { + PipelineComponent comp = pcp.getPipelineComponent(); + if (comp == null) + return; + comp.setError(error); + } + + /** + * Sets error for a component, if there is no existing error. + * @param pcp + * @param error + */ + private static void setError(PipeControlPoint pcp, String error) { + PipelineComponent comp = pcp.getPipelineComponent(); + if (comp == null) + return; + if (comp.getError() != null) + return; + comp.setError(error); } private static boolean updateVariableLength(PipeControlPoint icp, PipeControlPoint prev, PipeControlPoint next) { @@ -725,7 +931,7 @@ public class PipingRules { if (icp.isDeletable()) { if (!allowInsertRemove) { icp.setLength(MIN_INLINE_LENGTH); - icp.getPipelineComponent().setError("Not enough available space"); + setError(icp, "Not enough available space"); triedIR = true; return false; } @@ -751,7 +957,8 @@ public class PipingRules { double l2next = icp.getInlineLength(); double l2 = l2prev + l2next; double l2s = l2 * l2; - if (l > l2s) { + double diff = l - l2s; + if (diff >= MIN_INLINE_LENGTH) { if (allowInsertRemove) { dir.normalize(); double length = Math.sqrt(l) - l2; // true length of the variable length component @@ -773,7 +980,11 @@ public class PipingRules { Vector3d dir = new Vector3d(); dir.sub(currentPos, prevPos); - boolean simple = currentUpdates.contains(icp); + boolean simple; + synchronized (ruleMutex) { + simple = currentUpdates.contains(icp); + } + if (simple) { // Update based on position -> adjust length double currentLength = (dir.length() - prev.getInlineLength()) * 2.0; @@ -812,12 +1023,17 @@ public class PipingRules { } } - private static void ppNoOffset(UpdateStruct2 u) throws Exception { + /** + * Recalculates offset vector based on current direction, and calls checkExpandPathLeg + * @param u + * @param updateEnds + * @throws Exception + */ + private static void ppNoOffset(UpdateStruct2 u, boolean updateEnds) throws Exception { if (DEBUG) System.out.println("PipingRules.ppNoOffset() " + u); Vector3d offset = new Vector3d(); if (u.hasOffsets) { - u.dir.normalize(); for (PipeControlPoint icp : u.list) { if (icp.isOffset()) { offset.add(icp.getSizeChangeOffsetVector(u.dir)); @@ -826,7 +1042,7 @@ public class PipingRules { } } u.offset = offset; - checkExpandPathLeg(u, PathLegUpdateType.NONE); + checkExpandPathLeg(u, PathLegUpdateType.NONE, updateEnds); } private static void ppNoDir(PipeControlPoint start, Vector3d startPoint, ArrayList list, PipeControlPoint end, Vector3d endPoint, boolean hasOffsets, int iter, boolean reversed, ArrayList toRemove, PipeControlPoint updated) throws Exception { @@ -835,8 +1051,8 @@ public class PipingRules { // FIXME : extra loop (dir should be calculated here) Vector3d dir = new Vector3d(); Vector3d offset = new Vector3d(); - hasOffsets = calculateOffset(startPoint, endPoint, list, dir, offset); - ppNoOffset(new UpdateStruct2(start, startPoint, list, end, endPoint, dir, null, hasOffsets, iter, reversed, toRemove, updated)); + hasOffsets = calculateOffset(startPoint, endPoint, start, list, end, dir, offset); + ppNoOffset(new UpdateStruct2(start, startPoint, list, end, endPoint, dir, null, hasOffsets, iter, reversed, toRemove, updated),true); } private static void checkExpandPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception { @@ -876,7 +1092,7 @@ public class PipingRules { position = u.startPoint; dcpStart = true; if (!u.reversed) - canMoveOther = true; + canMoveOther = true; inlineEnd = u.end.isInline(); } else { @@ -884,10 +1100,10 @@ public class PipingRules { other = u.start; position = u.endPoint; if (u.reversed) - canMoveOther = true; + canMoveOther = true; inlineEnd = u.start.isInline(); } - + Vector3d directedDirection = direction(dcp, dcpStart ? Direction.NEXT : Direction.PREVIOUS); if (directedDirection == null) { //updateTurnControlPointTurn(dcp, dcp.getPrevious(), dcp.getNext()); @@ -897,30 +1113,48 @@ public class PipingRules { return; } } - Point3d directedEndPoint = new Point3d(u.endPoint); - if (u.hasOffsets) - directedEndPoint.add(u.offset); + + Point3d otherPosition = new Point3d(dcpStart ? u.endPoint : u.startPoint); + if (u.hasOffsets) { + Vector3d dir = dcp.getDirection(dcpStart ? Direction.NEXT : Direction.PREVIOUS); + if (!dcpStart) + dir.negate(); + + Vector3d offset = new Vector3d(); + calculateDirectedOffset(u.startPoint, u.endPoint, u.start, u.list, u.end, dir, offset); + u.dir = dir; + u.offset = offset; + + if (dcpStart) + otherPosition.sub(offset); + else + otherPosition.add(offset); + } double mu[] = new double[2]; Vector3d closest; Vector3d t = new Vector3d(); - if (dcpStart) { - closest = MathTools.closestPointOnStraight(directedEndPoint, u.startPoint, directedDirection, mu); - t.sub(closest, directedEndPoint); - } else { - closest = MathTools.closestPointOnStraight(u.startPoint, directedEndPoint, directedDirection, mu); - t.sub(closest, u.startPoint); - } + closest = MathTools.closestPointOnStraight(otherPosition, position, directedDirection, mu); + t.sub(closest, otherPosition); double distance = t.length(); boolean aligned = (distance < ALLOWED_OFFSET); + double requiredSpace = 0.0; + if (other.isVariableAngle()) { + requiredSpace = spaceForTurn(other, dcp); + } + if (mu[0] < requiredSpace) { + // At the moment, if next component is directly behind the nozzle, we must force moving the other component. + // Trying to solve the situation by adding new turn creates infinite loop... + aligned = false; + canMoveOther = true; + } if (aligned) { - if (u.start.isInline() || u.end.isInline() || u.start.asFixedAngle() || u.end.asFixedAngle()) - processPathLeg(u, true, false); - checkExpandPathLeg(u, lengthChange, inlineEnd); - + //if (u.start.isInline() || u.end.isInline() || u.start.asFixedAngle() || u.end.asFixedAngle()) + // processPathLeg(u, true, false); + checkExpandPathLeg(u, lengthChange, inlineEnd || u.start.isInline() || u.end.isInline() || u.start.asFixedAngle() || u.end.asFixedAngle()); } else { if (u.iter > 0) { backIter(u); @@ -939,28 +1173,40 @@ public class PipingRules { if (other.isVariableAngle()) { // TODO calculate needed space from next run end. - double space = spaceForTurn(other); - if (mu[0] < space) { + if (mu[0] < requiredSpace) { if (dcpStart) { closest.set(u.startPoint); } else { closest.set(u.endPoint); } Vector3d v = new Vector3d(directedDirection); - v.scale(space); + v.scale(requiredSpace); closest.add(v); } if (canMoveOther) { if (DEBUG) System.out.println("PipingRules.updateDirectedPipeRun() moved end " + other + " to " + closest); + + // Not aligned - we need to recalculate the offset to reflect new end points. + Vector3d offset; + if (u.hasOffsets) { + offset = new Vector3d(); + Vector3d newDir = new Vector3d(); + calculateDirectedOffset(position, closest, u.start, u.list, u.end, newDir, offset); + closest.add(offset); + } else { + offset = new Vector3d(); + } + other.setWorldPosition(closest); + if (dcpStart) { - ppNoOffset(new UpdateStruct2(u.start, u.startPoint, u.list, u.end, new Vector3d(closest), directedDirection, null, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated)); + 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); if (u.end.getNext() != null) updatePathLegNext(u.end, u.updated, PathLegUpdateType.NEXT); } else { - ppNoOffset(new UpdateStruct2(u.start, new Vector3d(closest), u.list, u.end, u.endPoint, directedDirection, null, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated)); + 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); if (u.start.getPrevious() != null) updatePathLegPrev(u.start, u.updated, PathLegUpdateType.PREV); } @@ -968,7 +1214,6 @@ public class PipingRules { // TODO : calculate needed space from next run end. if (allowInsertRemove) insertElbowUpdate(u, dcp, nextToMoved, dcpStart, position, directedDirection); - else triedIR = true; } @@ -1020,8 +1265,6 @@ public class PipingRules { } } } - - } private static void updateDualDirectedPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception { @@ -1032,14 +1275,19 @@ public class PipingRules { PipeControlPoint dcp2 = u.end; Point3d position1 = new Point3d(u.startPoint); Point3d position2 = new Point3d(u.endPoint); + + Vector3d dir = u.start.getDirection(Direction.NEXT), offset = new Vector3d(); + calculateDirectedOffset(new Vector3d(position1), new Vector3d(position2), u.start, u.list, u.end, dir, offset); + Point3d position1offset = new Point3d(position1); - position1offset.sub(u.offset); + position1offset.add(offset); Point3d position2offset = new Point3d(position2); - position2offset.add(u.offset); + position2offset.sub(offset); Vector3d dir1 = direction(dcp1, Direction.NEXT); Vector3d dir2 = direction(dcp2, Direction.PREVIOUS); - Vector3d p1 = MathTools.closestPointOnStraight(position1offset, position2, dir2); - Vector3d p2 = MathTools.closestPointOnStraight(position2offset, position1, dir1); + + Vector3d p1 = MathTools.closestPointOnStraight(position1, position2offset, dir2); + Vector3d p2 = MathTools.closestPointOnStraight(position2, position1offset, dir1); double d1 = position1.distance(new Point3d(p1)); double d2 = position2.distance(new Point3d(p2)); @@ -1079,9 +1327,9 @@ public class PipingRules { p1.add(v); if (!u.reversed) - p2 = MathTools.closestPointOnStraight(new Point3d(p1), position2, dir2); + p2 = MathTools.closestPointOnStraight(new Point3d(p1), position2offset, dir2); else - p2 = MathTools.closestPointOnStraight(new Point3d(p1), position1, dir1); + p2 = MathTools.closestPointOnStraight(new Point3d(p1), position1offset, dir1); // By default, the elbows are placed next to each other, by using 90 deg angles. // If the distance between elbows is not enough, we must move the other elbow (and create more shallow angle elbows) @@ -1123,11 +1371,71 @@ public class PipingRules { } - private static double spaceForTurn(PipeControlPoint tcp) { - // TODO : this returns now space for 90 deg turn. - // The challenge: position of tcp affects the turn angle, which then affects the required space. Perhaps we need to iterate... - // Additionally, if the path legs contain offset, using just positions of opposite path leg ends is not enough, - return ((TurnComponent)tcp.getPipelineComponent()).getTurnRadius(); + private static double spaceForTurn(PipeControlPoint tcp, PipeControlPoint dcp) { + // TODO : if the path legs contain offset, using just positions of opposite path leg ends is not enough. + // TODO : current iterative way for calculating required space may return longer length that is required. + double tr = ((TurnComponent)tcp.getPipelineComponent()).getTurnRadius(); + if (dcp == null) + return tr; // space for 90 deg + PipeControlPoint ne = tcp.findNextEnd(); + PipeControlPoint pe = tcp.findPreviousEnd(); + PipeControlPoint other = null; + if (dcp == ne) + other = pe; + else if (dcp == pe) + other = ne; + else + return tr; // space for 90 deg + if (other == null) + return tr; // space for 90 deg + Vector3d dir = dcp.getDirectedControlPointDirection(); + Vector3d dir2; + if (other == ne) { + dir2 = pathLegDirection(tcp); + } else { + dir2 = pathLegDirection(pe); + dir2.negate(); + } + + double d = dir.dot(dir2); + if (d > 0.9999) + return 0.0; // point following turn is directly in the front of the nozzle. + else if (d < -0.9999) + return tr*2.0; // point following turn is directly behind the nozzle, in theory, we should return Double.Inf... + + double curr = 0.0; + int iter = 10; + Vector3d tp0 = tcp.getPosition(); + try { + Vector3d dp = dcp.getWorldPosition(); + while (iter > 0) { + Vector3d tp = new Vector3d(dir); + tp.scaleAdd(curr, dp); + tcp._setPosition(tp); // no firing of listeners here + if (other == ne) { + dir2 = pathLegDirection(tcp); + } else { + dir2 = pathLegDirection(pe); + dir2.negate(); + } + + double a = dir.angle(dir2); + + // other is directly between dcp and tcp, a zero angle turn should do + if (Math.PI - a <= MathTools.NEAR_ZERO) + return 0.0; + + double R = tr * Math.tan(a * 0.5); + if (R <= curr) + break; + curr = R*1.001; + iter--; + } + } + finally { + tcp._setPosition(tp0); // return the original value + } + return curr; } private static void insertElbowUpdate(UpdateStruct2 u, PipeControlPoint dcp, PipeControlPoint next, boolean dcpStart, Vector3d position, Vector3d directedDirection) throws Exception{ @@ -1137,27 +1445,19 @@ public class PipingRules { // closest.add(directedDirection); PipeControlPoint tcp = null; - Vector3d closest; + Vector3d closest = new Vector3d(directedDirection); + closest.scaleAdd(dcp.getPipeRun().getTurnRadius(), position); if (dcpStart) { - closest = MathTools.closestPointOnStraight(next.getWorldPosition(), position, directedDirection); tcp = insertElbow(dcp, next, closest); } else { - closest = MathTools.closestPointOnStraight(dcp.getWorldPosition(), position, directedDirection); tcp = insertElbow(next, dcp, closest); } - // TODO properly calculate required distance between start and inserted elbow. - double d = MathTools.distance(position, closest); - double s = spaceForTurn(tcp); - if (d < s) { - d = s - d; - Vector3d p = new Vector3d(directedDirection); - p.scale(d); - p.add(closest); - tcp.setPosition(p); - closest = p; - } - + double s = spaceForTurn(tcp,dcp); + Vector3d p = new Vector3d(directedDirection); + p.scaleAdd(s, position); + tcp.setPosition(p); + closest = p; if (DEBUG) System.out.println("PipingRules.updateDirectedPipeRun() inserted " + tcp); @@ -1376,26 +1676,26 @@ public class PipingRules { updateTurnControlPointTurn(u.start, null, null); // updatePathLegPrev(u.start, u.start, PathLegUpdateType.NONE); } else if (u.start.isEnd()) { - updateEndComponentControlPoint(u.start, u.startPoint, u.endPoint); + updateEndComponentControlPoint(u.start, u.dir); } else if (u.start.isInline()) { - updateControlPointOrientation(u.start); + u.start.orientToDirection(u.dir); } if (u.end.isTurn()) { //updateTurnControlPointTurn(u.end, u.end.getPrevious(), u.end.getNext()); updateTurnControlPointTurn(u.end, null, null); // updatePathLegNext(u.end, u.end, PathLegUpdateType.NONE); } else if (u.end.isEnd()) { - updateEndComponentControlPoint(u.end, u.startPoint, u.endPoint); + updateEndComponentControlPoint(u.end, u.dir); } else if (u.end.isInline()) { - updateControlPointOrientation(u.end); + u.end.orientToDirection(u.dir); } } else { if (u.start.isEnd()) { - updateEndComponentControlPoint(u.start, u.startPoint, u.endPoint); + updateEndComponentControlPoint(u.start, u.dir); } if (u.end.isEnd()) { - updateEndComponentControlPoint(u.end, u.startPoint, u.endPoint); + updateEndComponentControlPoint(u.end, u.dir); } } if (updateInline) @@ -1516,7 +1816,7 @@ public class PipingRules { System.out.println(" " + newInlinePoint); icp.setWorldPosition(newInlinePoint); - updateControlPointOrientation(icp); + icp.orientToDirection(dir); } /** @@ -1528,12 +1828,12 @@ public class PipingRules { * @param nextPoint * @param prevPoint */ - private static void updateEndComponentControlPoint(PipeControlPoint ecp, Vector3d start, Vector3d end) throws Exception { + private static void updateEndComponentControlPoint(PipeControlPoint ecp, Vector3d dir) throws Exception { if (DEBUG) System.out.println("PipingRules.updateEndComponentControlPoint() " + ecp); - //FIXME : end control point cannot be fixed! - //if (!ecp.isFixed()) - updateControlPointOrientation(ecp); + + if (!ecp.isFixed()) // prevent overriding nozzle orientations.. + ecp.orientToDirection(dir); for (PipeControlPoint pcp : ecp.getChildPoints()) { // TODO update position @@ -1541,24 +1841,6 @@ public class PipingRules { } } - private static void updateControlPointOrientation(PipeControlPoint pcp) { - // FIXME : hack to bypass variable length components orientation -// if (pcp.getAtMostOneRelatedObject(ProcessResource.g3dResource.HasWorldOrientation) == null) -// return; -// if (pcp.rotationAngle == null) -// return; - Double angleO = pcp.getRotationAngle(); - double angle = 0.0; - if (angleO != null) - angle = angleO; - Boolean reversedO = pcp.getReversed(); - boolean reversed = false; - if (reversedO != null) - reversed = reversedO; - Quat4d q = pcp.getControlPointOrientationQuat(angle, reversed); - pcp.setWorldOrientation(q); - } - /** * Updates all branches when branch's position has been changed * @@ -1602,15 +1884,15 @@ public class PipingRules { return tcp.getTurnAngle(); return Math.PI; // FIXME : argh } - double turnAngle = prev.angle(next); - - double angle = Math.PI - turnAngle; + + final boolean isDegenerate = prev.lengthSquared() < MathTools.NEAR_ZERO || next.lengthSquared() < MathTools.NEAR_ZERO; + double turnAngle = isDegenerate ? 0.0 : prev.angle(next); Vector3d turnAxis = new Vector3d(); turnAxis.cross(prev, next); if (turnAxis.lengthSquared() > MathTools.NEAR_ZERO) { double elbowRadius = ((TurnComponent)tcp.getPipelineComponent()).getTurnRadius(); - double R = elbowRadius / Math.tan(angle * 0.5); + double R = elbowRadius * Math.tan(turnAngle * 0.5); turnAxis.normalize(); tcp.setTurnAngle(turnAngle); @@ -1624,7 +1906,7 @@ public class PipingRules { tcp.setTurnAxis(new Vector3d(MathTools.Y_AXIS)); } - updateControlPointOrientation(tcp); + tcp.orientToDirection(prev); if (DEBUG) System.out.println("PipingTools.updateTurnControlPointTurn " + prev + " " + next + " " + turnAngle + " " + turnAxis); @@ -1652,7 +1934,7 @@ public class PipingRules { return Math.PI; // FIXME : argh } - Quat4d q = PipeControlPoint.getControlPointOrientationQuat(dir, tcp.getRotationAngle() != null ? tcp.getRotationAngle() : 0.0); + Quat4d q = tcp.getControlPointOrientationQuat(dir, tcp.getRotationAngle() != null ? tcp.getRotationAngle() : 0.0); Vector3d v = new Vector3d(); MathTools.rotate(q, MathTools.Y_AXIS,v); tcp.setTurnAxis(v); @@ -1696,7 +1978,10 @@ public class PipingRules { List points = getControlPoints(pipeRun); PipeControlPoint pcp = points.get(0); if (pcp.isSizeChange() && pcp.getChildPoints().size() > 0) { - pipeRun = pcp.getPipeRun(); + PipeRun pr = pcp.getPipeRun(); + if (pr != pipeRun) + pipeRun = pr; + else break; } else { break; } @@ -1730,7 +2015,6 @@ public class PipingRules { List list2 = pcps.get(i+1); PipeControlPoint prev = list.get(list.size()-1); PipeControlPoint next = list2.get(0); - System.out.println(); if (prev == next) { // Reverse the component on the boundary. InlineComponent ic = (InlineComponent)prev.getPipelineComponent(); @@ -1817,50 +2101,52 @@ public class PipingRules { public static void validate(PipeRun pipeRun) { if (pipeRun == null) return; - Collection pcps = pipeRun.getControlPoints(); - int count = 0; - //System.out.println("Validate " + pipeRun.getName()); - for (PipeControlPoint pcp : pcps) { - if (pcp.getParentPoint() == null || pcp.getParentPoint().getPipeRun() != pipeRun) - count++; - } - List runPcps = getControlPoints(pipeRun); - if (runPcps.size() != count) { - System.out.println("Run " + pipeRun.getName() + " contains unconnected control points, found " + runPcps.size() + " connected, " + pcps.size() + " total."); + synchronized (ruleMutex) { + Collection pcps = pipeRun.getControlPoints(); + int count = 0; + //System.out.println("Validate " + pipeRun.getName()); for (PipeControlPoint pcp : pcps) { - if (!runPcps.contains(pcp)) { - System.out.println("Unconnected " + pcp + " " + pcp.getPipelineComponent()); - } + if (pcp.getParentPoint() == null || pcp.getParentPoint().getPipeRun() != pipeRun) + count++; } - } - for (PipeControlPoint pcp : pcps) { - if (pcp.getPipeRun() == null) { - System.out.println("PipeRun ref missing " + pcp + " " + pcp.getPipelineComponent()); - } - if (!pcp.isDirected() && pcp.getNext() == null && pcp.getPrevious() == null) - System.out.println("Orphan undirected " + pcp + " " + pcp.getPipelineComponent()); - } - for (PipeControlPoint pcp : pcps) { - if (pcp.getParentPoint() == null) { - PipeControlPoint sub = null; - if (pcp.isDualInline()) - sub = pcp.getDualSub(); - PipeControlPoint next = pcp.getNext(); - PipeControlPoint prev = pcp.getPrevious(); - if (next != null) { - if (!(next.getPrevious() == pcp || next.getPrevious() == sub)) { - System.out.println("Inconsistency between " + pcp + " -> " +next ); - } + List runPcps = getControlPoints(pipeRun); + if (runPcps.size() != count) { + System.out.println("Run " + pipeRun.getName() + " contains unconnected control points, found " + runPcps.size() + " connected, " + pcps.size() + " total."); + for (PipeControlPoint pcp : pcps) { + if (!runPcps.contains(pcp)) { + System.out.println("Unconnected " + pcp + " " + pcp.getPipelineComponent()); + } } - if (prev != null) { - PipeControlPoint prevParent = null; - if (prev.isDualSub()) { - prevParent = prev.getParentPoint(); - } else if (prev.isDualInline()) { - System.out.println("Inconsistency between " + pcp + " <-- " +prev ); + } + for (PipeControlPoint pcp : pcps) { + if (pcp.getPipeRun() == null) { + System.out.println("PipeRun ref missing " + pcp + " " + pcp.getPipelineComponent()); + } + if (!pcp.isDirected() && pcp.getNext() == null && pcp.getPrevious() == null) + System.out.println("Orphan undirected " + pcp + " " + pcp.getPipelineComponent()); + } + for (PipeControlPoint pcp : pcps) { + if (pcp.getParentPoint() == null) { + PipeControlPoint sub = null; + if (pcp.isDualInline()) + sub = pcp.getDualSub(); + PipeControlPoint next = pcp.getNext(); + PipeControlPoint prev = pcp.getPrevious(); + if (next != null) { + if (!(next.getPrevious() == pcp || next.getPrevious() == sub)) { + System.out.println("Inconsistency between " + pcp + " -> " +next ); + } } - if (!(prev.getNext() == pcp && (prevParent == null || prevParent.getNext() == pcp))) { - System.out.println("Inconsistency between " + pcp + " <-- " +prev ); + if (prev != null) { + PipeControlPoint prevParent = null; + if (prev.isDualSub()) { + prevParent = prev.getParentPoint(); + } else if (prev.isDualInline()) { + System.out.println("Inconsistency between " + pcp + " <-- " +prev ); + } + if (!(prev.getNext() == pcp && (prevParent == null || prevParent.getNext() == pcp))) { + System.out.println("Inconsistency between " + pcp + " <-- " +prev ); + } } } }