webgl2-sdf 0.0.7 → 0.0.9

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.
@@ -1,7 +1,28 @@
1
1
  import { ROW_COUNT } from "../row-count.js";
2
2
  const cache = {};
3
- function getFragment(colCount, padCount) {
4
- const fragment = cache[1024 * colCount + padCount];
3
+ const GLSL_PATTERN1 = `
4
+ float exponent = uCustom.x;
5
+ float alpha = res >= 1.0 ? 0.0 : 0.5;
6
+ res = pow(1.0 - res, exponent);
7
+ float red = inside ? 0.2 : 0.8;
8
+ float green = abs(sin(25.0 * res));
9
+ // float green = step(0.5, fract(10.0 * res));
10
+ float blue = 0.5;
11
+ `;
12
+ const GLSL_DEFAULT = `
13
+ float exponent = uCustom.x;
14
+ float adj = 0.5*pow(1.0 - res, exponent);
15
+ res = inside ? 1.0 - adj : adj;
16
+ float red = res;
17
+ float green = res;
18
+ float blue = res;
19
+ float alpha = res;
20
+ `;
21
+ function getFragment(colCount, padCount, calcFragColorStr, hash) {
22
+ // `colCount` and `padCount` can take at most 8 bits and `has` 32 bits and
23
+ // we have at least 53 bits to play with so we're fine
24
+ const key = (2 ** 32) * (256 * colCount + padCount) + hash;
25
+ const fragment = cache[key];
5
26
  if (fragment !== undefined) {
6
27
  return fragment;
7
28
  }
@@ -14,7 +35,11 @@ uniform float uMaxDistance;
14
35
  uniform highp sampler2D uSegs;
15
36
  uniform highp isampler2D uCloseCellIdxs;
16
37
  uniform highp isampler2D uCrossCellIdxs;
17
- uniform int uIncl; // bit 0 -> incl inside, bit 1 -> incl outside
38
+ // bit 0 -> calc distance inside
39
+ // bit 1 -> calc distance outside
40
+ // bit 2 -> calc whether fragment is inside or outside (else outside is assumed with winds == 0.0)
41
+ uniform int uTestInOut;
42
+ uniform vec4 uCustom;
18
43
 
19
44
  uniform SegIdxRangePerCellBlock {
20
45
  ivec4 uSegIdxRangePerCell[${(ROW_COUNT + 2 * padCount) * (colCount + 2 * padCount) / 2}];
@@ -29,6 +54,10 @@ flat in ivec2 closeCellIdxRange;
29
54
  flat in ivec2 crossCellIdxRange;
30
55
  out vec4 FragColor;
31
56
 
57
+ // testing!!
58
+ // float rand(vec2 co) {
59
+ // return mod(uCustom.w * fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453), 1.0);
60
+ // }
32
61
 
33
62
  float absDistToSegment(vec2 point, vec2 lineA, vec2 lineB) {
34
63
  vec2 lineDir = lineB - lineA;
@@ -42,72 +71,75 @@ float absDistToSegment(vec2 point, vec2 lineA, vec2 lineB) {
42
71
 
43
72
  void main() {
44
73
  ///////////////////////////////////////////////////////////////////////////
74
+ // Calculate \`winds\`:
75
+ //
45
76
  // Project a ray to the left to check if it crosses the segment in order
46
77
  // to find the fragment's winding number to determine whether fragment
47
78
  // is inside or outside the shape.
79
+ ///////////////////////////////////////////////////////////////////////////
48
80
 
49
- int crossIdxS = crossCellIdxRange.x;
50
- int crossLen = crossCellIdxRange.y;
51
81
  float winds = 0.0;
52
- // Iterate over all relevant cell indexes
53
- for (int i = crossIdxS; i < crossIdxS + crossLen; i++) {
54
- int crossIdx = texelFetch(uCrossCellIdxs, ivec2(i%256, i/256), 0).x;
82
+ if ((uTestInOut & 4) != 0) {
83
+ int crossIdxS = crossCellIdxRange.x;
84
+ int crossLen = crossCellIdxRange.y;
85
+ // Iterate over all relevant cell indexes
86
+ for (int i = crossIdxS; i < crossIdxS + crossLen; i++) {
87
+ int crossIdx = texelFetch(uCrossCellIdxs, ivec2(i%256, i/256), 0).x;
55
88
 
56
- ivec2 uSegIdxRange = crossIdx % 2 == 0
57
- ? uSegIdxRangePerCell[crossIdx / 2].xy
58
- : uSegIdxRangePerCell[crossIdx / 2].zw;
89
+ ivec2 uSegIdxRange = crossIdx % 2 == 0
90
+ ? uSegIdxRangePerCell[crossIdx / 2].xy
91
+ : uSegIdxRangePerCell[crossIdx / 2].zw;
59
92
 
60
- int segIdx = uSegIdxRange.x;
61
- int segLen = uSegIdxRange.y;
93
+ int segIdx = uSegIdxRange.x;
94
+ int segLen = uSegIdxRange.y;
62
95
 
63
- for (int j = segIdx; j < segIdx + segLen; j++) {
64
- // Fetch segment from texture
65
- vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
96
+ for (int j = segIdx; j < segIdx + segLen; j++) {
97
+ // Fetch segment from texture
98
+ vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
66
99
 
67
- // line segment's min-y is excluded
68
- bool crossing =
69
- (seg.y > vXY.y != seg.w > vXY.y) &&
70
- (vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
100
+ // line segment's min-y is excluded
101
+ bool crossing =
102
+ (seg.y > vXY.y != seg.w > vXY.y) &&
103
+ (vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
71
104
 
72
- bool crossingUp = seg.y < seg.w;
105
+ bool crossingUp = seg.y < seg.w;
73
106
 
74
- winds += crossing ? (crossingUp ? 1.0 : -1.0) : 0.0;
107
+ winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;
108
+ }
75
109
  }
76
- }
77
110
 
78
- {
79
- int cellIdx = (instanceId % ${ROW_COUNT});
111
+ {
112
+ int cellIdx = (instanceId % ${ROW_COUNT});
80
113
 
81
- bool isEven = cellIdx % 2 == 0;
114
+ bool isEven = cellIdx % 2 == 0;
82
115
 
83
- ivec4 uSegIdxRange = uSegIdxRangePerStrip[cellIdx / 2];
84
- int segIdx = isEven ? uSegIdxRange.x : uSegIdxRange.z;
85
- int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;
86
-
116
+ ivec4 uSegIdxRange = uSegIdxRangePerStrip[cellIdx / 2];
117
+ int segIdx = isEven ? uSegIdxRange.x : uSegIdxRange.z;
118
+ int segLen = isEven ? uSegIdxRange.y : uSegIdxRange.w;
119
+
87
120
 
88
- for (int j = segIdx; j < segIdx + segLen; j++) {
89
- // Fetch segment from texture
90
- vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
121
+ for (int j = segIdx; j < segIdx + segLen; j++) {
122
+ // Fetch segment from texture
123
+ vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
91
124
 
92
- // line segment's min-y is excluded
93
- bool crossing =
94
- (seg.y > vXY.y != seg.w > vXY.y) &&
95
- (vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
125
+ // line segment's min-y is excluded
126
+ bool crossing =
127
+ (seg.y > vXY.y != seg.w > vXY.y) &&
128
+ (vXY.x > (seg.z - seg.x)*(vXY.y - seg.y) / (seg.w - seg.y) + seg.x);
96
129
 
97
- bool crossingUp = seg.y < seg.w;
130
+ bool crossingUp = seg.y < seg.w;
98
131
 
99
- winds += crossing ? (crossingUp ? 1.0 : -1.0) : 0.0;
132
+ winds += crossing ? (crossingUp ? -1.0 : 1.0) : 0.0;
133
+ }
100
134
  }
101
135
  }
102
-
103
-
104
136
  bool inside = winds != 0.0;
105
137
  ///////////////////////////////////////////////////////////////////////////
106
-
138
+ // Calculate \`res\`: the distance to the nearest curve
107
139
  ///////////////////////////////////////////////////////////////////////////
108
140
  float res = 1.0; // sdf result
109
141
 
110
- if ((inside && (uIncl % 2 != 0)) || (!inside && (uIncl > 1))) {
142
+ if ((inside && ((uTestInOut & 1) != 0)) || (!inside && ((uTestInOut & 2) != 0))) {
111
143
  int cellIdxS = closeCellIdxRange.x;
112
144
  int cellLen = closeCellIdxRange.y;
113
145
  // Iterate over all relevant cell indexes
@@ -125,9 +157,8 @@ void main() {
125
157
  // Fetch segment from texture
126
158
  vec4 seg = texelFetch(uSegs, ivec2(j%256, j/256), 0);
127
159
 
128
- // Find unsigned distance to the segment; only the nearest will be kept
160
+ // Find normalized unsigned distance to the segment; only the nearest will be kept
129
161
  float d = absDistToSegment(vXY, seg.xy, seg.zw);
130
- // Apply exponential transform
131
162
  float val = clamp(d / uMaxDistance, 0.0, 1.0);
132
163
 
133
164
  res = min(res, val);
@@ -139,20 +170,14 @@ void main() {
139
170
  // DEBUG!
140
171
  // float alpha = ((instanceId + instanceId/${ROW_COUNT}) % 2 == 0 ? 0.3 : 0.5);
141
172
 
142
-
143
- float exponent = 2;
144
- res = pow(1.0 - val, exponent) * 0.5;
145
-
146
- float alpha = res == 1.0 ? 0.0 : 0.5;
147
- float red = inside ? 0.2 : 0.8;
148
- float green = abs(sin(25.0 * res));
149
- float blue = 0.5;
173
+
174
+ ${calcFragColorStr}
150
175
 
151
176
  FragColor = vec4(red, green, blue, alpha);
152
177
  }
153
178
  `;
154
- cache[1024 * colCount + padCount] = main_Fragment;
179
+ cache[key] = main_Fragment;
155
180
  return main_Fragment;
156
181
  }
157
- export { getFragment };
182
+ export { getFragment, GLSL_PATTERN1, GLSL_DEFAULT };
158
183
  //# sourceMappingURL=fragment.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fragment.js","sourceRoot":"","sources":["../../src/shaders/fragment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,KAAK,GAAkD,EAAE,CAAC;AAGhE,SAAS,WAAW,CACZ,QAAgB,EAChB,QAAgB;IAEpB,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,GAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC;IACjD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAAC,OAAO,QAAQ,CAAC;IAAC,CAAC;IAEpD,MAAM,aAAa;IACnB,QAAQ,CAAA;;;;;;;;;;;gCAWwB,CAAC,SAAS,GAAG,CAAC,GAAC,QAAQ,CAAC,GAAC,CAAC,QAAQ,GAAG,CAAC,GAAC,QAAQ,CAAC,GAAG,CAAC;;;iCAGnD,SAAS,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCAwDR,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iDA6DE,SAAS;;;;;;;;;;;;;CAazD,CAAA;IAEG,KAAK,CAAC,IAAI,GAAC,QAAQ,GAAG,QAAQ,CAAC,GAAG,aAAa,CAAC;IAChD,OAAO,aAAa,CAAC;AACzB,CAAC;AAGD,OAAO,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"fragment.js","sourceRoot":"","sources":["../../src/shaders/fragment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,MAAM,KAAK,GAAkD,EAAE,CAAC;AAGhE,MAAM,aAAa,GAAG;;;;;;;;CAQrB,CAAC;AAGF,MAAM,YAAY,GAAG;;;;;;;;CAQpB,CAAC;AAGF,SAAS,WAAW,CACZ,QAAgB,EAChB,QAAgB,EAChB,gBAAwB,EACxB,IAAY;IAEhB,0EAA0E;IAC1E,sDAAsD;IACtD,MAAM,GAAG,GAAG,CAAC,CAAC,IAAE,EAAE,CAAC,GAAC,CAAC,GAAG,GAAC,QAAQ,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC;IACrD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAAC,OAAO,QAAQ,CAAC;IAAC,CAAC;IAEpD,MAAM,aAAa;IACnB,QAAQ,CAAA;;;;;;;;;;;;;;;gCAewB,CAAC,SAAS,GAAG,CAAC,GAAC,QAAQ,CAAC,GAAC,CAAC,QAAQ,GAAG,CAAC,GAAC,QAAQ,CAAC,GAAG,CAAC;;;iCAGnD,SAAS,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0CAgEJ,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iDA2DF,SAAS;;;MAGpD,gBAAgB;;;;CAIrB,CAAA;IAEG,KAAK,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC;IAC3B,OAAO,aAAa,CAAC;AACzB,CAAC;AAGD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,CAAA"}
@@ -6,13 +6,8 @@ import type { Texture } from "./texture";
6
6
  *
7
7
  * * `textures`
8
8
  * * `programs`
9
- * * `framebufferStack`
10
9
  *
11
10
  * so they don't have to be recreated on each draw call.
12
- *
13
- * * an optional `width` and `height` that can be used for any purpose and that are not
14
- * set or read by any function within the library
15
- *
16
11
  */
17
12
  interface GlContext {
18
13
  readonly gl: WebGL2RenderingContext;
@@ -1,3 +1,4 @@
1
+ import { debugShaders } from "../debug-shaders.js";
1
2
  const cache = new WeakMap();
2
3
  /**
3
4
  * Returns a `GlContext` by reference via a cache of `WebGL2RenderingContext`s.
@@ -13,6 +14,7 @@ function getWebGlContext(gl) {
13
14
  return glContext;
14
15
  }
15
16
  }
17
+ debugShaders(gl);
16
18
  const programs = {};
17
19
  const textures = {};
18
20
  const glContext = { gl, textures, programs };
@@ -1 +1 @@
1
- {"version":3,"file":"get-web-gl-context.js","sourceRoot":"","sources":["../../src/webgl-utils/get-web-gl-context.ts"],"names":[],"mappings":"AAKA,MAAM,KAAK,GAAG,IAAI,OAAO,EAAqC,CAAC;AAG/D;;;;;;GAMG;AACH,SAAS,eAAe,CAChB,EAA0B;IAE9B,CAAC;QACG,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,SAAS,EAAE,CAAC;YAAC,OAAO,SAAS,CAAC;QAAC,CAAC;IACxC,CAAC;IAED,MAAM,QAAQ,GAAgC,EAAE,CAAC;IACjD,MAAM,QAAQ,GAAgC,EAAE,CAAC;IAEjD,MAAM,SAAS,GAAc,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAExD,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,KAAK,CAAC,EAAE;QACnD,iGAAiG;QAEjG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACzB,cAAc,CAAC,QAAQ,CAAC,CAAC;QACzB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEjB,SAAS,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC,EAAE,KAAK,CAAC,CAAC;IAEV,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAEzB,OAAO,SAAS,CAAC;AACrB,CAAC;AAGD,SAAS,cAAc,CAAC,CAA6B;IACjD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC;AAGD,OAAO,EAAE,eAAe,EAAE,CAAA"}
1
+ {"version":3,"file":"get-web-gl-context.js","sourceRoot":"","sources":["../../src/webgl-utils/get-web-gl-context.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,MAAM,KAAK,GAAG,IAAI,OAAO,EAAqC,CAAC;AAG/D;;;;;;GAMG;AACH,SAAS,eAAe,CAChB,EAA0B;IAE9B,CAAC;QACG,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,SAAS,EAAE,CAAC;YAAC,OAAO,SAAS,CAAC;QAAC,CAAC;IACxC,CAAC;IAED,YAAY,CAAC,EAAE,CAAC,CAAC;IAEjB,MAAM,QAAQ,GAAgC,EAAE,CAAC;IACjD,MAAM,QAAQ,GAAgC,EAAE,CAAC;IAEjD,MAAM,SAAS,GAAc,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAExD,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,KAAK,CAAC,EAAE;QACnD,iGAAiG;QAEjG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACzB,cAAc,CAAC,QAAQ,CAAC,CAAC;QACzB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAEjB,SAAS,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC,EAAE,KAAK,CAAC,CAAC;IAEV,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAEzB,OAAO,SAAS,CAAC;AACrB,CAAC;AAGD,SAAS,cAAc,CAAC,CAA6B;IACjD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC;AAGD,OAAO,EAAE,eAAe,EAAE,CAAA"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "webgl2-sdf",
3
3
  "sideEffects": false,
4
4
  "description": "Fast sdf generator from bezier curves or svg string.",
5
- "version": "0.0.7",
5
+ "version": "0.0.9",
6
6
  "author": {
7
7
  "name": "Floris Steenkamp"
8
8
  },
@@ -1,4 +1,4 @@
1
- import { getFragment } from "./shaders/fragment.js";
1
+ import { getFragment, GLSL_DEFAULT } from "./shaders/fragment.js";
2
2
  import { vertex } from "./shaders/vertex.js";
3
3
 
4
4
 
@@ -27,7 +27,7 @@ function debugShaders(
27
27
 
28
28
  try {
29
29
  debugGlsl(gl, gl.VERTEX_SHADER, vertex);
30
- debugGlsl(gl, gl.FRAGMENT_SHADER, getFragment(32,4));
30
+ debugGlsl(gl, gl.FRAGMENT_SHADER, getFragment(32,2, GLSL_DEFAULT, 0));
31
31
  } catch (e) {
32
32
  console.log(e);
33
33
  throw e;
@@ -1,17 +1,38 @@
1
1
  // import { getWebGLContext } from './webgl-utils/get-gl-context.js';
2
2
  import { vertex } from './shaders/vertex.js';
3
- import { getFragment } from './shaders/fragment.js';
3
+ import { getFragment, GLSL_DEFAULT } from './shaders/fragment.js';
4
4
  import { initProgram } from './webgl-utils/use-program.js';
5
5
  import { mainProgram } from './main-program.js';
6
6
  import { ROW_COUNT } from './row-count.js';
7
7
  import { getPathsFromStr } from './svg/get-paths-from-str.js';
8
8
  import { MAX_ASPECT_RATIO_BEFORE_STRETCH } from './max-aspect-ratio-before-stretch.js';
9
9
  import { GlContext } from './types/gl-context.js';
10
- import { debugShaders } from './debug-shaders.js';
11
10
 
12
11
  const { ceil, min, max } = Math;
13
12
 
14
13
 
14
+ interface SdfOptions {
15
+ /** the position where to draw, x-coordinate */
16
+ readonly x?: number | undefined;
17
+ readonly y?: number | undefined;
18
+ readonly testInteriorExterior?: boolean | undefined;
19
+ readonly calcSdfForInside?: boolean | undefined;
20
+ readonly calcSdfForOutside?: boolean | undefined;
21
+ readonly customData?: [number,number,number,number] | undefined;
22
+ readonly glslRgbaCalcStr?: string | undefined;
23
+ readonly colorMask: [boolean,boolean,boolean,boolean];
24
+ }
25
+
26
+
27
+ const defaultSdfOptions: SdfOptions = {
28
+ x: 0, y: 0,
29
+ testInteriorExterior: true,
30
+ calcSdfForInside: true, calcSdfForOutside: true,
31
+ customData: [1,0,0,0],
32
+ colorMask: [true, true, true, true]
33
+ }
34
+
35
+
15
36
  /**
16
37
  * Generates an sdf (signed distance field) from the given bezier curves,
17
38
  * viewbox, etc. and renders the result
@@ -22,16 +43,81 @@ const { ceil, min, max } = Math;
22
43
  * thereof) given by given by their ordered control points,
23
44
  * e.g. `[ [[0,0],[1,1],[2,1],[2,0]], [[2,0],[7,2],[1,5],[8,6]], ... ]` **OR**
24
45
  * * an SVG string, e.g. "M26.53 478.83 C028.89 481.61 031.33 484.32 ..."
46
+ * @param viewbox the viewbox given as `[x1,x2,y1,y2]` (**not as** `[x,y,widht,height]`)
25
47
  * @param width the width of the drawing rectangle
26
48
  * @param height the height of the drawing rectangle
27
- * @param viewbox the viewbox
28
49
  * @param maxDistance maximum sdf distance
29
- * @param sdfExponent TODO
30
- * @param inclInside if `true` the sdf will be calculate for the inside of the shape
31
- * @param inclOutside if `true` the sdf will be calculate for the outside of the shape
32
- * @param x the position where to draw, x-coordinate
33
- * @param y the position where to draw, y-coordinate
34
- * @param channel TODO
50
+ * @param options optional additional options (see below)
51
+ *
52
+ * **The following are properties of the `options` parameters**
53
+ *
54
+ * @param x defaults to `0`; the position where to draw on the canvas, x-coordinate
55
+ * @param y defaults to `0`; the position where to draw on the canvas, y-coordinate
56
+ * @param colorMask defaults to `[true,true,true,true]`; an array of length 4 for the rgba color mask values;
57
+ * will be called as `gl.setColorMask(...colorMask)` to allow/prevent selected color channels from updating
58
+ * @param testInteriorExterior defaults to `true`;
59
+ * if `false` winds will always be `0.0` and only an un-signed sdf can be calculated since all
60
+ * fragments are considered outside
61
+ * @param calcSdfForInside defaults to `true`;
62
+ * if `false` the sdf will not be calculate for the inside of the shape, in the shader, `res` will always be `1.0`
63
+ * @param calcSdfForOutside defaults to `true`;
64
+ * if `false` the sdf will not be calculate for the outside of the shape, in the shader, `res` will always be `1.0`
65
+ * @param customData optional custom data (must be an array of 4 numbers) to send to
66
+ * the fragment shader as a uniform, e.g. exponent, scale, a timer, or whatever
67
+ * @param glslRgbaCalcStr a glsl string (#version 300 es) inserted at the end of
68
+ * the fragment shader to modify the output frag color in any way (you can also discard the fragment);
69
+ * see below for available variables that can be used;
70
+ *
71
+ * defaults to (designed to match webgl-sdf-generator)
72
+ * ```glsl
73
+ * float exponent = uCustom.x;
74
+ * float adj = 0.5*pow(1.0 - res, exponent);
75
+ * res = inside ? 1.0 - adj : adj;
76
+ * float red = res;
77
+ * float green = res;
78
+ * float blue = res;
79
+ * float alpha = 0.5;
80
+ *
81
+ * ```
82
+ * You must define and assign \`red\`, \`green\`, \`blue\` and \`alpha\`.
83
+ *
84
+ * Usable variables:
85
+ * ```glsl
86
+ * // the result of the distance calculation for this fragment, a value from 0.0 to 1.0
87
+ * // with 1.0 occuring when the fragment is >= maxDistance away and 0.0 when the
88
+ * // fragment is exactly on a curve
89
+ * float res
90
+ *
91
+ * // the number of anti-clockwise winds around the fragment, a value != 0
92
+ * // means the fragment is inside the shape
93
+ * float winds
94
+ *
95
+ * // 4 custom values set via an options parameter of `generateSdf`; defaults to `[1,0,0,0]`;
96
+ * // the first value is used as the exponent within the default `glslRgbaCalcStr`
97
+ * vec4 uCustom;
98
+ *
99
+ * // the max distance value supplied via `generateSdf`
100
+ * float uMaxDistance
101
+ *
102
+ * // bit 0 -> calc sdf when fragment is inside; defaults to 1,
103
+ * // bit 1 -> calc sdf when fragment is outside; defaults to 1
104
+ * // bit 1 -> calc `winds` (required for signing the distance); defaults to 1
105
+ * // note: when the distance calculation is not done (via options from `generateSdf`),
106
+ * `res` will be set to 1.0 (max distance away)
107
+ * int uTestInOut
108
+ *
109
+ * // the original x,y coordinates of the fragment in the original space provided
110
+ * // via the `viewbox` in `generateSdf`, e.g. the very bottom-left fragment
111
+ * // will have vXY == (viebox[0], viebox[1]) and the very top right will have
112
+ * // coordinates vXY == (viebox[2], viebox[3])
113
+ * vec2 vXY
114
+ *
115
+ * // whether the point is inside or outside the shape, often used to sign `res`
116
+ * // it is identical to `winds != 0`
117
+ * bool inside
118
+ *
119
+ * // pretty much useless unless you want to create a checkerboard pattern for no good reason
120
+ * int instanceId
35
121
  */
36
122
  function generateSdf(
37
123
  glContext: GlContext,
@@ -39,21 +125,13 @@ function generateSdf(
39
125
  viewbox: [number,number,number,number],
40
126
  width: number,
41
127
  height: number,
42
- x = 0, y = 0,
43
128
  maxDistance: number,
44
- inclInside = true,
45
- inclOutside = true,
46
- customData: [number,number,number,number],
47
-
48
- // TODO
49
- channel = 0) {
129
+ options: SdfOptions = defaultSdfOptions) {
50
130
 
51
131
  const psss = typeof bezierCurves_or_svgStr === 'string'
52
132
  ? getPathsFromStr(bezierCurves_or_svgStr)
53
133
  : bezierCurves_or_svgStr;
54
134
 
55
- // const glContext = getWebGLContext(gl);
56
-
57
135
  let stretch = 1;
58
136
  const aspectRatio = width/height;
59
137
  if (aspectRatio > MAX_ASPECT_RATIO_BEFORE_STRETCH) {
@@ -69,23 +147,41 @@ function generateSdf(
69
147
 
70
148
  const padCount = 2*ceil(min(maxDistance, maxDim)/cellSize/2);
71
149
 
150
+ const { glslRgbaCalcStr } = options;
151
+ const hash = calcStrHash(glslRgbaCalcStr || '');
152
+
72
153
  const programMain = initProgram(
73
- glContext, `main${colCount}-${padCount}`,
74
- vertex, getFragment(colCount, padCount)
154
+ glContext, `main${colCount}-${padCount}-${hash}`,
155
+ vertex, getFragment(colCount, padCount, glslRgbaCalcStr || GLSL_DEFAULT, hash)
75
156
  );
76
157
 
77
158
  const { gl } = glContext;
78
159
 
79
- // debugShaders(gl); // comment for production
80
-
81
160
  gl.useProgram(programMain.program);
82
161
  mainProgram(
83
162
  glContext, programMain,
84
- psss, viewbox, maxDistance, inclInside, inclOutside, customData,
85
- x, y, width, height,
86
- colCount, cellSize, padCount, stretch
163
+ psss, viewbox, maxDistance,
164
+ width, height,
165
+ options,
166
+ colCount, cellSize, padCount, stretch,
167
+
87
168
  );
88
169
  }
89
170
 
90
171
 
91
- export { generateSdf }
172
+ /**
173
+ * Calculates and returns a hash of the given string
174
+ */
175
+ function calcStrHash(str: string) {
176
+ if (!str) return 0;
177
+
178
+ let hash = 5381;
179
+ for (let i=0; i<str.length; i++) {
180
+ hash = ((hash << 5) + hash) + str.charCodeAt(i);
181
+ }
182
+
183
+ return hash >>> 0; // Convert to unsigned 32-bit integer
184
+ }
185
+
186
+
187
+ export { generateSdf, SdfOptions }
package/src/index.ts CHANGED
@@ -2,3 +2,4 @@
2
2
  export { generateSdf } from './generate-sdf.js';
3
3
  export { freeGlContext } from './webgl-utils/free-gl-context.js';
4
4
  export { getWebGlContext } from './webgl-utils/get-web-gl-context.js';
5
+ export { GLSL_DEFAULT, GLSL_PATTERN1 } from './shaders/fragment.js';
@@ -7,6 +7,7 @@ import { GlContext } from './types/gl-context.js';
7
7
  import { prepareBuffers } from './prepare-buffers.js';
8
8
  import { TEX_WIDTH } from './tex-width.js';
9
9
  import { ROW_COUNT } from './row-count.js';
10
+ import { SdfOptions } from './generate-sdf.js';
10
11
 
11
12
 
12
13
  const SEG_TEX_INDEX = 0;
@@ -21,14 +22,11 @@ function mainProgram(
21
22
  psss: number[][][][],
22
23
  viewbox: [number,number,number,number],
23
24
  maxDistance: number,
24
- inclInside: boolean,
25
- inclOutside: boolean,
26
- customData: [number, number, number, number],
27
-
28
- x: number,
29
- y: number,
30
25
  width: number,
31
26
  height: number,
27
+
28
+ options: SdfOptions,
29
+
32
30
  colCount: number,
33
31
  cellSize: number,
34
32
  padCount: number,
@@ -36,6 +34,13 @@ function mainProgram(
36
34
 
37
35
  const { gl } = glContext;
38
36
 
37
+ const {
38
+ x = 0, y = 0, testInteriorExterior = true,
39
+ calcSdfForInside = true, calcSdfForOutside = true,
40
+ customData = [1,0,0,0],
41
+ colorMask = [true,true,true,true]
42
+ } = options;
43
+
39
44
  const vertices: number[] = [];
40
45
  const x0 = 0;
41
46
  const y0 = 0;
@@ -82,7 +87,11 @@ function mainProgram(
82
87
  // Init/update uniforms
83
88
  setUniform_('2f', 'uWidthHeight', width, height);
84
89
  setUniform_('1f', 'uMaxDistance', maxDistance);
85
- setUniform_('1i', 'uIncl', (inclInside ? 1 : 0) + (inclOutside ? 2 : 0));
90
+ setUniform_('1i', 'uTestInOut',
91
+ (calcSdfForInside ? 1 : 0) +
92
+ (calcSdfForOutside ? 2 : 0) +
93
+ (testInteriorExterior ? 4 : 0)
94
+ );
86
95
  setUniform_('4f', 'uCustom', ...customData);
87
96
 
88
97
  setUniformBlock(programMain)('SegIdxRangePerCellBlock', 0, segIdxs_PerCell_Range_Arr);
@@ -154,6 +163,8 @@ function mainProgram(
154
163
 
155
164
  gl.viewport(x, y, width, height);
156
165
 
166
+ gl.colorMask(...colorMask);
167
+
157
168
  // draw a square colCount * ROW_COUNT times - 6 vertics
158
169
  gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, colCount*ROW_COUNT);
159
170