UNPKG

26.1 kBJavaScriptView Raw
1import { findIndex } from "lodash/fp";
2import { getStartSideByOrientation } from "../geom-utils";
3import { DEFAULT_STUB_LENGTH } from "./edge";
4import { getOrientationOfHyperedgeStartPoint, getOrientationOfHyperedgeEndPoint, validateOrientation } from "./orientation";
5const INDEX_TO_DIMENSION = ["x", "y"];
6function getActiveOrientationIndexAndDimension(orientation) {
7 const activeOrientationIndex = findIndex((orientationScalar) => orientationScalar !== 0, orientation);
8 const activeOrientationDimension = INDEX_TO_DIMENSION[activeOrientationIndex];
9 const otherOrientationDimension = activeOrientationDimension === "x"
10 ? "y"
11 : "x";
12 return {
13 activeOrientationIndex,
14 activeOrientationDimension,
15 otherOrientationDimension
16 };
17}
18/**
19 * calculateAllPoints for edges of type Elbow and Curved
20 *
21 * PathVisio-Java does not always specify all the points needed to draw edges
22 * of type Elbow and Curved. Unless the user drags one or more of the
23 * waypoints, PathVisio-Java will only specify the first and last points,
24 * leaving implicit one or more additional points that are required to draw
25 * the edge.
26 *
27 * Kaavio requires that a PvjsonEdge specifies ALL the points required for
28 * drawing the edge, so this function calculates any implicit points required
29 * to unambiguously specify an edge and returns the full set of points
30 * (implicit points are made explicit).
31 *
32 * @param explicitPoints {Array}
33 * @param [sourceEntity] {Object} entity from which the EDGE emanates
34 * (never an Anchor)
35 * @param [targetEntity] {Object} entity into which the EDGE terminates
36 * (never an Anchor)
37 * @return {Array} Full set of points required to render the edge
38 */
39export function calculateAllPoints(explicitPoints, sourceEntity, targetEntity) {
40 let firstPoint = explicitPoints[0];
41 let lastPoint = explicitPoints[explicitPoints.length - 1];
42 // NOTE: we need at least one of the first point or the last point to have a
43 // valid orientation. If that's not the case already, we try setting it here,
44 // based on other information available to us.
45 if (!validateOrientation(firstPoint.orientation)) {
46 if (firstPoint.hasOwnProperty("isAttachedTo")) {
47 // It is correct to specify <PvjsonEdge> as the type for
48 // sourceEntity/targetEntity when calculating the orientation of a point
49 // attached to another edge below, because we get into there if neither
50 // the first nor last point have a valid orientation. If a point is
51 // attached to a SingleFreeNode, a Group or a GPML State, it would already
52 // have a valid orientation calculated by this point, so the point must
53 // be either attached to nothing or else attached to an edge.
54 firstPoint.orientation = getOrientationOfHyperedgeStartPoint(sourceEntity, firstPoint, lastPoint);
55 }
56 else {
57 firstPoint.orientation = [-1, 0];
58 }
59 }
60 if (!validateOrientation(lastPoint.orientation)) {
61 if (lastPoint.hasOwnProperty("isAttachedTo")) {
62 // It is correct to specify <PvjsonEdge> as the type for
63 // sourceEntity/targetEntity when calculating the orientation of a point
64 // attached to another edge below, because we get into there if neither
65 // the first nor last point have a valid orientation. If a point is
66 // attached to a SingleFreeNode, a Group or a GPML State, it would already
67 // have a valid orientation calculated by this point, so the point must
68 // be either attached to nothing or else attached to an edge.
69 lastPoint.orientation = getOrientationOfHyperedgeEndPoint(targetEntity, lastPoint, firstPoint);
70 }
71 else {
72 const { x: x0, y: y0 } = firstPoint;
73 const { x: x1, y: y1 } = lastPoint;
74 const firstSide = getStartSideByOrientation(firstPoint.orientation);
75 if (firstSide === "left") {
76 if (x0 >= x1 && x0 < x1 + DEFAULT_STUB_LENGTH) {
77 lastPoint.orientation = [1, 0];
78 }
79 else {
80 lastPoint.orientation = [-1, 0];
81 }
82 }
83 else if (firstSide === "right") {
84 if (x0 + DEFAULT_STUB_LENGTH <= x1) {
85 lastPoint.orientation = [1, 0];
86 }
87 else {
88 lastPoint.orientation = [-1, 0];
89 }
90 }
91 else {
92 lastPoint.orientation = [-1, 0];
93 }
94 }
95 }
96 if (explicitPoints.length > 2) {
97 return explicitPoints;
98 }
99 let startPoint;
100 let endPoint;
101 let endEntity;
102 let pointOrderReversed;
103 if (validateOrientation(firstPoint.orientation)) {
104 pointOrderReversed = false;
105 startPoint = firstPoint;
106 endPoint = lastPoint;
107 endEntity = targetEntity;
108 }
109 else if (validateOrientation(lastPoint.orientation)) {
110 pointOrderReversed = true;
111 startPoint = lastPoint;
112 endPoint = firstPoint;
113 endEntity = sourceEntity;
114 }
115 else {
116 throw new Error(`Either first or last point (or both) should have a valid
117 orientation by now in
118 calculateAllPoints(
119 ${JSON.stringify(explicitPoints)},
120 ${JSON.stringify(sourceEntity)},
121 ${JSON.stringify(targetEntity)}
122 )`);
123 }
124 const startOrientation = startPoint.orientation;
125 const endOrientation = endPoint.orientation;
126 const vectorSumOrientation = [
127 Math.sign(endPoint.x - startPoint.x),
128 Math.sign(endPoint.y - startPoint.y)
129 ];
130 const { activeOrientationIndex: activeStartOrientationIndex, activeOrientationDimension: activeStartOrientationDimension, otherOrientationDimension: otherStartOrientationDimension } = getActiveOrientationIndexAndDimension(startOrientation);
131 const { activeOrientationIndex: activeEndOrientationIndex, activeOrientationDimension: activeEndOrientationDimension, otherOrientationDimension: otherEndOrientationDimension } = getActiveOrientationIndexAndDimension(endOrientation);
132 const pvjsonPoints = [];
133 pvjsonPoints.push(startPoint);
134 // Calculate intermediate data points, which are implicit.
135 // Remember that this refers to the minimum number of points required to
136 // define the path, so 3 points could mean this:
137 //
138 // -------------------*-------------------
139 // | |
140 // | |
141 // * *
142 //
143 // or this:
144 // *
145 // |
146 // |
147 // -------------------*-------------------
148 // |
149 // |
150 // *
151 //
152 // or several other possible configurations
153 // NOTE: when an edge is connected to a SingleFreeNode or a Group (how about a State?),
154 // PathVisio-Java will route the edge around the side from which the edge
155 // emanates, if needed.
156 // But when an edge is connected to another edge, PathVisio-Java
157 // does not do any special re-routing for that connection.
158 if (activeStartOrientationIndex === activeEndOrientationIndex) {
159 // Start and end orientations are parallel, e.g.,
160 // starts at right and ends on either right or left side, or
161 // starts on top and ends on either top or bottom side.
162 const activeOrientationIndex = activeStartOrientationIndex;
163 const activeOrientationDimension = activeStartOrientationDimension;
164 const otherOrientationDimension = otherStartOrientationDimension;
165 const otherOrientationDimensionDisplacement = endPoint[otherOrientationDimension] -
166 startPoint[otherOrientationDimension];
167 if (startOrientation[activeOrientationIndex] ===
168 vectorSumOrientation[activeOrientationIndex]) {
169 // we don't have to avoid the start side
170 pvjsonPoints[1] = {};
171 pvjsonPoints[1][otherOrientationDimension] =
172 startPoint[otherOrientationDimension] +
173 otherOrientationDimensionDisplacement / 2;
174 if (startOrientation[activeOrientationIndex] ===
175 endOrientation[activeOrientationIndex]) {
176 // *---
177 // |
178 // |
179 // *
180 // |
181 // |
182 // ---------------------*
183 pvjsonPoints[1][activeOrientationDimension] =
184 startPoint[activeOrientationDimension] +
185 startOrientation[activeOrientationIndex] * DEFAULT_STUB_LENGTH;
186 }
187 else {
188 // *-------------------------
189 // |
190 // |
191 // *
192 // |
193 // |
194 // *---
195 pvjsonPoints[1][activeOrientationDimension] =
196 endPoint[activeOrientationDimension] -
197 endOrientation[activeOrientationIndex] * DEFAULT_STUB_LENGTH;
198 }
199 }
200 else {
201 // must initially route around start side
202 if (startOrientation[activeOrientationIndex] ===
203 endOrientation[activeOrientationIndex]) {
204 // *---
205 // |
206 // |
207 // *
208 // |
209 // |
210 // -----------*-----------
211 // |
212 // |
213 // *
214 // |
215 // |
216 // ---*
217 pvjsonPoints[1] = {};
218 pvjsonPoints[1][activeOrientationDimension] =
219 startPoint[activeOrientationDimension] +
220 startOrientation[activeOrientationIndex] * DEFAULT_STUB_LENGTH;
221 pvjsonPoints[1][otherOrientationDimension] =
222 startPoint[otherOrientationDimension] +
223 otherOrientationDimensionDisplacement / 4;
224 pvjsonPoints[2] = {};
225 pvjsonPoints[2][activeOrientationDimension] =
226 (startPoint[activeOrientationDimension] +
227 endPoint[activeOrientationDimension]) /
228 2;
229 pvjsonPoints[2][otherOrientationDimension] =
230 startPoint[otherOrientationDimension] +
231 otherOrientationDimensionDisplacement / 2;
232 pvjsonPoints[3] = {};
233 pvjsonPoints[3][activeOrientationDimension] =
234 endPoint[activeOrientationDimension] -
235 endOrientation[activeOrientationIndex] * DEFAULT_STUB_LENGTH;
236 pvjsonPoints[3][otherOrientationDimension] =
237 startPoint[otherOrientationDimension] +
238 3 * otherOrientationDimensionDisplacement / 4;
239 }
240 else {
241 // *---
242 // |
243 // |
244 // *
245 // |
246 // |
247 // *---------------------
248 pvjsonPoints[1] = {};
249 pvjsonPoints[1][activeOrientationDimension] =
250 startPoint[activeOrientationDimension] +
251 startOrientation[activeOrientationIndex] * DEFAULT_STUB_LENGTH;
252 pvjsonPoints[1][otherOrientationDimension] =
253 startPoint[otherOrientationDimension] +
254 otherOrientationDimensionDisplacement / 2;
255 }
256 }
257 }
258 else {
259 // Start and end orientations are perpendicular
260 if (startOrientation[activeStartOrientationIndex] ===
261 vectorSumOrientation[activeStartOrientationIndex] &&
262 endOrientation[activeEndOrientationIndex] ===
263 vectorSumOrientation[activeEndOrientationIndex]) {
264 // *
265 // |
266 // |
267 // |
268 // |
269 // |
270 // ---------------------*
271 //
272 // Do nothing.
273 }
274 else {
275 // ---*
276 // |
277 // |
278 // |
279 // *
280 // * |
281 // | |
282 // | |
283 // --------*--------
284 //
285 // or *---
286 // |
287 // |
288 // |
289 // *
290 // * |
291 // | |
292 // | |
293 // --------*--------
294 //
295 // or
296 //
297 // ----*
298 // |
299 // |
300 // *
301 // |
302 // |
303 // ---*---
304 // |
305 // |
306 // *
307 const otherStartOrientationDimensionDisplacement = endPoint[otherStartOrientationDimension] -
308 endOrientation[activeEndOrientationIndex] * DEFAULT_STUB_LENGTH -
309 startPoint[otherStartOrientationDimension];
310 pvjsonPoints[1] = {};
311 pvjsonPoints[1][activeStartOrientationDimension] =
312 startPoint[activeStartOrientationDimension] +
313 startOrientation[activeStartOrientationIndex] * DEFAULT_STUB_LENGTH;
314 pvjsonPoints[1][otherStartOrientationDimension] =
315 startPoint[otherStartOrientationDimension] +
316 otherStartOrientationDimensionDisplacement / 2;
317 pvjsonPoints[2] = {};
318 pvjsonPoints[2][activeEndOrientationDimension] =
319 endPoint[activeEndOrientationDimension] -
320 endOrientation[activeEndOrientationIndex] * DEFAULT_STUB_LENGTH;
321 pvjsonPoints[2][otherEndOrientationDimension] =
322 (pvjsonPoints[1][otherEndOrientationDimension] +
323 endPoint[otherEndOrientationDimension]) /
324 2;
325 }
326 }
327 pvjsonPoints.push(endPoint);
328 return pointOrderReversed ? pvjsonPoints.reverse() : pvjsonPoints;
329}
330//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FsY3VsYXRlQWxsUG9pbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2VkZ2UvY2FsY3VsYXRlQWxsUG9pbnRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBUSxTQUFTLEVBQTRCLE1BQU0sV0FBVyxDQUFDO0FBQ3RFLE9BQU8sRUFNTCx5QkFBeUIsRUFJMUIsTUFBTSxlQUFlLENBQUM7QUFHdkIsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQzdDLE9BQU8sRUFDTCxtQ0FBbUMsRUFDbkMsaUNBQWlDLEVBQ2pDLG1CQUFtQixFQUNwQixNQUFNLGVBQWUsQ0FBQztBQUV2QixNQUFNLGtCQUFrQixHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBRXRDLCtDQUErQyxXQUE2QjtJQUMxRSxNQUFNLHNCQUFzQixHQUFHLFNBQVMsQ0FDdEMsQ0FBQyxpQkFBeUIsRUFBRSxFQUFFLENBQUMsaUJBQWlCLEtBQUssQ0FBQyxFQUN0RCxXQUFXLENBQ1osQ0FBQztJQUNGLE1BQU0sMEJBQTBCLEdBQUcsa0JBQWtCLENBQUMsc0JBQXNCLENBQUMsQ0FBQztJQUM5RSxNQUFNLHlCQUF5QixHQUFHLDBCQUEwQixLQUFLLEdBQUc7UUFDbEUsQ0FBQyxDQUFDLEdBQUc7UUFDTCxDQUFDLENBQUMsR0FBRyxDQUFDO0lBQ1IsTUFBTSxDQUFDO1FBQ0wsc0JBQXNCO1FBQ3RCLDBCQUEwQjtRQUMxQix5QkFBeUI7S0FDMUIsQ0FBQztBQUNKLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvQkc7QUFDSCxNQUFNLDZCQUNKLGNBQXNDLEVBQ3RDLFlBQXNDLEVBQ3RDLFlBQXNDO0lBRXRDLElBQUksVUFBVSxHQUFHLGNBQWMsQ0FBQyxDQUFDLENBQWlDLENBQUM7SUFDbkUsSUFBSSxTQUFTLEdBQUcsY0FBYyxDQUFDLGNBQWMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUM1QyxD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
\No newline at end of file