webgl2-sdf 0.0.6 → 0.0.8
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 +56 -45
- package/browser/index.min.js +1 -1
- package/node/debug-shaders.d.ts +9 -0
- package/node/debug-shaders.js +28 -0
- package/node/debug-shaders.js.map +1 -0
- package/node/generate-sdf.d.ts +80 -9
- package/node/generate-sdf.js +91 -20
- package/node/generate-sdf.js.map +1 -1
- package/node/main-program.d.ts +2 -1
- package/node/main-program.js +19 -15
- package/node/main-program.js.map +1 -1
- package/node/prepare-buffers.js +21 -18
- package/node/prepare-buffers.js.map +1 -1
- package/node/shaders/fragment.d.ts +4 -2
- package/node/shaders/fragment.js +89 -57
- package/node/shaders/fragment.js.map +1 -1
- package/node/svg/get-paths-from-str.d.ts +1 -0
- package/node/svg/get-paths-from-str.js +1 -0
- package/node/svg/get-paths-from-str.js.map +1 -1
- package/node/svg/path-data-polyfill/source.d.ts +0 -1
- package/node/svg/path-data-polyfill/source.js +0 -2
- package/node/svg/path-data-polyfill/source.js.map +1 -1
- package/node/svg/path-state.d.ts +0 -1
- package/node/webgl-utils/get-web-gl-context.js +3 -0
- package/node/webgl-utils/get-web-gl-context.js.map +1 -1
- package/package.json +1 -1
- package/src/debug-shaders.ts +2 -2
- package/src/generate-sdf.ts +116 -31
- package/src/main-program.ts +38 -21
- package/src/prepare-buffers.ts +24 -21
- package/src/shaders/fragment.ts +96 -59
- package/src/svg/get-paths-from-str.ts +1 -0
- package/src/svg/path-data-polyfill/source.ts +0 -2
- package/src/svg/path-state.ts +0 -1
- package/src/webgl-utils/get-web-gl-context.ts +5 -0
package/src/shaders/fragment.ts
CHANGED
|
@@ -4,11 +4,36 @@ import { ROW_COUNT } from "../row-count.js";
|
|
|
4
4
|
const cache: { [padCount_times_colCount: number]: string } = {};
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
const GLSL_PATTERN1 = `
|
|
8
|
+
float exponent = uCustom.x;
|
|
9
|
+
res = pow(1.0 - res, exponent);
|
|
10
|
+
float alpha = res <= 0.0 ? 0.0 : 0.5;
|
|
11
|
+
float red = inside ? 0.2 : 0.8;
|
|
12
|
+
float green = abs(sin(25.0 * res));
|
|
13
|
+
float blue = 0.5;
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const GLSL_DEFAULT = `
|
|
18
|
+
float exponent = uCustom.x;
|
|
19
|
+
res = (pow(1.0 - res, exponent) * 0.5) * (inside ? -1.0 : 1.0);
|
|
20
|
+
float red = res;
|
|
21
|
+
float green = res;
|
|
22
|
+
float blue = res;
|
|
23
|
+
float alpha = res;
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
|
|
7
27
|
function getFragment(
|
|
8
28
|
colCount: number,
|
|
9
|
-
padCount: number
|
|
10
|
-
|
|
11
|
-
|
|
29
|
+
padCount: number,
|
|
30
|
+
calcFragColorStr: string,
|
|
31
|
+
hash: number): string {
|
|
32
|
+
|
|
33
|
+
// `colCount` and `padCount` can take at most 8 bits and `has` 32 bits and
|
|
34
|
+
// we have at least 53 bits to play with so we're fine
|
|
35
|
+
const key = (2**32)*(256*colCount + padCount) + hash;
|
|
36
|
+
const fragment = cache[key];
|
|
12
37
|
if (fragment !== undefined) { return fragment; }
|
|
13
38
|
|
|
14
39
|
const main_Fragment =
|
|
@@ -17,11 +42,14 @@ const main_Fragment =
|
|
|
17
42
|
precision highp float;
|
|
18
43
|
|
|
19
44
|
uniform float uMaxDistance;
|
|
20
|
-
uniform float uExponent;
|
|
21
45
|
uniform highp sampler2D uSegs;
|
|
22
46
|
uniform highp isampler2D uCloseCellIdxs;
|
|
23
47
|
uniform highp isampler2D uCrossCellIdxs;
|
|
24
|
-
|
|
48
|
+
// bit 0 -> calc distance inside
|
|
49
|
+
// bit 1 -> calc distance outside
|
|
50
|
+
// bit 2 -> calc whether fragment is inside or outside (else outside is assumed with winds == 0.0)
|
|
51
|
+
uniform int uTestInOut;
|
|
52
|
+
uniform vec4 uCustom;
|
|
25
53
|
|
|
26
54
|
uniform SegIdxRangePerCellBlock {
|
|
27
55
|
ivec4 uSegIdxRangePerCell[${(ROW_COUNT + 2*padCount)*(colCount + 2*padCount) / 2}];
|
|
@@ -36,12 +64,16 @@ flat in ivec2 closeCellIdxRange;
|
|
|
36
64
|
flat in ivec2 crossCellIdxRange;
|
|
37
65
|
out vec4 FragColor;
|
|
38
66
|
|
|
67
|
+
// testing!!
|
|
68
|
+
// float rand(vec2 co) {
|
|
69
|
+
// return mod(uCustom.w * fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453), 1.0);
|
|
70
|
+
// }
|
|
39
71
|
|
|
40
72
|
float absDistToSegment(vec2 point, vec2 lineA, vec2 lineB) {
|
|
41
73
|
vec2 lineDir = lineB - lineA;
|
|
42
74
|
float lenSq = dot(lineDir, lineDir);
|
|
43
75
|
float t = clamp(dot(point - lineA, lineDir) / lenSq, 0.0, 1.0);
|
|
44
|
-
vec2 linePt = lineA + t
|
|
76
|
+
vec2 linePt = lineA + t*lineDir;
|
|
45
77
|
|
|
46
78
|
return distance(point, linePt);
|
|
47
79
|
}
|
|
@@ -49,88 +81,96 @@ float absDistToSegment(vec2 point, vec2 lineA, vec2 lineB) {
|
|
|
49
81
|
|
|
50
82
|
void main() {
|
|
51
83
|
///////////////////////////////////////////////////////////////////////////
|
|
84
|
+
// Calculate \`winds\`:
|
|
85
|
+
//
|
|
52
86
|
// Project a ray to the left to check if it crosses the segment in order
|
|
53
87
|
// to find the fragment's winding number to determine whether fragment
|
|
54
88
|
// is inside or outside the shape.
|
|
89
|
+
///////////////////////////////////////////////////////////////////////////
|
|
55
90
|
|
|
56
|
-
int crossIdxS = crossCellIdxRange.x;
|
|
57
|
-
int crossLen = crossCellIdxRange.y;
|
|
58
91
|
float winds = 0.0;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
int
|
|
92
|
+
if ((uTestInOut & 4) == 0) {
|
|
93
|
+
int crossIdxS = crossCellIdxRange.x;
|
|
94
|
+
int crossLen = crossCellIdxRange.y;
|
|
95
|
+
// Iterate over all relevant cell indexes
|
|
96
|
+
for (int i = crossIdxS; i < crossIdxS + crossLen; i++) {
|
|
97
|
+
int crossIdx = texelFetch(uCrossCellIdxs, ivec2(i%256, i/256), 0).x;
|
|
62
98
|
|
|
63
|
-
|
|
99
|
+
ivec2 uSegIdxRange = crossIdx % 2 == 0
|
|
100
|
+
? uSegIdxRangePerCell[crossIdx / 2].xy
|
|
101
|
+
: uSegIdxRangePerCell[crossIdx / 2].zw;
|
|
64
102
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;
|
|
103
|
+
int segIdx = uSegIdxRange.x;
|
|
104
|
+
int segLen = uSegIdxRange.y;
|
|
68
105
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
106
|
+
for (int j = segIdx; j < segIdx + segLen; j++) {
|
|
107
|
+
// Fetch segment from texture
|
|
108
|
+
vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
|
|
72
109
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
110
|
+
// line segment's min-y is excluded
|
|
111
|
+
bool crossing =
|
|
112
|
+
(seg.y > vXY.y != seg.w > vXY.y) &&
|
|
113
|
+
(vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
|
|
77
114
|
|
|
78
|
-
|
|
115
|
+
bool crossingUp = seg.y < seg.w;
|
|
79
116
|
|
|
80
|
-
|
|
117
|
+
winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;
|
|
118
|
+
}
|
|
81
119
|
}
|
|
82
|
-
}
|
|
83
120
|
|
|
84
|
-
|
|
85
|
-
|
|
121
|
+
{
|
|
122
|
+
int cellIdx = (instanceId % ${ROW_COUNT});
|
|
86
123
|
|
|
87
|
-
|
|
88
|
-
int segIdx = isEven ? uSegIdxRange.x : uSegIdxRange.z;
|
|
89
|
-
int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;
|
|
124
|
+
bool isEven = cellIdx % 2 == 0;
|
|
90
125
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
126
|
+
ivec4 uSegIdxRange = uSegIdxRangePerStrip[cellIdx / 2];
|
|
127
|
+
int segIdx = isEven ? uSegIdxRange.x : uSegIdxRange.z;
|
|
128
|
+
int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;
|
|
129
|
+
|
|
94
130
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
(vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
|
|
131
|
+
for (int j = segIdx; j < segIdx + segLen; j++) {
|
|
132
|
+
// Fetch segment from texture
|
|
133
|
+
vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
|
|
99
134
|
|
|
100
|
-
|
|
135
|
+
// line segment's min-y is excluded
|
|
136
|
+
bool crossing =
|
|
137
|
+
(seg.y > vXY.y != seg.w > vXY.y) &&
|
|
138
|
+
(vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
|
|
101
139
|
|
|
102
|
-
|
|
140
|
+
bool crossingUp = seg.y < seg.w;
|
|
141
|
+
|
|
142
|
+
winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;
|
|
143
|
+
}
|
|
103
144
|
}
|
|
104
145
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
146
|
bool inside = winds != 0.0;
|
|
108
147
|
///////////////////////////////////////////////////////////////////////////
|
|
109
|
-
|
|
148
|
+
// Calculate \`res\`: the distance to the nearest curve
|
|
110
149
|
///////////////////////////////////////////////////////////////////////////
|
|
111
150
|
float res = 1.0; // sdf result
|
|
112
151
|
|
|
113
|
-
if ((inside && (
|
|
152
|
+
if ((inside && ((uTestInOut & 1) != 0)) || (!inside && ((uTestInOut & 2) != 0))) {
|
|
114
153
|
int cellIdxS = closeCellIdxRange.x;
|
|
115
154
|
int cellLen = closeCellIdxRange.y;
|
|
116
155
|
// Iterate over all relevant cell indexes
|
|
117
156
|
for (int i = cellIdxS; i < cellIdxS + cellLen; i++) {
|
|
118
157
|
int cellIdx = texelFetch(uCloseCellIdxs, ivec2(i%256, i/256), 0).x;
|
|
119
158
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
159
|
+
ivec2 uSegIdxRange = cellIdx % 2 == 0
|
|
160
|
+
? uSegIdxRangePerCell[cellIdx / 2].xy
|
|
161
|
+
: uSegIdxRangePerCell[cellIdx / 2].zw;
|
|
162
|
+
|
|
163
|
+
int segIdx = uSegIdxRange.x;
|
|
164
|
+
int segLen = uSegIdxRange.y;
|
|
124
165
|
|
|
125
166
|
for (int j = segIdx; j < segIdx + segLen; j++) {
|
|
126
167
|
// Fetch segment from texture
|
|
127
|
-
vec4 seg = texelFetch(uSegs, ivec2(j,
|
|
168
|
+
vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
|
|
128
169
|
|
|
129
|
-
// Find unsigned distance to the segment; only the nearest will be kept
|
|
170
|
+
// Find normalized unsigned distance to the segment; only the nearest will be kept
|
|
130
171
|
float d = absDistToSegment(vXY, seg.xy, seg.zw);
|
|
131
|
-
// Apply exponential transform TODO
|
|
132
|
-
// val = pow(1.0 - clamp(d / uMaxDistance, 0.0, 1.0), uExponent) * 0.5;
|
|
133
172
|
float val = clamp(d / uMaxDistance, 0.0, 1.0);
|
|
173
|
+
|
|
134
174
|
res = min(res, val);
|
|
135
175
|
}
|
|
136
176
|
}
|
|
@@ -139,20 +179,17 @@ void main() {
|
|
|
139
179
|
|
|
140
180
|
// DEBUG!
|
|
141
181
|
// float alpha = ((instanceId + instanceId/${ROW_COUNT}) % 2 == 0 ? 0.3 : 0.5);
|
|
142
|
-
float alpha = res == 1.0 ? 0.0 : 0.5;
|
|
143
182
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
float blue = 0.5;
|
|
147
|
-
// float alpha = inside ? 0.5 : 0.0;
|
|
183
|
+
|
|
184
|
+
${GLSL_PATTERN1}
|
|
148
185
|
|
|
149
186
|
FragColor = vec4(red, green, blue, alpha);
|
|
150
187
|
}
|
|
151
188
|
`
|
|
152
189
|
|
|
153
|
-
cache[
|
|
190
|
+
cache[key] = main_Fragment;
|
|
154
191
|
return main_Fragment;
|
|
155
192
|
}
|
|
156
193
|
|
|
157
|
-
|
|
158
|
-
export { getFragment }
|
|
194
|
+
// ${calcFragColorStr}
|
|
195
|
+
export { getFragment, GLSL_PATTERN1, GLSL_DEFAULT }
|
|
@@ -7,6 +7,7 @@ import { parsePathDataString } from './path-data-polyfill/parse-path-data-string
|
|
|
7
7
|
* and each bezier in turn consisting of an array of control points from the
|
|
8
8
|
* given SVG path string. An array of loops are returned (as opposed to a single
|
|
9
9
|
* loop) since an SVG path may have sub-paths.
|
|
10
|
+
*
|
|
10
11
|
* @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
|
*/
|
|
12
13
|
function getPathsFromStr(str: string): number[][][][] {
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { parseNumber } from './parse-number.js';
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
/** @hidden */
|
|
5
4
|
const COMMAND_MAP: { [index:string]: string } = {
|
|
6
5
|
Z:"Z", M:"M", L:"L", C:"C", Q:"Q", A:"A", H:"H", V:"V", S:"S", T:"T",
|
|
7
6
|
z:"Z", m:"m", l:"l", c:"c", q:"q", a:"a", h:"h", v:"v", s:"s", t:"t"
|
|
8
7
|
};
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
/** @hidden */
|
|
12
10
|
class Source {
|
|
13
11
|
_string: string;
|
|
14
12
|
_currentIndex: number;
|
package/src/svg/path-state.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Texture } from "../types/texture.js";
|
|
2
2
|
import type { Program } from "../types/program.js";
|
|
3
3
|
import type { GlContext } from "../types/gl-context.js";
|
|
4
|
+
import { debugShaders } from "../debug-shaders.js";
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
const cache = new WeakMap<WebGL2RenderingContext, GlContext>();
|
|
@@ -21,12 +22,16 @@ function getWebGlContext(
|
|
|
21
22
|
if (glContext) { return glContext; }
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
debugShaders(gl);
|
|
26
|
+
|
|
24
27
|
const programs: { [index:string]: Program } = {};
|
|
25
28
|
const textures: { [index:string]: Texture } = {};
|
|
26
29
|
|
|
27
30
|
const glContext: GlContext = { gl, textures, programs };
|
|
28
31
|
|
|
29
32
|
gl.canvas.addEventListener('webglcontextlost', event => {
|
|
33
|
+
// event.preventDefault(); // Prevent the default action (which is to not restore automatically)
|
|
34
|
+
|
|
30
35
|
deleteAllProps(programs);
|
|
31
36
|
deleteAllProps(textures);
|
|
32
37
|
cache.delete(gl);
|