/******************************************************************************* * Copyright (c) 2007 VTT Technical Research Centre of Finland and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package fi.vtt.simantics.processeditor.common; import java.util.ArrayList; import java.util.Collection; import javax.vecmath.AxisAngle4d; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import org.simantics.db.Graph; import org.simantics.db.Resource; import org.simantics.proconf.g3d.base.G3DTools; import org.simantics.proconf.g3d.base.MathTools; import org.simantics.utils.ErrorLogger; import fi.vtt.simantics.processeditor.ProcessResource; import fi.vtt.simantics.processeditor.stubs.PipeControlPoint; import fi.vtt.simantics.processeditor.stubs.PipeRun; import fi.vtt.simantics.processeditor.stubs.PipelineComponent; /** * Rules that update pipeline. * TODO : optimize, remove stubs * * FIXME : transformations * * TODO : FixedAngleTurnComponents are handled like VariableAngleTurnComponents * * * @author Marko Luukkainen * */ 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 enum PathLegUpdateType {NONE,PREV,NEXT,PREV_S,NEXT_S}; /** * Rule * * @param resources * @param pp * @throws TransactionException */ public static void pipeControlPointPositionUpdate(Graph g, Resource pp) { PipeControlPoint pcp = new PipeControlPoint(g,pp); if (DEBUG) System.out.println("PipeControlPoint changed " + pp); boolean changed = ControlPointTools.isControlPointChanged(pcp); if (changed) { if (pcp.isInstanceOf(ProcessResource.plant3Dresource.PathLegEndControlPoint)) { updatePathLegEndControlPoint(pcp); } else { updateInlineControlPoint(pcp); } } } 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) { if (DEBUG) System.out.println("PipingTools.updateRunEndControlPoint() " + pcp.getResource()); 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) { if (DEBUG) System.out.println("PipingTools.updateInlineControlPoint() " + pcp.getResource()); PipeControlPoint start = ControlPointTools.findPreviousEnd(pcp); updatePathLegNext(start,pcp,PathLegUpdateType.NONE); } private static PipeControlPoint insertElbow(PipeControlPoint pcp1 , PipeControlPoint pcp2, Point3d pos) { if (DEBUG) System.out.println("PipingRules.insertElbow() " + pcp1.getResource() + " " + pcp2.getResource()+ " " + pos); PipelineComponent elbow = PipingTools2.instantiatePipelineComponent(pcp1.getGraph(), ControlPointTools.getPipeRun(pcp1).getResource(), ProcessResource.plant3Dresource.Elbow); PipeControlPoint pcp = elbow.getControlPoint(); ControlPointTools.insertControlPoint(pcp, pcp1,pcp2); ControlPointTools.setWorldPosition(pcp, pos); return pcp; } private static void updatePathLegNext(PipeControlPoint start, PipeControlPoint updated, PathLegUpdateType lengthChange){ ArrayList list = new ArrayList(); PipeControlPoint end = ControlPointTools.findNextEnd(start,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 list, PipeControlPoint end, PipeControlPoint updated, PathLegUpdateType lengthChange) { updatePathLeg(start,list,end,false,0,new ArrayList(),updated, lengthChange); } private static class UpdateStruct2 { public PipeControlPoint start; public Point3d startPoint; public ArrayList list; public PipeControlPoint end; public Point3d endPoint; public Vector3d dir; public Vector3d offset; public boolean hasOffsets; public int iter; public boolean reversed; public ArrayList toRemove; public PipeControlPoint updated; public UpdateStruct2(PipeControlPoint start, Point3d startPoint, ArrayList list, PipeControlPoint end, Point3d endPoint, Vector3d dir, Vector3d offset, boolean hasOffsets, int iter, boolean reversed, ArrayList toRemove, PipeControlPoint updated) { super(); 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; } public String toString() { return start.getResource() + " " + end.getResource() + " " + dir + " " + hasOffsets + " " + offset + " " + iter + " " + toRemove.size(); } } private static boolean calculateOffset(Point3d startPoint, Point3d endPoint, ArrayList list, Vector3d dir, Vector3d offset) { boolean hasOffsets = false; dir.set(startPoint); dir.sub(endPoint); dir.normalize(); offset.set(0.0,0.0,0.0); for (PipeControlPoint icp : list) { if (icp.isInstanceOf(ProcessResource.plant3Dresource.OffsettingPoint)) { hasOffsets = true; offset.add(ControlPointTools.getSizeChangeOffsetVector(icp,dir)); } else if (icp.isInstanceOf(ProcessResource.plant3Dresource.DualSubControlPoint)) ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp.getResource(), 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 list, PipeControlPoint end, boolean reversed, int iter, ArrayList toRemove, PipeControlPoint updated, PathLegUpdateType lengthChange) { // FIXME: direction is calculated wrong way! boolean hasOffsets = false; Vector3d offset = new Vector3d(); Point3d startPoint = G3DTools.getPoint(start.getWorldPosition()); Point3d endPoint = G3DTools.getPoint(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){ int directed = 0; if (u.start.isInstanceOf(ProcessResource.plant3Dresource.DirectedControlPoint)) directed ++; if (u.end.isInstanceOf(ProcessResource.plant3Dresource.DirectedControlPoint)) 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) { if (DEBUG) System.out.println("PipingRules.updateFreePipeRun " + u + " " + lengthChange); checkExpandPathLeg(u, lengthChange); } private static void updateInlineControlPoints(UpdateStruct2 u, boolean checkSizes) { if (DEBUG) System.out.println("PipingTools.updateInlineControlPoints() " + u); if (!u.hasOffsets) { // FIXME : cache positions if (!checkSizes) { for (PipeControlPoint icp : u.list) { updateInlineControlPoint(icp, u.startPoint, u.endPoint,u.dir); } return; } ArrayList pathLegPoints = new ArrayList(); 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; Point3d prevPos; prev = pathLegPoints.get(i-1); prevPos = G3DTools.getPoint(prev.getWorldPosition()); Point3d currentPos = G3DTools.getPoint(icp.getWorldPosition()); if (icp.isInstanceOf(ProcessResource.plant3Dresource.VariableLengthControlPoint)) { if (i != pathLegPoints.size() - 1) { PipeControlPoint next; Point3d nextPos; next = pathLegPoints.get(i + 1); nextPos = G3DTools.getPoint(next.getWorldPosition()); Vector3d dir = new Vector3d(nextPos); dir.sub(prevPos); double l = dir.lengthSquared(); // distance between control points (square) double l2prev = ControlPointTools.getInlineLength(prev); // distance taken by components double l2next = ControlPointTools.getInlineLength(next); 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); ControlPointTools.setWorldPosition(icp,dir); icp.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, length); } else { //components leave no space to the component and it must be removed ControlPointTools.removeControlPoint(icp); } } 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.getSingleRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength); Vector3d dir = new Vector3d(); dir.sub(currentPos,prevPos); 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 = ControlPointTools.getInlineLength(prev); Point3d beginPos = new Point3d(dir); beginPos.scale(offset); beginPos.add(prevPos); //this is the connected end of the component double l = beginPos.distance(endPos); dir.scale(l*0.5); beginPos.add(dir); //center position if (DEBUG) System.out.println("PipingRules.updateInlineControlPoints() setting variable length to " + l); icp.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, l); ControlPointTools.setWorldPosition(icp, beginPos); } i++; } else if (!prev.isInstanceOf(ProcessResource.plant3Dresource.VariableLengthControlPoint)){ // 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 = ControlPointTools.getInlineLength(prev); double l2next = ControlPointTools.getInlineLength(icp); double l2 = l2prev + l2next; double l2s = l2 * l2; if (l > l2s) { PipelineComponent component = PipingTools2.instantiatePipelineComponent(prev.getGraph(), ControlPointTools.getPipeRun(prev).getResource(), ProcessResource.plant3Dresource.Straight); PipeControlPoint scp = component.getControlPoint(); ControlPointTools.insertControlPoint(scp, prev, icp); 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); ControlPointTools.setWorldPosition(scp, dir); scp.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, length); } } } } 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.isInstanceOf(ProcessResource.plant3Dresource.OffsettingPoint)) { // TODO : offset vector is already calculated and should be // cached u.offset = ControlPointTools.getSizeChangeOffsetVector(icp, u.dir); updateOffsetPoint( icp, u.offset); u.startPoint.add(u.offset); u.endPoint.add(u.offset); } } } } private static void ppNoOffset(UpdateStruct2 u) { 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.isInstanceOf(ProcessResource.plant3Dresource.OffsettingPoint)) { offset.add(ControlPointTools.getSizeChangeOffsetVector(icp,u.dir)); } else if (icp.isInstanceOf(ProcessResource.plant3Dresource.DualSubControlPoint)) ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp.getResource(), new Exception("ASSERT!")); } } u.offset = offset; checkExpandPathLeg(u,PathLegUpdateType.NONE); } private static void ppNoDir(PipeControlPoint start, Point3d startPoint,ArrayList list, PipeControlPoint end,Point3d endPoint, boolean hasOffsets,int iter,boolean reversed, ArrayList toRemove, PipeControlPoint updated) { if (DEBUG)System.out.println("PipingRules.ppNoDir() " + start.getResource() + " " + end.getResource() + " " + 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) { 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,false,false); int type = checkTurns(u,lengthChange); if (type == REMOVE_NONE) { processPathLeg(u,false,true); } else { expandPathLeg(u, type); } } else { processPathLeg(u,false,true); } } private static void updateDirectedPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) { if (DEBUG)System.out.println("PipingRules.updateDirectedPipeRun() " + u + " " + lengthChange); PipeControlPoint dcp; PipeControlPoint other; boolean canMoveOther = false; boolean dcpStart = false; Point3d position; if (u.start.isInstanceOf(ProcessResource.plant3Dresource.DirectedControlPoint)) { dcp = u.start; other = u.end; position = u.startPoint; dcpStart = true; if (!u.reversed) canMoveOther = true; } else { dcp = u.end; other = u.start; position = u.endPoint; if (u.reversed) canMoveOther = true; } Vector3d directedDirection = ControlPointTools.getDirectedControlPointDirection(dcp); 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.001); if (aligned) { checkExpandPathLeg(u,lengthChange); } 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.isInstanceOf(ProcessResource.plant3Dresource.VariableAngleTurnControlPoint)) { // 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.getResource() + " to " + closest); ControlPointTools.setWorldPosition(other, closest); if (dcpStart) { ppNoOffset(new UpdateStruct2(u.start, u.startPoint, u.list, u.end, new Point3d(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 Point3d(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. insertElbowUpdate(u, dcp, nextToMoved, dcpStart, position, directedDirection); } } else if (other.isInstanceOf(ProcessResource.plant3Dresource.UndirectedControlPoint) && other.getSubPointOf() != null) { // FIXME : this code was for updating branches Vector3d bintersect = new Vector3d(); PipeControlPoint bcp = other.getSubPointOf(); if (bcp != null && canMoveOther) { Point3d bstart = new Point3d(); Point3d bend = new Point3d(); Vector3d bdir = new Vector3d(); ControlPointTools.getInlineControlPointEnds(bcp, bstart, bend, bdir); Vector3d nintersect = new Vector3d(); MathTools.intersectStraightStraight(position, directedDirection, bstart, 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.getResource() + " to " + bintersect); // is required branch position is in possible range ControlPointTools.setWorldPosition(bcp, bintersect); if (dcpStart) { checkExpandPathLeg(new UpdateStruct2(u.start, u.startPoint, u.list, u.end, new Point3d(bintersect),directedDirection, u.offset, u.hasOffsets, u.iter, u.reversed, u.toRemove,u.updated),lengthChange); } else { checkExpandPathLeg(new UpdateStruct2(u.start, new Point3d(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 insertElbowUpdate(u , dcp, nextToMoved, dcpStart, position, directedDirection); } } else { // assume that control point cannot be moved, but can be rotated insertElbowUpdate(u, dcp, nextToMoved, dcpStart, position, directedDirection); } } } } private static void updateDualDirectedPathLeg(UpdateStruct2 u, PathLegUpdateType lengthChange) { if (DEBUG) System.out.println("PipingRules.updateDualDirectedPipeRun() " + u + " " + lengthChange); PipeControlPoint dcp1 = u.start; PipeControlPoint dcp2 = u.end; Point3d position1 = u.startPoint; Point3d position2 = u.endPoint; Point3d position1offset = new Point3d(position1); position1offset.sub(u.offset); Point3d position2offset = new Point3d(position2); position2offset.add(u.offset); Vector3d dir1 = ControlPointTools.getDirectedControlPointDirection(dcp1); Vector3d dir2 = ControlPointTools.getDirectedControlPointDirection(dcp2); 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 { 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; } PipeRun pipeline = ControlPointTools.getPipeRun(dcp1); PipelineComponent elbow1 = PipingTools2.instantiatePipelineComponent(u.start.getGraph(), pipeline.getResource(), ProcessResource.plant3Dresource.Elbow); PipelineComponent elbow2 = PipingTools2.instantiatePipelineComponent(u.start.getGraph(), pipeline.getResource(), ProcessResource.plant3Dresource.Elbow); PipeControlPoint tcp1 = elbow1.getControlPoint(); PipeControlPoint tcp2 = elbow2.getControlPoint(); // Straight s1 = getStraight(dcp, next); ControlPointTools.insertControlPoint(tcp1, dcp, next); // s1 = getStraight(tcp1, next); ControlPointTools.insertControlPoint(tcp2, tcp1, next); p1 = G3DTools.getVector(dcp.getLocalPosition()); 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); ControlPointTools.setWorldPosition(tcp1, p1); ControlPointTools.setWorldPosition(tcp2, p2); if (DEBUG) System.out.println("PipingRules.updateDualDirectedPipeRun() created two turns " + tcp1.getResource() + " " + tcp2.getResource()); if (!u.reversed) { Vector3d dd = new Vector3d(p2); dd.sub(p1); dir2.negate(); processPathLeg(new UpdateStruct2(u.start, u.startPoint,new ArrayList(), tcp1, new Point3d(p1),dir1, new Vector3d(), false, 0, false,new ArrayList(), u.updated)); processPathLeg(new UpdateStruct2(tcp1,new Point3d(p1), new ArrayList(),tcp2, new Point3d(p2), dd,new Vector3d(), false, 0, false,new ArrayList(), u.updated)); // offset is recalculated processPathLegNoOffset(new UpdateStruct2(tcp2, new Point3d(p2), u.list,u.end, u.endPoint, dir2, null, u.hasOffsets,u.iter, u.reversed, u.toRemove, u.updated)); } else { Vector3d dd = new Vector3d(p1); dd.sub(p2); dir2.negate(); processPathLeg(new UpdateStruct2(tcp1,new Point3d(p1), new ArrayList(),u.end, u.endPoint, dir2, new Vector3d(), false, 0,false, new ArrayList(), u.updated)); processPathLeg(new UpdateStruct2(tcp2,new Point3d(p2), new ArrayList(),tcp1, new Point3d(p1), dd,new Vector3d(), false, 0, false,new ArrayList(), u.updated)); // offset is recalculated processPathLegNoOffset(new UpdateStruct2(u.start, u.startPoint,u.list, tcp2, new Point3d(p2),dir1, null, u.hasOffsets, u.iter, u.reversed,u.toRemove, u.updated)); } } } } private static void insertElbowUpdate(UpdateStruct2 u, PipeControlPoint dcp, PipeControlPoint next, boolean dcpStart, Point3d position, Vector3d directedDirection) { Vector3d closest = new Vector3d(position); closest.add(directedDirection); PipeControlPoint tcp = insertElbow(dcp, next, new Point3d(closest)); if (DEBUG) System.out.println("PipingRules.updateDirectedPipeRun() inserted " + tcp.getResource()); if (dcpStart) { // update pipe run from new turn to other end ppNoDir(tcp, new Point3d(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(), tcp, new Point3d(closest), directedDirection, new Vector3d(), false, 0, false, new ArrayList(),u.updated)); } else { // update pipe run from other end to new turn ppNoDir(u.start, u.startPoint, u.list, tcp, new Point3d(closest), u.hasOffsets, u.iter, u.reversed, u.toRemove,u.updated); // update pipe run from new turn to directed processPathLeg(new UpdateStruct2(tcp, new Point3d(closest), new ArrayList(), u.end, u.endPoint, directedDirection, new Vector3d(), false, 0, false, new ArrayList(),u.updated)); } } /** * Checks if turns can be removed (turn angle near zero) */ private static int checkTurns(UpdateStruct2 u, PathLegUpdateType lengthChange) { if (DEBUG) System.out.println("PipingRules.checkTurns() " + u.start.getResource() + " " + u.end.getResource()); boolean startRemoved = false; boolean endRemoved = false; if (u.start.isInstanceOf(ProcessResource.plant3Dresource.VariableAngleTurnControlPoint)) { // 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 { Point3d ep = new Point3d(u.endPoint); ep.add(u.offset); a = updateTurnControlPointTurn( u.start,u.startPoint, G3DTools.getPoint(startPrev.getLocalPosition()), ep); } if (a < MIN_TURN_ANGLE) 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.isInstanceOf(ProcessResource.plant3Dresource.VariableAngleTurnControlPoint)) { PipeControlPoint endNext = u.end.getNext(); if (endNext != null) { double a; if (!u.hasOffsets) { a = updateTurnControlPointTurn(u.end,u.start, endNext); } else { Point3d sp = new Point3d(u.startPoint); sp.sub(u.offset); a = updateTurnControlPointTurn(u.end, u.endPoint, sp, G3DTools.getPoint(endNext.getLocalPosition())); } if (a < MIN_TURN_ANGLE) 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) { if (DEBUG) System.out.println("PipingRules.expandPipeline " + u.start.getResource() + " " + u.end.getResource()); ArrayList newList = new ArrayList (); 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 = ControlPointTools.findPreviousEnd(u.start); u.startPoint = G3DTools.getPoint(u.start.getLocalPosition()); ControlPointTools.findNextEnd(u.start,newList); newList.addAll(u.list); u.list = newList; break; case REMOVE_END : u.toRemove.add(new ExpandIterInfo(u.end,REMOVE_END)); u.end = ControlPointTools.findNextEnd(u.end,newList); u.endPoint = G3DTools.getPoint(u.end.getLocalPosition()); u.list.addAll(newList); break; case REMOVE_BOTH : u.toRemove.add(new ExpandIterInfo(u.start,u.end)); u.start = ControlPointTools.findPreviousEnd(u.start); u.startPoint = G3DTools.getPoint(u.start.getLocalPosition()); ControlPointTools.findNextEnd(u.start,newList); newList.addAll(u.list); u.list = newList; newList = new ArrayList (); u.end = ControlPointTools.findNextEnd(u.end,newList); u.endPoint = G3DTools.getPoint(u.end.getLocalPosition()); 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.isInstanceOf(ProcessResource.plant3Dresource.OffsettingPoint)) { u.offset.add(ControlPointTools.getSizeChangeOffsetVector(icp,u.dir)); } else if (icp.isInstanceOf(ProcessResource.plant3Dresource.DualSubControlPoint)) ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp.getResource(), new Exception("ASSERT!")); } } if (DEBUG) System.out.println("PipingRules.expandPipeline expanded " + u.start.getResource() + " " + u.end.getResource()); u.iter++; updatePathLeg(u,PathLegUpdateType.NONE); } /** * reverts one iteration of turn removing back) */ private static void backIter(UpdateStruct2 u) { if (DEBUG) System.out.println("PipingRules.backIter" + u.start.getResource() + " " + u.end.getResource()); 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().getResource().equals(info.getStart().getResource())) 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().getResource().equals(info.getEnd().getResource())) 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.isInstanceOf(ProcessResource.plant3Dresource.OffsettingPoint)) { u.offset.add(ControlPointTools.getSizeChangeOffsetVector(icp,u.dir)); } else if (icp.isInstanceOf(ProcessResource.plant3Dresource.DualSubControlPoint)) ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp.getResource(), new Exception("ASSERT!")); } } processPathLeg(u); } /** * Processes pipe run (removes necessary turns and updates run ends) */ // private static void processPathLeg(PipeControlPoint start, Point3d startPoint,ArrayList list, PipeControlPoint end,Point3d endPoint, Vector3d dir,Vector3d offset, boolean hasOffsets,int iter, boolean reversed, ArrayList toRemove) throws TransactionException { private static void processPathLeg(UpdateStruct2 u) { if (DEBUG) System.out.println("PipingRules.processPathLeg " + u.start.getResource() + " " + u.end.getResource()); processPathLeg(u, true, true); } private static void processPathLeg(UpdateStruct2 u, boolean updateEnds, boolean updateInline) { if (DEBUG) System.out.println("PipingRules.processPathLeg " + u.start.getResource() + " " + u.end.getResource()); if (u.toRemove.size() > 0) { for (ExpandIterInfo info : u.toRemove) { if (info.getStart() != null) { ControlPointTools.removeControlPoint(info.getStart()); } if (info.getEnd() != null) { ControlPointTools.removeControlPoint(info.getEnd()); } } // ControlPointTools.removeControlPoint may remove mo0re than one CP; // we must populate inline CP list again. u.list.clear(); ControlPointTools.findNextEnd(u.start, 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.isInstanceOf(ProcessResource.plant3Dresource.TurnControlPoint)) { updateTurnControlPointTurn(u.start, u.start.getPrevious(), u.start.getNext()); updatePathLegPrev(u.start, u.start, PathLegUpdateType.NONE); } else if (u.start.isInstanceOf(ProcessResource.plant3Dresource.EndComponentControlPoint)) { updateEndComponentControlPoint(u.start, u.startPoint, u.endPoint); } if (u.end.isInstanceOf(ProcessResource.plant3Dresource.TurnControlPoint)) { updateTurnControlPointTurn( u.end, u.end.getPrevious(), u.end.getNext()); updatePathLegNext(u.end, u.end, PathLegUpdateType.NONE); } else if (u.end.isInstanceOf(ProcessResource.plant3Dresource.EndComponentControlPoint)) { updateEndComponentControlPoint(u.end, u.startPoint, u.endPoint); } } else { if (u.start.isInstanceOf(ProcessResource.plant3Dresource.EndComponentControlPoint)) { updateEndComponentControlPoint(u.start, u.startPoint, u.endPoint); } if (u.end.isInstanceOf(ProcessResource.plant3Dresource.EndComponentControlPoint)) { 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 list, PipeControlPoint end,Point3d endPoint, Vector3d dir, boolean hasOffsets,int iter, boolean reversed, ArrayList toRemove) throws TransactionException { private static void processPathLegNoOffset(UpdateStruct2 u) { if (DEBUG) System.out.println("PipingRules.processPathLeg " + u.start.getResource() + " " + u.end.getResource()); Vector3d offset = new Vector3d(); if (u.hasOffsets) { u.dir.normalize(); for (PipeControlPoint icp : u.list) { if (icp.isInstanceOf(ProcessResource.plant3Dresource.OffsetComponent)) { offset.add(ControlPointTools.getSizeChangeOffsetVector(icp,u.dir)); } else if (icp.isInstanceOf(ProcessResource.plant3Dresource.DualSubControlPoint)) { ErrorLogger.defaultLogError("Updating pipe run, found offset controlpoint " + icp.getResource(), new Exception("ASSERT!")); } } } processPathLeg(u); } private static void updateOffsetPoint(PipeControlPoint sccp, Vector3d offset) { Point3d world = G3DTools.getPoint(sccp.getWorldPosition()); world.add(offset); PipeControlPoint ocp = sccp.getSubPoint().iterator().next(); ControlPointTools.setWorldPosition(ocp, world); } private static void updatePathLegPrev(PipeControlPoint start, PipeControlPoint updated, PathLegUpdateType lengthChange) { ArrayList list = new ArrayList(); PipeControlPoint end = ControlPointTools.findPreviousEnd(start,list); updatePathLegPrev(start, list, end,updated,lengthChange); } private static void updatePathLegPrev(PipeControlPoint start, ArrayList list, PipeControlPoint end, PipeControlPoint updated, PathLegUpdateType lengthChange) { // reverses the list ArrayList nextList = new ArrayList(); for (PipeControlPoint icp : list) { if (icp.isInstanceOf(ProcessResource.plant3Dresource.DualSubControlPoint)) { nextList.add(0, icp.getSubPointOf()); } else { nextList.add(0,icp); } } updatePathLeg(end, nextList, start, true,0,new ArrayList(),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, Point3d nextPoint, Point3d prevPoint, Vector3d dir) { if (DEBUG) System.out.println("PipingRules.updateInlineControlPoint() " + icp.getResource()); Point3d inlinePoint = G3DTools.getPoint(icp.getLocalPosition()); if (DEBUG) System.out.print("InlineControlPoint update "+icp.getResource() + " " + inlinePoint + " " + prevPoint + " " + nextPoint); Vector3d newInlinePoint = null; boolean branchUpdate = false; PipeControlPoint becp = null; for (PipeControlPoint pcp : icp.getSubPoint()) if (pcp.isInstanceOf(ProcessResource.plant3Dresource.UndirectedControlPoint)) { 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 = ControlPointTools.findNextEnd(becp); } else if (becp.getPrevious() != null) { p = ControlPointTools.findPreviousEnd(becp); } if (p == null) { newInlinePoint = MathTools.closestPointOnEdge(new Vector3d(inlinePoint), new Vector3d(nextPoint), new Vector3d(prevPoint)); } else { Point3d branchLegEnd = G3DTools.getPoint(p.getLocalPosition()); 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); ControlPointTools.setWorldPosition(icp, 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, Point3d start, Point3d end) { if (DEBUG) System.out.println("PipingRules.updateEndComponentControlPoint() " + ecp.getResource()); // 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); 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; Double angleO = pcp.getAtMostOneRelatedScalarDouble(ProcessResource.plant3Dresource.HasRotationAngle); double angle = 0.0; if (angleO != null) angle = angleO; AxisAngle4d aa = ControlPointTools.getControlPointWorldRotation(pcp, angle); ControlPointTools.setWorldOrientation(pcp,aa); } /** * Updates all branches when branch's position has been changed * @param bcp */ private static void updateBranchControlPointBranches(PipeControlPoint bcp) { if (DEBUG) System.out.println("PipingRules.updateBranchControlPointBranches() " + bcp.getResource()); Collection 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.getResource()); if (next == null || prev == null) return Math.PI; // FIXME : argh Point3d middlePoint = G3DTools.getPoint(tcp.getWorldPosition()); Point3d nextPoint = G3DTools.getPoint(next.getWorldPosition()); Point3d prevPoint = G3DTools.getPoint(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, Point3d middlePoint, Point3d prevPoint, Point3d 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.getResource() + " " + 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; double elbowRadius = tcp.getSingleRelatedScalarDouble(ProcessResource.plant3Dresource.HasTurnRadius); double R = elbowRadius / Math.tan(angle * 0.5); Vector3d turnAxis = new Vector3d(); turnAxis.cross(dir1, dir2); turnAxis.normalize(); tcp.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasTurnAngle,turnAngle); tcp.setRelatedScalarDouble(ProcessResource.plant3Dresource.HasLength, R);//setComponentOffsetValue(R); G3DTools.setTuple3(tcp.getSingleRelatedObject(ProcessResource.plant3Dresource.HasTurnAxis), turnAxis); if (DEBUG) System.out.println("PipingTools.updateTurnControlPointTurn " + dir1 + " " + dir2 + " " + turnAngle + " " + turnAxis); return turnAngle; } }