-
Notifications
You must be signed in to change notification settings - Fork 17
/
draw-utils.ts
97 lines (85 loc) · 3.56 KB
/
draw-utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import { Point } from '../../geom';
import { DrawDesign, NODE_SEPARATION_HORIZONTAL } from '../const';
type SingleDraw = (p: Point) => string;
type DoubleDraw = (p1: Point, p2: Point, startIndentX?: number, junctionOffset?: number) => string;
type TripleDraw = (p1: Point, p2: Point, p3: Point, curveSize?: { x: number, y: number }) => string;
type DetermineDirection = (p1: Point, p2: Point) => boolean;
const join = (...segments: string[]) => segments.filter(seg => !!seg).join(' ');
const leftRight: DetermineDirection = (p1, p2) => p1.x < p2.x;
const topDown: DetermineDirection = (p1, p2) => p1.y < p2.y;
const bottomUp: DetermineDirection = (p1, p2) => p1.y > p2.y;
const point: SingleDraw = p => `${p.x},${p.y}`;
const moveTo: SingleDraw = p => `M ${point(p)}`;
const lineTo: SingleDraw = p => `L ${point(p)}`;
const quadTo: DoubleDraw = (corner, end) => `Q ${point(corner)} ${point(end)}`;
// TODO: Try to simplify
// x should not be greater than (NODE_SEPARATION_HORIZONTAL / 2)
const CURVE_SIZE = { x: 8, y: 10 };
const curve: TripleDraw = (fromPoint, cornerPoint, toPoint, curveSize = CURVE_SIZE) => {
const topToBottom = topDown(fromPoint, toPoint);
if (topToBottom) {
const rightAndDown = leftRight(fromPoint, cornerPoint) && topDown(cornerPoint, toPoint);
const downAndRight = topDown(fromPoint, cornerPoint) && leftRight(cornerPoint, toPoint);
if (rightAndDown) {
return join(
lineTo(cornerPoint.clone().translate(-curveSize.x, 0)),
quadTo(cornerPoint, cornerPoint.clone().translate(0, curveSize.y))
);
}
if (downAndRight) {
return join(
lineTo(cornerPoint.clone().translate(0, -curveSize.y)),
quadTo(cornerPoint, cornerPoint.clone().translate(curveSize.x, 0))
);
}
} else {
const rightAndUp = leftRight(fromPoint, cornerPoint) && bottomUp(cornerPoint, toPoint);
const upAndRight = bottomUp(fromPoint, cornerPoint) && leftRight(cornerPoint, toPoint);
if (rightAndUp) {
return join(
lineTo(cornerPoint.clone().translate(-curveSize.x, 0)),
quadTo(cornerPoint, cornerPoint.clone().translate(0, -curveSize.y))
);
}
if (upAndRight) {
return join(
lineTo(cornerPoint.clone().translate(0, curveSize.y)),
quadTo(cornerPoint, cornerPoint.clone().translate(curveSize.x, 0))
);
}
}
return '';
};
export const straightPath: DoubleDraw = (start, finish) => join(moveTo(start), lineTo(finish));
export const integralShapePath: DoubleDraw = (
start,
finish,
startIndentX = 0,
nodeSeparation = NODE_SEPARATION_HORIZONTAL
) => {
// Integral shape: ∫
let firstCurve: string = null;
let secondCurve: string = null;
if (start.y !== finish.y) {
const cornerX = Math.floor(start.x + nodeSeparation / 2);
const firstCorner = new Point(cornerX, start.y);
const secondCorner = new Point(cornerX, finish.y);
if (Math.abs(start.y - finish.y) > CURVE_SIZE.y) {
firstCurve = curve(start, firstCorner, secondCorner);
secondCurve = curve(firstCorner, secondCorner, finish);
} else {
firstCurve = curve(start, firstCorner, finish, {x: CURVE_SIZE.x, y: Math.abs(start.y - finish.y)});
}
}
const indentedStart = new Point(start.x - startIndentX, start.y);
return join(moveTo(indentedStart), firstCurve, secondCurve, lineTo(finish));
};
export const path = (start: Point, finish: Point, drawDesign?: DrawDesign) => {
switch (drawDesign) {
case DrawDesign.INTEGRAL_SHAPE:
return integralShapePath(start, finish);
case DrawDesign.STRAIGHT:
default:
return straightPath(start, finish);
}
};