+ 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(prev, icp, dir,true);
+ }
+ if (next != null)
+ checkOverlap(icp, next, dir,true);
+ }
+
+ 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);
+ }
+