+package org.simantics.plant3d.scenegraph.controlpoint;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.vecmath.Point3d;
+import javax.vecmath.Quat4d;
+import javax.vecmath.Vector3d;
+
+import org.simantics.g3d.math.MathTools;
+import org.simantics.plant3d.scenegraph.InlineComponent;
+import org.simantics.plant3d.scenegraph.Nozzle;
+import org.simantics.plant3d.scenegraph.P3DRootNode;
+import org.simantics.plant3d.scenegraph.PipeRun;
+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.ui.ErrorLogger;
+
+public class PipingRules {
+ private static final boolean DEBUG = false;
+ private static final boolean DUMMY = false;
+
+ private static final double MIN_TURN_ANGLE = 0.01;
+
+ private static final int REMOVE_NONE = 0;
+ private static final int REMOVE_START = 1;
+ private static final int REMOVE_END = 2;
+ private static final int REMOVE_BOTH = 3;
+
+// private P3DRootNode root;
+
+// public PipingRules(P3DRootNode root) {
+// this.root = root;
+// }
+
+ private enum PathLegUpdateType {
+ NONE, PREV, NEXT, PREV_S, NEXT_S
+ };
+
+ private static boolean enabled = true;
+ private static boolean updating = false;
+ private static boolean allowInsertRemove = true;
+ private static boolean triedIR = false;
+
+
+ private static List<PipeControlPoint> updates = new ArrayList<PipeControlPoint>();
+
+ private static Object mutex = new Object();
+
+ public static void requestUpdate(PipeControlPoint pcp) {
+ if (DEBUG) System.out.println("PipingRules request " + pcp);
+ synchronized (mutex) {
+ if (!updates.contains(pcp))
+ updates.add(pcp);
+ }
+ }
+
+ public static synchronized boolean update() throws Exception {
+ if (updates.size() == 0)
+ return false;
+ List<PipeControlPoint> temp = new ArrayList<PipeControlPoint>(updates.size());
+ synchronized(mutex) {
+ temp.addAll(updates);
+ updates.clear();
+ }
+
+ for (PipeControlPoint pcp : temp)
+ positionUpdate(pcp);
+ return true;
+ }
+
+ public static boolean positionUpdate(PipeControlPoint pcp) throws Exception {
+
+ return positionUpdate(pcp, true);
+ }
+
+ public static boolean positionUpdate(PipeControlPoint pcp, boolean allowIR) throws Exception {
+ if (updating || !enabled)
+ return true;
+ if (pcp.getPipeRun() == null)
+ return false;
+ try {
+ if (DEBUG) System.out.println("PipingRules " + pcp);
+ updating = true;
+ allowInsertRemove = allowIR;
+ triedIR = false;
+ validate(pcp.getPipeRun());
+ if (pcp.isPathLegEnd()) {
+ updatePathLegEndControlPoint(pcp); // FXIME: Rules won't work properly, if they are not run twice.
+ updatePathLegEndControlPoint(pcp);
+ } else {
+ updateInlineControlPoint(pcp);
+ updateInlineControlPoint(pcp);
+ }
+ validate(pcp.getPipeRun());
+ if (!allowInsertRemove)
+ return !triedIR;
+ return true;
+ } finally {
+ updating = false;
+// System.out.println("PipingRules done " + pcp);
+ }
+ }
+
+ public static void setEnabled(boolean enabled) {
+ PipingRules.enabled = enabled;
+ if(!enabled)
+ updates.clear();
+ }
+
+ public static boolean isEnabled() {
+ return enabled;
+ }
+
+// private void commit() {
+// root.getNodeMap().commit();
+// }
+
+ public static class ExpandIterInfo {
+ // these two are turn control points
+ private PipeControlPoint start;
+ private PipeControlPoint end;
+ private int type;
+
+ public ExpandIterInfo() {
+
+ }
+
+ public ExpandIterInfo(PipeControlPoint tcp, int type) {
+ if (type == REMOVE_START)
+ start = tcp;
+ else
+ end = tcp;
+ this.type = type;
+ }
+
+ public ExpandIterInfo(PipeControlPoint start, PipeControlPoint end) {
+ this.start = start;
+ this.end = end;
+ this.type = REMOVE_BOTH;
+ }
+
+ public PipeControlPoint getEnd() {
+ return end;
+ }
+
+ public void setEnd(PipeControlPoint end) {
+ this.end = end;
+ }
+
+ public PipeControlPoint getStart() {
+ return start;
+ }
+
+ public void setStart(PipeControlPoint start) {
+ this.start = start;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ }
+
+ private static void updatePathLegEndControlPoint(PipeControlPoint pcp) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.updatePathLegEndControlPoint() " + pcp);
+ if (pcp.getNext() != null) {
+ updatePathLegNext(pcp, pcp, PathLegUpdateType.NEXT_S);
+ }
+ if (pcp.getPrevious() != null) {
+ updatePathLegPrev(pcp, pcp, PathLegUpdateType.PREV_S);
+ }
+
+ }
+
+ private static void updateInlineControlPoint(PipeControlPoint pcp) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.updateInlineControlPoint() " + pcp);
+ PipeControlPoint start = pcp.findPreviousEnd();
+ updatePathLegNext(start, pcp, PathLegUpdateType.NONE);
+ }
+
+ private static PipeControlPoint insertElbow(PipeControlPoint pcp1, PipeControlPoint pcp2, Vector3d pos) throws Exception{
+ if (DEBUG)
+ System.out.println("PipingRules.insertElbow() " + pcp1 + " " + pcp2 + " " + pos);
+ if (pcp1.getNext() == pcp2 && pcp2.getPrevious() == pcp1) {
+
+ } else if (pcp1.getNext() == pcp2 && pcp1.isDualInline() && pcp2.getPrevious() == pcp1.getSubPoint().get(0)) {
+ pcp1 = pcp1.getSubPoint().get(0);
+ } else if (pcp1.getPrevious() == pcp2 && pcp2.getNext() == pcp1) {
+ PipeControlPoint t = pcp1;
+ pcp1 = pcp2;
+ pcp2 = t;
+ } else if (pcp2.isDualInline() && pcp1.getPrevious() == pcp2.getSubPoint().get(0) && pcp2.getNext() == pcp1) {
+ PipeControlPoint t = pcp1;
+ pcp1 = pcp2.getSubPoint().get(0);
+ pcp2 = t;
+ } else {
+ throw new RuntimeException();
+ }
+ TurnComponent elbow = ComponentUtils.createTurn((P3DRootNode)pcp1.getRootNode());
+ PipeControlPoint pcp = elbow.getControlPoint();
+ if (pcp1.isDualInline())
+ pcp1 = pcp1.getSubPoint().get(0);
+ String name = pcp1.getPipeRun().getUniqueName("Elbow");
+ elbow.setName(name);
+ pcp1.getPipeRun().addChild(elbow);
+
+ pcp.insert(pcp1, pcp2);
+
+ pcp.setWorldPosition(pos);
+ validate(pcp.getPipeRun());
+ return pcp;
+ }
+
+ private static PipeControlPoint insertStraight(PipeControlPoint pcp1, PipeControlPoint pcp2, Vector3d pos, double length) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.insertStraight() " + pcp1 + " " + pcp2 + " " + pos);
+ if (pcp1.getNext() == pcp2 && pcp2.getPrevious() == pcp1) {
+
+ } else if (pcp1.getNext() == pcp2 && pcp1.isDualInline() && pcp2.getPrevious() == pcp1.getSubPoint().get(0)) {
+ pcp1 = pcp1.getSubPoint().get(0);
+ } else if (pcp1.getPrevious() == pcp2 && pcp2.getNext() == pcp1) {
+ PipeControlPoint t = pcp1;
+ pcp1 = pcp2;
+ pcp2 = t;
+ } else if (pcp2.isDualInline() && pcp1.getPrevious() == pcp2.getSubPoint().get(0) && pcp2.getNext() == pcp1) {
+ PipeControlPoint t = pcp1;
+ pcp1 = pcp2.getSubPoint().get(0);
+ pcp2 = t;
+ } else {
+ throw new RuntimeException();
+ }
+ InlineComponent component = ComponentUtils.createStraight((P3DRootNode)pcp1.getRootNode());
+ PipeControlPoint scp = component.getControlPoint();
+ if (pcp1.isDualInline())
+ pcp1 = pcp1.getSubPoint().get(0);
+ String name = pcp1.getPipeRun().getUniqueName("Pipe");
+ component.setName(name);
+ pcp1.getPipeRun().addChild(component);
+
+ scp.insert(pcp1, pcp2);
+
+ scp.setWorldPosition(pos);
+ scp.setLength(length);
+ validate(scp.getPipeRun());
+ return scp;
+ }
+
+ private static PipeControlPoint insertStraight(PipeControlPoint pcp, Direction direction , Vector3d pos, double length) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.insertStraight() " + pcp + " " + direction + " " + pos);
+
+ InlineComponent component = ComponentUtils.createStraight((P3DRootNode)pcp.getRootNode());
+ PipeControlPoint scp = component.getControlPoint();
+ if (pcp.isDualInline() && direction == Direction.NEXT)
+ pcp = pcp.getSubPoint().get(0);
+ String name = pcp.getPipeRun().getUniqueName("Pipe");
+ component.setName(name);
+ pcp.getPipeRun().addChild(component);
+
+ scp.insert(pcp,direction);
+
+ scp.setWorldPosition(pos);
+ scp.setLength(length);
+ validate(scp.getPipeRun());
+ return scp;
+ }
+
+ private static void updatePathLegNext(PipeControlPoint start, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
+ ArrayList<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
+ PipeControlPoint end = start.findNextEnd(list);
+ // this is for inline cp that is also path leg end
+ if (start.equals(updated))
+ lengthChange = PathLegUpdateType.NEXT;
+ else if (end.equals(updated))
+ lengthChange = PathLegUpdateType.PREV;
+ updatePathLegNext(start, list, end, updated, lengthChange);
+ }
+
+ private static void updatePathLegNext(PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
+ updatePathLeg(start, list, end, false, 0, new ArrayList<ExpandIterInfo>(), updated, lengthChange);
+ }
+
+ private static class UpdateStruct2 {
+ public PipeControlPoint start;
+ public Vector3d startPoint;
+ public ArrayList<PipeControlPoint> list;
+ public PipeControlPoint end;
+ public Vector3d endPoint;
+ public Vector3d dir;
+ public Vector3d offset;
+ public boolean hasOffsets;
+ public int iter;
+ public boolean reversed;
+ public ArrayList<ExpandIterInfo> toRemove;
+ public PipeControlPoint updated;
+
+ 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) {
+ if (start == null || end == null)
+ throw new NullPointerException();
+ this.start = start;
+ this.startPoint = startPoint;
+ this.list = list;
+ this.end = end;
+ this.endPoint = endPoint;
+ this.dir = dir;
+ this.offset = offset;
+ this.hasOffsets = hasOffsets;
+ this.iter = iter;
+ this.reversed = reversed;
+ this.toRemove = toRemove;
+ this.updated = updated;
+
+ if (!MathTools.isValid(startPoint) ||
+ !MathTools.isValid(endPoint) ||
+ !MathTools.isValid(dir)) {
+ throw new RuntimeException();
+ }
+ }
+
+ public String toString() {
+ return start + " " + end+ " " + dir + " " + hasOffsets + " " + offset + " " + iter + " " + toRemove.size();
+ }
+
+ }
+
+ private static boolean calculateOffset(Vector3d startPoint, Vector3d endPoint, ArrayList<PipeControlPoint> list, Vector3d dir, Vector3d offset) {
+ boolean hasOffsets = false;
+ dir.set(startPoint);
+ dir.sub(endPoint);
+ if (dir.lengthSquared() > MathTools.NEAR_ZERO)
+ dir.normalize();
+ offset.set(0.0, 0.0, 0.0);
+ for (PipeControlPoint icp : list) {
+ if (icp.isOffset()) {
+ hasOffsets = true;
+ offset.add(icp.getSizeChangeOffsetVector(dir));
+ } else if (icp.isDualSub())
+ ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
+ }
+ return hasOffsets;
+ }
+
+ /**
+ * @param start
+ * starting point of the pipe run
+ * @param list
+ * list of inline control points in the pipe run
+ * @param end
+ * ending point of the pipe run
+ * @param reversed
+ * boolean flag indicating wether start or end control point was
+ * modified (if true then end point was modified)
+ * @throws TransactionException
+ */
+ private static void updatePathLeg(PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, boolean reversed, int iter, ArrayList<ExpandIterInfo> toRemove, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
+ if (start == end)
+ return;
+ // FIXME: direction is calculated wrong way!
+ boolean hasOffsets = false;
+ Vector3d offset = new Vector3d();
+ Vector3d startPoint = start.getWorldPosition();
+ Vector3d endPoint = end.getWorldPosition();
+ Vector3d dir = new Vector3d();
+ hasOffsets = calculateOffset(startPoint, endPoint, list, dir, offset);
+ updatePathLeg(new UpdateStruct2(start, startPoint, list, end, endPoint, dir, offset, hasOffsets, iter, reversed, toRemove, updated), lengthChange);
+
+ }
+
+ private static void updatePathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
+ int directed = 0;
+ if (u.start.isDirected())
+ directed++;
+ if (u.end.isDirected())
+ directed++;
+ switch (directed) {
+ case 0:
+ updateFreePathLeg(u, lengthChange);
+ break;
+ case 1:
+ updateDirectedPathLeg(u, lengthChange);
+ break;
+ case 2:
+ updateDualDirectedPathLeg(u, lengthChange);
+ break;
+ }
+
+ }
+
+ private static void updateFreePathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.updateFreePipeRun " + u + " " + lengthChange);
+ checkExpandPathLeg(u, lengthChange);
+ if (u.start.isInline() || u.end.isInline())
+ processPathLeg(u, true, false);
+ }
+
+ private static void updateInlineControlPoints(UpdateStruct2 u, boolean checkSizes) throws Exception{
+ if (DEBUG)
+ System.out.println("PipingRules.updateInlineControlPoints() " + u);
+
+ if (!u.hasOffsets) {
+ // FIXME : cache positions
+ if (!checkSizes) {
+ Vector3d start = new Vector3d(u.startPoint);
+ Vector3d end = new Vector3d(u.endPoint);
+ // create offsets.
+ MathTools.mad(start, u.dir, 0.1);
+ MathTools.mad(end, u.dir, -0.1);
+ for (PipeControlPoint icp : u.list) {
+ updateInlineControlPoint(icp, start, end, u.dir);
+ }
+ return;
+ }
+
+ ArrayList<PipeControlPoint> pathLegPoints = 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);
+ }
+ pathLegPoints.add(u.end);
+
+ // TODO : values can be cached in the loop
+ for (int i = 1; i < pathLegPoints.size(); i++) {
+ PipeControlPoint icp = pathLegPoints.get(i);
+
+ PipeControlPoint prev;
+ Vector3d prevPos;
+ prev = pathLegPoints.get(i - 1);
+ prevPos = prev.getWorldPosition();
+ Vector3d currentPos = icp.getWorldPosition();
+
+ if (icp.isVariableLength()) {
+ if (i != pathLegPoints.size() - 1) {
+ PipeControlPoint next;
+ Vector3d nextPos;
+ next = pathLegPoints.get(i + 1);
+ nextPos = next.getWorldPosition();
+ Vector3d dir = new Vector3d(nextPos);
+ dir.sub(prevPos);
+ double l = dir.lengthSquared(); // distance between
+ // control points
+ // (square)
+ double l2prev = prev.getInlineLength(); // distance
+ // taken
+ // by
+ // components
+ double l2next = next.getInlineLength();
+ double l2 = l2prev + l2next;
+ double l2s = MathTools.square(l2);
+ if (l2s < l) { // check if there is enough space for
+ // variable length component.
+ // components fit
+ dir.normalize();
+ double length = Math.sqrt(l) - l2; // true length of
+ // the variable
+ // length
+ // component
+ dir.scale(length * 0.5 + l2prev); // calculate
+ // center
+ // position of
+ // the component
+ dir.add(prevPos);
+ icp.setWorldPosition(dir);
+ icp.setLength(length);
+ } else {
+ // components leave no space to the component and it
+ // must be removed
+ if (icp.isDeletable())
+ icp._remove();
+ }
+
+ } 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.
+ double currentLength = icp.getLength();
+
+ Vector3d dir = new Vector3d();
+ dir.sub(currentPos, prevPos);
+
+ if (currentLength < MathTools.NEAR_ZERO) {
+ currentLength = (dir.length() - prev.getInlineLength()) * 2.0;
+ }
+
+ if (dir.lengthSquared() > MathTools.NEAR_ZERO)
+ dir.normalize();
+ Point3d endPos = new Point3d(dir);
+ endPos.scale(currentLength * 0.5);
+ endPos.add(currentPos); // this is the free end of the
+ // component
+
+ double offset = prev.getInlineLength();
+ Point3d beginPos = new Point3d(dir);
+ beginPos.scale(offset);
+ beginPos.add(prevPos); // this is the connected end of
+ // the component
+
+ double l = beginPos.distance(endPos);
+
+ if (Double.isNaN(l))
+ System.out.println();
+
+ dir.scale(l * 0.5);
+ beginPos.add(dir); // center position
+
+ if (DEBUG)
+ System.out.println("PipingRules.updateInlineControlPoints() setting variable length to " + l);
+ icp.setLength(l);
+
+ icp.setWorldPosition(new Vector3d(beginPos));
+ }
+
+
+ } else if (!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.
+ Vector3d dir = new Vector3d(currentPos);
+ dir.sub(prevPos);
+ double l = dir.lengthSquared();
+ double l2prev = prev.getInlineLength();
+ double l2next = icp.getInlineLength();
+ double l2 = l2prev + l2next;
+ double l2s = l2 * l2;
+ if (l > l2s) {
+ if (allowInsertRemove) {
+ dir.normalize();
+ double length = Math.sqrt(l) - l2; // true length of the
+ // variable length
+ // component
+ dir.scale(length * 0.5 + l2prev); // calculate center
+ // position of the
+ // component
+ dir.add(prevPos);
+ PipeControlPoint scp = insertStraight(prev, icp, dir, length);
+ } else {
+ triedIR = true;
+ }
+ }
+ }
+ }
+ } else {
+ u.endPoint.sub(u.offset);
+ // FIXME : straights
+ for (PipeControlPoint icp : u.list) {
+ updateInlineControlPoint(icp, u.startPoint, u.endPoint, u.dir);
+ updateBranchControlPointBranches(icp);
+
+ if (icp.isOffset()) {
+ // TODO : offset vector is already calculated and should be
+ // cached
+ u.offset = icp.getSizeChangeOffsetVector(u.dir);
+ updateOffsetPoint(icp, u.offset);
+ u.startPoint.add(u.offset);
+ u.endPoint.add(u.offset);
+ }
+ }
+ }
+ }
+
+ private static void ppNoOffset(UpdateStruct2 u) 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));
+ } else if (icp.isDualSub())
+ ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
+ }
+ }
+ u.offset = offset;
+ checkExpandPathLeg(u, PathLegUpdateType.NONE);
+ }
+
+ 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 {
+ if (DEBUG)
+ System.out.println("PipingRules.ppNoDir() " + start + " " + end + " " + iter + " " + toRemove.size());
+ // 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));
+ }
+
+ private static void checkExpandPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
+ checkExpandPathLeg(u, lengthChange, false);
+ }
+
+ private static void checkExpandPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange, boolean forceUpdate) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.checkExpandPathLeg() " + u + " " + lengthChange);
+ if (lengthChange != PathLegUpdateType.NONE) {
+ // FIXME : turns cannot be checked before inline cps are updated,
+ // since their position affects calculation of turns
+ processPathLeg(u, forceUpdate, false);
+ int type = checkTurns(u, lengthChange);
+ if (type == REMOVE_NONE) {
+ processPathLeg(u, forceUpdate, true);
+ } else {
+ expandPathLeg(u, type);
+ }
+ } else {
+ processPathLeg(u, forceUpdate, true);
+ }
+ }
+
+ private static void updateDirectedPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.updateDirectedPipeRun() " + u + " " + lengthChange);
+ PipeControlPoint dcp;
+ PipeControlPoint other;
+ boolean canMoveOther = false;
+ boolean dcpStart = false;
+ boolean inlineEnd = false;
+ Vector3d position;
+ if (u.start.isDirected()) {
+ dcp = u.start;
+ other = u.end;
+ position = u.startPoint;
+ dcpStart = true;
+ if (!u.reversed)
+ canMoveOther = true;
+ inlineEnd = u.end.isInline();
+
+ } else {
+ dcp = u.end;
+ other = u.start;
+ position = u.endPoint;
+ if (u.reversed)
+ canMoveOther = true;
+ inlineEnd = u.start.isInline();
+ }
+
+ Vector3d directedDirection = dcp.getDirection();
+ Point3d directedEndPoint = new Point3d(u.endPoint);
+ if (u.hasOffsets)
+ directedEndPoint.add(u.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);
+ }
+
+ double distance = t.lengthSquared();
+ boolean aligned = (distance < 0.002);
+ if (aligned) {
+ checkExpandPathLeg(u, lengthChange, inlineEnd);
+
+ } else {
+ if (u.iter > 0) {
+ backIter(u);
+ } else {
+ PipeControlPoint nextToMoved;
+
+ if (u.list.size() > 0)
+ if (dcpStart)
+ nextToMoved = u.list.get(0);
+ else
+ nextToMoved = u.list.get(u.list.size() - 1);
+ else if (dcpStart)
+ nextToMoved = u.end;
+ else
+ nextToMoved = u.start;
+ if (other.isVariableAngle()) {
+
+ // TODO calculate needed space from next run end.
+ if (mu[0] < 1.0) {
+ if (dcpStart) {
+ closest.set(u.startPoint);
+ } else {
+ closest.set(u.endPoint);
+ }
+ closest.add(directedDirection);
+ }
+
+ if (canMoveOther) {
+ if (DEBUG)
+ System.out.println("PipingRules.updateDirectedPipeRun() moved end " + other + " to " + closest);
+ 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));
+ 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));
+ if (u.start.getPrevious() != null)
+ updatePathLegPrev(u.start, u.updated, PathLegUpdateType.PREV);
+ }
+ } else {
+ // TODO : calculate needed space from next run end.
+ if (allowInsertRemove)
+ insertElbowUpdate(u, dcp, nextToMoved, dcpStart, position, directedDirection);
+
+ else
+ triedIR = true;
+ }
+ } else if (other.isNonDirected() && other.getParentPoint() != null) {
+ // FIXME : this code was for updating branches
+ Vector3d bintersect = new Vector3d();
+ PipeControlPoint bcp = other.getParentPoint();
+ if (bcp != null && canMoveOther) {
+ Point3d bstart = new Point3d();
+ Point3d bend = new Point3d();
+ Vector3d bdir = new Vector3d();
+ bcp.getInlineControlPointEnds(bstart, bend, bdir);
+ Vector3d nintersect = new Vector3d();
+
+ MathTools.intersectStraightStraight(position, directedDirection, bend, bdir, nintersect, bintersect, mu);
+ Vector3d dist = new Vector3d(nintersect);
+ dist.sub(bintersect);
+ canMoveOther = mu[1] > 0.0 && mu[1] < 1.0 && dist.lengthSquared() < 0.01;
+ } else {
+ // TODO : endControlPoints are undirected: calculcate
+ // correct position for it
+ throw new UnsupportedOperationException("not implemented");
+ }
+ if (canMoveOther) {
+ if (DEBUG)
+ System.out.println("PipingRules.updateDirectedPipeRun() moved end " + other + " to " + bintersect);
+ // is required branch position is in possible range
+ bcp.setWorldPosition(bintersect);
+ if (dcpStart) {
+ 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);
+ } else {
+ 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);
+ }
+ } else {
+ // branch cannot be moved into right position, new turn
+ // / elbow must be inserted
+ if (allowInsertRemove)
+ insertElbowUpdate(u, dcp, nextToMoved, dcpStart, position, directedDirection);
+ else
+ triedIR = true;
+ }
+
+ } else { // assume that control point cannot be moved, but can
+ // be rotated
+ if (allowInsertRemove)
+ insertElbowUpdate(u, dcp, nextToMoved, dcpStart, position, directedDirection);
+ else
+ triedIR = true;
+ }
+ }
+ }
+
+
+ }
+
+ private static void updateDualDirectedPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.updateDualDirectedPipeRun() " + u + " " + lengthChange);
+
+ PipeControlPoint dcp1 = u.start;
+ PipeControlPoint dcp2 = u.end;
+ Point3d position1 = new Point3d(u.startPoint);
+ Point3d position2 = new Point3d(u.endPoint);
+ Point3d position1offset = new Point3d(position1);
+ position1offset.sub(u.offset);
+ Point3d position2offset = new Point3d(position2);
+ position2offset.add(u.offset);
+ Vector3d dir1 = dcp1.getDirection();
+ Vector3d dir2 = dcp2.getDirection();
+ Vector3d p1 = MathTools.closestPointOnStraight(position1offset, position2, dir2);
+ Vector3d p2 = MathTools.closestPointOnStraight(position2offset, position1, dir1);
+ double d1 = position1.distance(new Point3d(p1));
+ double d2 = position2.distance(new Point3d(p2));
+
+ boolean aligned = (d1 < 0.01 && d2 < 0.01);
+ if (aligned) {
+ processPathLeg(u);
+ } else {
+ if (u.iter > 0) {
+ backIter(u);
+ } else if (allowInsertRemove){
+ PipeControlPoint dcp;
+ PipeControlPoint next;
+ if (!u.reversed) {
+ dcp = dcp1;
+ if (u.list.size() > 0)
+ next = u.list.get(0);
+ else
+ next = dcp2;
+ } else {
+ dcp = dcp2;
+ if (u.list.size() > 0)
+ next = u.list.get(u.list.size() - 1);
+ else
+ next = dcp1;
+ }
+
+ p1 = dcp.getWorldPosition();
+ // FIXME: calculate position of the elbows properly.
+ if (!u.reversed)
+ p1.add(dir1);
+ else
+ p1.add(dir2);
+
+ if (!u.reversed)
+ p2 = MathTools.closestPointOnStraight(new Point3d(p1), position2, dir2);
+ else
+ p2 = MathTools.closestPointOnStraight(new Point3d(p1), position1, dir1);
+
+
+ PipeControlPoint tcp1 = insertElbow(dcp, next, p1);
+ PipeControlPoint tcp2 = insertElbow(tcp1, next, p2);
+
+ if (DEBUG)
+ System.out.println("PipingRules.updateDualDirectedPipeRun() created two turns " + tcp1 + " " + tcp2);
+
+ if (!u.reversed) {
+ Vector3d dd = new Vector3d(p2);
+ dd.sub(p1);
+ dir2.negate();
+ updatePathLegNext(u.start, u.updated, PathLegUpdateType.NONE);
+ updatePathLegNext(tcp1, u.updated, PathLegUpdateType.NONE);
+ if (!u.reversed)
+ updatePathLegNext(tcp2, u.updated, PathLegUpdateType.NONE);
+ else
+ updatePathLegPrev(tcp2, u.updated, PathLegUpdateType.NONE);
+ } else {
+ Vector3d dd = new Vector3d(p1);
+ dd.sub(p2);
+ dir2.negate();
+ updatePathLegNext(tcp1, u.updated, PathLegUpdateType.NONE);
+ updatePathLegNext(tcp2, u.updated, PathLegUpdateType.NONE);
+ if (!u.reversed)
+ updatePathLegNext(u.start, u.updated, PathLegUpdateType.NONE);
+ else
+ updatePathLegPrev(u.start, u.updated, PathLegUpdateType.NONE);
+ }
+ } else {
+ triedIR = true;
+ }
+ }
+
+ }
+
+ private static void insertElbowUpdate(UpdateStruct2 u, PipeControlPoint dcp, PipeControlPoint next, boolean dcpStart, Vector3d position, Vector3d directedDirection) throws Exception{
+
+ Vector3d closest = new Vector3d(position);
+ closest.add(directedDirection);
+ PipeControlPoint tcp = null;
+ if (dcpStart)
+ tcp = insertElbow(dcp, next, new Vector3d(closest));
+ else
+ tcp = insertElbow(next, dcp, new Vector3d(closest));
+
+ if (DEBUG)
+ System.out.println("PipingRules.updateDirectedPipeRun() inserted " + tcp);
+
+ if (dcpStart) {
+ // update pipe run from new turn to other end
+ ppNoDir(tcp, new Vector3d(closest), u.list, u.end, u.endPoint, u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated);
+ // update pipe run from directed to new turn
+ 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));
+ } else {
+ // update pipe run from other end to new turn
+ ppNoDir(u.start, u.startPoint, u.list, tcp, new Vector3d(closest), u.hasOffsets, u.iter, u.reversed, u.toRemove, u.updated);
+ // update pipe run from new turn to directed
+ 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));
+ }
+ }
+
+ /**
+ * Checks if turns can be removed (turn angle near zero)
+ */
+ private static int checkTurns(UpdateStruct2 u, PathLegUpdateType lengthChange) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.checkTurns() " + u.start + " " + u.end);
+ boolean startRemoved = false;
+ boolean endRemoved = false;
+ if (u.start.isVariableAngle()) {
+ // this won't work properly if inline control points are not updated
+ PipeControlPoint startPrev = u.start.getPrevious();
+ if (startPrev != null) {
+ double a;
+ if (!u.hasOffsets) {
+ a = updateTurnControlPointTurn(u.start, startPrev, u.end);
+ } else {
+ Vector3d ep = new Vector3d(u.endPoint);
+ ep.add(u.offset);
+ a = updateTurnControlPointTurn(u.start, u.startPoint, startPrev.getPosition(), ep);
+
+ }
+ if (a < MIN_TURN_ANGLE && u.start.isDeletable())
+ startRemoved = true;
+ else if (lengthChange == PathLegUpdateType.PREV || lengthChange == PathLegUpdateType.PREV_S) {
+ PathLegUpdateType type;
+ if (lengthChange == PathLegUpdateType.PREV_S)
+ type = PathLegUpdateType.PREV;
+ else
+ type = PathLegUpdateType.NONE;
+ updatePathLegPrev(u.start, u.start, type);
+ }
+ }
+ }
+ if (u.end.isVariableAngle()) {
+
+ PipeControlPoint endNext = u.end.getNext();
+ if (endNext != null) {
+ double a;
+ if (!u.hasOffsets) {
+ a = updateTurnControlPointTurn(u.end, u.start, endNext);
+ } else {
+ Vector3d sp = new Vector3d(u.startPoint);
+ sp.sub(u.offset);
+ a = updateTurnControlPointTurn(u.end, u.endPoint, sp, endNext.getPosition());
+ }
+ if (a < MIN_TURN_ANGLE && u.end.isDeletable())
+ endRemoved = true;
+ else if (lengthChange == PathLegUpdateType.NEXT || lengthChange == PathLegUpdateType.NEXT_S) {
+ PathLegUpdateType type;
+ if (lengthChange == PathLegUpdateType.NEXT_S)
+ type = PathLegUpdateType.NEXT;
+ else
+ type = PathLegUpdateType.NONE;
+ updatePathLegNext(u.end, u.end, type);
+ }
+ }
+ }
+ if (DEBUG)
+ System.out.println("PipingRules.checkTurns() res " + startRemoved + " " + endRemoved);
+ if (!startRemoved && !endRemoved)
+ return REMOVE_NONE;
+ if (startRemoved && endRemoved)
+ return REMOVE_BOTH;
+ if (startRemoved)
+ return REMOVE_START;
+ return REMOVE_END;
+ }
+
+ /**
+ * Expands piperun search over turns that are going to be removed
+ *
+ */
+ private static void expandPathLeg(UpdateStruct2 u, int type) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.expandPipeline " + u.start + " " + u.end);
+ ArrayList<PipeControlPoint> newList = new ArrayList<PipeControlPoint>();
+ switch (type) {
+ case REMOVE_NONE:
+ throw new RuntimeException("Error in piping rules");
+ case REMOVE_START:
+ u.toRemove.add(new ExpandIterInfo(u.start, REMOVE_START));
+ u.start = u.start.findPreviousEnd();
+ u.startPoint = u.start.getPosition();
+ u.start.findNextEnd(newList);
+ newList.addAll(u.list);
+ u.list = newList;
+ break;
+ case REMOVE_END:
+ u.toRemove.add(new ExpandIterInfo(u.end, REMOVE_END));
+ u.end = u.end.findNextEnd(newList);
+ u.endPoint = u.end.getPosition();
+ u.list.addAll(newList);
+ break;
+ case REMOVE_BOTH:
+ u.toRemove.add(new ExpandIterInfo(u.start, u.end));
+ u.start = u.start.findPreviousEnd();
+ u.startPoint = u.start.getPosition();
+ u.start.findNextEnd(newList);
+ newList.addAll(u.list);
+ u.list = newList;
+ newList = new ArrayList<PipeControlPoint>();
+ u.end = u.end.findNextEnd(newList);
+ u.endPoint = u.end.getPosition();
+ u.list.addAll(newList);
+ break;
+ default:
+ throw new RuntimeException("Error in piping rules");
+
+ }
+ u.offset = new Vector3d();
+ if (u.hasOffsets) {
+ u.dir.normalize();
+ for (PipeControlPoint icp : u.list) {
+ if (icp.isOffset()) {
+ u.offset.add(icp.getSizeChangeOffsetVector(u.dir));
+ } else if (icp.isDualSub())
+ ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
+ }
+ }
+ if (DEBUG)
+ System.out.println("PipingRules.expandPipeline expanded " + u.start + " " + u.end);
+ u.iter++;
+ updatePathLeg(u, PathLegUpdateType.NONE);
+ }
+
+ /**
+ * reverts one iteration of turn removing back)
+ */
+ private static void backIter(UpdateStruct2 u) throws Exception {
+
+ if (DEBUG)
+ System.out.println("PipingRules.backIter" + u.start + " " + u.end);
+ if (u.iter == 0)
+ throw new RuntimeException("Error in piping rules");
+ ExpandIterInfo info = u.toRemove.get(u.toRemove.size() - 1);
+ u.toRemove.remove(u.toRemove.size() - 1);
+ if (info.getType() == REMOVE_START || info.getType() == REMOVE_BOTH) {
+ while (u.list.size() > 0) {
+ PipeControlPoint icp = u.list.get(0);
+ if (icp.getPrevious().equals(info.getStart()))
+ break;
+ u.list.remove(icp);
+ }
+ u.start = info.getStart();
+ }
+ if (info.getType() == REMOVE_END || info.getType() == REMOVE_BOTH) {
+ while (u.list.size() > 0) {
+ PipeControlPoint icp = u.list.get(u.list.size() - 1);
+ if (icp.getNext().equals(info.getEnd()))
+ break;
+ u.list.remove(icp);
+ }
+ u.end = info.getEnd();
+ }
+ u.offset = new Vector3d();
+ if (u.hasOffsets) {
+ u.dir.normalize();
+ for (PipeControlPoint icp : u.list) {
+ if (icp.isOffset()) {
+ u.offset.add(icp.getSizeChangeOffsetVector(u.dir));
+ } else if (icp.isDualSub())
+ ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
+ }
+ }
+ processPathLeg(u);
+
+ }
+
+ /**
+ * Processes pipe run (removes necessary turns and updates run ends)
+ */
+ // private static void processPathLeg(PipeControlPoint start, Point3d
+ // startPoint,ArrayList<InlineControlPoint> list, PipeControlPoint
+ // end,Point3d endPoint, Vector3d dir,Vector3d offset, boolean
+ // hasOffsets,int iter, boolean reversed, ArrayList<ExpandIterInfo>
+ // toRemove) throws TransactionException {
+
+ private static void processPathLeg(UpdateStruct2 u) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.processPathLeg " + u.start + " " + u.end);
+ processPathLeg(u, true, true);
+ }
+
+ private static void processPathLeg(UpdateStruct2 u, boolean updateEnds, boolean updateInline) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.processPathLeg " + u.start + " " + u.end);
+
+ if (u.toRemove.size() > 0) {
+ for (ExpandIterInfo info : u.toRemove) {
+ if (info.getStart() != null) {
+ info.getStart()._remove();
+ }
+ if (info.getEnd() != null) {
+ info.getEnd()._remove();
+ }
+ }
+ // ControlPointTools.removeControlPoint may remove mo0re than one
+ // CP;
+ // we must populate inline CP list again.
+ u.list.clear();
+ u.start.findNextEnd( u.list);
+ }
+ // FIXME : inline CPs are update twice because their positions must be
+ // updated before and after ends.
+ updateInlineControlPoints(u, false);
+
+ if (updateEnds) {
+ if (u.start.isTurn()) {
+ updateTurnControlPointTurn(u.start, u.start.getPrevious(), u.start.getNext());
+// updatePathLegPrev(u.start, u.start, PathLegUpdateType.NONE);
+ } else if (u.start.isEnd()) {
+ updateEndComponentControlPoint(u.start, u.startPoint, u.endPoint);
+ } else if (u.start.isInline()) {
+ updateControlPointOrientation(u.start);
+ }
+ if (u.end.isTurn()) {
+ updateTurnControlPointTurn(u.end, u.end.getPrevious(), u.end.getNext());
+// updatePathLegNext(u.end, u.end, PathLegUpdateType.NONE);
+ } else if (u.end.isEnd()) {
+ updateEndComponentControlPoint(u.end, u.startPoint, u.endPoint);
+ } else if (u.end.isInline()) {
+ updateControlPointOrientation(u.end);
+ }
+
+ } else {
+ if (u.start.isEnd()) {
+ updateEndComponentControlPoint(u.start, u.startPoint, u.endPoint);
+ }
+ if (u.end.isEnd()) {
+ updateEndComponentControlPoint(u.end, u.startPoint, u.endPoint);
+ }
+ }
+ if (updateInline)
+ updateInlineControlPoints(u, true);
+
+ }
+
+ /**
+ * Processes pipe run and recalculates offset
+ */
+ // private static void processPathLeg(PipeControlPoint start, Point3d
+ // startPoint,ArrayList<InlineControlPoint> list, PipeControlPoint
+ // end,Point3d endPoint, Vector3d dir, boolean hasOffsets,int iter, boolean
+ // reversed, ArrayList<ExpandIterInfo> toRemove) throws TransactionException
+ // {
+ private static void processPathLegNoOffset(UpdateStruct2 u) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.processPathLeg " + u.start + " " + u.end);
+ Vector3d offset = new Vector3d();
+ if (u.hasOffsets) {
+ u.dir.normalize();
+ for (PipeControlPoint icp : u.list) {
+ if (icp.isOffset()) {
+ offset.add(icp.getSizeChangeOffsetVector(u.dir));
+ } else if (icp.isDualSub()) {
+ ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp, new Exception("ASSERT!"));
+ }
+ }
+ }
+ processPathLeg(u);
+ }
+
+ private static void updateOffsetPoint(PipeControlPoint sccp, Vector3d offset) {
+ Vector3d world = sccp.getWorldPosition();
+ world.add(offset);
+ PipeControlPoint ocp = sccp.getSubPoint().iterator().next();
+ ocp.setWorldPosition(world);
+ }
+
+ private static void updatePathLegPrev(PipeControlPoint start, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
+ ArrayList<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
+ PipeControlPoint end = start.findPreviousEnd(list);
+ updatePathLegPrev(start, list, end, updated, lengthChange);
+ }
+
+ private static void updatePathLegPrev(PipeControlPoint start, ArrayList<PipeControlPoint> list, PipeControlPoint end, PipeControlPoint updated, PathLegUpdateType lengthChange) throws Exception {
+ // reverses the list
+ ArrayList<PipeControlPoint> nextList = new ArrayList<PipeControlPoint>();
+ for (PipeControlPoint icp : list) {
+ if (icp.isDualSub()) {
+ nextList.add(0, icp.getParentPoint());
+ } else {
+ nextList.add(0, icp);
+ }
+
+ }
+ updatePathLeg(end, nextList, start, true, 0, new ArrayList<ExpandIterInfo>(), updated, lengthChange);
+
+ }
+
+ /**
+ * Updates InlineControlPoints position when straight pipe's end(s) have
+ * been changed)
+ *
+ * @param pipeline
+ * @param icp
+ * @param nextPoint
+ * @param prevPoint
+ */
+ private static void updateInlineControlPoint(PipeControlPoint icp, Vector3d nextPoint, Vector3d prevPoint, Vector3d dir) {
+ if (DEBUG)
+ System.out.println("PipingRules.updateInlineControlPoint() " + icp);
+
+ Vector3d inlinePoint = icp.getWorldPosition();
+ if (DEBUG)
+ System.out.print("InlineControlPoint update " + icp + " " + inlinePoint + " " + prevPoint + " " + nextPoint);
+ Vector3d newInlinePoint = null;
+ boolean branchUpdate = false;
+ PipeControlPoint becp = null;
+ for (PipeControlPoint pcp : icp.getSubPoint())
+ if (pcp.isNonDirected()) {
+ branchUpdate = true;
+ becp = pcp;
+ break;
+ }
+
+ if (DUMMY || !branchUpdate) {
+ newInlinePoint = MathTools.closestPointOnEdge(new Vector3d(inlinePoint), new Vector3d(nextPoint), new Vector3d(prevPoint));
+ } else {
+
+ // FIXME : can only handle one branch
+ PipeControlPoint p = null;
+ if (becp.getNext() != null) {
+ p = becp.findNextEnd();
+ } else if (becp.getPrevious() != null) {
+ p = becp.findPreviousEnd();
+ }
+ if (p == null) {
+ newInlinePoint = MathTools.closestPointOnEdge(new Vector3d(inlinePoint), new Vector3d(nextPoint), new Vector3d(prevPoint));
+ } else {
+ Vector3d branchLegEnd = p.getWorldPosition();
+ Vector3d dir2 = new Vector3d(inlinePoint);
+ dir2.sub(branchLegEnd);
+ Vector3d dir1 = new Vector3d(nextPoint);
+ dir1.sub(prevPoint);
+ newInlinePoint = new Vector3d();
+ double mu[] = new double[2];
+ MathTools.intersectStraightStraight(new Vector3d(prevPoint), dir1, new Vector3d(branchLegEnd), dir2, newInlinePoint, new Vector3d(), mu);
+ if (DEBUG)
+ System.out.println(mu[0]);
+ // FIXME : reserve space
+ if (mu[0] < 0.0) {
+ newInlinePoint = new Vector3d(prevPoint);
+ } else if (mu[0] > 1.0) {
+ newInlinePoint = new Vector3d(nextPoint);
+ }
+ }
+ }
+ if (DEBUG)
+ System.out.println(" " + newInlinePoint);
+
+ icp.setWorldPosition(newInlinePoint);
+ updateControlPointOrientation(icp);
+ }
+
+ /**
+ * Updates InlineControlPoints position when straight pipe's end(s) have
+ * been changed)
+ *
+ * @param pipeline
+ * @param icp
+ * @param nextPoint
+ * @param prevPoint
+ */
+ private static void updateEndComponentControlPoint(PipeControlPoint ecp, Vector3d start, Vector3d end) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.updateEndComponentControlPoint() " + ecp);
+ // PipeControlPoint next = ecp.getNext();
+ // PipeControlPoint prev = ecp.getPrevious();
+ // if (next != null) {
+ // end = G3DTools.getPoint(next.getLocalPosition());
+ // start = G3DTools.getPoint(ecp.getLocalPosition());
+ // } else if (prev != null) {
+ // end = G3DTools.getPoint(ecp.getLocalPosition());
+ // start = G3DTools.getPoint(prev.getLocalPosition());
+ // } else {
+ // // TODO : warning?
+ // return;
+ // }
+ // Vector3d dir = new Vector3d (end);
+ // dir.sub(start);
+ // dir.normalize();
+ // G3DTools.setTuple(ecp.getDirection(), dir);
+ if (!ecp.isFixed())
+ updateControlPointOrientation(ecp);
+
+ for (PipeControlPoint pcp : ecp.getSubPoint()) {
+ // TODO update position
+ updatePathLegEndControlPoint(pcp);
+ }
+ }
+
+ 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;
+
+ Quat4d q = pcp.getControlPointOrientationQuat(angle);
+ pcp.setWorldOrientation(q);
+ }
+
+ /**
+ * Updates all branches when branch's position has been changed
+ *
+ * @param bcp
+ */
+ private static void updateBranchControlPointBranches(PipeControlPoint bcp) throws Exception {
+ if (DEBUG)
+ System.out.println("PipingRules.updateBranchControlPointBranches() " + bcp);
+ if (bcp.isDualInline())
+ return;
+ Collection<PipeControlPoint> branches = bcp.getSubPoint();
+ if (branches.size() == 0) {
+ if (DEBUG)
+ System.out.println("No Branches found");
+ return;
+ }
+
+ for (PipeControlPoint pcp : branches) {
+ updatePathLegEndControlPoint(pcp);
+ }
+ }
+
+ /**
+ * Recalculates turn control point's internal data (turn angle and offset)
+ *
+ * @param tcp
+ * @param prev
+ * @param next
+ */
+ private static double updateTurnControlPointTurn(PipeControlPoint tcp, PipeControlPoint prev, PipeControlPoint next) {
+ if (DEBUG)
+ System.out.println("PipingTools.updateTurnControlPointTurn()" + tcp);
+ if (next == null || prev == null)
+ return Math.PI; // FIXME : argh
+ Vector3d middlePoint = tcp.getWorldPosition();
+ Vector3d nextPoint = next.getWorldPosition();
+ Vector3d prevPoint = prev.getWorldPosition();
+ return updateTurnControlPointTurn(tcp, middlePoint, prevPoint, nextPoint);
+ }
+
+ /**
+ * Recalculates turn control point's internal data (turn angle and offset)
+ *
+ * @param tcp
+ * @param middlePoint
+ * @param nextPoint
+ * @param prevPoint
+ */
+ private static double updateTurnControlPointTurn(PipeControlPoint tcp, Vector3d middlePoint, Vector3d prevPoint, Vector3d nextPoint) {
+
+ Vector3d dir1 = new Vector3d(middlePoint);
+ dir1.sub(prevPoint);
+ Vector3d dir2 = new Vector3d(nextPoint);
+ dir2.sub(middlePoint);
+ if (DEBUG)
+ System.out.println("PipingTools.updateTurnControlPointTurn " + tcp + " " + prevPoint + " " + middlePoint + " " + nextPoint);
+ return updateTurnControlPointTurn(tcp, dir1, dir2);
+ }
+
+ private static double updateTurnControlPointTurn(PipeControlPoint tcp, Vector3d dir1, Vector3d dir2) {
+ double turnAngle = dir1.angle(dir2);
+
+ double angle = Math.PI - turnAngle;
+
+ Vector3d turnAxis = new Vector3d();
+ turnAxis.cross(dir1, dir2);
+ if (turnAxis.lengthSquared() > MathTools.NEAR_ZERO) {
+ double elbowRadius = tcp.getPipelineComponent().getPipeRun().getTurnRadius();
+ double R = elbowRadius / Math.tan(angle * 0.5);
+
+ turnAxis.normalize();
+ tcp.setTurnAngle(turnAngle);
+ tcp.setLength(R);// setComponentOffsetValue(R);
+ tcp.setTurnAxis(turnAxis);
+// tcp.setPosition(tcp.getPosition());
+ } else {
+ turnAngle = 0.0;
+ tcp.setTurnAngle(0.0);
+ tcp.setLength(0.0);
+ tcp.setTurnAxis(MathTools.Y_AXIS);
+ }
+ updateControlPointOrientation(tcp);
+ if (DEBUG)
+ System.out.println("PipingTools.updateTurnControlPointTurn " + dir1 + " " + dir2 + " " + turnAngle + " " + turnAxis);
+ return turnAngle;
+ }
+
+ public static List<PipeControlPoint> getControlPoints(PipeRun pipeRun) {
+ List<PipeControlPoint> list = new ArrayList<PipeControlPoint>();
+ if (pipeRun.getControlPoints().size() == 0)
+ return list;
+ PipeControlPoint pcp = pipeRun.getControlPoints().iterator().next();
+ while (pcp.getPrevious() != null) {
+ PipeControlPoint prev = pcp.getPrevious();
+ if (prev.getPipeRun() != pipeRun)
+ break;
+ pcp = prev;
+ }
+ if (pcp.isDualSub()) {
+ pcp = pcp.getParentPoint();
+ }
+ list.add(pcp);
+ while (pcp.getNext() != null) {
+ pcp = pcp.getNext();
+ if (pcp.getPipeRun() != pipeRun)
+ break;
+ list.add(pcp);
+ }
+ return list;
+ }
+
+ public static void reverse(PipeRun pipeRun) {
+ List<PipeControlPoint> list = getControlPoints(pipeRun);
+ if (list.size() <= 1)
+ return; // nothing to do.
+
+ for (int i = 0 ; i < list.size(); i++) {
+ boolean first = i == 0;
+ boolean last = i == list.size() - 1;
+ PipeControlPoint current = list.get(i);
+ PipeControlPoint currentSub = null;
+ if (current.isDualInline())
+ currentSub = current.getSubPoint().get(0);
+ if (first) {
+ PipeControlPoint next = list.get(i+1);
+ if (next.isDualInline())
+ next = next.getSubPoint().get(0);
+ current.setNext(null);
+ current.setPrevious(next);
+ if (currentSub != null) {
+ currentSub.setNext(null);
+ currentSub.setPrevious(next);
+ }
+ } else if (last) {
+ PipeControlPoint prev = list.get(i-1);
+
+ current.setPrevious(null);
+ current.setNext(prev);
+
+ if (currentSub != null) {
+ currentSub.setPrevious(null);
+ currentSub.setNext(prev);
+ }
+ } else {
+ PipeControlPoint prev = list.get(i-1);
+ PipeControlPoint next = list.get(i+1);
+ if (next.isDualInline())
+ next = next.getSubPoint().get(0);
+
+
+ current.setPrevious(next);
+ current.setNext(prev);
+
+ if (currentSub != null) {
+ currentSub.setPrevious(next);
+ currentSub.setNext(prev);
+ }
+
+ }
+ }
+ }
+
+ public static void merge(PipeRun run1, PipeRun r2) {
+ Map<PipeControlPoint, Vector3d> positions = new HashMap<PipeControlPoint, Vector3d>();
+ Map<PipeControlPoint, Quat4d> orientations = new HashMap<PipeControlPoint, Quat4d>();
+ for (PipeControlPoint pcp : r2.getControlPoints()) {
+ positions.put(pcp, pcp.getWorldPosition());
+ orientations.put(pcp, pcp.getWorldOrientation());
+ }
+ for (PipeControlPoint pcp : r2.getControlPoints()) {
+ r2.deattachChild(pcp);
+ run1.addChild(pcp);
+ PipelineComponent component = pcp.getPipelineComponent();
+ if (component != null) {
+ if (!(component instanceof Nozzle)) {
+ component.deattach();
+ run1.addChild(component);
+ } else {
+ Nozzle n = (Nozzle)component;
+ n.setPipeRun(run1);
+ }
+ }
+ }
+ r2.remove();
+
+ }
+
+ public static void validate(PipeRun pipeRun) {
+ if (pipeRun == null)
+ return;
+ Collection<PipeControlPoint> pcps = pipeRun.getControlPoints();
+ int count = 0;
+ for (PipeControlPoint pcp : pcps) {
+ if (pcp.getParentPoint() == null || pcp.getParentPoint().getPipeRun() != pipeRun)
+ count++;
+ }
+ List<PipeControlPoint> runPcps = getControlPoints(pipeRun);
+ if (runPcps.size() != count) {
+ System.out.println("Run is not connected");
+ }
+ for (PipeControlPoint pcp : pcps) {
+ if (pcp.getParentPoint() == null) {
+ PipeControlPoint sub = null;
+ if (pcp.isDualInline())
+ sub = pcp.getSubPoint().get(0);
+ 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 != 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 );
+ }
+ }
+ }
+ }
+ }
+
+ public static void splitVariableLengthComponent(PipelineComponent newComponent, InlineComponent splittingComponent, boolean assignPos) throws Exception{
+ assert(!splittingComponent.getControlPoint().isFixed());
+ assert(!(newComponent instanceof InlineComponent && !newComponent.getControlPoint().isFixed()));
+ PipeControlPoint newCP = newComponent.getControlPoint();
+ PipeControlPoint splittingCP = splittingComponent.getControlPoint();
+ PipeControlPoint nextCP = splittingCP.getNext();
+ PipeControlPoint prevCP = splittingCP.getPrevious();
+
+ /* there are many different cases to insert new component when
+ it splits existing VariableLengthinlineComponent.
+
+ 1. VariableLengthComponet is connected from both sides:
+ - insert new component between VariableLength component and component connected to it
+ - insert new VariableLengthComponent between inserted component and component selected in previous step
+
+ 2. VariableLengthComponent is connected from one side
+ - Use previous case or:
+ - Insert new component to empty end
+ - Insert new VariableLength component to inserted components empty end
+
+ 3. VariableLength is not connected to any component.
+ - Should not be possible, at least in current implementation.
+ - Could be done using second case
+
+ */
+
+ if (nextCP == null && prevCP == null) {
+ // this should not be possible
+ throw new RuntimeException("VariableLengthComponent " + splittingComponent + " is not connected to anything.");
+ }
+ double reservedLength = splittingComponent.getControlPoint().getLength();
+ double newLength = newComponent.getControlPoint().getLength();
+
+
+ Point3d next = new Point3d();
+ Point3d prev = new Point3d();
+ splittingCP.getInlineControlPointEnds(prev, next);
+
+ Vector3d newPos = null;
+ if (assignPos) {
+ newPos = new Vector3d(prev);
+ Vector3d dir = new Vector3d(next);
+ dir.sub(prev);
+ dir.scale(0.5);
+ newPos.add(dir);
+ newComponent.setWorldPosition(newPos);
+ } else {
+ newPos = newComponent.getWorldPosition();
+ }
+
+
+
+ Vector3d dir = new Vector3d(next);
+ dir.sub(prev);
+ dir.normalize();
+ dir.scale(newLength * 0.5);
+ Point3d vn = new Point3d(newPos);
+ Point3d vp = new Point3d(newPos);
+ vn.add(dir);
+ vp.sub(dir);
+ double ln = vn.distance(next);
+ double lp = vp.distance(prev);
+ vp.interpolate(prev, 0.5);
+ vn.interpolate(next, 0.5);
+
+
+ PipeControlPoint newVariableLengthCP = null;//insertStraight(pcp1, pcp2, pos, length);
+ if (nextCP == null) {
+ newCP.insert(splittingCP, Direction.NEXT);
+ newVariableLengthCP = insertStraight(newCP, Direction.NEXT, new Vector3d(vn), ln);
+ splittingCP.setWorldPosition(new Vector3d(vp));
+// ControlPointTools.setWorldPosition(splittingCP, vp);
+// splittingCP.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, lp);
+ } else if (prevCP == null) {
+ newCP.insert(splittingCP, Direction.PREVIOUS);
+ newVariableLengthCP = insertStraight(newCP, Direction.PREVIOUS, new Vector3d(vp), lp);
+ splittingCP.setWorldPosition(new Vector3d(vn));
+// splittingCP.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, ln);
+ } else {
+ newCP.insert(splittingCP, nextCP);
+ newVariableLengthCP = insertStraight(newCP, nextCP, new Vector3d(vn), ln);
+ splittingCP.setWorldPosition(new Vector3d(vp));
+// splittingCP.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, lp);
+ }
+ positionUpdate(newCP);
+
+ }
+
+ public static void addSizeChange(boolean reversed, PipeRun pipeRun, PipeRun other, InlineComponent reducer, PipeControlPoint previous, PipeControlPoint next) {
+ PipeControlPoint pcp = reducer.getControlPoint();
+ PipeControlPoint ocp = pcp.getSubPoint().get(0);
+ if (!reversed) {
+ String name = pipeRun.getUniqueName("Reducer");
+ reducer.setName(name);
+ pipeRun.addChild(reducer);
+ other.addChild(ocp);
+ reducer.setAlternativePipeRun(other);
+
+ previous.setNext(pcp);
+ pcp.setPrevious(previous);
+ ocp.setPrevious(previous);
+ if (next != null) {
+ pcp.setNext(next);
+ ocp.setNext(next);
+ next.setPrevious(ocp);
+ }
+ } else {
+ String name = other.getUniqueName("Reducer");
+ reducer.setName(name);
+ other.addChild(reducer);
+ pipeRun.addChild(ocp);
+ reducer.setAlternativePipeRun(pipeRun);
+
+ if (next != null) {
+ next.setNext(pcp);
+ pcp.setPrevious(next);
+ ocp.setPrevious(next);
+ }
+ pcp.setNext(previous);
+ ocp.setNext(previous);
+ previous.setPrevious(ocp);
+ }
+ }
+}