gpml2pvjson
Version:
convert GPML (XML) to pvjson (json)
436 lines • 33.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.postprocessPVJSON = exports.preprocessGPML = exports.DEFAULT_STUB_LENGTH = void 0;
var fp_1 = require("lodash/fp");
var gpml_utilities_1 = require("../gpml-utilities");
var geom_utils_1 = require("../geom-utils");
var calculateAllPoints_1 = require("./calculateAllPoints");
var VError = require("verror");
var MarkerMappings = require("./MarkerMappings.json");
// a stub is a short path segment that is used for the first and/or last segment(s) of a path
exports.DEFAULT_STUB_LENGTH = 20;
/**
* getOffsetAndOrientationScalarsAlongAxis
*
* @param relValue {number}
* @param axis {string}
* @param referencedEntity
* @return {OffsetOrientationAndPositionScalarsAlongAxis}
*/
function getOffsetAndOrientationScalarsAlongAxis(positionScalar, relativeOffsetScalar, axis,
// TODO are we correctly handling the case of a group as the referenced
// entity? Do we have the group width and height yet to properly calculate
// this?
referencedEntity) {
var offsetScalar = relativeOffsetScalar *
(axis === "x" ? referencedEntity.width : referencedEntity.height);
// TODO WP536 has a referenced entity that lacks width/height. Why?
// The referenced entity was a group.
// Is the problem that the group was nested?
// Or is it a problem with the order of evaluation of entities (trying to
// parse a dependent entity before its dependencies were parsed)?
if (!fp_1.isFinite(offsetScalar)) {
throw new Error("\n\t\t\tGot non-finite value " + offsetScalar + " for offsetScalar\n\t\t\talong " + axis + " axis for\n\t\t\tgetOffsetAndOrientationScalarsAlongAxis(\n\t\t\t\tpositionScalar=" + positionScalar + ",\n\t\t\t\trelativeOffsetScalar=" + relativeOffsetScalar + ",\n\t\t\t\treferencedEntity=\n\t\t\t\t" + JSON.stringify(referencedEntity, null, " ") + "\n\t\t\t)\n\t\t");
}
// orientationScalar here refers to the initial direction the edge takes as
// it moves away from the entity to which it is attached.
var orientationScalar;
if (positionScalar === 0) {
orientationScalar = -1;
}
else if (positionScalar === 1) {
orientationScalar = 1;
}
else {
orientationScalar = 0;
}
return { offsetScalar: offsetScalar, orientationScalar: orientationScalar, positionScalar: positionScalar };
}
/**
* preprocessGPML
*
* @param edge {GPMLEdge}
* @return {GPMLEdge}
*/
function preprocessGPML(Edge) {
var isAttachedToOrVia = Edge.Graphics.Point.filter(function (p) { return p.GraphRef && gpml_utilities_1.isDefinedCXML(p.GraphRef); }).map(function (p) { return p.GraphRef; });
if (isAttachedToOrVia.length > 0) {
// In pvjson, an edge attaches directly to another entity (Node, Edge, Group),
// not to an anchor.
// If the edge attaches to another edge, it does so VIA an anchor.
Edge["isAttachedToOrVia"] = isAttachedToOrVia;
}
return Edge;
}
exports.preprocessGPML = preprocessGPML;
/**
* postprocessPVJSON
*
* @param referencedEntities
* @param pvjsonEdge {pvjsonEdge}
* @return {pvjsonEdge}
*/
function postprocessPVJSON(referencedEntities, pvjsonEdge) {
var points = pvjsonEdge.points, drawAs = pvjsonEdge.drawAs;
var pointCount = points.length;
var index = 0;
var pvjsonEdgeIsAttachedTo = [];
var providedPvjsonPoints = fp_1.map(function (point) {
var marker = point.marker, x = point.x, y = point.y;
if (!!marker) {
// NOTE: side effects below
if (index === 0) {
pvjsonEdge.markerStart = marker;
}
else if (index === pointCount - 1) {
pvjsonEdge.markerEnd = marker;
}
if (MarkerMappings.hasOwnProperty(marker)) {
pvjsonEdge.type = fp_1.toPairs(MarkerMappings[marker]).reduce(function (acc, _a) {
var namespace = _a[0], moreTypes = _a[1];
return gpml_utilities_1.unionLSV(acc, moreTypes);
}, pvjsonEdge.type);
}
}
if (gpml_utilities_1.isAttachablePoint(point)) {
// NOTE: pvjson allows for expressing one edge attached to another edge.
// When we do this, we say that the POINT attaches to an ANCHOR on the other edge,
// but the EDGE attaches to the other EDGE, never the anchor.
var isAttachedTo = point.isAttachedTo, attachmentDisplay = point.attachmentDisplay;
if (!attachmentDisplay.offset) {
throw new Error("attachmentDisplay for a Point has no offset property.\n\t\t\t\t\tpostprocessPVJSON(\n\t\t\t\t\t\treferencedEntities=" + JSON.stringify(referencedEntities, null, " ") + ",\n\t\t\t\t\t\tpvjsonEdge=" + JSON.stringify(pvjsonEdge, null, " ") + "\n\t\t\t\t\t)");
}
// entityReferencedByPoint can be a regular node (DataNode, Shape, Label)
// or an Anchor. If connected to an Anchor, the biological meaning is
// that the edge is connected to another edge, but in this code, we
// implement this by treating the Anchor as a node, as if it were
// a "burr" that is always stuck (isAttachedTo) the other edge.
var entityReferencedByPoint = referencedEntities &&
!!isAttachedTo &&
referencedEntities[isAttachedTo];
var entityIdReferencedByEdge = gpml_utilities_1.isGPMLAnchor(entityReferencedByPoint)
? entityReferencedByPoint.isAttachedTo
: entityReferencedByPoint.id;
// WARNING: side effect
pvjsonEdgeIsAttachedTo.push(entityIdReferencedByEdge);
var entityReferencedByEdge = referencedEntities[entityIdReferencedByEdge];
var orientation_1 = (point.orientation = (point.orientation ||
[]));
// attachmentDisplay: { position: [x: number, y: number], offset: [xOffset: number, yOffset: number], orientation: [dx: number, dy: number] }
//
// x = xDistance / width (relative: [0,1])
// y = yDistance / height (relative: [0,1])
// xOffset = distance offset in x direction (absolute)
// yOffset = distance offset in y direction (absolute)
// dx = x component of edge emanation angle (unit: [0,1])
// dy = y component of edge emanation angle (unit: [0,1])
//
// 0 ----------------- x ------------------->
// | ========================================
// | || ||
// | || ||
// | || ||
// y || ||
// | || ||
// | || ||
// | || ||
// | || ||
// v ===================*====================
// |
// yOffset |
// | |
// v |
// ----------*
// xOffset> \
// \
// \ dx>
// dy \
// | \
// v \
// \
//
// example above is an attachmentDisplay specifying an edge that emanates down and to the right
// at a 45 deg. angle (1, 1), offset right 5 x units and down 11 y units from the center (0.5)
// of the bottom side (1) of the node: {position: [0.75, 1], offset: [5, 11], orientation: [1, 1]}
//
//
// where x is distance from left side along width axis as a percentage of the total width
// y is distance from top side along height axis as a percentage of the total height
// offsetX, offsetY are obvious from the name. Notice they are absolute, unlike x,y.
// dx, dy are unit vector coordinates of a point that specifies how the edge emanates from the node
if (gpml_utilities_1.isPvjsonSingleFreeNode(entityReferencedByEdge) ||
gpml_utilities_1.isPvjsonGroup(entityReferencedByEdge) ||
gpml_utilities_1.isPvjsonBurr(entityReferencedByEdge)) {
var position = attachmentDisplay.position, relativeOffset = attachmentDisplay.relativeOffset;
// edge connected to a SingleFreeNode, a Group or a Burr, but NOT another edge or an anchor
try {
var _a = getOffsetAndOrientationScalarsAlongAxis(position[0], relativeOffset[0], "x", entityReferencedByEdge), offsetScalarX = _a.offsetScalar, orientationScalarX = _a.orientationScalar;
var _b = getOffsetAndOrientationScalarsAlongAxis(position[1], relativeOffset[1], "y", entityReferencedByEdge), offsetScalarY = _b.offsetScalar, orientationScalarY = _b.orientationScalar;
if (index === 0) {
orientation_1[0] = orientationScalarX;
orientation_1[1] = orientationScalarY;
}
else {
orientation_1[0] = -1 * orientationScalarX;
orientation_1[1] = -1 * orientationScalarY;
}
// TODO is there a case where we would ever use offset for edges?
attachmentDisplay.offset[0] = offsetScalarX;
attachmentDisplay.offset[1] = offsetScalarY;
point.attachmentDisplay = fp_1.omit(["relativeOffset"], attachmentDisplay);
}
catch (err) {
throw new VError(err, "\n\t\t\t\t\t\tError for:\n\t\t\t\t\t\tpostprocessPVJSON(\n\t\t\t\t\t\t\treferencedEntities=" + JSON.stringify(referencedEntities, null, " ") + ",\n\t\t\t\t\t\t\tpvjsonEdge=" + JSON.stringify(pvjsonEdge, null, " ") + "\n\t\t\t\t\t\t)\n\t\t\t\t\t");
/* TODO should we use this?
console.warn(`Setting offsetScalar equal to 0.`);
offsetScalar = 0;
//*/
}
}
else if (gpml_utilities_1.isGPMLAnchor(entityReferencedByPoint)) {
// edge is connected to another edge via an anchor
point.attachmentDisplay.position =
entityReferencedByPoint.attachmentDisplay.position;
}
else {
throw new Error("\n\t\t\t\t\tEdge or Point attached to unexpected entity.\n\t\t\t\t\tPoint is attached to:\n\t\t\t\t\t" + JSON.stringify(entityReferencedByPoint, null, " ") + "\n\t\t\t\t\tPoint is attached to:\n\t\t\t\t\t" + JSON.stringify(entityReferencedByEdge, null, " ") + "\n\t\t\t\t\tfor:\n\t\t\t\t\tpostprocessPVJSON(\n\t\t\t\t\treferencedEntities=" + JSON.stringify(referencedEntities, null, " ") + ",\n\t\t\t\t\tpvjsonEdge=" + JSON.stringify(pvjsonEdge, null, " ") + "\n\t\t\t\t\t)\n\t\t\t\t");
}
}
// NOTE: side effect
index += 1;
return fp_1.omit(["marker"], point);
}, points);
var pvjsonEdgeAttachedToCount = pvjsonEdgeIsAttachedTo.length;
if (pvjsonEdgeAttachedToCount > 0) {
pvjsonEdge.isAttachedTo = pvjsonEdgeIsAttachedTo;
}
var allPvjsonPoints;
if (["StraightLine", "SegmentedLine"].indexOf(drawAs) > -1) {
allPvjsonPoints = providedPvjsonPoints;
}
else if (["ElbowLine", "CurvedLine"].indexOf(drawAs) > -1) {
// pvjsonEdge.isAttachedTo refers to what the EDGE is fundamentally attached to.
// pvjsonEdge.points[0].isAttachedTo refers to what the POINT is attached to.
//
// From the perspective of the biological meaning, the edge is always attached to
// a regular node like a DataNode or Shape (maybe Label?) but never to an Anchor.
//
// From the perspective of the implementation of the graphics, we say the edge
// has points, one or more of which can be connected to an Anchor.
var sourceEntity = void 0;
var targetEntity = void 0;
if (pvjsonEdgeAttachedToCount === 2) {
sourceEntity = referencedEntities[pvjsonEdgeIsAttachedTo[0]];
targetEntity = referencedEntities[pvjsonEdgeIsAttachedTo[1]];
}
else if (pvjsonEdgeAttachedToCount === 1) {
var firstPoint = providedPvjsonPoints[0];
var lastPoint = providedPvjsonPoints[providedPvjsonPoints.length - 1];
if (firstPoint.hasOwnProperty("isAttachedTo")) {
sourceEntity = referencedEntities[pvjsonEdgeIsAttachedTo[0]];
}
else if (lastPoint.hasOwnProperty("isAttachedTo")) {
targetEntity = referencedEntities[pvjsonEdgeIsAttachedTo[0]];
}
else {
throw new Error("edge \"" + pvjsonEdge.id + "\" is said to be attached to \"" + pvjsonEdge.isAttachedTo.join() + "\",\n\t\t\t\t\tbut neither first nor last points have \"isAttachedTo\" property");
}
}
allPvjsonPoints = calculateAllPoints_1.calculateAllPoints(providedPvjsonPoints.map(function (point) { return new geom_utils_1.SmartPoint(point); }), sourceEntity, targetEntity);
}
else {
throw new Error("\n\t\t\tUnknown edge drawer \"" + drawAs + "\" for:\n\t\t\tpostprocessPVJSON(\n\t\t\treferencedEntities=" + JSON.stringify(referencedEntities, null, " ") + ",\n\t\t\tpvjsonEdge=" + JSON.stringify(pvjsonEdge, null, " ") + "\n\t\t\t)\n\t\t");
// TODO should we use this?
// allPvjsonPoints = providedPvjsonPoints;
}
// TODO how do we distinguish between intermediate (not first or last) points that a user
// has explicitly specified vs. intermediate points that are only implied?
// Do we need to? I think once a user specifies any implicit points, they may all be
// made explicit.
// GPML currently does not specify implicit intermediate points, but
// pvjson does.
pvjsonEdge.points = allPvjsonPoints;
// TODO can I get rid of isAttachedToOrVia earlier?
return fp_1.omit(["isAttachedToOrVia"], pvjsonEdge);
}
exports.postprocessPVJSON = postprocessPVJSON;
//function recursivelyGetReferencedElements(acc, gpmlElement: GPMLElement) {
// const { Graphics } = gpmlElement;
// const graphRefIds: string[] = !!Graphics.Point &&
// Graphics.Point[0]._exists !== false
// ? Graphics.Point.filter(P => isString(P.GraphRef)).map(P => P.GraphRef)
// : gpmlElement.hasOwnProperty("GraphRef")
// ? arrayify(gpmlElement.GraphRef)
// : [];
//
// const referencedElementIds = arrayify(graphRefIds);
// //const referencedElementIds = unionLSV(graphRefIds, gpmlElement.GroupRef);
// return referencedElementIds.length === 0
// ? acc
// : hl([
// acc,
// hl(referencedElementIds)
// .flatMap(referencedElementId =>
// hl(getGPMLElementByGraphId(referencedElementId))
// )
// .flatMap(function(referencedElement: GPMLElement) {
// return recursivelyGetReferencedElements(
// hl([referencedElement]),
// referencedElement
// );
// })
// ]).merge();
//}
//
//export function postprocessPVJSON(
// pvjsonEdge: PvjsonEdge
//): Highland.Stream<PvjsonEdge> {
// return hl([
// hl([pvjsonEdge])
// .reduce(hl([]), recursivelyGetReferencedElements)
// .merge()
// .flatMap(function(referencedGPMLElement: GPMLElement) {
// return hl(
// getPvjsonEntityLatestByGraphId(
// referencedGPMLElement.GraphId
// )
// );
// })
// ])
// .merge()
// .reduce({}, function(
// acc: {
// [key: string]: (PvjsonNode | PvjsonEdge);
// },
// referencedEntity: (PvjsonNode | PvjsonEdge)
// ) {
// acc[referencedEntity.id] = referencedEntity;
// return acc;
// })
// .map(function(referencedEntities) {
// return process(pvjsonEdge, referencedEntities);
// });
// .merge();
//}
//
//export function createEdgeTransformStream(
// processor,
// edgeType: "Interaction" | "GraphicalLine"
//): (
// s: Highland.Stream<GPML2013a.InteractionType | GPML2013a.GraphicalLineType>
//) => Highland.Stream<(PvjsonNode | PvjsonEdge)> {
// const {
// fillInGPMLPropertiesFromParent,
// getGPMLElementByGraphId,
// getPvjsonEntityLatestByGraphId,
// ensureGraphIdExists,
// preprocessGPMLElement,
// processPropertiesAndType
// } = processor;
//
// function recursivelyGetReferencedElements(acc, gpmlElement: GPMLElement) {
// const { Graphics } = gpmlElement;
// const graphRefIds: string[] = !!Graphics.Point &&
// Graphics.Point[0]._exists !== false
// ? Graphics.Point.filter(P => isString(P.GraphRef)).map(P => P.GraphRef)
// : gpmlElement.hasOwnProperty("GraphRef")
// ? arrayify(gpmlElement.GraphRef)
// : [];
//
// const referencedElementIds = arrayify(graphRefIds);
// //const referencedElementIds = unionLSV(graphRefIds, gpmlElement.GroupRef);
// return referencedElementIds.length === 0
// ? acc
// : hl([
// acc,
// hl(referencedElementIds)
// .flatMap(referencedElementId =>
// hl(getGPMLElementByGraphId(referencedElementId))
// )
// .flatMap(function(referencedElement: GPMLElement) {
// return recursivelyGetReferencedElements(
// hl([referencedElement]),
// referencedElement
// );
// })
// ]).merge();
// }
//
// return function(s) {
// return s
// .map(preprocessGPMLElement)
// .flatMap(function(
// gpmlEdge: GPMLElement
// ): Highland.Stream<Highland.Stream<PvjsonNode | PvjsonEdge>> {
// const { Graphics } = gpmlEdge;
//
// const gpmlAnchors = Graphics.hasOwnProperty("Anchor") &&
// Graphics.Anchor &&
// Graphics.Anchor[0] &&
// Graphics.Anchor[0]._exists !== false
// ? Graphics.Anchor.filter(a => a.hasOwnProperty("GraphId"))
// : [];
//
// const fillInGPMLPropertiesFromEdge = fillInGPMLPropertiesFromParent(
// gpmlEdge
// );
//
// return hl([
// hl([gpmlEdge])
// .map(processPropertiesAndType(edgeType))
// .flatMap(function(pvjsonEdge: PvjsonEdge) {
// return hl([
// hl([gpmlEdge])
// .reduce(hl([]), recursivelyGetReferencedElements)
// .merge()
// .flatMap(function(referencedGPMLElement: GPMLElement) {
// return hl(
// getPvjsonEntityLatestByGraphId(
// referencedGPMLElement.GraphId
// )
// );
// })
// ])
// .merge()
// .reduce({}, function(
// acc: {
// [key: string]: (PvjsonNode | PvjsonEdge);
// },
// referencedEntity: (PvjsonNode | PvjsonEdge)
// ) {
// acc[referencedEntity.id] = referencedEntity;
// return acc;
// })
// .map(function(referencedEntities) {
// return process(pvjsonEdge, referencedEntities);
// });
// }),
// hl(gpmlAnchors)
// .map(preprocessGPMLElement)
// .map(function(gpmlAnchor: GPMLElement) {
// const filledInAnchor = fillInGPMLPropertiesFromEdge(gpmlAnchor);
// filledInAnchor.GraphRef = gpmlEdge.GraphId;
// return filledInAnchor;
// })
// .map(processPropertiesAndType("Anchor"))
// .map(function(pvjsonAnchor: PvjsonNode): PvjsonNode {
// const drawAnchorAs = pvjsonAnchor.drawAs;
// if (drawAnchorAs === "None") {
// defaultsDeep(pvjsonAnchor, {
// Height: 4,
// Width: 4
// });
// } else if (drawAnchorAs === "Circle") {
// defaultsDeep(pvjsonAnchor, {
// Height: 8,
// Width: 8
// });
// }
// return pvjsonAnchor;
// })
// ]);
// })
// .merge();
// };
//}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWRnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9lZGdlL2VkZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsZ0NBQWdFO0FBQ2hFLG9EQVEyQjtBQUMzQiw0Q0FBMkM7QUFjM0MsMkRBQTBEO0FBQzFELCtCQUFpQztBQUNqQyxzREFBd0Q7QUFFeEQsNkZBQTZGO0FBQ2hGLFFBQUEsbUJBQW1CLEdBQUcsRUFBRSxDQUFDO0FBRXRDOzs7Ozs7O0dBT0c7QUFDSCxTQUFTLHVDQUF1QyxDQUM5QyxjQUFzQixFQUN0QixvQkFBNEIsRUFDNUIsSUFBZTtBQUNmLHVFQUF1RTtBQUN2RSwwRUFBMEU7QUFDMUUsUUFBUTtBQUNSLGdCQUE0QjtJQUU1QixJQUFJLFlBQVksR0FDZCxvQkFBb0I7UUFDcEIsQ0FBQyxJQUFJLEtBQUssR0FBRyxDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3BFLG1FQUFtRTtJQUNuRSxxQ0FBcUM7SUFDckMsNENBQTRDO0lBQzVDLHlFQUF5RTtJQUN6RSxtRUFBbUU7SUFDbkUsSUFBSSxDQUFDLGFBQVEsQ0FBQyxZQUFZLENBQUMsRUFBRTtRQUMzQixNQUFNLElBQUksS0FBSyxDQUNiLGtDQUNvQixZQUFZLHVDQUMzQixJQUFJLDBGQUVNLGNBQWMsd0NBQ1Isb0JBQW9CLDhDQUV6QyxJQUFJLENBQUMsU0FBUyxDQUFDLGdCQUFnQixFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsb0JBRS9DLENBQ0UsQ0FBQztLQUNIO0lBRUQsMkVBQTJFO0lBQzNFLHlEQUF5RDtJQUN6RCxJQUFJLGlCQUFpQixDQUFDO0lBQ3RCLElBQUksY0FBYyxLQUFLLENBQUMsRUFBRTtRQUN4QixpQkFBaUIsR0FBRyxDQUFDLENBQUMsQ0FBQztLQUN4QjtTQUFNLElBQUksY0FBYyxLQUFLLENBQUMsRUFBRTtRQUMvQixpQkFBaUIs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