webgl2-sdf 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -0
- package/node/bezier/bezier-curves-to-line-segs.d.ts +10 -0
- package/node/bezier/eval-de-casteljau.d.ts +17 -0
- package/node/bezier/from-to/from-to-2.d.ts +12 -0
- package/node/bezier/from-to/from-to-3.d.ts +12 -0
- package/node/bezier/from-to/from-to.d.ts +10 -0
- package/node/bezier/is-cubic-obtuse.d.ts +11 -0
- package/node/bezier/is-quad-obtuse.d.ts +11 -0
- package/node/bezier/is-really-point.d.ts +9 -0
- package/node/bezier/split-by-deviation-from-straight-line-cubic.d.ts +20 -0
- package/node/bezier/split-by-deviation-from-straight-line-quad.d.ts +11 -0
- package/node/bezier/split-into-line-segments.d.ts +10 -0
- package/node/debug-shaders.d.ts +9 -0
- package/node/generate-sdf.d.ts +18 -0
- package/node/helpers/calc-circs.d.ts +11 -0
- package/node/helpers/clip-line-segment-to-grid.d.ts +14 -0
- package/node/helpers/clip-line-segment-to-strips.d.ts +13 -0
- package/node/helpers/create-empty-grid.d.ts +8 -0
- package/node/helpers/create-empty-strips.d.ts +7 -0
- package/node/helpers/distance-seg-to-p.d.ts +5 -0
- package/node/helpers/find-close-cells.d.ts +3 -0
- package/node/helpers/find-crossing-cells.d.ts +3 -0
- package/node/helpers/get-distance-to-line-function.d.ts +10 -0
- package/node/helpers/jump-idx.d.ts +2 -0
- package/node/helpers/map-to-viewbox.d.ts +2 -0
- package/node/helpers/seg-box-x.d.ts +10 -0
- package/node/helpers/seg-strip-x.d.ts +9 -0
- package/node/index.d.ts +1 -0
- package/node/main-program.d.ts +4 -0
- package/node/max-aspect-ratio-before-stretch.d.ts +2 -0
- package/node/prepare-buffers.d.ts +10 -0
- package/node/row-count.d.ts +2 -0
- package/node/shaders/main.fragment.d.ts +2 -0
- package/node/shaders/main.vertex.d.ts +2 -0
- package/node/svg/get-beziers-from-raw-paths.d.ts +12 -0
- package/node/svg/get-paths-from-str.d.ts +9 -0
- package/node/svg/path-data-polyfill/parse-number.d.ts +10 -0
- package/node/svg/path-data-polyfill/parse-path-data-string.d.ts +9 -0
- package/node/svg/path-data-polyfill/source.d.ts +19 -0
- package/node/svg/path-segment/c.d.ts +19 -0
- package/node/svg/path-segment/h.d.ts +15 -0
- package/node/svg/path-segment/l.d.ts +16 -0
- package/node/svg/path-segment/q.d.ts +16 -0
- package/node/svg/path-segment/s.d.ts +20 -0
- package/node/svg/path-segment/t.d.ts +18 -0
- package/node/svg/path-segment/v.d.ts +15 -0
- package/node/svg/path-segment/z.d.ts +13 -0
- package/node/svg/path-state.d.ts +11 -0
- package/node/tex-width.d.ts +11 -0
- package/node/types/attribute.d.ts +6 -0
- package/node/types/cell.d.ts +18 -0
- package/node/types/gl-context.d.ts +28 -0
- package/node/types/gl-type.d.ts +2 -0
- package/node/types/gl-usage.d.ts +2 -0
- package/node/types/program.d.ts +16 -0
- package/node/types/strip.d.ts +8 -0
- package/node/types/texture.d.ts +4 -0
- package/node/utils/calc-circs.d.ts +11 -0
- package/node/utils/clip-line-segment-to-grid.d.ts +14 -0
- package/node/utils/clip-line-segment-to-strips.d.ts +13 -0
- package/node/utils/create-empty-grid.d.ts +8 -0
- package/node/utils/create-empty-strips.d.ts +7 -0
- package/node/utils/distance-seg-to-p.d.ts +5 -0
- package/node/utils/find-close-cells.d.ts +3 -0
- package/node/utils/find-crossing-cells.d.ts +3 -0
- package/node/utils/get-distance-to-line-function.d.ts +10 -0
- package/node/utils/jump-idx.d.ts +2 -0
- package/node/utils/map-to-viewbox.d.ts +2 -0
- package/node/utils/seg-box-x.d.ts +10 -0
- package/node/utils/seg-strip-x.d.ts +9 -0
- package/node/vector/dot.d.ts +8 -0
- package/node/vector/from-to-vec.d.ts +8 -0
- package/node/vector/len.d.ts +6 -0
- package/node/webgl-utils/compile-shader.d.ts +2 -0
- package/node/webgl-utils/get-gl-context.d.ts +10 -0
- package/node/webgl-utils/set-attribute.d.ts +15 -0
- package/node/webgl-utils/set-uniform-block.d.ts +9 -0
- package/node/webgl-utils/set-uniform.d.ts +4 -0
- package/node/webgl-utils/uniform-block.d.ts +6 -0
- package/node/webgl-utils/uniform-type.d.ts +2 -0
- package/node/webgl-utils/use-program.d.ts +17 -0
- package/node/webgl-utils/use-texture.d.ts +9 -0
- package/node/webgl2.d.ts +2 -0
- package/package.json +56 -0
- package/src/bezier/bezier-curves-to-line-segs.ts +39 -0
- package/src/bezier/eval-de-casteljau.ts +78 -0
- package/src/bezier/from-to/from-to-2.ts +159 -0
- package/src/bezier/from-to/from-to-3.ts +176 -0
- package/src/bezier/from-to/from-to.ts +30 -0
- package/src/bezier/is-cubic-obtuse.ts +31 -0
- package/src/bezier/is-quad-obtuse.ts +26 -0
- package/src/bezier/is-really-point.ts +25 -0
- package/src/bezier/split-by-deviation-from-straight-line-cubic.ts +109 -0
- package/src/bezier/split-by-deviation-from-straight-line-quad.ts +66 -0
- package/src/bezier/split-into-line-segments.ts +39 -0
- package/src/debug-shaders.ts +38 -0
- package/src/generate-sdf.ts +91 -0
- package/src/index.ts +2 -0
- package/src/main-program.ts +160 -0
- package/src/max-aspect-ratio-before-stretch.ts +5 -0
- package/src/prepare-buffers.ts +149 -0
- package/src/row-count.ts +6 -0
- package/src/shaders/main.fragment.ts +157 -0
- package/src/shaders/main.vertex.ts +55 -0
- package/src/svg/get-beziers-from-raw-paths.ts +112 -0
- package/src/svg/get-paths-from-str.ts +19 -0
- package/src/svg/path-data-polyfill/parse-number.ts +138 -0
- package/src/svg/path-data-polyfill/parse-path-data-string.ts +26 -0
- package/src/svg/path-data-polyfill/source.ts +176 -0
- package/src/svg/path-segment/c.ts +34 -0
- package/src/svg/path-segment/h.ts +28 -0
- package/src/svg/path-segment/l.ts +30 -0
- package/src/svg/path-segment/q.ts +30 -0
- package/src/svg/path-segment/s.ts +40 -0
- package/src/svg/path-segment/t.ts +35 -0
- package/src/svg/path-segment/v.ts +28 -0
- package/src/svg/path-segment/z.ts +27 -0
- package/src/svg/path-state.ts +15 -0
- package/src/tex-width.ts +14 -0
- package/src/types/attribute.ts +9 -0
- package/src/types/cell.ts +21 -0
- package/src/types/gl-context.ts +28 -0
- package/src/types/gl-type.ts +16 -0
- package/src/types/gl-usage.ts +14 -0
- package/src/types/program.ts +14 -0
- package/src/types/strip.ts +11 -0
- package/src/types/texture.ts +7 -0
- package/src/types/typed-array.ts +16 -0
- package/src/utils/calc-circs.ts +129 -0
- package/src/utils/clip-line-segment-to-grid.ts +133 -0
- package/src/utils/clip-line-segment-to-strips.ts +196 -0
- package/src/utils/create-empty-grid.ts +32 -0
- package/src/utils/create-empty-strips.ts +21 -0
- package/src/utils/distance-seg-to-p.ts +50 -0
- package/src/utils/find-close-cells.ts +171 -0
- package/src/utils/find-crossing-cells.ts +40 -0
- package/src/utils/get-distance-to-line-function.ts +59 -0
- package/src/utils/is-point-in-box.ts +16 -0
- package/src/utils/jump-idx.ts +107 -0
- package/src/utils/map-to-viewbox.ts +41 -0
- package/src/utils/path.ts +137 -0
- package/src/utils/seg-box-x.ts +84 -0
- package/src/utils/seg-strip-x.ts +72 -0
- package/src/utils/sum.ts +13 -0
- package/src/vector/dot.ts +13 -0
- package/src/vector/from-to-vec.ts +13 -0
- package/src/vector/len.ts +11 -0
- package/src/webgl-utils/compile-shader.ts +15 -0
- package/src/webgl-utils/get-gl-context.ts +61 -0
- package/src/webgl-utils/set-attribute.ts +74 -0
- package/src/webgl-utils/set-uniform-block.ts +45 -0
- package/src/webgl-utils/set-uniform.ts +24 -0
- package/src/webgl-utils/uniform-block.ts +9 -0
- package/src/webgl-utils/uniform-type.ts +10 -0
- package/src/webgl-utils/use-program.ts +48 -0
- package/src/webgl-utils/use-texture.ts +34 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { PathState } from './path-state.js';
|
|
2
|
+
import { z } from './path-segment/z.js';
|
|
3
|
+
import { c } from './path-segment/c.js';
|
|
4
|
+
import { s } from './path-segment/s.js';
|
|
5
|
+
import { l } from './path-segment/l.js';
|
|
6
|
+
import { h } from './path-segment/h.js';
|
|
7
|
+
import { v } from './path-segment/v.js';
|
|
8
|
+
import { q } from './path-segment/q.js';
|
|
9
|
+
import { t } from './path-segment/t.js';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const pathFs: { [index:string] : (s: PathState) => number[][] } = {
|
|
13
|
+
c, // cubic bezier
|
|
14
|
+
h, // horizontal line
|
|
15
|
+
l, // line
|
|
16
|
+
q, // quadratic bezier
|
|
17
|
+
s, // cubic bezier (smooth)
|
|
18
|
+
t, // quadratic bezier (smooth)
|
|
19
|
+
v, // vertical line
|
|
20
|
+
z // close path
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns order 1, 2 and 3 beziers from the given SVG DOM element. If a path
|
|
26
|
+
* data tag is not "C, Q or L, etc", i.e. if it is not an absolute bezier
|
|
27
|
+
* coordinate then it is converted into one.
|
|
28
|
+
*
|
|
29
|
+
* @param paths An SVG element
|
|
30
|
+
*/
|
|
31
|
+
function getBeziersFromRawPaths(paths: { type: string, values: number[] }[]) {
|
|
32
|
+
|
|
33
|
+
if (paths.length === 0) {
|
|
34
|
+
return []; // A shape is not described
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (paths[0].type.toLowerCase() !== 'm') {
|
|
38
|
+
throw new Error(
|
|
39
|
+
'Invalid SVG - every new path must start with an M or m.'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const s: PathState = { p: [0,0] };
|
|
44
|
+
|
|
45
|
+
const beziersArrays: number[][][][] = [];
|
|
46
|
+
let beziers: number[][][] = [];
|
|
47
|
+
|
|
48
|
+
let prevType: string | undefined = undefined;
|
|
49
|
+
for (let i=0; i<paths.length; i++) {
|
|
50
|
+
const pathSeg = paths[i];
|
|
51
|
+
|
|
52
|
+
const type = pathSeg.type.toLowerCase();
|
|
53
|
+
s.vals = pathSeg.values;
|
|
54
|
+
|
|
55
|
+
// If pathSeg was lowercase, it is relative - make absolute
|
|
56
|
+
if (pathSeg.type === type) {
|
|
57
|
+
if (type === 'v') {
|
|
58
|
+
s.vals[0] += s.p[1];
|
|
59
|
+
} else if (type === 'a') {
|
|
60
|
+
s.vals[5] += s.p[0];
|
|
61
|
+
s.vals[6] += s.p[1];
|
|
62
|
+
} else {
|
|
63
|
+
for (let i=0; i<s.vals.length; i++) {
|
|
64
|
+
s.vals[i] += s.p[i%2];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (type === 'm') {
|
|
70
|
+
if (beziers.length) {
|
|
71
|
+
// This is a subpath, close as if the previous command was a
|
|
72
|
+
// Z or z.
|
|
73
|
+
if (prevType !== 'z') {
|
|
74
|
+
beziers.push(z(s));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Start new path
|
|
78
|
+
beziersArrays.push(beziers);
|
|
79
|
+
beziers = [];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
s.initialPoint = s.p = s.vals;
|
|
83
|
+
prevType = type;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const f = pathFs[type];
|
|
88
|
+
if (!f) { throw new Error('Invalid SVG - command not recognized.'); }
|
|
89
|
+
|
|
90
|
+
const ps = f(s);
|
|
91
|
+
s.p = ps[ps.length-1]; // Update current point
|
|
92
|
+
beziers.push(ps);
|
|
93
|
+
|
|
94
|
+
prevType = type;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if (beziers.length > 0) {
|
|
99
|
+
// This is a subpath, close as if the previous command was a Z or z.
|
|
100
|
+
if (prevType !== 'z') {
|
|
101
|
+
beziers.push(z(s));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Start new path
|
|
105
|
+
beziersArrays.push(beziers);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return beziersArrays;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
export { getBeziersFromRawPaths }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getBeziersFromRawPaths } from './get-beziers-from-raw-paths.js';
|
|
2
|
+
import { parsePathDataString } from './path-data-polyfill/parse-path-data-string.js';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns an array of loops with each loop consisting of an array of beziers
|
|
7
|
+
* and each bezier in turn consisting of an array of control points from the
|
|
8
|
+
* given SVG path string. An array of loops are returned (as opposed to a single
|
|
9
|
+
* loop) since an SVG path may have sub-paths.
|
|
10
|
+
* @param str The SVG path string, e.g. 'M1 1 C 5 1 5 2 4 2 C 3 3 1 3 1 1 z'
|
|
11
|
+
*/
|
|
12
|
+
function getPathsFromStr(str: string): number[][][][] {
|
|
13
|
+
return getBeziersFromRawPaths(
|
|
14
|
+
parsePathDataString(str)
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
export { getPathsFromStr }
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Source } from './source.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @hidden
|
|
6
|
+
* Parse a number from an SVG path. This very closely follows genericParseNumber(...) from
|
|
7
|
+
* Source/core/svg/SVGParserUtilities.cpp.
|
|
8
|
+
* Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF
|
|
9
|
+
* @param source
|
|
10
|
+
*/
|
|
11
|
+
function parseNumber(source: Source): number {
|
|
12
|
+
let exponent = 0;
|
|
13
|
+
let integer = 0;
|
|
14
|
+
let frac = 1;
|
|
15
|
+
let decimal = 0;
|
|
16
|
+
let sign = 1;
|
|
17
|
+
let expsign = 1;
|
|
18
|
+
const startIndex = source._currentIndex;
|
|
19
|
+
|
|
20
|
+
source._skipOptionalSpaces();
|
|
21
|
+
|
|
22
|
+
// Read the sign.
|
|
23
|
+
if (source._currentIndex < source._endIndex && source._string[source._currentIndex] === "+") {
|
|
24
|
+
source._currentIndex += 1;
|
|
25
|
+
} else if (source._currentIndex < source._endIndex && source._string[source._currentIndex] === "-") {
|
|
26
|
+
source._currentIndex += 1;
|
|
27
|
+
sign = -1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
source._currentIndex === source._endIndex ||
|
|
32
|
+
(
|
|
33
|
+
(source._string[source._currentIndex] < "0" || source._string[source._currentIndex] > "9") &&
|
|
34
|
+
source._string[source._currentIndex] !== "."
|
|
35
|
+
)
|
|
36
|
+
) {
|
|
37
|
+
throw new Error('The first character of a number must be one of [0-9+-.].');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Read the integer part, build right-to-left.
|
|
41
|
+
const startIntPartIndex = source._currentIndex;
|
|
42
|
+
|
|
43
|
+
while (
|
|
44
|
+
source._currentIndex < source._endIndex &&
|
|
45
|
+
source._string[source._currentIndex] >= "0" &&
|
|
46
|
+
source._string[source._currentIndex] <= "9"
|
|
47
|
+
) {
|
|
48
|
+
source._currentIndex += 1; // Advance to first non-digit.
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (source._currentIndex !== startIntPartIndex) {
|
|
52
|
+
let scanIntPartIndex = source._currentIndex - 1;
|
|
53
|
+
let multiplier = 1;
|
|
54
|
+
|
|
55
|
+
while (scanIntPartIndex >= startIntPartIndex) {
|
|
56
|
+
integer += multiplier * (Number(source._string[scanIntPartIndex]) - 0);
|
|
57
|
+
scanIntPartIndex -= 1;
|
|
58
|
+
multiplier *= 10;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Read the decimals.
|
|
63
|
+
if (source._currentIndex < source._endIndex && source._string[source._currentIndex] === ".") {
|
|
64
|
+
source._currentIndex += 1;
|
|
65
|
+
|
|
66
|
+
if (
|
|
67
|
+
source._currentIndex >= source._endIndex ||
|
|
68
|
+
source._string[source._currentIndex] < "0" ||
|
|
69
|
+
source._string[source._currentIndex] > "9"
|
|
70
|
+
) {
|
|
71
|
+
throw new Error('There must be a least one digit following the .')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
while (
|
|
75
|
+
source._currentIndex < source._endIndex &&
|
|
76
|
+
source._string[source._currentIndex] >= "0" &&
|
|
77
|
+
source._string[source._currentIndex] <= "9"
|
|
78
|
+
) {
|
|
79
|
+
frac *= 10;
|
|
80
|
+
decimal += (Number(source._string.charAt(source._currentIndex))) / frac;
|
|
81
|
+
source._currentIndex += 1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Read the exponent part.
|
|
86
|
+
if (
|
|
87
|
+
source._currentIndex !== startIndex &&
|
|
88
|
+
source._currentIndex + 1 < source._endIndex &&
|
|
89
|
+
(source._string[source._currentIndex] === "e" || source._string[source._currentIndex] === "E") &&
|
|
90
|
+
(source._string[source._currentIndex + 1] !== "x" && source._string[source._currentIndex + 1] !== "m")
|
|
91
|
+
) {
|
|
92
|
+
source._currentIndex += 1;
|
|
93
|
+
|
|
94
|
+
// Read the sign of the exponent.
|
|
95
|
+
if (source._string[source._currentIndex] === "+") {
|
|
96
|
+
source._currentIndex += 1;
|
|
97
|
+
} else if (source._string[source._currentIndex] === "-") {
|
|
98
|
+
source._currentIndex += 1;
|
|
99
|
+
expsign = -1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (
|
|
103
|
+
source._currentIndex >= source._endIndex ||
|
|
104
|
+
source._string[source._currentIndex] < "0" ||
|
|
105
|
+
source._string[source._currentIndex] > "9"
|
|
106
|
+
) {
|
|
107
|
+
throw new Error('There must be an exponent.')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
while (
|
|
111
|
+
source._currentIndex < source._endIndex &&
|
|
112
|
+
source._string[source._currentIndex] >= "0" &&
|
|
113
|
+
source._string[source._currentIndex] <= "9"
|
|
114
|
+
) {
|
|
115
|
+
exponent *= 10;
|
|
116
|
+
exponent += (Number(source._string[source._currentIndex]));
|
|
117
|
+
source._currentIndex += 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let number = integer + decimal;
|
|
122
|
+
number *= sign;
|
|
123
|
+
|
|
124
|
+
if (exponent) {
|
|
125
|
+
number *= Math.pow(10, expsign * exponent);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (startIndex === source._currentIndex) {
|
|
129
|
+
throw new Error('Internal error: startIndex === source._currentIndex');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
source._skipOptionalSpacesOrDelimiter();
|
|
133
|
+
|
|
134
|
+
return number;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
export { parseNumber }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Source } from './source.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @hidden
|
|
6
|
+
* @param string
|
|
7
|
+
*/
|
|
8
|
+
function parsePathDataString(string: string) {
|
|
9
|
+
if (!string.length) { return []; }
|
|
10
|
+
|
|
11
|
+
const source = new Source(string);
|
|
12
|
+
const pathData = [];
|
|
13
|
+
|
|
14
|
+
if (!source.initialCommandIsMoveTo()) {
|
|
15
|
+
throw new Error('Path must start with m or M');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
while (source.hasMoreData()) {
|
|
19
|
+
pathData.push(source.parseSegment());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return pathData;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
export { parsePathDataString }
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { parseNumber } from './parse-number.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/** @hidden */
|
|
5
|
+
const COMMAND_MAP: { [index:string]: string } = {
|
|
6
|
+
Z:"Z", M:"M", L:"L", C:"C", Q:"Q", A:"A", H:"H", V:"V", S:"S", T:"T",
|
|
7
|
+
z:"Z", m:"m", l:"l", c:"c", q:"q", a:"a", h:"h", v:"v", s:"s", t:"t"
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/** @hidden */
|
|
12
|
+
class Source {
|
|
13
|
+
_string: string;
|
|
14
|
+
_currentIndex: number;
|
|
15
|
+
_endIndex: number;
|
|
16
|
+
_prevCommand: string | undefined;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
constructor(string: string) {
|
|
20
|
+
this._string = string;
|
|
21
|
+
this._currentIndex = 0;
|
|
22
|
+
this._endIndex = this._string.length;
|
|
23
|
+
this._prevCommand = undefined;
|
|
24
|
+
this._skipOptionalSpaces();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
parseSegment(): { type: string, values: number[] } {
|
|
29
|
+
const char = this._string[this._currentIndex];
|
|
30
|
+
let command = COMMAND_MAP[char];
|
|
31
|
+
|
|
32
|
+
if (command === undefined) {
|
|
33
|
+
if (this._prevCommand === undefined) {
|
|
34
|
+
throw new Error('Implicit command not allowed for first commands.')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Check for remaining coordinates in the current command.
|
|
38
|
+
if ((char === "+" || char === "-" || char === "." || (char >= "0" && char <= "9")) &&
|
|
39
|
+
this._prevCommand !== "Z") {
|
|
40
|
+
|
|
41
|
+
if (this._prevCommand === "M") {
|
|
42
|
+
command = "L";
|
|
43
|
+
} else if (this._prevCommand === "m") {
|
|
44
|
+
command = "l";
|
|
45
|
+
} else {
|
|
46
|
+
command = this._prevCommand;
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
throw new Error('Remaining coordinates not found for implicit command');
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
this._currentIndex += 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this._prevCommand = command;
|
|
56
|
+
|
|
57
|
+
let values: number[] | undefined = undefined;
|
|
58
|
+
const cmd = command.toUpperCase();
|
|
59
|
+
|
|
60
|
+
if (cmd === "H" || cmd === "V") {
|
|
61
|
+
values = [parseNumber(this)];
|
|
62
|
+
} else if (cmd === "M" || cmd === "L" || cmd === "T") {
|
|
63
|
+
values = [parseNumber(this), parseNumber(this)];
|
|
64
|
+
} else if (cmd === "S" || cmd === "Q") {
|
|
65
|
+
values = [parseNumber(this), parseNumber(this), parseNumber(this), parseNumber(this)];
|
|
66
|
+
} else if (cmd === "C") {
|
|
67
|
+
values = [
|
|
68
|
+
parseNumber(this),
|
|
69
|
+
parseNumber(this),
|
|
70
|
+
parseNumber(this),
|
|
71
|
+
parseNumber(this),
|
|
72
|
+
parseNumber(this),
|
|
73
|
+
parseNumber(this)
|
|
74
|
+
];
|
|
75
|
+
} else if (cmd === "A") {
|
|
76
|
+
values = [
|
|
77
|
+
parseNumber(this),
|
|
78
|
+
parseNumber(this),
|
|
79
|
+
parseNumber(this),
|
|
80
|
+
this._parseArcFlag(),
|
|
81
|
+
this._parseArcFlag(),
|
|
82
|
+
parseNumber(this),
|
|
83
|
+
parseNumber(this)
|
|
84
|
+
];
|
|
85
|
+
} else if (cmd === "Z") {
|
|
86
|
+
this._skipOptionalSpaces();
|
|
87
|
+
values = [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (values === undefined) {
|
|
91
|
+
throw new Error('Unknown command')
|
|
92
|
+
} else {
|
|
93
|
+
return { type: command, values };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
hasMoreData(): boolean {
|
|
99
|
+
return this._currentIndex < this._endIndex;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
initialCommandIsMoveTo(): boolean {
|
|
104
|
+
// If the path is empty it is still valid, so return true.
|
|
105
|
+
if (!this.hasMoreData()) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const command = COMMAND_MAP[this._string[this._currentIndex]];
|
|
110
|
+
|
|
111
|
+
return command === "M" || command === "m";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
_isCurrentSpace(): boolean {
|
|
116
|
+
const char = this._string[this._currentIndex];
|
|
117
|
+
return char <= " " && (char === " " || char === "\n" || char === "\t" || char === "\r" || char === "\f");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
_skipOptionalSpaces(): boolean {
|
|
122
|
+
while (this._currentIndex < this._endIndex && this._isCurrentSpace()) {
|
|
123
|
+
this._currentIndex += 1;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return this._currentIndex < this._endIndex;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
_skipOptionalSpacesOrDelimiter(): boolean {
|
|
131
|
+
if (
|
|
132
|
+
this._currentIndex < this._endIndex &&
|
|
133
|
+
!this._isCurrentSpace() &&
|
|
134
|
+
this._string[this._currentIndex] !== ","
|
|
135
|
+
) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (this._skipOptionalSpaces()) {
|
|
140
|
+
if (this._currentIndex < this._endIndex && this._string[this._currentIndex] === ",") {
|
|
141
|
+
this._currentIndex += 1;
|
|
142
|
+
this._skipOptionalSpaces();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return this._currentIndex < this._endIndex;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
_parseArcFlag(): number {
|
|
151
|
+
if (this._currentIndex >= this._endIndex) {
|
|
152
|
+
throw new Error('Unable to parse arc flag');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let flag: number | undefined = undefined;
|
|
156
|
+
const flagChar = this._string[this._currentIndex];
|
|
157
|
+
|
|
158
|
+
this._currentIndex += 1;
|
|
159
|
+
|
|
160
|
+
if (flagChar === "0") {
|
|
161
|
+
flag = 0;
|
|
162
|
+
} else if (flagChar === "1") {
|
|
163
|
+
flag = 1;
|
|
164
|
+
} else {
|
|
165
|
+
throw new Error('Unable to parse arc flag - arc flag must be 0 or 1');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this._skipOptionalSpacesOrDelimiter();
|
|
169
|
+
|
|
170
|
+
return flag;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
export { Source }
|
|
176
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PathState } from "../path-state.js";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @hidden
|
|
6
|
+
* C and c: (from www.w3.org)
|
|
7
|
+
*
|
|
8
|
+
* params: x1 y1 x2 y2 x y
|
|
9
|
+
*
|
|
10
|
+
* Draws a cubic Bézier curve from the current point to (x,y)
|
|
11
|
+
* using (x1,y1) as the control point at the beginning of the
|
|
12
|
+
* curve and (x2,y2) as the control point at the end of the
|
|
13
|
+
* curve. C (uppercase) indicates that absolute coordinates
|
|
14
|
+
* will follow; c (lowercase) indicates that relative
|
|
15
|
+
* coordinates will follow. Multiple sets of coordinates may
|
|
16
|
+
* be specified to draw a polybézier. At the end of the
|
|
17
|
+
* command, the new current point becomes the final (x,y)
|
|
18
|
+
* coordinate pair used in the polybézier.
|
|
19
|
+
*/
|
|
20
|
+
function c(s: PathState): number[][] {
|
|
21
|
+
const ps = [
|
|
22
|
+
s.p,
|
|
23
|
+
[s.vals![0], s.vals![1]],
|
|
24
|
+
[s.vals![2], s.vals![3]],
|
|
25
|
+
[s.vals![4], s.vals![5]]
|
|
26
|
+
];
|
|
27
|
+
s.prev2ndCubicControlPoint = ps[2];
|
|
28
|
+
s.prev2ndQuadraticControlPoint = undefined;
|
|
29
|
+
|
|
30
|
+
return ps;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
export { c }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { PathState } from '../path-state.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @hidden
|
|
6
|
+
* H and h: (from www.w3.org)
|
|
7
|
+
*
|
|
8
|
+
* params: x
|
|
9
|
+
*
|
|
10
|
+
* Draws a horizontal line from the current point (cpx, cpy) to (x, cpy). H
|
|
11
|
+
* (uppercase) indicates that absolute coordinates will follow; h (lowercase)
|
|
12
|
+
* indicates that relative coordinates will follow. Multiple x values can be
|
|
13
|
+
* provided (although usually this doesn't make sense). At the end of the
|
|
14
|
+
* command, the new current point becomes (x, cpy) for the final value of x.
|
|
15
|
+
*/
|
|
16
|
+
function h(s: PathState): number[][] {
|
|
17
|
+
const ps = [
|
|
18
|
+
s.p,
|
|
19
|
+
[s.vals![0], s.p[1]]
|
|
20
|
+
];
|
|
21
|
+
s.prev2ndCubicControlPoint = undefined;
|
|
22
|
+
s.prev2ndQuadraticControlPoint = undefined;
|
|
23
|
+
|
|
24
|
+
return ps;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
export { h }
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PathState } from '../path-state.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @hidden
|
|
6
|
+
* L and l: (from www.w3.org)
|
|
7
|
+
*
|
|
8
|
+
* params: x y
|
|
9
|
+
*
|
|
10
|
+
* Draw a line from the current point to the given (x,y) coordinate which
|
|
11
|
+
* becomes the new current point. L (uppercase) indicates that absolute
|
|
12
|
+
* coordinates will follow; l (lowercase) indicates that relative coordinates
|
|
13
|
+
* will follow. A number of coordinates pairs may be specified to draw a
|
|
14
|
+
* polyline. At the end of the command, the new current point is set to the
|
|
15
|
+
* final set of coordinates provided.
|
|
16
|
+
*/
|
|
17
|
+
function l(s: PathState): number[][] {
|
|
18
|
+
const ps = [
|
|
19
|
+
s.p,
|
|
20
|
+
s.vals!
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
s.prev2ndCubicControlPoint = undefined;
|
|
24
|
+
s.prev2ndQuadraticControlPoint = undefined;
|
|
25
|
+
|
|
26
|
+
return ps;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export { l }
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PathState } from '../path-state.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @hidden
|
|
6
|
+
* Q and q: (from www.w3.org)
|
|
7
|
+
*
|
|
8
|
+
* params: x1 y1 x y
|
|
9
|
+
*
|
|
10
|
+
* Draws a quadratic Bézier curve from the current point to (x,y) using (x1,y1)
|
|
11
|
+
* as the control point. Q (uppercase) indicates that absolute coordinates will
|
|
12
|
+
* follow; q (lowercase) indicates that relative coordinates will follow.
|
|
13
|
+
* Multiple sets of coordinates may be specified to draw a polybézier. At the
|
|
14
|
+
* end of the command, the new current point becomes the final (x,y) coordinate
|
|
15
|
+
* pair used in the polybézier.
|
|
16
|
+
*/
|
|
17
|
+
function q(s: PathState): number[][] {
|
|
18
|
+
const QP1 = [s.vals![0], s.vals![1]];
|
|
19
|
+
const QP2 = [s.vals![2], s.vals![3]];
|
|
20
|
+
|
|
21
|
+
s.prev2ndCubicControlPoint = undefined;
|
|
22
|
+
s.prev2ndQuadraticControlPoint = QP1;
|
|
23
|
+
|
|
24
|
+
const ps = [s.p, QP1, QP2];
|
|
25
|
+
|
|
26
|
+
return ps;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export { q }
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { PathState } from "../path-state.js";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @hidden
|
|
6
|
+
* S and s: (from www.w3.org)
|
|
7
|
+
*
|
|
8
|
+
* params: x2 y2 x y
|
|
9
|
+
*
|
|
10
|
+
* Draws a cubic Bézier curve from the current point to (x,y). The first control
|
|
11
|
+
* point is assumed to be the reflection of the second control point on the
|
|
12
|
+
* previous command relative to the current point. (If there is no previous
|
|
13
|
+
* command or if the previous command was not an C, c, S or s, assume the first
|
|
14
|
+
* control point is coincident with the current point.) (x2,y2) is the second
|
|
15
|
+
* control point (i.e., the control point at the end of the curve). S
|
|
16
|
+
* (uppercase) indicates that absolute coordinates will follow; s (lowercase)
|
|
17
|
+
* indicates that relative coordinates will follow. Multiple sets of coordinates
|
|
18
|
+
* may be specified to draw a polybézier. At the end of the command, the new
|
|
19
|
+
* current point becomes the final (x,y) coordinate pair used in the polybézier.
|
|
20
|
+
*/
|
|
21
|
+
function s(s: PathState): number[][] {
|
|
22
|
+
const p = s.prev2ndCubicControlPoint
|
|
23
|
+
? [(s.p[0] - s.prev2ndCubicControlPoint[0]) + s.p[0],
|
|
24
|
+
(s.p[1] - s.prev2ndCubicControlPoint[1]) + s.p[1]]
|
|
25
|
+
: s.p;
|
|
26
|
+
|
|
27
|
+
const ps = [
|
|
28
|
+
s.p,
|
|
29
|
+
p,
|
|
30
|
+
[s.vals![0], s.vals![1]],
|
|
31
|
+
[s.vals![2], s.vals![3]]
|
|
32
|
+
];
|
|
33
|
+
s.prev2ndCubicControlPoint = ps[2];
|
|
34
|
+
s.prev2ndQuadraticControlPoint = undefined;
|
|
35
|
+
|
|
36
|
+
return ps;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
export { s }
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PathState } from '../path-state.js';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @hidden
|
|
6
|
+
* T and t: (from www.w3.org)
|
|
7
|
+
*
|
|
8
|
+
* params: x y
|
|
9
|
+
*
|
|
10
|
+
* Draws a quadratic Bézier curve from the current point to (x,y). The control
|
|
11
|
+
* point is assumed to be the reflection of the control point on the previous
|
|
12
|
+
* command relative to the current point. (If there is no previous command or if
|
|
13
|
+
* the previous command was not a Q, q, T or t, assume the control point is
|
|
14
|
+
* coincident with the current point.) T (uppercase) indicates that absolute
|
|
15
|
+
* coordinates will follow; t (lowercase) indicates that relative coordinates
|
|
16
|
+
* will follow. At the end of the command, the new current point becomes the
|
|
17
|
+
* final (x,y) coordinate pair used in the polybézier.
|
|
18
|
+
*/
|
|
19
|
+
function t(s: PathState): number[][] {
|
|
20
|
+
const p = s.prev2ndQuadraticControlPoint
|
|
21
|
+
? [(s.p[0] - s.prev2ndQuadraticControlPoint[0]) + s.p[0],
|
|
22
|
+
(s.p[1] - s.prev2ndQuadraticControlPoint[1]) + s.p[1]]
|
|
23
|
+
: s.p;
|
|
24
|
+
|
|
25
|
+
const QP1 = p;
|
|
26
|
+
const QP2 = [s.vals![0], s.vals![1]];
|
|
27
|
+
|
|
28
|
+
s.prev2ndCubicControlPoint = undefined;
|
|
29
|
+
s.prev2ndQuadraticControlPoint = QP1;
|
|
30
|
+
|
|
31
|
+
return [s.p, QP1, QP2];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
export { t }
|