UNPKG

gpml2pvjson

Version:
436 lines 33.2 kB
"use strict"; 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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