gpml2pvjson
Version:
convert GPML (XML) to pvjson (json)
156 lines • 11.4 kB
JavaScript
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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