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.
@@ -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): string {
10
-
11
- const fragment = cache[1024*colCount + padCount];
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
- uniform int uIncl; // bit 0 -> incl inside, bit 1 -> incl outside
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 * lineDir;
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
- // Iterate over all relevant cell indexes
60
- for (int i = crossIdxS; i < crossIdxS + crossLen; i++) {
61
- int crossIdx = texelFetch(uCrossCellIdxs, ivec2(i%256, i/256), 0).x;
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
- bool isEven = crossIdx % 2 == 0;
99
+ ivec2 uSegIdxRange = crossIdx % 2 == 0
100
+ ? uSegIdxRangePerCell[crossIdx / 2].xy
101
+ : uSegIdxRangePerCell[crossIdx / 2].zw;
64
102
 
65
- ivec4 uSegIdxRange = uSegIdxRangePerCell[crossIdx / 2];
66
- int segIdx = isEven ? uSegIdxRange.x : uSegIdxRange.z;
67
- int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;
103
+ int segIdx = uSegIdxRange.x;
104
+ int segLen = uSegIdxRange.y;
68
105
 
69
- for (int j = segIdx; j < segIdx + segLen; j++) {
70
- // Fetch segment from texture
71
- vec4 seg = texelFetch(uSegs, ivec2(j, 0), 0);
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
- // line segment's min-y is excluded
74
- bool crossing =
75
- (seg.y > vXY.y != seg.w > vXY.y) &&
76
- (vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
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
- bool crossingUp = seg.y < seg.w;
115
+ bool crossingUp = seg.y < seg.w;
79
116
 
80
- winds += crossing ? (crossingUp ? 1.0 : -1.0) : 0.0;
117
+ winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;
118
+ }
81
119
  }
82
- }
83
120
 
84
- {
85
- bool isEven = (instanceId % ${ROW_COUNT}) % 2 == 0;
121
+ {
122
+ int cellIdx = (instanceId % ${ROW_COUNT});
86
123
 
87
- ivec4 uSegIdxRange = uSegIdxRangePerStrip[(instanceId % ${ROW_COUNT}) / 2];
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
- for (int j = segIdx; j < segIdx + segLen; j++) {
92
- // Fetch segment from texture
93
- vec4 seg = texelFetch(uSegs, ivec2(j, 0), 0);
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
- // line segment's min-y is excluded
96
- bool crossing =
97
- (seg.y > vXY.y != seg.w > vXY.y) &&
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
- bool crossingUp = seg.y < seg.w;
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
- winds += crossing ? (crossingUp ? 1.0 : -1.0) : 0.0;
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 && (uIncl % 2 != 0)) || (!inside && (uIncl > 1))) {
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
- bool isEven = cellIdx % 2 == 0;
121
- ivec4 uSegIdxRange = uSegIdxRangePerCell[cellIdx / 2];
122
- int segIdx = isEven ? uSegIdxRange.x : uSegIdxRange.z;
123
- int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;
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, 0), 0);
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
- float red = inside ? 0.2 : 0.8;
145
- float green = abs(sin(50.0 * res));
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[1024*colCount + padCount] = main_Fragment;
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;
@@ -1,5 +1,4 @@
1
1
 
2
- /** @hidden */
3
2
  interface PathState {
4
3
  initialPoint?: number[] | undefined;
5
4
  p: number[];
@@ -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);