1 | /*
|
2 | * I need to do the following:
|
3 | * diff angles between vectors
|
4 | * find perpendicular vector to a point on a path
|
5 | * find tangent to a point on a path
|
6 | * transform (translate, rotate) for nodes and edges
|
7 | * es modules so I can pull out just what I need
|
8 | *
|
9 | * Specs to compare:
|
10 | * tests
|
11 | * typescript
|
12 | * maintained (open issues unresolved for a long time?)
|
13 | * node and browser
|
14 | */
|
15 | import { assign as assignM } from "lodash";
|
16 | import { fromPairs, isFinite, isUndefined, last, toPairs } from "lodash/fp";
|
17 | import { degreesToRadians, distance, fromSlope, normalize } from "./spinoffs/Angle";
|
18 | import { position } from "points";
|
19 | // TODO why doesn't the following work?
|
20 | // Also, why doesn't ../node_modules/kaavio/lib/drawers/edges/ exist?
|
21 | //import * as edgeDrawers from "kaavio/src/drawers/edges/index";
|
22 | //import * as edgeDrawers from "../node_modules/kaavio/src/drawers/edges/index";
|
23 | //import * as edgeDrawers from "kaavio/src/drawers/edges/index";
|
24 | //import * as edgeDrawers from "kaavio/lib/drawers/edges/index";
|
25 | import * as edgeDrawers from "./edge/edgeDrawers";
|
26 | // We are using the standard SVG coordinate system where:
|
27 | // the origin is the upper-left-most point
|
28 | // positive x is to the right
|
29 | // positive y is down
|
30 | // uses left hand rule, so positive angle is clockwise,
|
31 | // starting with 0 pointing to the right
|
32 | // The orientation is a unit vector that indicates the orientation of an
|
33 | // at a point. When it is attached to a rectangle, we almost always want it to
|
34 | // point away from the side to which it is attached.
|
35 | export const START_SIDE_TO_ORIENTATION_MAP = {
|
36 | right: [1, 0],
|
37 | bottom: [0, 1],
|
38 | left: [-1, 0],
|
39 | top: [0, -1]
|
40 | };
|
41 | export const START_SIDE_TO_EMANATION_ANGLE_MAPPINGS = fromPairs(toPairs(START_SIDE_TO_ORIENTATION_MAP).map(function ([startSide, orientation]) {
|
42 | return [startSide, fromSlope([0, 0], orientation)];
|
43 | }));
|
44 | export const EMANATION_ANGLE_TO_START_SIDE_MAPPINGS = toPairs(START_SIDE_TO_EMANATION_ANGLE_MAPPINGS).reduce(function (acc, [side, angle]) {
|
45 | acc.set(angle, side);
|
46 | return acc;
|
47 | }, new Map());
|
48 | export const START_SEGMENT_DETAILS_MAPS = toPairs(START_SIDE_TO_ORIENTATION_MAP).map(function ([startSide, orientation]) {
|
49 | const [orientationX, orientationY] = orientation;
|
50 | return {
|
51 | sideAttachedTo: startSide,
|
52 | orientation: orientation,
|
53 | angle: normalize(Math.atan2(orientationY, orientationX))
|
54 | };
|
55 | });
|
56 | export class SmartPoint {
|
57 | //orientationVector?: SmartVector;
|
58 | constructor(point) {
|
59 | this.angle = () => {
|
60 | return fromSlope([0, 0], this.orientation);
|
61 | };
|
62 | this.fromArray = ([x, y]) => {
|
63 | this.x = x;
|
64 | this.y = y;
|
65 | };
|
66 | this.toArray = () => {
|
67 | return [this.x, this.y];
|
68 | };
|
69 | assignM(this, point);
|
70 | /*
|
71 | if (!isUndefined(this.orientation)) {
|
72 | this.orientationVector = new SmartVector(
|
73 | { x: 0, y: 0 },
|
74 | { x: this.orientation[0], y: this.orientation[1] }
|
75 | );
|
76 | }
|
77 | //*/
|
78 | }
|
79 | }
|
80 | export class SmartVector {
|
81 | constructor(p0, p1) {
|
82 | this.angleDistance = vector2 => {
|
83 | return distance(this.angle, vector2.angle);
|
84 | };
|
85 | this.p0 = new SmartPoint(p0);
|
86 | this.p1 = new SmartPoint(p1);
|
87 | this.angle = fromSlope(this.p0.toArray(), this.p1.toArray());
|
88 | }
|
89 | }
|
90 | export class SmartPath {
|
91 | constructor(points, edge) {
|
92 | this.position = (scalar, accuracy) => {
|
93 | const { x, y, angle: degreesFromNorth } = position(this.path.points, scalar, accuracy);
|
94 | /* the points library returns the angle from north, in degrees, increasing CW, so
|
95 | * this has an angle of 0 deg.:
|
96 | *
|
97 | * ^
|
98 | * |
|
99 | * |
|
100 | * |
|
101 | *
|
102 | * and this has an angle of 90 deg.:
|
103 | *
|
104 | * ------->
|
105 | */
|
106 | return {
|
107 | x,
|
108 | y,
|
109 | // convert to radians and use angle orientation of SVG coordinate system
|
110 | angle: normalize(degreesToRadians(degreesFromNorth + 270))
|
111 | };
|
112 | };
|
113 | const smartPoints = points.map(point => new SmartPoint(point));
|
114 | this.points = smartPoints;
|
115 | this.sum = new SmartVector(smartPoints[0], last(smartPoints));
|
116 | if (!isUndefined(edge)) {
|
117 | const { points, markerStart, markerEnd } = edge;
|
118 | this.path = new edgeDrawers[edge.drawAs](smartPoints, markerStart, markerEnd);
|
119 | }
|
120 | }
|
121 | }
|
122 | // TODO explore using the packages points and angles (and maybe vectory) together
|
123 | const smartPath1 = new SmartPath([
|
124 | { x: 50, y: 30, moveTo: true },
|
125 | { x: 50, y: 70, curve: { type: "arc", rx: 20, ry: 20, sweepFlag: 1 } },
|
126 | { x: 150, y: 100, curve: { type: "arc", rx: 20, ry: 20, sweepFlag: 1 } }
|
127 | ]);
|
128 | const smartPath2 = new SmartPath([
|
129 | { x: 100, y: 50, moveTo: true },
|
130 | { x: 50, y: 70, curve: { type: "arc", rx: 20, ry: 20, sweepFlag: 1 } }
|
131 | //{ x: 200, y: 100 }
|
132 | ]);
|
133 | /* OLD CODE BELOW */
|
134 | export function addAngles(angle1, angle2) {
|
135 | const sum = angle1 + angle2;
|
136 | const singleRevolutionSum = sum % (2 * Math.PI);
|
137 | return Math.sign(singleRevolutionSum) === -1
|
138 | ? 2 * Math.PI + singleRevolutionSum
|
139 | : singleRevolutionSum;
|
140 | }
|
141 | // see https://gist.github.com/ahwolf/4349166 and
|
142 | // http://www.blackpawn.com/texts/pointinpoly/default.html
|
143 | export function crossProduct(u, v) {
|
144 | return u[0] * v[1] - v[0] * u[1];
|
145 | }
|
146 | export function flipOrientation(orientation) {
|
147 | return orientation.map(orientationScalar => -1 * orientationScalar);
|
148 | }
|
149 | export function flipSide(side) {
|
150 | return EMANATION_ANGLE_TO_START_SIDE_MAPPINGS.get(reverseAngle(START_SIDE_TO_EMANATION_ANGLE_MAPPINGS[side]));
|
151 | }
|
152 | export function getMinimumAngleBetweenVectors(vectorDirectionAngle1, vectorDirectionAngle2) {
|
153 | const vectors = [vectorDirectionAngle1, vectorDirectionAngle2];
|
154 | const minVector = Math.min.apply(undefined, vectors);
|
155 | const maxVector = Math.max.apply(undefined, vectors);
|
156 | if (minVector < 0 || maxVector >= 2 * Math.PI) {
|
157 | throw new Error(`getMinimumAngleBetweenVectors(${vectorDirectionAngle1}, ${vectorDirectionAngle2})
|
158 | inputs must be in interval [0, 2 * Math.PI).`);
|
159 | }
|
160 | return (Math.max(vectorDirectionAngle1, vectorDirectionAngle2) -
|
161 | Math.min(vectorDirectionAngle1, vectorDirectionAngle2));
|
162 | /*
|
163 | const diff = addAngles(vectorDirectionAngle1, -1 * vectorDirectionAngle2);
|
164 | return diff <= Math.PI ? diff : diff % Math.PI;
|
165 | //*/
|
166 | //return diff > Math.PI ? diff - Math.PI : diff;
|
167 | }
|
168 | export function getAngleOfEmanationFromPoint(point) {
|
169 | const [orientationX, orientationY] = point.orientation;
|
170 | return Math.atan2(orientationY, orientationX);
|
171 | }
|
172 | export function reverseAngle(angle) {
|
173 | return addAngles(angle, Math.PI);
|
174 | }
|
175 | export function getAngleAtPoint(edge, positionX) {
|
176 | const { id, points, markerStart, markerEnd } = edge;
|
177 | const referencedPath = new edgeDrawers[edge.drawAs.toLowerCase()](points, markerStart, markerEnd);
|
178 | const tangentLength = 0.02;
|
179 | const firstPointOfTangent = referencedPath.getPointAtPosition(Math.max(0, positionX - tangentLength / 2));
|
180 | const lastPointOfTangent = referencedPath.getPointAtPosition(Math.min(1, positionX + tangentLength / 2));
|
181 | return getAngleFromPointToPoint(firstPointOfTangent, lastPointOfTangent);
|
182 | }
|
183 | export function getAngleFromPointToPoint({ x: x0, y: y0 }, { x: x1, y: y1 }) {
|
184 | return Math.atan2(y1 - y0, x1 - x0);
|
185 | }
|
186 | export function getStartSideByOrientation([orientationX, orientationY]) {
|
187 | if (Math.abs(orientationX) > Math.abs(orientationY)) {
|
188 | if (orientationX > 0) {
|
189 | return "right"; //East
|
190 | }
|
191 | else {
|
192 | return "left"; //West
|
193 | }
|
194 | }
|
195 | else {
|
196 | if (orientationY > 0) {
|
197 | return "bottom"; //South
|
198 | }
|
199 | else {
|
200 | return "top"; //North
|
201 | }
|
202 | }
|
203 | }
|
204 | // see http://blog.acipo.com/matrix-inversion-in-javascript/
|
205 | /**
|
206 | * Calculate the inverse matrix.
|
207 | * @returns {Matrix}
|
208 | */
|
209 | export function invertMatrix(M) {
|
210 | // I use Guassian Elimination to calculate the inverse:
|
211 | // (1) 'augment' the matrix (left) by the identity (on the right)
|
212 | // (2) Turn the matrix on the left into the identity by elemetry row ops
|
213 | // (3) The matrix on the right is the inverse (was the identity matrix)
|
214 | // There are 3 elemtary row ops: (I combine b and c in my code)
|
215 | // (a) Swap 2 rows
|
216 | // (b) Multiply a row by a scalar
|
217 | // (c) Add 2 rows
|
218 | //if the matrix isn't square: exit (error)
|
219 | if (M.length !== M[0].length) {
|
220 | return;
|
221 | }
|
222 | //create the identity matrix (I), and a copy (C) of the original
|
223 | var i = 0, ii = 0, j = 0, dim = M.length, e = 0, t = 0;
|
224 | var I = [], C = [];
|
225 | for (i = 0; i < dim; i += 1) {
|
226 | // Create the row
|
227 | I[I.length] = [];
|
228 | C[C.length] = [];
|
229 | for (j = 0; j < dim; j += 1) {
|
230 | //if we're on the diagonal, put a 1 (for identity)
|
231 | if (i === j) {
|
232 | I[i][j] = 1;
|
233 | }
|
234 | else {
|
235 | I[i][j] = 0;
|
236 | }
|
237 | // Also, make the copy of the original
|
238 | C[i][j] = M[i][j];
|
239 | }
|
240 | }
|
241 | // Perform elementary row operations
|
242 | for (i = 0; i < dim; i += 1) {
|
243 | // get the element e on the diagonal
|
244 | e = C[i][i];
|
245 | // if we have a 0 on the diagonal (we'll need to swap with a lower row)
|
246 | if (e === 0) {
|
247 | //look through every row below the i'th row
|
248 | for (ii = i + 1; ii < dim; ii += 1) {
|
249 | //if the ii'th row has a non-0 in the i'th col
|
250 | if (C[ii][i] !== 0) {
|
251 | //it would make the diagonal have a non-0 so swap it
|
252 | for (j = 0; j < dim; j++) {
|
253 | e = C[i][j]; //temp store i'th row
|
254 | C[i][j] = C[ii][j]; //replace i'th row by ii'th
|
255 | C[ii][j] = e; //repace ii'th by temp
|
256 | e = I[i][j]; //temp store i'th row
|
257 | I[i][j] = I[ii][j]; //replace i'th row by ii'th
|
258 | I[ii][j] = e; //repace ii'th by temp
|
259 | }
|
260 | //don't bother checking other rows since we've swapped
|
261 | break;
|
262 | }
|
263 | }
|
264 | //get the new diagonal
|
265 | e = C[i][i];
|
266 | //if it's still 0, not invertable (error)
|
267 | if (e === 0) {
|
268 | return;
|
269 | }
|
270 | }
|
271 | // Scale this row down by e (so we have a 1 on the diagonal)
|
272 | for (j = 0; j < dim; j++) {
|
273 | C[i][j] = C[i][j] / e; //apply to original matrix
|
274 | I[i][j] = I[i][j] / e; //apply to identity
|
275 | }
|
276 | // Subtract this row (scaled appropriately for each row) from ALL of
|
277 | // the other rows so that there will be 0's in this column in the
|
278 | // rows above and below this one
|
279 | for (ii = 0; ii < dim; ii++) {
|
280 | // Only apply to other rows (we want a 1 on the diagonal)
|
281 | if (ii === i) {
|
282 | continue;
|
283 | }
|
284 | // We want to change this element to 0
|
285 | e = C[ii][i];
|
286 | // Subtract (the row above(or below) scaled by e) from (the
|
287 | // current row) but start at the i'th column and assume all the
|
288 | // stuff left of diagonal is 0 (which it should be if we made this
|
289 | // algorithm correctly)
|
290 | for (j = 0; j < dim; j++) {
|
291 | C[ii][j] -= e * C[i][j]; //apply to original matrix
|
292 | I[ii][j] -= e * I[i][j]; //apply to identity
|
293 | }
|
294 | }
|
295 | }
|
296 | //we've done all operations, C should be the identity
|
297 | //matrix I should be the inverse:
|
298 | return I;
|
299 | }
|
300 | // from http://tech.pro/tutorial/1527/matrix-multiplication-in-functional-javascript
|
301 | export function multiplyMatrices(m1, m2) {
|
302 | var result = [];
|
303 | for (var i = 0; i < m1.length; i++) {
|
304 | result[i] = [];
|
305 | for (var j = 0; j < m2[0].length; j++) {
|
306 | var sum = 0;
|
307 | for (var k = 0; k < m1[0].length; k++) {
|
308 | sum += m1[i][k] * m2[k][j];
|
309 | }
|
310 | result[i][j] = sum;
|
311 | }
|
312 | }
|
313 | return result;
|
314 | }
|
315 | /**
|
316 | * rotate
|
317 | *
|
318 | * @param theta (float): rotation angle in radians, measured clockwise
|
319 | * @return transformation matrix for rotation
|
320 | *
|
321 | * Note that for Canvas and SVG, the y axis points down:
|
322 | *
|
323 | * *---------> x
|
324 | * |
|
325 | * |
|
326 | * |
|
327 | * v
|
328 | *
|
329 | * y
|
330 | *
|
331 | * The transformation matrix returned takes this into account and is intentionally
|
332 | * different from the transformation matrix that would be returned if the y-axis
|
333 | * pointed up, as is common in many math classes.
|
334 | */
|
335 | export function rotate(theta) {
|
336 | if (!isFinite(theta)) {
|
337 | throw new Error(`Invalid input: rotate(${theta}). Requires a finite number.`);
|
338 | }
|
339 | return [
|
340 | [Math.cos(theta), -1 * Math.sin(theta), 0],
|
341 | [Math.sin(theta), Math.cos(theta), 0],
|
342 | [0, 0, 1]
|
343 | ];
|
344 | }
|
345 | export function scale([xScale, yScale]) {
|
346 | if (!isFinite(xScale) || !isFinite(yScale)) {
|
347 | throw new Error(`Invalid input: rotate([${xScale}, ${yScale}]). Requires array of two finite numbers.`);
|
348 | }
|
349 | return [[xScale, 0, 0], [0, yScale, 0], [0, 0, 1]];
|
350 | }
|
351 | export function translate([xTranslation, yTranslation]) {
|
352 | if (!isFinite(xTranslation) || !isFinite(yTranslation)) {
|
353 | throw new Error(`Invalid input: translate([${xTranslation}, ${yTranslation}]). Requires array of two finite numbers.`);
|
354 | }
|
355 | return [[1, 0, xTranslation], [0, 1, yTranslation], [0, 0, 1]];
|
356 | }
|
357 | const transformations = {
|
358 | rotate,
|
359 | scale,
|
360 | translate
|
361 | };
|
362 | export function getTransformationMatrix(transformationSequence) {
|
363 | // Start with identity matrix
|
364 | var concatenatedTransformationMatrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
|
365 | transformationSequence.forEach(function (transformation) {
|
366 | var thisTransformationMatrix = transformations[transformation.key](transformation.value);
|
367 | concatenatedTransformationMatrix = multiplyMatrices(concatenatedTransformationMatrix, thisTransformationMatrix);
|
368 | });
|
369 | return concatenatedTransformationMatrix;
|
370 | }
|
371 | export function multiplyMatrixByVector(transformationMatrix, vector) {
|
372 | var x = vector[0][0] * transformationMatrix[0][0] +
|
373 | vector[1][0] * transformationMatrix[0][1] +
|
374 | vector[2][0] * transformationMatrix[0][2], y = vector[0][0] * transformationMatrix[1][0] +
|
375 | vector[1][0] * transformationMatrix[1][1] +
|
376 | vector[2][0] * transformationMatrix[1][2], z = vector[0][0] * transformationMatrix[2][0] +
|
377 | vector[1][0] * transformationMatrix[2][1] +
|
378 | vector[2][0] * transformationMatrix[2][2];
|
379 | return [[x], [y], [z]];
|
380 | }
|
381 | /**
|
382 | * sameSide
|
383 | *
|
384 | * Calculate whether the current edge's second point, a, (end of first segment)
|
385 | * and its final point, b, are both on the same side of the referenced edge.
|
386 | *
|
387 | * current edge: pipes/hyphens
|
388 | * referenced edge: dots
|
389 | *
|
390 | * Example of True
|
391 | *
|
392 | * p1
|
393 | * .
|
394 | * .
|
395 | * *------------a
|
396 | * . |
|
397 | * . |
|
398 | * . |
|
399 | * . |
|
400 | * . |
|
401 | * . |
|
402 | * . |
|
403 | * . |
|
404 | * . |
|
405 | * . |
|
406 | * . *-----b
|
407 | * .
|
408 | * .
|
409 | * p2
|
410 | *
|
411 | *
|
412 | * Example of False
|
413 | *
|
414 | * p1
|
415 | * .
|
416 | * *------------a
|
417 | * . |
|
418 | * . |
|
419 | * . |
|
420 | * . |
|
421 | * . |
|
422 | * .|
|
423 | * |.
|
424 | * | .
|
425 | * | .
|
426 | * | .
|
427 | * *-----b .
|
428 | * .
|
429 | * p2
|
430 | *
|
431 | *
|
432 | * @param {Object} p1 - first point of the referenced edge
|
433 | * @param {Object} p2 - last point of the referenced edge
|
434 | * @param {Object} a - last point of the first segment of the current edge (the point following the start point)
|
435 | * @param {Object} b - point where the current edge ends
|
436 | * @return {Boolean) - whether the last point of the first segment of the current edge is on the same side as the last point of the current edge
|
437 | */
|
438 | export function sameSide(p1, p2, a, b) {
|
439 | const bMinusA = [b.x - a.x, b.y - a.y];
|
440 | const p1MinusA = [p1.x - a.x, p1.y - a.y];
|
441 | const p2MinusA = [p2.x - a.x, p2.y - a.y];
|
442 | const crossProduct1 = crossProduct(bMinusA, p1MinusA);
|
443 | const crossProduct2 = crossProduct(bMinusA, p2MinusA);
|
444 | return Math.sign(crossProduct1) === Math.sign(crossProduct2);
|
445 | }
|
446 | export function transform({ element, transformOrigin, transformationSequence }) {
|
447 | const { x, y, width, height } = element;
|
448 | (transformOrigin = transformOrigin || "50% 50%"),
|
449 | (transformationSequence = transformationSequence || []);
|
450 | var transformOriginKeywordMappings = {
|
451 | left: "0%",
|
452 | center: "50%",
|
453 | right: "100%",
|
454 | top: "0%",
|
455 | bottom: "100%"
|
456 | };
|
457 | var transformOriginKeywordMappingsKeys = Object.keys(transformOriginKeywordMappings);
|
458 | var transformOriginPoint = transformOrigin
|
459 | .split(" ")
|
460 | .map(function (value, i) {
|
461 | let numericOrPctValue;
|
462 | let numericValue;
|
463 | if (transformOriginKeywordMappingsKeys.indexOf(value) > -1) {
|
464 | numericOrPctValue = transformOriginKeywordMappings[value];
|
465 | }
|
466 | else {
|
467 | numericOrPctValue = value;
|
468 | }
|
469 | if (numericOrPctValue.indexOf("%") > -1) {
|
470 | var decimalPercent = parseFloat(numericOrPctValue) / 100;
|
471 | if (i === 0) {
|
472 | numericValue = decimalPercent * width;
|
473 | }
|
474 | else {
|
475 | numericValue = decimalPercent * height;
|
476 | }
|
477 | }
|
478 | else if (value.indexOf("em") > -1) {
|
479 | // TODO refactor. this is hacky.
|
480 | numericValue = parseFloat(numericOrPctValue) * 12;
|
481 | }
|
482 | else {
|
483 | numericValue = parseFloat(numericOrPctValue);
|
484 | }
|
485 | if (i === 0) {
|
486 | numericValue += x;
|
487 | }
|
488 | else {
|
489 | numericValue += y;
|
490 | }
|
491 | return numericValue;
|
492 | });
|
493 | // shift origin from top left corner of element bounding box to point specified by transformOrigin (default: center of bounding box)
|
494 | transformationSequence.unshift({
|
495 | key: "translate",
|
496 | value: [transformOriginPoint[0], transformOriginPoint[1]]
|
497 | });
|
498 | // shift origin back to top left corner of element bounding box
|
499 | transformationSequence.push({
|
500 | key: "translate",
|
501 | value: [-1 * transformOriginPoint[0], -1 * transformOriginPoint[1]]
|
502 | });
|
503 | var transformationMatrix = getTransformationMatrix(transformationSequence);
|
504 | var topLeftPoint = [[x], [y], [1]];
|
505 | var bottomRightPoint = [[x + width], [y + height], [1]];
|
506 | var topLeftPointTransformed = multiplyMatrixByVector(transformationMatrix, topLeftPoint);
|
507 | var bottomRightPointTransformed = multiplyMatrixByVector(transformationMatrix, bottomRightPoint);
|
508 | element.x = topLeftPointTransformed[0][0];
|
509 | element.y = topLeftPointTransformed[1][0];
|
510 | element.width = bottomRightPointTransformed[0][0] - element.x;
|
511 | element.height = bottomRightPointTransformed[1][0] - element.y;
|
512 | return element;
|
513 | }
|
514 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VvbS11dGlscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9nZW9tLXV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFFSCxPQUFPLEVBQUUsTUFBTSxJQUFJLE9BQU8sRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUMzQyxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUU1RSxPQUFPLEVBQ0wsZ0JBQWdCLEVBQ2hCLFFBQVEsRUFDUixTQUFTLEVBQ1QsU0FBUyxFQUNWLE1BQU0sa0JBQWtCLENBQUM7QUFDMUIsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUNsQyx1Q0FBdUM7QUFDdkMscUVBQXFFO0FBQ3JFLGdFQUFnRTtBQUNoRSxnRkFBZ0Y7QUFDaEYsZ0VBQWdFO0FBQ2hFLGdFQUFnRTtBQUNoRSxPQUFPLEtBQUssV0FBVyxNQUFNLG9CQUFvQixDQUFDO0FBV2xELHlEQUF5RDtBQUN6RCw0Q0FBNEM7QUFDNUMsK0JBQStCO0FBQy9CLHVCQUF1QjtBQUN2Qix5REFBeUQ7QUFDekQsNENBQTRDO0FBRTVDLHdFQUF3RTtBQUN4RSw4RUFBOEU7QUFDOUUsb0RBQW9EO0FBQ3BELE1BQU0sQ0FBQyxNQUFNLDZCQUE2QixHQUFHO0lBQzNDLEtBQUssRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDYixNQUFNLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ2QsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQ2IsR0FBRyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO0NBQ2IsQ0FBQztBQUVGLE1BQU0sQ0FBQyxNQUFNLHNDQUFzQyxHQUFHLFNBQVMsQ0FDN0QsT0FBTyxDQUFDLDZCQUE2QixDQUFDLENBQUMsR0FB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 |
\ | No newline at end of file |