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 {
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;
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);
}
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<PipeControlPoint> pathLegPoints = new ArrayList<PipeControlPoint>();
- ArrayList<PipeControlPoint> fixedLengthPoints = new ArrayList<PipeControlPoint>();
- 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);
+
+
+ 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<PipeControlPoint> pathLegPoints = new ArrayList<>();
+ // Collect all fixed length components with their offsets.
+ ArrayList<Pair<PipeControlPoint,Vector3d>> fixedLengthPoints = new ArrayList<>();
+
+ pathLegPoints.add(u.start);
+ fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(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<PipeControlPoint, Vector3d>(icp, new Vector3d(off)));
+ Vector3d offset = icp.getSizeChangeOffsetVector(u.dir);
+ off.add(offset);
+ } else if (!icp.isVariableLength()) {
+ fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(icp, new Vector3d(off)));
+ }
+ }
+ pathLegPoints.add(u.end);
+ fixedLengthPoints.add(new Pair<PipeControlPoint, Vector3d>(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 (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);
- }
- }
- } else { // with offset
- Vector3d sp = new Vector3d(start);
- Vector3d ep = new Vector3d(end);
- ep.sub(u.offset);
-
- ArrayList<PipeControlPoint> pathLegPoints = new ArrayList<PipeControlPoint>();
- 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();
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<PipeControlPoint,Vector3d> pcp1;
+ Pair<PipeControlPoint,Vector3d> pcp2;
+ }
+
+ private static void updateFixedLengths(List<Pair<PipeControlPoint,Vector3d>> fixedLengthPoints, Vector3d s, Vector3d e, Vector3d dir) {
+ double totalLength = MathTools.distance(s, e);
+ double reservedLength = 0.0;
+ List<Double> distances = new ArrayList<>(fixedLengthPoints.size());
+ distances.add(0.0);
+ for (int i = 1; i < fixedLengthPoints.size()-1; i++) {
+ Pair<PipeControlPoint,Vector3d> 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<GapObj> gaps = new ArrayList<>(fixedLengthPoints.size()-1);
+ int overlaps = 0;
+ // Analyze gaps between components
+ for (int i = 0; i < fixedLengthPoints.size()-1; i++) {
+ Pair<PipeControlPoint,Vector3d> pcp1 = fixedLengthPoints.get(i);
+ Pair<PipeControlPoint,Vector3d> 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 (curr < -MIN_INLINE_LENGTH) {
+ GapObj next = i+d >= 0 ? 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<PipeControlPoint,Vector3d> pcp = gaps.get(j).pcp2;
+ Vector3d p = new Vector3d(pcp.first.getWorldPosition());
+ p.add(mv);
+ pcp.first.setWorldPosition(p);
+ }
+ }
+ if (curr < -MIN_INLINE_LENGTH && prev != null && prev.gap == Gap.SPACE) {
+ double move = Math.min(-curr, prev.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<PipeControlPoint,Vector3d> pcp = gaps.get(j).pcp1;
+ Vector3d p = new Vector3d(pcp.first.getWorldPosition());
+ p.add(mv);
+ pcp.first.setWorldPosition(p);
+ }
+ }
+ }
+ }
+ } else {
+ for (int i = 1; i < fixedLengthPoints.size()-1; i++) {
+ Pair<PipeControlPoint,Vector3d> prev = i == 0 ? null : fixedLengthPoints.get(i-1);
+ Pair<PipeControlPoint,Vector3d> curr = fixedLengthPoints.get(i);
+ Pair<PipeControlPoint,Vector3d> next = i == fixedLengthPoints.size() -1 ? null : fixedLengthPoints.get(i+1);
+ updateFixedLength(curr, prev, next, s,e, dir);
+ }
+ }
+ }
+
+ private static void updateFixedLength(Pair<PipeControlPoint,Vector3d> icp, Pair<PipeControlPoint,Vector3d> prev, Pair<PipeControlPoint,Vector3d> 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<PipeControlPoint,Vector3d> icp, Pair<PipeControlPoint,Vector3d> 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) {
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;
}
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);
+ updateControlPointOrientation(u.start, 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);
+ updateControlPointOrientation(u.end, 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)
System.out.println(" " + newInlinePoint);
icp.setWorldPosition(newInlinePoint);
- updateControlPointOrientation(icp);
+ updateControlPointOrientation(icp, dir);
}
/**
* @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);
+ updateControlPointOrientation(ecp, dir);
for (PipeControlPoint pcp : ecp.getChildPoints()) {
// TODO update position
}
}
- 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;
+ private static void updateControlPointOrientation(PipeControlPoint pcp, Vector3d dir) {
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);
+ boolean reversed = pcp._getReversed();
+ Quat4d q = null;
+ if (dir != null) {
+ q = pcp.getControlPointOrientationQuat(dir, angle, reversed);
+ } else {
+ q = pcp.getControlPointOrientationQuat(angle, reversed);
+ }
pcp.setWorldOrientation(q);
}
tcp.setTurnAxis(new Vector3d(MathTools.Y_AXIS));
}
- updateControlPointOrientation(tcp);
+ updateControlPointOrientation(tcp,prev);
if (DEBUG)
System.out.println("PipingTools.updateTurnControlPointTurn " + prev + " " + next + " " + turnAngle + " " + turnAxis);