UNPKG

gpml2pvjson

Version:
156 lines 11.4 kB
import { flow, isFinite, last, map } from "lodash/fp"; import { START_SEGMENT_DETAILS_MAPS, flipOrientation, sameSide, SmartPath } from "../geom-utils"; import { distance } from "../spinoffs/Angle"; import { DEFAULT_STUB_LENGTH } from "./edge"; /** * getOrientationOfHyperedgeStartPoint * * Get orientation of the start point of an edge that is attached to another * edge. * * @param referencedEdge {PvjsonEdge} * @param startPoint {Point} * @param otherPoint {Point} * @return {Orientation} */ export function getOrientationOfHyperedgeStartPoint(referencedEdge, startPoint, endPoint) { if (!referencedEdge) { throw new Error("Missing referencedEdge when calculating orientation of point attached to other edge."); } const currentPath = new SmartPath([startPoint, endPoint]); const angleOfCurrentVectorSum = currentPath.sum.angle; const referencedPath = new SmartPath(referencedEdge.points, referencedEdge); // This is the angle of the line tangent to the referenced edge at // startPoint. const angleOfReferencedEdgeAtPointOnEdge = referencedPath.position(startPoint.attachmentDisplay.position[0]).angle; const firstSegmentCalculations = START_SEGMENT_DETAILS_MAPS.map(function (startSegmentDetailsMap) { /* * referenced edge * / * / * /. * / angle (rad) * / . * /-------------- * / ^ | -------------- * / | | | | * | ------------| | * | | | * firstSegment -------------- * (of current edge) * */ const { orientation } = startSegmentDetailsMap; const [orientationX, orientationY] = orientation; const firstSegmentEndPoint = { x: startPoint.x + DEFAULT_STUB_LENGTH * orientationX, y: startPoint.y + DEFAULT_STUB_LENGTH * orientationY }; return { firstSegmentEndPoint, endPoint, emanationAngle: startSegmentDetailsMap.angle, orientation: orientation }; }) .map(function ({ firstSegmentEndPoint, endPoint, emanationAngle: emanationAngle, orientation }) { const isSameSide = sameSide(referencedEdge.points[0], last(referencedEdge.points), firstSegmentEndPoint, endPoint); const angleBetweenOrientationVectorAndCurrentEdge = distance(emanationAngle, angleOfCurrentVectorSum); const angleBetweenOrientationVectorAndReferencedEdge = distance(angleOfReferencedEdgeAtPointOnEdge, emanationAngle); return { isSameSide, angleBetweenOrientationVectorAndCurrentEdge, angleBetweenOrientationVectorAndVectorPerpendicularToReferencedEdge: Math.abs(angleBetweenOrientationVectorAndReferencedEdge - Math.PI / 2), orientation }; }) // Sorting by three criteria, with the most important first: // 1. The second point and the end point of the current edge are on the // same of the referenced edge. // 2. The emanation angle is as close as possible to being perpendicular to // the referenced edge. // 3. The angle of the first segment matches the angle of the current edge. .sort(function (a, b) { if (a.isSameSide && !b.isSameSide) { return -1; } else if (!a.isSameSide && b.isSameSide) { return 1; } else { if (a.angleBetweenOrientationVectorAndVectorPerpendicularToReferencedEdge < b.angleBetweenOrientationVectorAndVectorPerpendicularToReferencedEdge) { return -1; } else if (a.angleBetweenOrientationVectorAndVectorPerpendicularToReferencedEdge > b.angleBetweenOrientationVectorAndVectorPerpendicularToReferencedEdge) { return 1; } else { if (a.angleBetweenOrientationVectorAndCurrentEdge < b.angleBetweenOrientationVectorAndCurrentEdge) { return -1; } else if (a.angleBetweenOrientationVectorAndCurrentEdge > b.angleBetweenOrientationVectorAndCurrentEdge) { return 1; } else { return 0; } } } }); const formatted = map(function (firstSegmentCalculation) { const { angleBetweenOrientationVectorAndVectorPerpendicularToReferencedEdge, angleBetweenOrientationVectorAndCurrentEdge, isSameSide } = firstSegmentCalculation; return { angleBetweenOrientationVectorAndVectorPerpendicularToReferencedEdge: String(angleBetweenOrientationVectorAndVectorPerpendicularToReferencedEdge / Math.PI) + " * PI", angleBetweenOrientationVectorAndCurrentEdge: String(angleBetweenOrientationVectorAndCurrentEdge / Math.PI) + " * PI", isSameSide }; }, firstSegmentCalculations); return firstSegmentCalculations[0].orientation; } // Uses the same sorting criteria as getOrientationOfHyperedgeStartPoint, // except with the obvious changes in direction to account for being the // endPoint instead of the startPoint. export const getOrientationOfHyperedgeEndPoint = flow(getOrientationOfHyperedgeStartPoint, flipOrientation); // Validate orientation of attachment display. // // When attached to a normal Node, we can calculate the orientation immediately // so this will return true right away. // When attached to an Edge or Group, we need to do more processing, // so this will at least initially return false. // TODO: Not sure whether the orientation of a point attached to an Edge or a // Group is ever set. export function validateOrientation(orientation) { return !!orientation && isFinite(orientation[0]) && isFinite(orientation[1]); } /* // NOTE: PathVisio-Java appears to try to avoid making an edge // terminate into another edge at an approach angle of anything // less than about 30 deg. if (endPoint.hasOwnProperty("isAttachedTo")) { const referencedEndPath = new SmartPath(endEntity.points, endEntity); const angleOfReferencedEdgeAtPointOnEdge = referencedEndPath.position( startPoint.attachmentDisplay.position[0] ).angle; if ( Math.abs( START_SIDE_TO_EMANATION_ANGLE_MAPPINGS[endSide] - angleOfReferencedEdgeAtPointOnEdge ) < 30 * (Math.PI / 180) ) { // edge1 is almost parallel with edge2, making it hard to read, so we need to recalculate. endPoint.orientation = getOrientationOfHyperedgeEndPoint( endEntity, endPoint, startPoint ); return getAndCompareSides(startPoint, endPoint, endEntity); } } //*/ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JpZW50YXRpb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZWRnZS9vcmllbnRhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3RELE9BQU8sRUFDTCwwQkFBMEIsRUFDMUIsZUFBZSxFQUNmLFFBQVEsRUFFUixTQUFTLEVBQ1YsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBUTdDLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUU3Qzs7Ozs7Ozs7OztHQVVHO0FBQ0gsTUFBTSxVQUFVLG1DQUFtQyxDQUNqRCxjQUEwQixFQUMxQixVQUF3QyxFQUN4QyxRQUFzQztJQUV0QyxJQUFJLENBQUMsY0FBYyxFQUFFO1FBQ25CLE1BQU0sSUFBSSxLQUFLLENBQ2Isc0ZBQXNGLENBQ3ZGLENBQUM7S0FDSDtJQUVELE1BQU0sV0FBVyxHQUFHLElBQUksU0FBUyxDQUFDLENBQUMsVUFBVSxFQUFFLFFBQVEsQ0FBQyxDQUFDLENBQUM7SUFDMUQsTUFBTSx1QkFBdUIsR0FBRyxXQUFXLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQztJQUN0RCxNQUFNLGNBQWMsR0FBRyxJQUFJLFNBQVMsQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFLGNBQWMsQ0FBQyxDQUFDO0lBRTVFLGtFQUFrRTtJQUNsRSxjQUFjO0lBQ2QsTUFBTSxrQ0FBa0MsR0FBRyxjQUFjLENBQUMsUUFBUSxDQUNoRSxVQUFVLENBQUMsaUJBQWlCLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUN6QyxDQUFDLEtBQUssQ0FBQztJQUVSLE1BQU0sd0JBQXdCLEdBQUcsMEJBQTBCLENBQUMsR0FBRyxDQUFDLFVBQzlELHNCQUFzQjtRQUV0Qjs7Ozs7Ozs7Ozs7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