UNPKG

33.6 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var fp_1 = require("lodash/fp");
4var gpml_utilities_1 = require("../gpml-utilities");
5var geom_utils_1 = require("../geom-utils");
6var calculateAllPoints_1 = require("./calculateAllPoints");
7var VError = require("verror");
8var MarkerMappings = require("./MarkerMappings.json");
9// a stub is a short path segment that is used for the first and/or last segment(s) of a path
10exports.DEFAULT_STUB_LENGTH = 20;
11/**
12 * getOffsetAndOrientationScalarsAlongAxis
13 *
14 * @param relValue {number}
15 * @param axis {string}
16 * @param referencedEntity
17 * @return {OffsetOrientationAndPositionScalarsAlongAxis}
18 */
19function getOffsetAndOrientationScalarsAlongAxis(positionScalar, relativeOffsetScalar, axis,
20// TODO are we correctly handling the case of a group as the referenced
21// entity? Do we have the group width and height yet to properly calculate
22// this?
23referencedEntity) {
24 var offsetScalar = relativeOffsetScalar *
25 (axis === "x" ? referencedEntity.width : referencedEntity.height);
26 // TODO WP536 has a referenced entity that lacks width/height. Why?
27 // The referenced entity was a group.
28 // Is the problem that the group was nested?
29 // Or is it a problem with the order of evaluation of entities (trying to
30 // parse a dependent entity before its dependencies were parsed)?
31 if (!fp_1.isFinite(offsetScalar)) {
32 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");
33 }
34 // orientationScalar here refers to the initial direction the edge takes as
35 // it moves away from the entity to which it is attached.
36 var orientationScalar;
37 if (positionScalar === 0) {
38 orientationScalar = -1;
39 }
40 else if (positionScalar === 1) {
41 orientationScalar = 1;
42 }
43 else {
44 orientationScalar = 0;
45 }
46 return { offsetScalar: offsetScalar, orientationScalar: orientationScalar, positionScalar: positionScalar };
47}
48/**
49 * preprocessGPML
50 *
51 * @param edge {GPMLEdge}
52 * @return {GPMLEdge}
53 */
54function preprocessGPML(Edge) {
55 var isAttachedToOrVia = Edge.Graphics.Point
56 .filter(function (p) { return p.GraphRef && gpml_utilities_1.isDefinedCXML(p.GraphRef); })
57 .map(function (p) { return p.GraphRef; });
58 if (isAttachedToOrVia.length > 0) {
59 // In pvjson, an edge attaches directly to another entity (Node, Edge, Group),
60 // not to an anchor.
61 // If the edge attaches to another edge, it does so VIA an anchor.
62 Edge["isAttachedToOrVia"] = isAttachedToOrVia;
63 }
64 return Edge;
65}
66exports.preprocessGPML = preprocessGPML;
67/**
68 * postprocessPVJSON
69 *
70 * @param referencedEntities
71 * @param pvjsonEdge {pvjsonEdge}
72 * @return {pvjsonEdge}
73 */
74function postprocessPVJSON(referencedEntities, pvjsonEdge) {
75 var points = pvjsonEdge.points, drawAs = pvjsonEdge.drawAs;
76 var pointCount = points.length;
77 var index = 0;
78 var pvjsonEdgeIsAttachedTo = [];
79 var providedPvjsonPoints = fp_1.map(function (point) {
80 var marker = point.marker, x = point.x, y = point.y;
81 if (!!marker) {
82 // NOTE: side effects below
83 if (index === 0) {
84 pvjsonEdge.markerStart = marker;
85 }
86 else if (index === pointCount - 1) {
87 pvjsonEdge.markerEnd = marker;
88 }
89 if (MarkerMappings.hasOwnProperty(marker)) {
90 pvjsonEdge.type = fp_1.toPairs(MarkerMappings[marker]).reduce(function (acc, _a) {
91 var namespace = _a[0], moreTypes = _a[1];
92 return gpml_utilities_1.unionLSV(acc, moreTypes);
93 }, pvjsonEdge.type);
94 }
95 }
96 if (gpml_utilities_1.isAttachablePoint(point)) {
97 // NOTE: pvjson allows for expressing one edge attached to another edge.
98 // When we do this, we say that the POINT attaches to an ANCHOR on the other edge,
99 // but the EDGE attaches to the other EDGE, never the anchor.
100 var isAttachedTo = point.isAttachedTo, attachmentDisplay = point.attachmentDisplay;
101 if (!attachmentDisplay.offset) {
102 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)");
103 }
104 // entityReferencedByPoint can be a regular node (DataNode, Shape, Label)
105 // or an Anchor. If connected to an Anchor, the biological meaning is
106 // that the edge is connected to another edge, but in this code, we
107 // implement this by treating the Anchor as a node, as if it were
108 // a "burr" that is always stuck (isAttachedTo) the other edge.
109 var entityReferencedByPoint = referencedEntities &&
110 !!isAttachedTo &&
111 referencedEntities[isAttachedTo];
112 var entityIdReferencedByEdge = gpml_utilities_1.isGPMLAnchor(entityReferencedByPoint)
113 ? entityReferencedByPoint.isAttachedTo
114 : entityReferencedByPoint.id;
115 // WARNING: side effect
116 pvjsonEdgeIsAttachedTo.push(entityIdReferencedByEdge);
117 var entityReferencedByEdge = referencedEntities[entityIdReferencedByEdge];
118 var orientation_1 = (point.orientation =
119 point.orientation || []);
120 // attachmentDisplay: { position: [x: number, y: number], offset: [xOffset: number, yOffset: number], orientation: [dx: number, dy: number] }
121 //
122 // x = xDistance / width (relative: [0,1])
123 // y = yDistance / height (relative: [0,1])
124 // xOffset = distance offset in x direction (absolute)
125 // yOffset = distance offset in y direction (absolute)
126 // dx = x component of edge emanation angle (unit: [0,1])
127 // dy = y component of edge emanation angle (unit: [0,1])
128 //
129 // 0 ----------------- x ------------------->
130 // | ========================================
131 // | || ||
132 // | || ||
133 // | || ||
134 // y || ||
135 // | || ||
136 // | || ||
137 // | || ||
138 // | || ||
139 // v ===================*====================
140 // |
141 // yOffset |
142 // | |
143 // v |
144 // ----------*
145 // xOffset> \
146 // \
147 // \ dx>
148 // dy \
149 // | \
150 // v \
151 // \
152 //
153 // example above is an attachmentDisplay specifying an edge that emanates down and to the right
154 // at a 45 deg. angle (1, 1), offset right 5 x units and down 11 y units from the center (0.5)
155 // of the bottom side (1) of the node: {position: [0.75, 1], offset: [5, 11], orientation: [1, 1]}
156 //
157 //
158 // where x is distance from left side along width axis as a percentage of the total width
159 // y is distance from top side along height axis as a percentage of the total height
160 // offsetX, offsetY are obvious from the name. Notice they are absolute, unlike x,y.
161 // dx, dy are unit vector coordinates of a point that specifies how the edge emanates from the node
162 if (gpml_utilities_1.isPvjsonSingleFreeNode(entityReferencedByEdge) ||
163 gpml_utilities_1.isPvjsonGroup(entityReferencedByEdge) ||
164 gpml_utilities_1.isPvjsonBurr(entityReferencedByEdge)) {
165 var position = attachmentDisplay.position, relativeOffset = attachmentDisplay.relativeOffset;
166 // edge connected to a SingleFreeNode, a Group or a Burr, but NOT another edge or an anchor
167 try {
168 var _a = getOffsetAndOrientationScalarsAlongAxis(position[0], relativeOffset[0], "x", entityReferencedByEdge), offsetScalarX = _a.offsetScalar, orientationScalarX = _a.orientationScalar;
169 var _b = getOffsetAndOrientationScalarsAlongAxis(position[1], relativeOffset[1], "y", entityReferencedByEdge), offsetScalarY = _b.offsetScalar, orientationScalarY = _b.orientationScalar;
170 if (index === 0) {
171 orientation_1[0] = orientationScalarX;
172 orientation_1[1] = orientationScalarY;
173 }
174 else {
175 orientation_1[0] = -1 * orientationScalarX;
176 orientation_1[1] = -1 * orientationScalarY;
177 }
178 // TODO is there a case where we would ever use offset for edges?
179 attachmentDisplay.offset[0] = offsetScalarX;
180 attachmentDisplay.offset[1] = offsetScalarY;
181 point.attachmentDisplay = fp_1.omit(["relativeOffset"], attachmentDisplay);
182 }
183 catch (err) {
184 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");
185 /* TODO should we use this?
186 console.warn(`Setting offsetScalar equal to 0.`);
187 offsetScalar = 0;
188 //*/
189 }
190 }
191 else if (gpml_utilities_1.isGPMLAnchor(entityReferencedByPoint)) {
192 // edge is connected to another edge via an anchor
193 point.attachmentDisplay.position =
194 entityReferencedByPoint.attachmentDisplay.position;
195 }
196 else {
197 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");
198 }
199 }
200 // NOTE: side effect
201 index += 1;
202 return fp_1.omit(["marker"], point);
203 }, points);
204 var pvjsonEdgeAttachedToCount = pvjsonEdgeIsAttachedTo.length;
205 if (pvjsonEdgeAttachedToCount > 0) {
206 pvjsonEdge.isAttachedTo = pvjsonEdgeIsAttachedTo;
207 }
208 var allPvjsonPoints;
209 if (["StraightLine", "SegmentedLine"].indexOf(drawAs) > -1) {
210 allPvjsonPoints = providedPvjsonPoints;
211 }
212 else if (["ElbowLine", "CurvedLine"].indexOf(drawAs) > -1) {
213 // pvjsonEdge.isAttachedTo refers to what the EDGE is fundamentally attached to.
214 // pvjsonEdge.points[0].isAttachedTo refers to what the POINT is attached to.
215 //
216 // From the perspective of the biological meaning, the edge is always attached to
217 // a regular node like a DataNode or Shape (maybe Label?) but never to an Anchor.
218 //
219 // From the perspective of the implementation of the graphics, we say the edge
220 // has points, one or more of which can be connected to an Anchor.
221 var sourceEntity = void 0;
222 var targetEntity = void 0;
223 if (pvjsonEdgeAttachedToCount === 2) {
224 sourceEntity = referencedEntities[pvjsonEdgeIsAttachedTo[0]];
225 targetEntity = referencedEntities[pvjsonEdgeIsAttachedTo[1]];
226 }
227 else if (pvjsonEdgeAttachedToCount === 1) {
228 var firstPoint = providedPvjsonPoints[0];
229 var lastPoint = providedPvjsonPoints[providedPvjsonPoints.length - 1];
230 if (firstPoint.hasOwnProperty("isAttachedTo")) {
231 sourceEntity = referencedEntities[pvjsonEdgeIsAttachedTo[0]];
232 }
233 else if (lastPoint.hasOwnProperty("isAttachedTo")) {
234 targetEntity = referencedEntities[pvjsonEdgeIsAttachedTo[0]];
235 }
236 else {
237 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");
238 }
239 }
240 allPvjsonPoints = calculateAllPoints_1.calculateAllPoints(providedPvjsonPoints.map(function (point) { return new geom_utils_1.SmartPoint(point); }), sourceEntity, targetEntity);
241 }
242 else {
243 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");
244 // TODO should we use this?
245 // allPvjsonPoints = providedPvjsonPoints;
246 }
247 // TODO how do we distinguish between intermediate (not first or last) points that a user
248 // has explicitly specified vs. intermediate points that are only implied?
249 // Do we need to? I think once a user specifies any implicit points, they may all be
250 // made explicit.
251 // GPML currently does not specify implicit intermediate points, but
252 // pvjson does.
253 pvjsonEdge.points = allPvjsonPoints;
254 // TODO can I get rid of isAttachedToOrVia earlier?
255 return fp_1.omit(["isAttachedToOrVia"], pvjsonEdge);
256}
257exports.postprocessPVJSON = postprocessPVJSON;
258//function recursivelyGetReferencedElements(acc, gpmlElement: GPMLElement) {
259// const { Graphics } = gpmlElement;
260// const graphRefIds: string[] = !!Graphics.Point &&
261// Graphics.Point[0]._exists !== false
262// ? Graphics.Point.filter(P => isString(P.GraphRef)).map(P => P.GraphRef)
263// : gpmlElement.hasOwnProperty("GraphRef")
264// ? arrayify(gpmlElement.GraphRef)
265// : [];
266//
267// const referencedElementIds = arrayify(graphRefIds);
268// //const referencedElementIds = unionLSV(graphRefIds, gpmlElement.GroupRef);
269// return referencedElementIds.length === 0
270// ? acc
271// : hl([
272// acc,
273// hl(referencedElementIds)
274// .flatMap(referencedElementId =>
275// hl(getGPMLElementByGraphId(referencedElementId))
276// )
277// .flatMap(function(referencedElement: GPMLElement) {
278// return recursivelyGetReferencedElements(
279// hl([referencedElement]),
280// referencedElement
281// );
282// })
283// ]).merge();
284//}
285//
286//export function postprocessPVJSON(
287// pvjsonEdge: PvjsonEdge
288//): Highland.Stream<PvjsonEdge> {
289// return hl([
290// hl([pvjsonEdge])
291// .reduce(hl([]), recursivelyGetReferencedElements)
292// .merge()
293// .flatMap(function(referencedGPMLElement: GPMLElement) {
294// return hl(
295// getPvjsonEntityLatestByGraphId(
296// referencedGPMLElement.GraphId
297// )
298// );
299// })
300// ])
301// .merge()
302// .reduce({}, function(
303// acc: {
304// [key: string]: (PvjsonNode | PvjsonEdge);
305// },
306// referencedEntity: (PvjsonNode | PvjsonEdge)
307// ) {
308// acc[referencedEntity.id] = referencedEntity;
309// return acc;
310// })
311// .map(function(referencedEntities) {
312// return process(pvjsonEdge, referencedEntities);
313// });
314// .merge();
315//}
316//
317//export function createEdgeTransformStream(
318// processor,
319// edgeType: "Interaction" | "GraphicalLine"
320//): (
321// s: Highland.Stream<GPML2013a.InteractionType | GPML2013a.GraphicalLineType>
322//) => Highland.Stream<(PvjsonNode | PvjsonEdge)> {
323// const {
324// fillInGPMLPropertiesFromParent,
325// getGPMLElementByGraphId,
326// getPvjsonEntityLatestByGraphId,
327// ensureGraphIdExists,
328// preprocessGPMLElement,
329// processPropertiesAndType
330// } = processor;
331//
332// function recursivelyGetReferencedElements(acc, gpmlElement: GPMLElement) {
333// const { Graphics } = gpmlElement;
334// const graphRefIds: string[] = !!Graphics.Point &&
335// Graphics.Point[0]._exists !== false
336// ? Graphics.Point.filter(P => isString(P.GraphRef)).map(P => P.GraphRef)
337// : gpmlElement.hasOwnProperty("GraphRef")
338// ? arrayify(gpmlElement.GraphRef)
339// : [];
340//
341// const referencedElementIds = arrayify(graphRefIds);
342// //const referencedElementIds = unionLSV(graphRefIds, gpmlElement.GroupRef);
343// return referencedElementIds.length === 0
344// ? acc
345// : hl([
346// acc,
347// hl(referencedElementIds)
348// .flatMap(referencedElementId =>
349// hl(getGPMLElementByGraphId(referencedElementId))
350// )
351// .flatMap(function(referencedElement: GPMLElement) {
352// return recursivelyGetReferencedElements(
353// hl([referencedElement]),
354// referencedElement
355// );
356// })
357// ]).merge();
358// }
359//
360// return function(s) {
361// return s
362// .map(preprocessGPMLElement)
363// .flatMap(function(
364// gpmlEdge: GPMLElement
365// ): Highland.Stream<Highland.Stream<PvjsonNode | PvjsonEdge>> {
366// const { Graphics } = gpmlEdge;
367//
368// const gpmlAnchors = Graphics.hasOwnProperty("Anchor") &&
369// Graphics.Anchor &&
370// Graphics.Anchor[0] &&
371// Graphics.Anchor[0]._exists !== false
372// ? Graphics.Anchor.filter(a => a.hasOwnProperty("GraphId"))
373// : [];
374//
375// const fillInGPMLPropertiesFromEdge = fillInGPMLPropertiesFromParent(
376// gpmlEdge
377// );
378//
379// return hl([
380// hl([gpmlEdge])
381// .map(processPropertiesAndType(edgeType))
382// .flatMap(function(pvjsonEdge: PvjsonEdge) {
383// return hl([
384// hl([gpmlEdge])
385// .reduce(hl([]), recursivelyGetReferencedElements)
386// .merge()
387// .flatMap(function(referencedGPMLElement: GPMLElement) {
388// return hl(
389// getPvjsonEntityLatestByGraphId(
390// referencedGPMLElement.GraphId
391// )
392// );
393// })
394// ])
395// .merge()
396// .reduce({}, function(
397// acc: {
398// [key: string]: (PvjsonNode | PvjsonEdge);
399// },
400// referencedEntity: (PvjsonNode | PvjsonEdge)
401// ) {
402// acc[referencedEntity.id] = referencedEntity;
403// return acc;
404// })
405// .map(function(referencedEntities) {
406// return process(pvjsonEdge, referencedEntities);
407// });
408// }),
409// hl(gpmlAnchors)
410// .map(preprocessGPMLElement)
411// .map(function(gpmlAnchor: GPMLElement) {
412// const filledInAnchor = fillInGPMLPropertiesFromEdge(gpmlAnchor);
413// filledInAnchor.GraphRef = gpmlEdge.GraphId;
414// return filledInAnchor;
415// })
416// .map(processPropertiesAndType("Anchor"))
417// .map(function(pvjsonAnchor: PvjsonNode): PvjsonNode {
418// const drawAnchorAs = pvjsonAnchor.drawAs;
419// if (drawAnchorAs === "None") {
420// defaultsDeep(pvjsonAnchor, {
421// Height: 4,
422// Width: 4
423// });
424// } else if (drawAnchorAs === "Circle") {
425// defaultsDeep(pvjsonAnchor, {
426// Height: 8,
427// Width: 8
428// });
429// }
430// return pvjsonAnchor;
431// })
432// ]);
433// })
434// .merge();
435// };
436//}
437//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWRnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9lZGdlL2VkZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxnQ0FBZ0U7QUFDaEUsb0RBUTJCO0FBQzNCLDRDQUEyQztBQWMzQywyREFBMEQ7QUFDMUQsK0JBQWlDO0FBQ2pDLHNEQUF3RDtBQUV4RCw2RkFBNkY7QUFDaEYsUUFBQSxtQkFBbUIsR0FBRyxFQUFFLENBQUM7QUFFdEM7Ozs7Ozs7R0FPRztBQUNILGlEQUNFLGNBQXNCLEVBQ3RCLG9CQUE0QixFQUM1QixJQUFlO0FBQ2YsdUVBQXVFO0FBQ3ZFLDBFQUEwRTtBQUMxRSxRQUFRO0FBQ1IsZ0JBQTRCO0lBRTVCLElBQUksWUFBWSxHQUNkLG9CQUFvQjtRQUNwQixDQUFDLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLGdCQUFnQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDcEUsbUVBQW1FO0lBQ25FLHFDQUFxQztJQUNyQyw0Q0FBNEM7SUFDNUMseUVBQXlFO0lBQ3pFLG1FQUFtRTtJQUNuRSxFQUFFLENBQUMsQ0FBQyxDQUFDLGFBQVEsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDNUIsTUFBTSxJQUFJLEtBQUssQ0FDYixrQ0FDb0IsWUFBWSx1Q0FDM0IsSUFBSSwwRkFFTSxjQUFjLHdDQUNSLG9CQUFvQiw4Q0FFekMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxnQkFBZ0IsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLG9CQUUvQyxDQUNFLENBQUM7SUFDSixDQUFDO0lBRUQsMkVBQTJFO0lBQzNFLHlEQUF5RDtJQUN6RCxJQUFJLGlCQUFpQixDQUFDO0lBQ3RCLEVBQUUsQ0FBQyxDQUFDLGNBQWMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3pCLGlCQUFpQixHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ3pCLENBQUM7SUFBQyxJ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
\No newline at end of file