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.
Files changed (156) hide show
  1. package/README.md +100 -0
  2. package/node/bezier/bezier-curves-to-line-segs.d.ts +10 -0
  3. package/node/bezier/eval-de-casteljau.d.ts +17 -0
  4. package/node/bezier/from-to/from-to-2.d.ts +12 -0
  5. package/node/bezier/from-to/from-to-3.d.ts +12 -0
  6. package/node/bezier/from-to/from-to.d.ts +10 -0
  7. package/node/bezier/is-cubic-obtuse.d.ts +11 -0
  8. package/node/bezier/is-quad-obtuse.d.ts +11 -0
  9. package/node/bezier/is-really-point.d.ts +9 -0
  10. package/node/bezier/split-by-deviation-from-straight-line-cubic.d.ts +20 -0
  11. package/node/bezier/split-by-deviation-from-straight-line-quad.d.ts +11 -0
  12. package/node/bezier/split-into-line-segments.d.ts +10 -0
  13. package/node/debug-shaders.d.ts +9 -0
  14. package/node/generate-sdf.d.ts +18 -0
  15. package/node/helpers/calc-circs.d.ts +11 -0
  16. package/node/helpers/clip-line-segment-to-grid.d.ts +14 -0
  17. package/node/helpers/clip-line-segment-to-strips.d.ts +13 -0
  18. package/node/helpers/create-empty-grid.d.ts +8 -0
  19. package/node/helpers/create-empty-strips.d.ts +7 -0
  20. package/node/helpers/distance-seg-to-p.d.ts +5 -0
  21. package/node/helpers/find-close-cells.d.ts +3 -0
  22. package/node/helpers/find-crossing-cells.d.ts +3 -0
  23. package/node/helpers/get-distance-to-line-function.d.ts +10 -0
  24. package/node/helpers/jump-idx.d.ts +2 -0
  25. package/node/helpers/map-to-viewbox.d.ts +2 -0
  26. package/node/helpers/seg-box-x.d.ts +10 -0
  27. package/node/helpers/seg-strip-x.d.ts +9 -0
  28. package/node/index.d.ts +1 -0
  29. package/node/main-program.d.ts +4 -0
  30. package/node/max-aspect-ratio-before-stretch.d.ts +2 -0
  31. package/node/prepare-buffers.d.ts +10 -0
  32. package/node/row-count.d.ts +2 -0
  33. package/node/shaders/main.fragment.d.ts +2 -0
  34. package/node/shaders/main.vertex.d.ts +2 -0
  35. package/node/svg/get-beziers-from-raw-paths.d.ts +12 -0
  36. package/node/svg/get-paths-from-str.d.ts +9 -0
  37. package/node/svg/path-data-polyfill/parse-number.d.ts +10 -0
  38. package/node/svg/path-data-polyfill/parse-path-data-string.d.ts +9 -0
  39. package/node/svg/path-data-polyfill/source.d.ts +19 -0
  40. package/node/svg/path-segment/c.d.ts +19 -0
  41. package/node/svg/path-segment/h.d.ts +15 -0
  42. package/node/svg/path-segment/l.d.ts +16 -0
  43. package/node/svg/path-segment/q.d.ts +16 -0
  44. package/node/svg/path-segment/s.d.ts +20 -0
  45. package/node/svg/path-segment/t.d.ts +18 -0
  46. package/node/svg/path-segment/v.d.ts +15 -0
  47. package/node/svg/path-segment/z.d.ts +13 -0
  48. package/node/svg/path-state.d.ts +11 -0
  49. package/node/tex-width.d.ts +11 -0
  50. package/node/types/attribute.d.ts +6 -0
  51. package/node/types/cell.d.ts +18 -0
  52. package/node/types/gl-context.d.ts +28 -0
  53. package/node/types/gl-type.d.ts +2 -0
  54. package/node/types/gl-usage.d.ts +2 -0
  55. package/node/types/program.d.ts +16 -0
  56. package/node/types/strip.d.ts +8 -0
  57. package/node/types/texture.d.ts +4 -0
  58. package/node/utils/calc-circs.d.ts +11 -0
  59. package/node/utils/clip-line-segment-to-grid.d.ts +14 -0
  60. package/node/utils/clip-line-segment-to-strips.d.ts +13 -0
  61. package/node/utils/create-empty-grid.d.ts +8 -0
  62. package/node/utils/create-empty-strips.d.ts +7 -0
  63. package/node/utils/distance-seg-to-p.d.ts +5 -0
  64. package/node/utils/find-close-cells.d.ts +3 -0
  65. package/node/utils/find-crossing-cells.d.ts +3 -0
  66. package/node/utils/get-distance-to-line-function.d.ts +10 -0
  67. package/node/utils/jump-idx.d.ts +2 -0
  68. package/node/utils/map-to-viewbox.d.ts +2 -0
  69. package/node/utils/seg-box-x.d.ts +10 -0
  70. package/node/utils/seg-strip-x.d.ts +9 -0
  71. package/node/vector/dot.d.ts +8 -0
  72. package/node/vector/from-to-vec.d.ts +8 -0
  73. package/node/vector/len.d.ts +6 -0
  74. package/node/webgl-utils/compile-shader.d.ts +2 -0
  75. package/node/webgl-utils/get-gl-context.d.ts +10 -0
  76. package/node/webgl-utils/set-attribute.d.ts +15 -0
  77. package/node/webgl-utils/set-uniform-block.d.ts +9 -0
  78. package/node/webgl-utils/set-uniform.d.ts +4 -0
  79. package/node/webgl-utils/uniform-block.d.ts +6 -0
  80. package/node/webgl-utils/uniform-type.d.ts +2 -0
  81. package/node/webgl-utils/use-program.d.ts +17 -0
  82. package/node/webgl-utils/use-texture.d.ts +9 -0
  83. package/node/webgl2.d.ts +2 -0
  84. package/package.json +56 -0
  85. package/src/bezier/bezier-curves-to-line-segs.ts +39 -0
  86. package/src/bezier/eval-de-casteljau.ts +78 -0
  87. package/src/bezier/from-to/from-to-2.ts +159 -0
  88. package/src/bezier/from-to/from-to-3.ts +176 -0
  89. package/src/bezier/from-to/from-to.ts +30 -0
  90. package/src/bezier/is-cubic-obtuse.ts +31 -0
  91. package/src/bezier/is-quad-obtuse.ts +26 -0
  92. package/src/bezier/is-really-point.ts +25 -0
  93. package/src/bezier/split-by-deviation-from-straight-line-cubic.ts +109 -0
  94. package/src/bezier/split-by-deviation-from-straight-line-quad.ts +66 -0
  95. package/src/bezier/split-into-line-segments.ts +39 -0
  96. package/src/debug-shaders.ts +38 -0
  97. package/src/generate-sdf.ts +91 -0
  98. package/src/index.ts +2 -0
  99. package/src/main-program.ts +160 -0
  100. package/src/max-aspect-ratio-before-stretch.ts +5 -0
  101. package/src/prepare-buffers.ts +149 -0
  102. package/src/row-count.ts +6 -0
  103. package/src/shaders/main.fragment.ts +157 -0
  104. package/src/shaders/main.vertex.ts +55 -0
  105. package/src/svg/get-beziers-from-raw-paths.ts +112 -0
  106. package/src/svg/get-paths-from-str.ts +19 -0
  107. package/src/svg/path-data-polyfill/parse-number.ts +138 -0
  108. package/src/svg/path-data-polyfill/parse-path-data-string.ts +26 -0
  109. package/src/svg/path-data-polyfill/source.ts +176 -0
  110. package/src/svg/path-segment/c.ts +34 -0
  111. package/src/svg/path-segment/h.ts +28 -0
  112. package/src/svg/path-segment/l.ts +30 -0
  113. package/src/svg/path-segment/q.ts +30 -0
  114. package/src/svg/path-segment/s.ts +40 -0
  115. package/src/svg/path-segment/t.ts +35 -0
  116. package/src/svg/path-segment/v.ts +28 -0
  117. package/src/svg/path-segment/z.ts +27 -0
  118. package/src/svg/path-state.ts +15 -0
  119. package/src/tex-width.ts +14 -0
  120. package/src/types/attribute.ts +9 -0
  121. package/src/types/cell.ts +21 -0
  122. package/src/types/gl-context.ts +28 -0
  123. package/src/types/gl-type.ts +16 -0
  124. package/src/types/gl-usage.ts +14 -0
  125. package/src/types/program.ts +14 -0
  126. package/src/types/strip.ts +11 -0
  127. package/src/types/texture.ts +7 -0
  128. package/src/types/typed-array.ts +16 -0
  129. package/src/utils/calc-circs.ts +129 -0
  130. package/src/utils/clip-line-segment-to-grid.ts +133 -0
  131. package/src/utils/clip-line-segment-to-strips.ts +196 -0
  132. package/src/utils/create-empty-grid.ts +32 -0
  133. package/src/utils/create-empty-strips.ts +21 -0
  134. package/src/utils/distance-seg-to-p.ts +50 -0
  135. package/src/utils/find-close-cells.ts +171 -0
  136. package/src/utils/find-crossing-cells.ts +40 -0
  137. package/src/utils/get-distance-to-line-function.ts +59 -0
  138. package/src/utils/is-point-in-box.ts +16 -0
  139. package/src/utils/jump-idx.ts +107 -0
  140. package/src/utils/map-to-viewbox.ts +41 -0
  141. package/src/utils/path.ts +137 -0
  142. package/src/utils/seg-box-x.ts +84 -0
  143. package/src/utils/seg-strip-x.ts +72 -0
  144. package/src/utils/sum.ts +13 -0
  145. package/src/vector/dot.ts +13 -0
  146. package/src/vector/from-to-vec.ts +13 -0
  147. package/src/vector/len.ts +11 -0
  148. package/src/webgl-utils/compile-shader.ts +15 -0
  149. package/src/webgl-utils/get-gl-context.ts +61 -0
  150. package/src/webgl-utils/set-attribute.ts +74 -0
  151. package/src/webgl-utils/set-uniform-block.ts +45 -0
  152. package/src/webgl-utils/set-uniform.ts +24 -0
  153. package/src/webgl-utils/uniform-block.ts +9 -0
  154. package/src/webgl-utils/uniform-type.ts +10 -0
  155. package/src/webgl-utils/use-program.ts +48 -0
  156. package/src/webgl-utils/use-texture.ts +34 -0
@@ -0,0 +1,28 @@
1
+ import { PathState } from '../path-state.js';
2
+
3
+
4
+ /**
5
+ * @hidden
6
+ * V and v: (from www.w3.org)
7
+ *
8
+ * params: y
9
+ *
10
+ * Draws a vertical line from the current point (cpx, cpy) to (cpx, y). V
11
+ * (uppercase) indicates that absolute coordinates will follow; v (lowercase)
12
+ * indicates that relative coordinates will follow. Multiple y values can be
13
+ * provided (although usually this doesn't make sense). At the end of the
14
+ * command, the new current point becomes (cpx, y) for the final value of y.
15
+ */
16
+ function v(s: PathState): number[][] {
17
+ const ps = [
18
+ s.p,
19
+ [s.p[0], s.vals![0]]
20
+ ];
21
+ s.prev2ndCubicControlPoint = undefined;
22
+ s.prev2ndQuadraticControlPoint = undefined;
23
+
24
+ return ps;
25
+ }
26
+
27
+
28
+ export { v }
@@ -0,0 +1,27 @@
1
+ import { PathState } from '../path-state.js';
2
+
3
+
4
+ /**
5
+ * @hidden
6
+ * Z and z: (from www.w3.org)
7
+ *
8
+ * params: (none)
9
+ *
10
+ * Close the current subpath by drawing a straight line from the current point
11
+ * to current subpath's initial point. Since the Z and z commands take no
12
+ * parameters, they have an identical effect.
13
+ */
14
+ function z(s: PathState): number[][] {
15
+ const ps = [
16
+ s.p,
17
+ s.initialPoint!
18
+ ];
19
+
20
+ s.prev2ndCubicControlPoint = undefined;
21
+ s.prev2ndQuadraticControlPoint = undefined;
22
+
23
+ return ps;
24
+ }
25
+
26
+
27
+ export { z }
@@ -0,0 +1,15 @@
1
+
2
+ /** @hidden */
3
+ interface PathState {
4
+ initialPoint?: number[] | undefined;
5
+ p: number[];
6
+ vals?: number[] | undefined;
7
+
8
+ /** Used in conjunction with "S", "s" */
9
+ prev2ndCubicControlPoint?: number[] | undefined;
10
+ /** Used in conjunction with "T", "t" */
11
+ prev2ndQuadraticControlPoint?: number[] | undefined;
12
+ }
13
+
14
+
15
+ export { PathState };
@@ -0,0 +1,14 @@
1
+
2
+ /**
3
+ * Just a general texture width for our textures holding general data.
4
+ * * width cannot be too small otherwise width or height might exceed
5
+ * `gl.getParameter(gl.MAX_TEXTURE_SIZE)`
6
+ * * if width is too large we might need to pad too many zeros so `256` is a
7
+ * good balance since we are guaranteed at least 4096 for MAX_TEXTURE_SIZE
8
+ * so we can store 256 * 4096 = 1_048_576 values per texture, more than
9
+ * adequate for our purposes
10
+ */
11
+ const TEX_WIDTH = 256;
12
+
13
+
14
+ export { TEX_WIDTH }
@@ -0,0 +1,9 @@
1
+
2
+ interface Attribute {
3
+ readonly buf: WebGLBuffer;
4
+ readonly loc: number;
5
+ data: AllowSharedBufferSource | null;
6
+ }
7
+
8
+
9
+ export type { Attribute }
@@ -0,0 +1,21 @@
1
+
2
+ /**
3
+ * A single cell in the grid
4
+ */
5
+ interface Cell {
6
+ /** line segments contained within the cell */
7
+ readonly lineSegs: number[][][];
8
+ /**
9
+ * other cells (represented by their u and v indexes as ROW_COUNT*u + v)
10
+ * containing line segments and being close to this one in
11
+ * some (specific) sense
12
+ */
13
+ readonly closeCells: number[];
14
+ /**
15
+ * represented by their u and v indexes as ROW_COUNT*u + v
16
+ */
17
+ readonly crossingCells: number[];
18
+ }
19
+
20
+
21
+ export type { Cell }
@@ -0,0 +1,28 @@
1
+ import type { Program } from "./program";
2
+ import type { Texture } from "./texture";
3
+
4
+
5
+ /**
6
+ * An object wrapping a `WebGL2RenderingContext` and including additional state
7
+ * which includes the following:
8
+ *
9
+ * * `textures`
10
+ * * `programs`
11
+ * * `framebufferStack`
12
+ *
13
+ * so they don't have to be recreated on each draw call.
14
+ *
15
+ * * an optional `width` and `height` that can be used for any purpose and that are not
16
+ * set or read by any function within the library
17
+ *
18
+ */
19
+ interface GlContext {
20
+ readonly gl: WebGL2RenderingContext;
21
+ readonly textures: { [index:string]: Texture };
22
+ readonly programs: { [index:string]: Program };
23
+ readonly framebufferStack: WebGLFramebuffer[];
24
+ readonly onContextLoss: () => void;
25
+ }
26
+
27
+
28
+ export type { GlContext }
@@ -0,0 +1,16 @@
1
+
2
+ type GLattrType =
3
+ | typeof WebGL2RenderingContext.BYTE
4
+ | typeof WebGL2RenderingContext.SHORT
5
+ | typeof WebGL2RenderingContext.UNSIGNED_BYTE
6
+ | typeof WebGL2RenderingContext.UNSIGNED_SHORT
7
+ | typeof WebGL2RenderingContext.FLOAT
8
+ // WebGL2 only
9
+ | typeof WebGL2RenderingContext.HALF_FLOAT
10
+ | typeof WebGL2RenderingContext.INT
11
+ | typeof WebGL2RenderingContext.UNSIGNED_INT
12
+ | typeof WebGL2RenderingContext.INT_2_10_10_10_REV
13
+ | typeof WebGL2RenderingContext.UNSIGNED_INT_2_10_10_10_REV;
14
+
15
+
16
+ export type { GLattrType }
@@ -0,0 +1,14 @@
1
+
2
+ type GLusage =
3
+ | typeof WebGL2RenderingContext.STATIC_DRAW
4
+ | typeof WebGL2RenderingContext.DYNAMIC_DRAW
5
+ | typeof WebGL2RenderingContext.STREAM_DRAW
6
+ | typeof WebGL2RenderingContext.STATIC_READ
7
+ | typeof WebGL2RenderingContext.DYNAMIC_READ
8
+ | typeof WebGL2RenderingContext.STREAM_READ
9
+ | typeof WebGL2RenderingContext.STATIC_COPY
10
+ | typeof WebGL2RenderingContext.DYNAMIC_COPY
11
+ | typeof WebGL2RenderingContext.STREAM_COPY;
12
+
13
+
14
+ export type { GLusage }
@@ -0,0 +1,14 @@
1
+ import type { Attribute } from "./attribute";
2
+ import type { UniformBlock } from "../webgl-utils/uniform-block";
3
+
4
+
5
+ interface Program {
6
+ readonly gl: WebGL2RenderingContext;
7
+ readonly program: WebGLProgram;
8
+ readonly attributes: { [index:string]: Attribute };
9
+ readonly uniforms: { [index:string]: WebGLUniformLocation | null };
10
+ readonly uniformBlocks: { [index:string]: UniformBlock };
11
+ }
12
+
13
+
14
+ export { Program }
@@ -0,0 +1,11 @@
1
+
2
+ /**
3
+ * A single strip within strips
4
+ */
5
+ interface Strip {
6
+ /** line segments contained within the strip */
7
+ readonly lineSegs: number[][][];
8
+ }
9
+
10
+
11
+ export type { Strip }
@@ -0,0 +1,7 @@
1
+
2
+ interface Texture {
3
+ readonly tex: WebGLTexture;
4
+ }
5
+
6
+
7
+ export type { Texture }
@@ -0,0 +1,16 @@
1
+
2
+ type TypedArray =
3
+ | Int8Array
4
+ | Uint8Array
5
+ | Uint8ClampedArray
6
+ | Int16Array
7
+ | Uint16Array
8
+ | Int32Array
9
+ | Uint32Array
10
+ | Float32Array
11
+ | Float64Array
12
+ | BigInt64Array
13
+ | BigUint64Array;
14
+
15
+
16
+ export type { TypedArray }
@@ -0,0 +1,129 @@
1
+ import { MAX_ASPECT_RATIO_BEFORE_STRETCH } from '../max-aspect-ratio-before-stretch';
2
+ import { ROW_COUNT } from '../row-count';
3
+ import { len } from '../vector/len';
4
+
5
+
6
+ /**
7
+ * Represents the max distance (in cell side length units) from one corner of
8
+ * the viewbox to the opposing corner.
9
+ */
10
+ // const diagLen = ceil(sqrt((MAX_ASPECT_RATIO_BEFORE_STRETCH*ROW_COUNT)**2 + ROW_COUNT**2));
11
+
12
+
13
+ const circsCache = calcCircs();
14
+
15
+
16
+ interface Range {
17
+ readonly from: number;
18
+ readonly u: number;
19
+ readonly v: number;
20
+ }
21
+
22
+
23
+ function calcCircs() {
24
+ const ranges: Range[] = [{ from: 0, u: 0, v: 0 }];
25
+
26
+ // for (let i=0; i<ROW_COUNT; i++) {
27
+ for (let i=0; i<MAX_ASPECT_RATIO_BEFORE_STRETCH*ROW_COUNT; i++) {
28
+ ranges.push({ from: i + 0.5, u: i + 1, v: 0 });
29
+ ranges.push({ from: i + 0.5, u: -i - 1, v: 0 });
30
+ ranges.push({ from: i + 0.5, u: 0, v: i + 1 });
31
+ ranges.push({ from: i + 0.5, u: 0, v: -i - 1 });
32
+ for (let j=0; j<ROW_COUNT; j++) {
33
+ const p = [0.5 + i, 0.5 + j];
34
+ const d = len(p);
35
+
36
+ const u = i + 1;
37
+ const v = j + 1;
38
+ ranges.push({ from: d, u, v });
39
+ if (u !== 0) {
40
+ ranges.push({ from: d, u: -u, v });
41
+ }
42
+ if (v !== 0) {
43
+ ranges.push({ from: d, u, v: -v });
44
+ }
45
+ if (u !== 0 && v !== 0) {
46
+ ranges.push({ from: d, u: -u, v: -v });
47
+ }
48
+ }
49
+
50
+ ranges.sort((a,b) => (a.from - b.from));
51
+ }
52
+
53
+
54
+
55
+ return ranges;
56
+ }
57
+
58
+
59
+ export { circsCache }
60
+
61
+
62
+ // Quokka tests
63
+ // const { E, trunc, max, sqrt, ceil } = Math;
64
+
65
+ // calcCircs().length;//?
66
+
67
+ // const cs = calcCircs().map(c => c.from);
68
+
69
+ // const longest = (() => {
70
+ // let l = 0;
71
+ // let maxL = 0;
72
+ // let _v = 0;
73
+ // for (let i=0; i<cs.length; i++) {
74
+ // const v = cs[i];
75
+ // if (_v === cs[i]) {
76
+ // l++;
77
+ // } else {
78
+ // l = 0;
79
+ // }
80
+ // _v = v;
81
+ // if (l > maxL) {
82
+ // maxL = l;
83
+ // }
84
+ // }
85
+
86
+ // return maxL;
87
+ // })();//?
88
+
89
+ // const froms = [
90
+ // { from: 0, to: 50, skip: 1 },
91
+ // { from: 50, to: 3050, skip: 50 },
92
+ // { from: 400, to: 4000, skip: 100 },
93
+ // // { from: 10, to: 100, skip: 10 }
94
+ // ];
95
+
96
+ // for (let j=0; j<froms.length; j++) {
97
+ // const { from, to, skip } = froms[j];
98
+ // let strs: string[] = [];
99
+ // for (let i=from; i<to; i += skip) {
100
+ // const v = cs[i];
101
+ // strs.push(`${v.toString()} ${i}`);
102
+ // }
103
+ // const csStr = strs.join('\n');
104
+
105
+ // console.log(csStr);
106
+ // }
107
+
108
+ // jumpIdx(3.7);//?
109
+ // for (let i=30; i<44; i += 0.25) {
110
+ // jumpIdx(i);//?
111
+ // [(cs[jumpIdx(i)]), i];//?
112
+ // }
113
+
114
+
115
+ // let tot = 0;
116
+ // const vs: number[] = [];
117
+ // for (let i=400; i<=3800; i++) {
118
+ // const c = cs[i];
119
+ // const ap = A*(E**(B*c)) + C*(E**(-D*c)) + F;
120
+ // const S = B*A*(E**(B*c)) + -D*C*(E**(-D*c));
121
+ // // i;//?
122
+ // // ap;//?
123
+ // const err = abs(i - ap);
124
+ // tot += err;
125
+ // vs.push(err);
126
+ // }
127
+ // vs.sort((a,b) => b - a);//?
128
+ // vs[3400];//?
129
+ // tot/(3800 - 400);//?
@@ -0,0 +1,133 @@
1
+ import type { Cell } from "../types/cell";
2
+ import { ROW_COUNT } from "../row-count";
3
+ import { segBoxX } from "./seg-box-x";
4
+
5
+
6
+ const { floor, ceil } = Math;
7
+
8
+
9
+ /**
10
+ * Clips a line segment to grid boundaries and returns multiple segments.
11
+ *
12
+ * * modifies grid by adding line segments to cells
13
+ * * size/count *must* be a power of 2
14
+ *
15
+ * @param count the number of grid cells per dimension
16
+ * @param width
17
+ * @param height
18
+ * @param seg the line segment (array of 2 points)
19
+ */
20
+ function clipLineSegmentToGrid(
21
+ grid: Cell[][],
22
+ width: number,
23
+ height: number,
24
+ colCount: number,
25
+ cellSize: number,
26
+ seg: number[][],
27
+ padCount: number): void {
28
+
29
+ /** `cellSize` *must* be a power of two */
30
+ // const cellSize = width/ROW_COUNT;
31
+
32
+ const padding = cellSize*padCount;
33
+
34
+ const [[x0,y0],[x1,y1]] = seg;
35
+
36
+ const paddedWidth = width + padding;
37
+ const paddedHeight = height + padding;
38
+ // TODO - investigate lines on edge and corners - ps could be length 4!!
39
+ const ps = segBoxX(seg, [[-padding,-padding],[paddedWidth,paddedHeight]]);
40
+
41
+ const p0IsInBox = x0 > -padding && x0 < paddedWidth && y0 > -padding && y0 < paddedHeight;
42
+ const p1IsInBox = x1 > -padding && x1 < paddedWidth && y1 > -padding && y1 < paddedHeight;
43
+
44
+ // if line is completely outside box
45
+ if (ps.length < 2 && !p0IsInBox && !p1IsInBox) {
46
+ return;
47
+ }
48
+
49
+ // Determine which grid cells the line passes through
50
+
51
+ const dX = x1 - x0;
52
+ const dY = y1 - y0;
53
+
54
+ // Use Bresenham-like approach to find all grid cells the line crosses
55
+ const stepX = x1 > x0 ? cellSize : -cellSize;
56
+ const stepY = y1 > y0 ? cellSize : -cellSize;
57
+
58
+ /** first point of each line segment to connect */
59
+
60
+ /** current x position */
61
+ let x = x0;
62
+ /** current y position */
63
+ let y = y0;
64
+
65
+ // current intersection index
66
+ let xIdx = 0;
67
+ // left-to-right
68
+ const ltr = stepX > 0;
69
+ // bottom-to-top
70
+ const btt = stepY > 0;
71
+
72
+ // // line segment min-y excluded
73
+ // const crossing =
74
+ // (seg.y > y != seg.w > y) &&
75
+ // (x > (seg.z - seg.x)*(y - seg.y) / (seg.w - seg.y) + seg.x);
76
+
77
+ const fX = ltr ? floor : ceil;
78
+ const fY = btt ? floor : ceil;
79
+ while (true) {
80
+ // Find next grid boundary
81
+ let nextGridX = cellSize*fX((x + stepX)/cellSize);
82
+ let nextGridY = cellSize*fY((y + stepY)/cellSize);
83
+
84
+ // Calculate parameter t for intersection with vertical and horizontal grid lines
85
+ // eq. (1)
86
+ const tX = (nextGridX - x0)/dX;
87
+ // eq. (2)
88
+ const tY = (nextGridY - y0)/dY;
89
+
90
+ const useTx = (tX < tY && dX !== 0) || dY === 0;
91
+ /** intersection X */
92
+ const xX = useTx ? nextGridX : x0 + tY*dX;
93
+ /** intersection Y */
94
+ const xY = useTx ? y0 + tX*dY : nextGridY;
95
+
96
+ // if we're past the line endpoint
97
+ if ((tX > 1 || dX === 0) && (tY > 1 || dY === 0)) {
98
+ const u = floor(x1/cellSize) + padCount;
99
+ const v = floor(y1/cellSize) + padCount;
100
+ grid[u]?.[v]?.lineSegs.push([[x,y], [x1,y1]]);
101
+ break;
102
+ }
103
+
104
+ // if next grid intersection is on edge of grid
105
+ if ((xX <= -padding || xY <= -padding) || (xX >= paddedWidth || xY >= paddedHeight)) {
106
+ const entering = !p0IsInBox && xIdx === 0;
107
+
108
+ if (entering) {
109
+ [x,y] = ps[0]; // update current x,y position
110
+ xIdx++;
111
+ continue;
112
+ } else {
113
+ const u = fX(x/cellSize) - (ltr ? 0 : 1) + padCount; // current u
114
+ const v = fY(y/cellSize) - (btt ? 0 : 1) + padCount; // current v
115
+ const seg_ = [[x,y], ps[xIdx]];
116
+ grid[u]?.[v]?.lineSegs.push(seg_);
117
+ break;
118
+ }
119
+ }
120
+
121
+ const u = fX(x/cellSize) - (ltr ? 0 : 1) + padCount; // current u
122
+ const v = fY(y/cellSize) - (btt ? 0 : 1) + padCount; // current v
123
+
124
+ grid[u]?.[v]?.lineSegs.push([[x,y], [xX,xY]]);
125
+
126
+ // update current position
127
+ x = xX;
128
+ y = xY;
129
+ }
130
+ }
131
+
132
+
133
+ export { clipLineSegmentToGrid }
@@ -0,0 +1,196 @@
1
+ import type { Strip } from "../types/strip";
2
+ import { segStripX } from "./seg-strip-x";
3
+
4
+
5
+ const { floor, ceil } = Math;
6
+
7
+
8
+ /**
9
+ * Clips a line segment to strip boundaries and returns multiple segments.
10
+ *
11
+ * * modifies strips by adding line segments to each strip
12
+ * * size/count *must* be a power of 2
13
+ *
14
+ * @param count the number of strips
15
+ * @param height the height of a strip
16
+ * @param seg the line segment (array of 2 points)
17
+ */
18
+ function clipLineSegmentToStrips(
19
+ strips: Strip[],
20
+ height: number,
21
+ seg: number[][]): void {
22
+
23
+ const count = strips.length;
24
+ /** `cellSize` *must* be a power of two */
25
+ const cellSize = height/count;
26
+
27
+ const [p0,p1] = seg;
28
+ const ps = segStripX(seg, [0,0,height]);
29
+ const [x0,y0] = p0;
30
+ const [x1,y1] = p1;
31
+
32
+ const dX = x1 - x0;
33
+ const dY = y1 - y0;
34
+
35
+ const p0IsInStrip = x0 < 0 && y0 > 0 && y0 < height;
36
+ const p1IsInStrip = x1 < 0 && y1 > 0 && y1 < height;
37
+
38
+ // if line is completely outside strip
39
+ if ((ps.length < 2 && !p0IsInStrip && !p1IsInStrip) &&
40
+ ((x0 !== 0 || dX !== 0) ||
41
+ ((y0 <= 0 && y1 <= 0) || (y0 >= height && y1 >= height)))) {
42
+
43
+ return;
44
+ }
45
+
46
+ // Determine which strips the line passes through
47
+
48
+ if (dY === 0) { // no crossings possible
49
+ return;
50
+ }
51
+
52
+ // Use Bresenham-like approach to find all strips the line crosses
53
+ const stepY = y1 > y0 ? cellSize : -cellSize;
54
+
55
+ /** first point of each line segment to connect */
56
+
57
+ /** current x position */
58
+ let x = x0;
59
+ /** current y position */
60
+ let y = y0;
61
+
62
+ let i = 0;
63
+ // left-to-right
64
+ const ltr = x1 > x0;
65
+ // bottom-to-top
66
+ const btt = y1 > y0;
67
+
68
+ // // line segment min-y excluded
69
+ // const crossing =
70
+ // (seg.y > y != seg.w > y) &&
71
+ // (x > (seg.z - seg.x)*(y - seg.y) / (seg.w - seg.y) + seg.x);
72
+
73
+ const fY = btt ? floor : ceil;
74
+ while (true) {
75
+ // Find next strip boundary
76
+ let nextGridX = ((ltr && x < 0) || (!ltr && x > 0)) ? 0 : Number.NEGATIVE_INFINITY;
77
+
78
+ let nextGridY = cellSize*fY((y + stepY)/cellSize);
79
+
80
+ // Calculate parameter t for intersection with horizontal strip lines
81
+ // eq. (1)
82
+ const tX = (nextGridX - x0)/dX;
83
+ // eq. (2)
84
+ const tY = (nextGridY - y0)/dY;
85
+
86
+ const useTx = tX < tY && dX !== 0;
87
+
88
+ /** intersection X */
89
+ const xX = useTx ? 0 : x0 + tY*dX;
90
+ /** intersection Y */
91
+ const xY = useTx ? y0 + tX*dY : nextGridY;
92
+
93
+ // if we're past the line endpoint
94
+ if ((tX > 1 || dX === 0) && tY > 1) {
95
+ const v = floor(p1[1]/cellSize);
96
+ strips[v]?.lineSegs.push([[x,y],p1]);
97
+ break;
98
+ }
99
+
100
+ if (((xX >= 0 && (x0 !== 0 || dX !== 0)) || xY <= 0 || xY >= height)) {
101
+ // next strip intersection is on edge of strips
102
+ const entering = !p0IsInStrip && i === 0;
103
+
104
+ if (entering) {
105
+ // update current x,y position
106
+ [x,y] = ps[0];
107
+ i++;
108
+ continue;
109
+ } else {
110
+ const v = fY(y/cellSize) - (btt ? 0 : 1); // previous v
111
+ const seg_ = [[x,y],ps[i]];
112
+ strips[v]?.lineSegs.push(seg_);
113
+ break;
114
+ }
115
+ }
116
+
117
+ // previous v
118
+ const v = fY(y/cellSize) - (btt ? 0 : 1);
119
+
120
+ strips[v]?.lineSegs.push([[x,y], [xX,xY]]);
121
+
122
+ // update current position
123
+ x = xX;
124
+ y = xY;
125
+ }
126
+ }
127
+
128
+
129
+ export { clipLineSegmentToStrips }
130
+
131
+
132
+ // Quokka tests - https://www.desmos.com/calculator/uyqsdkviih
133
+ // import { createEmptyStrips } from "./create-empty-strips";
134
+
135
+ // {
136
+ // const strips = createEmptyStrips(8);
137
+ // const seg = [[0, 100], [0, 400]];
138
+
139
+ // toDesmosStr(seg);
140
+ // clipLineSegmentToStrips(strips, 512, seg);
141
+ // testAllEmptyExcept(strips,[1,2,3,4,5,6]);
142
+ // }
143
+
144
+
145
+ // {
146
+ // const strips = createEmptyStrips(8);
147
+ // const seg = [[83, 166], [-90, 440]];
148
+
149
+ // toDesmosStr(seg);
150
+ // clipLineSegmentToStrips(strips, 512, seg);
151
+ // testAllEmptyExcept(strips,[4,5,6]);
152
+ // }
153
+
154
+ // {
155
+ // const strips = createEmptyStrips(8);
156
+ // const seg = [[-570, 236], [-392, 546]];
157
+
158
+ // toDesmosStr(seg);
159
+ // clipLineSegmentToStrips(strips, 512, seg);
160
+ // testAllEmptyExcept(strips,[3,4,5,6,7]);
161
+ // }
162
+
163
+ // {
164
+ // const strips = createEmptyStrips(8);
165
+ // const seg = [[-462, 632], [-152, 611]];
166
+
167
+ // toDesmosStr(seg);
168
+ // clipLineSegmentToStrips(strips, 512, seg);
169
+ // testAllEmptyExcept(strips,[]);
170
+ // }
171
+
172
+
173
+ // function toDesmosStr(seg: number[][]) {
174
+ // const [[x0,y0],[x1,y1]] = seg;
175
+ // return `\\left(\\left(1-t\\right)\\cdot${x0.toFixed(2)}+t\\cdot\\left(${x1.toFixed(2)}\\right),\\left(1-t\\right)\\cdot${y0.toFixed(2)}+t\\cdot${y1.toFixed(2)}\\right)`;
176
+ // }
177
+ // function testAllEmptyExcept(
178
+ // strips: Strip[],
179
+ // exceptions: number[]) {
180
+
181
+ // for (let i=0; i<strips.length; i++) {
182
+ // const strip = strips[i];
183
+ // const idx = exceptions.findIndex(v => v === i);
184
+ // const exception = exceptions[idx];
185
+ // const len = strip.lineSegs.length;
186
+ // if (exception === undefined) {
187
+ // if (len !== 0) {
188
+ // throw new Error(`Strip ${i} must be empty`);
189
+ // }
190
+ // } else {
191
+ // if (len !== 1) {
192
+ // throw new Error(`Strip ${i} must contain 1 segments, found ${len}`);
193
+ // }
194
+ // }
195
+ // }
196
+ // }