@matboks/utilities 0.0.1 → 0.0.2

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 (33) hide show
  1. package/dist/constants 2.js +1 -0
  2. package/dist/constants 3.js +1 -0
  3. package/dist/constants.d 2.ts +1 -0
  4. package/dist/constants.d 3.ts +1 -0
  5. package/dist/index 2.js +11 -0
  6. package/dist/index 3.js +11 -0
  7. package/dist/index.d 2.ts +11 -0
  8. package/dist/index.d 3.ts +11 -0
  9. package/dist/shader-modules/modules/camera.js +42 -42
  10. package/dist/shader-modules/modules/color/blend.js +107 -107
  11. package/dist/shader-modules/modules/color/index.js +134 -134
  12. package/dist/shader-modules/modules/constants.js +13 -13
  13. package/dist/shader-modules/modules/geometry.js +109 -109
  14. package/dist/shader-modules/modules/math.js +18 -18
  15. package/dist/shader-modules/modules/noise.d.ts +1 -1
  16. package/dist/shader-modules/modules/noise.js +449 -409
  17. package/dist/shader-modules/modules/random.d.ts +1 -1
  18. package/dist/shader-modules/modules/random.js +172 -146
  19. package/dist/shader-modules/modules/ray-marching.js +53 -53
  20. package/dist/shader-modules/modules/sdf/index.js +182 -182
  21. package/dist/shader-modules/modules/sdf/operations.js +76 -76
  22. package/dist/shader-modules/modules/sdf.d.ts +1 -0
  23. package/dist/shader-modules/modules/sdf.js +126 -0
  24. package/dist/shader-modules/modules/utils.js +114 -114
  25. package/dist/shader-modules/shaders.js +107 -107
  26. package/dist/shader-renderer/pixel-vertex-shader.js +13 -13
  27. package/dist/tilings/tiler.d.ts +1 -0
  28. package/dist/tilings/tiler.js +4 -0
  29. package/dist/types 2.js +1 -0
  30. package/dist/types 3.js +1 -0
  31. package/dist/types.d 2.ts +1 -0
  32. package/dist/types.d 3.ts +1 -0
  33. package/package.json +2 -2
@@ -1,183 +1,183 @@
1
- export const sdfDefinition = /*glsl*/ `
2
-
3
- import { barycentric, transformToZAxis, rotate } from "@/geometry";
4
- import { lerp, signed, dot2 } from "@/utils";
5
- import { PI } from "@/constants";
6
-
7
- export {
8
- sphere, box, rectangularCylinder,
9
- discoBall, donut, boxFrame,
10
- triangleSDFSquared, meshSDF, regularPolygon,
11
- polygon, lineSegment
12
- }
13
-
14
- float sphere(vec3 p, vec3 center, float radius) {
15
- return length(p - center) - radius;
16
- }
17
-
18
- float box(vec3 p, vec3 center, vec3 direction, vec3 size) {
19
- p = transformToZAxis(p, center, direction, 0.0);
20
- p = abs(p) - size/2.0;
21
-
22
- // First part gives distance inside box, second part outside
23
- return min(max(max(p.x, p.y), p.z), 0.0) + length(max(p, vec3(0)));
24
- }
25
-
26
- float rectangularCylinder(vec3 p, vec3 center, vec3 direction, vec2 size, float height, bool offsetCenter, float rotation) {
27
- if (offsetCenter) center += 0.5*height*normalize(direction);
28
- p = transformToZAxis(p, center, direction, rotation);
29
- return box(p, vec3(0), vec3(0, 0, 1), vec3(size, height));
30
- }
31
-
32
- float discoBall(vec3 p, vec3 center, float radius, float n, float o) {
33
- p -= center;
34
- float phi = -PI + 2.0*PI*(round(lerp(atan(p.y, p.x), -PI, PI)*n)/n + o);
35
- float theta = PI*(round(lerp(acos(p.z/length(p)), 0.0, PI)*n)/n + o);
36
-
37
- vec3 bp = vec3(sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta));
38
-
39
- return dot(bp, p - radius*bp);
40
- }
41
-
42
- float discoBall(vec3 p, vec3 center, float radius, float n) {
43
- return discoBall(p, center, radius, n, 0.0);
44
- }
45
-
46
- float regularPolygon(vec2 p, float radius, float n) {
47
- float theta = PI*signed((floor(lerp(atan(p.y, p.x), -PI, PI)*n) + .5)/n);
48
- vec2 pp = vec2(cos(theta), sin(theta));
49
- return dot(pp, p - radius*pp);
50
- }
51
-
52
- float polygon(vec2 p, sampler2D vertices, int startIndex, int endIndex, int total) {
53
- float nf = float(total);
54
- float sf = float(startIndex);
55
- float ef = float(endIndex);
56
- float minDistSquared = 1e9;
57
- float textureStep = 1.0 / nf;
58
- float winding = 0.0; // >0 → inside for CCW, <0 → inside for CW
59
-
60
- vec2 start = texture(vertices, vec2((sf + 0.5) * textureStep, 0.5)).xy;
61
-
62
- for (float i = sf + 1.0; i <= ef; i++) {
63
- float idx = mod(i + 0.5, nf);
64
- vec2 end = texture(vertices, vec2(idx * textureStep, 0.5)).xy;
65
-
66
- // Distance to segment (start to end)
67
- vec2 seg = end - start;
68
- vec2 rel = p - start;
69
- float t = clamp(dot(rel, seg) / dot(seg, seg), 0.0, 1.0);
70
- float d2 = dot2(rel - seg*t);
71
- minDistSquared = min(minDistSquared, d2);
72
-
73
- // Portion for finding out if point is inside polygon
74
- // Equivalent to classic point-in-polygon test:
75
- // Does segment cross horizontal ray to the right of p?
76
- bool aboveA = start.y > p.y;
77
- bool aboveB = end.y > p.y;
78
- // Sign: +1 when edge crosses in one direction, -1 in the opposite
79
- if (aboveA != aboveB) {
80
- float xCross = (end.x - start.x) * (p.y - start.y) / (end.y - start.y) + start.x;
81
- if (xCross > p.x) {
82
- winding += aboveB ? -1.0 : 1.0;
83
- }
84
- }
85
-
86
- start = end;
87
- }
88
-
89
- float signValue = (winding == 0.0 ? 1.0 : -1.0);
90
- return signValue * sqrt(minDistSquared);
91
- }
92
-
93
-
94
- float donut(vec3 p, vec3 center, vec3 direction, float innerRadius, float outerRadius, float rotation, float sections, float sides) {
95
- p = transformToZAxis(p, center, direction, 0.0);
96
-
97
- vec3 xy = normalize(vec3(p.xy, 0));
98
- float angle = mod(atan(xy.y, xy.x), 2.*PI);
99
-
100
- angle = 2.*PI*round(sections*angle/(2.*PI))/sections;
101
- innerRadius *= sqrt(PI/(sections*tan(PI/sections)));
102
- outerRadius *= sqrt(PI/(sides*tan(PI/sides)));
103
- return regularPolygon(rotate(vec2(dot(p, vec3(cos(angle), sin(angle), 0)) - innerRadius, p.z), rotation + 0.25*PI), outerRadius, sides);
104
- }
105
-
106
- float boxFrame(vec3 p, vec3 center, vec3 direction, vec3 size, float thickness) {
107
- float dBox = box(p, center, direction, size);
108
-
109
- p = transformToZAxis(p, center, direction, 0.0);
110
-
111
- vec3 o = 2.0*vec3(thickness, thickness, -1e9);
112
- float dX = box(p, vec3(0), vec3(1, 0, 0), size.yzx - o);
113
- float dY = box(p, vec3(0), vec3(0, 1, 0), size.zxy - o);
114
- float dZ = box(p, vec3(0), vec3(0, 0, 1), size.xyz - o);
115
-
116
- float dInner = min(dX, min(dY, dZ));
117
- return max(dBox, -dInner);
118
- }
119
-
120
- // x is distance squared, y indicates which side of triangle p is on
121
- // (relative to normal: positive if p is above, negative if below)
122
- vec2 triangleSDFSquared(vec3 p, vec3 a, vec3 b, vec3 c) {
123
- vec3 ab = b - a;
124
- vec3 ca = a - c;
125
- vec3 bc = c - b;
126
-
127
- vec3 ap = p - a;
128
- vec3 bp = p - b;
129
- vec3 cp = p - c;
130
-
131
- vec3 normal = normalize(cross(ab, -ca)); // Negate to make cross(ab, ac), follows right-hand rule assuming abc is CCW
132
-
133
- bool isInsideTriangle = all(greaterThan(barycentric(p, a, b, c), vec3(0)));
134
- bool isInsideXYProjection = abs(normal.z) > 1e-5 && all(greaterThan(barycentric(p.xy, a.xy, b.xy, c.xy), vec3(0)));
135
-
136
- float distSquared;
137
- bool isAboveXYProjection;
138
-
139
- // If p is above XY projection, then dist = dot(ap, normal) > 0 when normal points upwards
140
- // and dist < 0 when it points downwards. sign(normal.z)*dist > 0.0 then tells us if p is above
141
- if (isInsideTriangle) {
142
- float dist = dot(ap, normal);
143
- distSquared = dist*dist;
144
- isAboveXYProjection = isInsideXYProjection && sign(normal.z)*dist > 0.0;
145
- } else {
146
- distSquared = min(min(
147
- dot2(ap - ab*clamp(dot(ap, ab) / dot2(ab), 0.0, 1.0)),
148
- dot2(bp - bc*clamp(dot(bp, bc) / dot2(bc), 0.0, 1.0))),
149
- dot2(cp - ca*clamp(dot(cp, ca) / dot2(ca), 0.0, 1.0))
150
- );
151
- isAboveXYProjection = isInsideXYProjection && sign(normal.z)*dot(ap, normal) > 0.0;
152
- }
153
-
154
- return vec2(distSquared, isAboveXYProjection ? -1.0 : 1.0);
155
- }
156
-
157
- float lineSegment(vec2 p, vec2 a, vec2 b, float o) {
158
- vec2 ap = p - a;
159
- vec2 ab = b - a;
160
-
161
- float t = clamp(dot(ap, ab)/dot(ab, ab), 0.0, 1.0);
162
-
163
- return length(ap - ab*t) - o;
164
- }
165
-
166
- float lineSegment(vec2 p, vec2 a, vec2 b) {
167
- return lineSegment(p, a, b, 0.0);
168
- }
169
-
170
- float lineSegment(vec3 p, vec2 a, vec2 b, float o) {
171
- vec3 ap = p - a;
172
- vec3 ab = b - a;
173
-
174
- float t = clamp(dot(ap, ab)/dot(ab, ab), 0.0, 1.0);
175
-
176
- return length(ap - ab*t) - o;
177
- }
178
-
179
- float lineSegment(vec3 p, vec2 a, vec2 b) {
180
- return lineSegment(p, a, b, 0.0);
181
- }
182
-
1
+ export const sdfDefinition = /*glsl*/ `
2
+
3
+ import { barycentric, transformToZAxis, rotate } from "@/geometry";
4
+ import { lerp, signed, dot2 } from "@/utils";
5
+ import { PI } from "@/constants";
6
+
7
+ export {
8
+ sphere, box, rectangularCylinder,
9
+ discoBall, donut, boxFrame,
10
+ triangleSDFSquared, meshSDF, regularPolygon,
11
+ polygon, lineSegment
12
+ }
13
+
14
+ float sphere(vec3 p, vec3 center, float radius) {
15
+ return length(p - center) - radius;
16
+ }
17
+
18
+ float box(vec3 p, vec3 center, vec3 direction, vec3 size) {
19
+ p = transformToZAxis(p, center, direction, 0.0);
20
+ p = abs(p) - size/2.0;
21
+
22
+ // First part gives distance inside box, second part outside
23
+ return min(max(max(p.x, p.y), p.z), 0.0) + length(max(p, vec3(0)));
24
+ }
25
+
26
+ float rectangularCylinder(vec3 p, vec3 center, vec3 direction, vec2 size, float height, bool offsetCenter, float rotation) {
27
+ if (offsetCenter) center += 0.5*height*normalize(direction);
28
+ p = transformToZAxis(p, center, direction, rotation);
29
+ return box(p, vec3(0), vec3(0, 0, 1), vec3(size, height));
30
+ }
31
+
32
+ float discoBall(vec3 p, vec3 center, float radius, float n, float o) {
33
+ p -= center;
34
+ float phi = -PI + 2.0*PI*(round(lerp(atan(p.y, p.x), -PI, PI)*n)/n + o);
35
+ float theta = PI*(round(lerp(acos(p.z/length(p)), 0.0, PI)*n)/n + o);
36
+
37
+ vec3 bp = vec3(sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta));
38
+
39
+ return dot(bp, p - radius*bp);
40
+ }
41
+
42
+ float discoBall(vec3 p, vec3 center, float radius, float n) {
43
+ return discoBall(p, center, radius, n, 0.0);
44
+ }
45
+
46
+ float regularPolygon(vec2 p, float radius, float n) {
47
+ float theta = PI*signed((floor(lerp(atan(p.y, p.x), -PI, PI)*n) + .5)/n);
48
+ vec2 pp = vec2(cos(theta), sin(theta));
49
+ return dot(pp, p - radius*pp);
50
+ }
51
+
52
+ float polygon(vec2 p, sampler2D vertices, int startIndex, int endIndex, int total) {
53
+ float nf = float(total);
54
+ float sf = float(startIndex);
55
+ float ef = float(endIndex);
56
+ float minDistSquared = 1e9;
57
+ float textureStep = 1.0 / nf;
58
+ float winding = 0.0; // >0 → inside for CCW, <0 → inside for CW
59
+
60
+ vec2 start = texture(vertices, vec2((sf + 0.5) * textureStep, 0.5)).xy;
61
+
62
+ for (float i = sf + 1.0; i <= ef; i++) {
63
+ float idx = mod(i + 0.5, nf);
64
+ vec2 end = texture(vertices, vec2(idx * textureStep, 0.5)).xy;
65
+
66
+ // Distance to segment (start to end)
67
+ vec2 seg = end - start;
68
+ vec2 rel = p - start;
69
+ float t = clamp(dot(rel, seg) / dot(seg, seg), 0.0, 1.0);
70
+ float d2 = dot2(rel - seg*t);
71
+ minDistSquared = min(minDistSquared, d2);
72
+
73
+ // Portion for finding out if point is inside polygon
74
+ // Equivalent to classic point-in-polygon test:
75
+ // Does segment cross horizontal ray to the right of p?
76
+ bool aboveA = start.y > p.y;
77
+ bool aboveB = end.y > p.y;
78
+ // Sign: +1 when edge crosses in one direction, -1 in the opposite
79
+ if (aboveA != aboveB) {
80
+ float xCross = (end.x - start.x) * (p.y - start.y) / (end.y - start.y) + start.x;
81
+ if (xCross > p.x) {
82
+ winding += aboveB ? -1.0 : 1.0;
83
+ }
84
+ }
85
+
86
+ start = end;
87
+ }
88
+
89
+ float signValue = (winding == 0.0 ? 1.0 : -1.0);
90
+ return signValue * sqrt(minDistSquared);
91
+ }
92
+
93
+
94
+ float donut(vec3 p, vec3 center, vec3 direction, float innerRadius, float outerRadius, float rotation, float sections, float sides) {
95
+ p = transformToZAxis(p, center, direction, 0.0);
96
+
97
+ vec3 xy = normalize(vec3(p.xy, 0));
98
+ float angle = mod(atan(xy.y, xy.x), 2.*PI);
99
+
100
+ angle = 2.*PI*round(sections*angle/(2.*PI))/sections;
101
+ innerRadius *= sqrt(PI/(sections*tan(PI/sections)));
102
+ outerRadius *= sqrt(PI/(sides*tan(PI/sides)));
103
+ return regularPolygon(rotate(vec2(dot(p, vec3(cos(angle), sin(angle), 0)) - innerRadius, p.z), rotation + 0.25*PI), outerRadius, sides);
104
+ }
105
+
106
+ float boxFrame(vec3 p, vec3 center, vec3 direction, vec3 size, float thickness) {
107
+ float dBox = box(p, center, direction, size);
108
+
109
+ p = transformToZAxis(p, center, direction, 0.0);
110
+
111
+ vec3 o = 2.0*vec3(thickness, thickness, -1e9);
112
+ float dX = box(p, vec3(0), vec3(1, 0, 0), size.yzx - o);
113
+ float dY = box(p, vec3(0), vec3(0, 1, 0), size.zxy - o);
114
+ float dZ = box(p, vec3(0), vec3(0, 0, 1), size.xyz - o);
115
+
116
+ float dInner = min(dX, min(dY, dZ));
117
+ return max(dBox, -dInner);
118
+ }
119
+
120
+ // x is distance squared, y indicates which side of triangle p is on
121
+ // (relative to normal: positive if p is above, negative if below)
122
+ vec2 triangleSDFSquared(vec3 p, vec3 a, vec3 b, vec3 c) {
123
+ vec3 ab = b - a;
124
+ vec3 ca = a - c;
125
+ vec3 bc = c - b;
126
+
127
+ vec3 ap = p - a;
128
+ vec3 bp = p - b;
129
+ vec3 cp = p - c;
130
+
131
+ vec3 normal = normalize(cross(ab, -ca)); // Negate to make cross(ab, ac), follows right-hand rule assuming abc is CCW
132
+
133
+ bool isInsideTriangle = all(greaterThan(barycentric(p, a, b, c), vec3(0)));
134
+ bool isInsideXYProjection = abs(normal.z) > 1e-5 && all(greaterThan(barycentric(p.xy, a.xy, b.xy, c.xy), vec3(0)));
135
+
136
+ float distSquared;
137
+ bool isAboveXYProjection;
138
+
139
+ // If p is above XY projection, then dist = dot(ap, normal) > 0 when normal points upwards
140
+ // and dist < 0 when it points downwards. sign(normal.z)*dist > 0.0 then tells us if p is above
141
+ if (isInsideTriangle) {
142
+ float dist = dot(ap, normal);
143
+ distSquared = dist*dist;
144
+ isAboveXYProjection = isInsideXYProjection && sign(normal.z)*dist > 0.0;
145
+ } else {
146
+ distSquared = min(min(
147
+ dot2(ap - ab*clamp(dot(ap, ab) / dot2(ab), 0.0, 1.0)),
148
+ dot2(bp - bc*clamp(dot(bp, bc) / dot2(bc), 0.0, 1.0))),
149
+ dot2(cp - ca*clamp(dot(cp, ca) / dot2(ca), 0.0, 1.0))
150
+ );
151
+ isAboveXYProjection = isInsideXYProjection && sign(normal.z)*dot(ap, normal) > 0.0;
152
+ }
153
+
154
+ return vec2(distSquared, isAboveXYProjection ? -1.0 : 1.0);
155
+ }
156
+
157
+ float lineSegment(vec2 p, vec2 a, vec2 b, float o) {
158
+ vec2 ap = p - a;
159
+ vec2 ab = b - a;
160
+
161
+ float t = clamp(dot(ap, ab)/dot(ab, ab), 0.0, 1.0);
162
+
163
+ return length(ap - ab*t) - o;
164
+ }
165
+
166
+ float lineSegment(vec2 p, vec2 a, vec2 b) {
167
+ return lineSegment(p, a, b, 0.0);
168
+ }
169
+
170
+ float lineSegment(vec3 p, vec2 a, vec2 b, float o) {
171
+ vec3 ap = p - a;
172
+ vec3 ab = b - a;
173
+
174
+ float t = clamp(dot(ap, ab)/dot(ab, ab), 0.0, 1.0);
175
+
176
+ return length(ap - ab*t) - o;
177
+ }
178
+
179
+ float lineSegment(vec3 p, vec2 a, vec2 b) {
180
+ return lineSegment(p, a, b, 0.0);
181
+ }
182
+
183
183
  `;
@@ -1,77 +1,77 @@
1
- export const operationsDefinition = /*glsl*/ `
2
- import { tile } from "@/utils";
3
-
4
- export {
5
- sdfNormal,
6
- combine, intersect, subtract, xor,
7
- smoothCombine, smoothIntersect, smoothSubtract, smoothXOR,
8
- repeatGrid
9
- }
10
-
11
- const float DEFAULT_SMOOTH = 0.2;
12
-
13
- vec3 sdfNormal(vec3 p, float sdf(vec3), float h) {
14
- vec2 o = vec2(1, -1)*h;
15
- return normalize(
16
- o.xyy*sdf(p + o.xyy) +
17
- o.yyx*sdf(p + o.yyx) +
18
- o.yxy*sdf(p + o.yxy) +
19
- o.xxx*sdf(p + o.xxx)
20
- );
21
- }
22
-
23
- float combine(float a, float b) {
24
- return min(a, b);
25
- }
26
-
27
- float intersect(float a, float b) {
28
- return max(a, b);
29
- }
30
-
31
- float subtract(float a, float b) {
32
- return max(a, -b);
33
- }
34
-
35
- float xor(float a, float b) {
36
- return subtract(combine(a, b), intersect(a, b));
37
- }
38
-
39
-
40
- float smoothCombine(float d1, float d2, float k) {
41
- float h = clamp(0.5 + 0.5*(d2 - d1)/k, 0.0, 1.0);
42
- return mix(d2, d1, h) - k*h*(1.0 - h);
43
- }
44
-
45
- float smoothCombine(float d1, float d2) {
46
- return smoothCombine(d1, d2, DEFAULT_SMOOTH);
47
- }
48
-
49
- float smoothIntersect(float d1, float d2, float k) {
50
- return -smoothCombine(-d1, -d2, k);
51
- }
52
-
53
- float smoothIntersect(float d1, float d2) {
54
- return smoothIntersect(d1, d2, DEFAULT_SMOOTH);
55
- }
56
-
57
- float smoothSubtract(float d1, float d2, float k) {
58
- return -smoothCombine(-d1, d2, k);
59
- }
60
-
61
- float smoothSubtract(float d1, float d2) {
62
- return smoothSubtract(d1, d2, DEFAULT_SMOOTH);
63
- }
64
-
65
- float smoothXOR(float d1, float d2, float k) {
66
- return smoothSubtract(smoothCombine(d1, d2, k), smoothIntersect(d1, d2, k), k);
67
- }
68
-
69
- float smoothXOR(float d1, float d2) {
70
- return smoothXOR(d1, d2, DEFAULT_SMOOTH);
71
- }
72
-
73
- float repeatGrid(vec3 p, float size, float sdf(vec3)) {
74
- return size*sdf(tile(p, size)/size);
75
- }
76
-
1
+ export const operationsDefinition = /*glsl*/ `
2
+ import { tile } from "@/utils";
3
+
4
+ export {
5
+ sdfNormal,
6
+ combine, intersect, subtract, xor,
7
+ smoothCombine, smoothIntersect, smoothSubtract, smoothXOR,
8
+ repeatGrid
9
+ }
10
+
11
+ const float DEFAULT_SMOOTH = 0.2;
12
+
13
+ vec3 sdfNormal(vec3 p, float sdf(vec3), float h) {
14
+ vec2 o = vec2(1, -1)*h;
15
+ return normalize(
16
+ o.xyy*sdf(p + o.xyy) +
17
+ o.yyx*sdf(p + o.yyx) +
18
+ o.yxy*sdf(p + o.yxy) +
19
+ o.xxx*sdf(p + o.xxx)
20
+ );
21
+ }
22
+
23
+ float combine(float a, float b) {
24
+ return min(a, b);
25
+ }
26
+
27
+ float intersect(float a, float b) {
28
+ return max(a, b);
29
+ }
30
+
31
+ float subtract(float a, float b) {
32
+ return max(a, -b);
33
+ }
34
+
35
+ float xor(float a, float b) {
36
+ return subtract(combine(a, b), intersect(a, b));
37
+ }
38
+
39
+
40
+ float smoothCombine(float d1, float d2, float k) {
41
+ float h = clamp(0.5 + 0.5*(d2 - d1)/k, 0.0, 1.0);
42
+ return mix(d2, d1, h) - k*h*(1.0 - h);
43
+ }
44
+
45
+ float smoothCombine(float d1, float d2) {
46
+ return smoothCombine(d1, d2, DEFAULT_SMOOTH);
47
+ }
48
+
49
+ float smoothIntersect(float d1, float d2, float k) {
50
+ return -smoothCombine(-d1, -d2, k);
51
+ }
52
+
53
+ float smoothIntersect(float d1, float d2) {
54
+ return smoothIntersect(d1, d2, DEFAULT_SMOOTH);
55
+ }
56
+
57
+ float smoothSubtract(float d1, float d2, float k) {
58
+ return -smoothCombine(-d1, d2, k);
59
+ }
60
+
61
+ float smoothSubtract(float d1, float d2) {
62
+ return smoothSubtract(d1, d2, DEFAULT_SMOOTH);
63
+ }
64
+
65
+ float smoothXOR(float d1, float d2, float k) {
66
+ return smoothSubtract(smoothCombine(d1, d2, k), smoothIntersect(d1, d2, k), k);
67
+ }
68
+
69
+ float smoothXOR(float d1, float d2) {
70
+ return smoothXOR(d1, d2, DEFAULT_SMOOTH);
71
+ }
72
+
73
+ float repeatGrid(vec3 p, float size, float sdf(vec3)) {
74
+ return size*sdf(tile(p, size)/size);
75
+ }
76
+
77
77
  `;
@@ -0,0 +1 @@
1
+ export declare const sdfDefinition = "\n\nimport { barycentric, transformToZAxis } from \"@/geometry\";\nimport { lerp, signed } from \"@/utils\";\n\nexport {\n sphere, box, rectangularCylinder, \n discoBall, polygon, donut, boxFrame,\n triangleSDFSquared, meshSDF, sdfNormal\n}\n\nfloat sphere(vec3 p, vec3 center, float radius) {\n return length(p - center) - radius;\n}\n\nfloat box(vec3 p, vec3 center, vec3 direction, vec3 size) {\n p = transformToZAxis(p, center, direction, 0.0);\n p = abs(p) - size/2.0;\n\n // First part gives distance inside box, second part outside\n return min(max(max(p.x, p.y), p.z), 0.0) + length(max(p, vec3(0)));\n}\n\nfloat rectangularCylinder(vec3 p, vec3 center, vec3 direction, vec2 size, float height, bool offsetCenter, float rotation) {\n if (offsetCenter) center += 0.5*height*normalize(direction);\n p = transformToZAxis(p, center, direction, rotation);\n return box(p, vec3(0), vec3(0, 0, 1), vec3(size, height));\n}\n\nfloat discoBall(vec3 p, vec3 center, float radius, float n, float o) {\n p -= center;\n float phi = -PI + 2.0*PI*(round(lerp(atan(p.y, p.x), -PI, PI)*n)/n + o);\n float theta = PI*(round(lerp(acos(p.z/length(p)), 0.0, PI)*n)/n + o);\n\n vec3 bp = vec3(sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta));\n\n return dot(bp, p - radius*bp);\n}\n\nfloat discoBall(vec3 p, vec3 center, float radius, float n) {\n return discoBall(p, center, radius, n, 0.0);\n}\n\nfloat polygon(vec2 p, float radius, float n) {\n float theta = PI*signed((floor(lerp(atan(p.y, p.x), -PI, PI)*n) + .5)/n);\n vec2 pp = vec2(cos(theta), sin(theta));\n return dot(pp, p - radius*pp);\n}\n\nfloat donut(vec3 p, vec3 center, vec3 direction, float innerRadius, float outerRadius, float rotation, float sections, float sides) {\n p = transformToZAxis(p, center, direction, 0.0);\n\n vec3 xy = normalize(vec3(p.xy, 0));\n float angle = mod(atan(xy.y, xy.x), 2.*PI);\n\n angle = 2.*PI*round(sections*angle/(2.*PI))/sections;\n innerRadius *= sqrt(PI/(sections*tan(PI/sections)));\n outerRadius *= sqrt(PI/(sides*tan(PI/sides)));\n return polygon(rotate(vec2(dot(p, vec3(cos(angle), sin(angle), 0)) - innerRadius, p.z), rotation + PI/4.), outerRadius, sides);\n}\n\nfloat boxFrame(vec3 p, vec3 center, vec3 direction, vec3 size, float thickness) {\n float dBox = box(p, center, direction, size);\n\n p = transformToZAxis(p, center, direction, 0.0);\n\n vec3 o = 2.0*vec3(thickness, thickness, -1e9);\n float dX = box(p, vec3(0), vec3(1, 0, 0), size.yzx - o);\n float dY = box(p, vec3(0), vec3(0, 1, 0), size.zxy - o);\n float dZ = box(p, vec3(0), vec3(0, 0, 1), size.xyz - o);\n\n float dInner = min(dX, min(dY, dZ));\n return max(dBox, -dInner);\n}\n\n// x is distance squared, y indicates which side of triangle p is on \n// (relative to normal: positive if p is above, negative if below)\nvec2 triangleSDFSquared(vec3 p, vec3 a, vec3 b, vec3 c) {\n vec3 ab = b - a;\n vec3 ca = a - c;\n vec3 bc = c - b;\n\n vec3 ap = p - a;\n vec3 bp = p - b;\n vec3 cp = p - c;\n\n vec3 normal = cross(ab, -ca); // Negate to make cross(ab, ac), follows right-hand rule assuming abc is CCW\n \n bool isInsideTriangle = all(greaterThan(barycentric(p, a, b, c), vec3(0)));\n \n float dot2 = (vec3 v) => dot(v, v); \n\n bool isInsideXYProjection = all(greaterThan(barycentric(p.xy, a.xy, b.xy, c.xy), vec3(0)));\n\n float distSquared;\n bool isAboveXYProjection;\n\n // If p is above XY projection, then dist = dot(ap, normal) > 0 when normal points upwards\n // and dist < 0 when it points downwards. sign(normal.z)*dist > 0.0 then tells us if p is above \n if (isInsideTriangle) {\n float dist = dot(ap, normalize(normal));\n distSquared = dist*dist;\n isAboveXYProjection = isInsideXYProjection && sign(normal.z)*dist > 0.0;\n } else {\n distSquared = min(min(\n dot2(ap - ab*clamp(dot(ap, ab) / dot2(ab), 0.0, 1.0)),\n dot2(bp - bc*clamp(dot(bp, bc) / dot2(bc), 0.0, 1.0))),\n dot2(cp - ca*clamp(dot(cp, ca) / dot2(ca), 0.0, 1.0))\n );\n isAboveXYProjection = isInsideXYProjection && sign(normal.z)*dot(ap, normal) > 0.0;\n }\n\n return vec2(distSquared, isAboveXYProjection ? -1.0 : 1.0);\n}\n\nvec3 sdfNormal(vec3 p, float sdf(vec3), float h) {\n vec2 o = vec2(1, -1)*h;\n return normalize( \n o.xyy*sdf(p + o.xyy) + \n o.yyx*sdf(p + o.yyx) + \n o.yxy*sdf(p + o.yxy) + \n o.xxx*sdf(p + o.xxx)\n );\n}\n\n";
@@ -0,0 +1,126 @@
1
+ export const sdfDefinition = /*glsl*/ `
2
+
3
+ import { barycentric, transformToZAxis } from "@/geometry";
4
+ import { lerp, signed } from "@/utils";
5
+
6
+ export {
7
+ sphere, box, rectangularCylinder,
8
+ discoBall, polygon, donut, boxFrame,
9
+ triangleSDFSquared, meshSDF, sdfNormal
10
+ }
11
+
12
+ float sphere(vec3 p, vec3 center, float radius) {
13
+ return length(p - center) - radius;
14
+ }
15
+
16
+ float box(vec3 p, vec3 center, vec3 direction, vec3 size) {
17
+ p = transformToZAxis(p, center, direction, 0.0);
18
+ p = abs(p) - size/2.0;
19
+
20
+ // First part gives distance inside box, second part outside
21
+ return min(max(max(p.x, p.y), p.z), 0.0) + length(max(p, vec3(0)));
22
+ }
23
+
24
+ float rectangularCylinder(vec3 p, vec3 center, vec3 direction, vec2 size, float height, bool offsetCenter, float rotation) {
25
+ if (offsetCenter) center += 0.5*height*normalize(direction);
26
+ p = transformToZAxis(p, center, direction, rotation);
27
+ return box(p, vec3(0), vec3(0, 0, 1), vec3(size, height));
28
+ }
29
+
30
+ float discoBall(vec3 p, vec3 center, float radius, float n, float o) {
31
+ p -= center;
32
+ float phi = -PI + 2.0*PI*(round(lerp(atan(p.y, p.x), -PI, PI)*n)/n + o);
33
+ float theta = PI*(round(lerp(acos(p.z/length(p)), 0.0, PI)*n)/n + o);
34
+
35
+ vec3 bp = vec3(sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta));
36
+
37
+ return dot(bp, p - radius*bp);
38
+ }
39
+
40
+ float discoBall(vec3 p, vec3 center, float radius, float n) {
41
+ return discoBall(p, center, radius, n, 0.0);
42
+ }
43
+
44
+ float polygon(vec2 p, float radius, float n) {
45
+ float theta = PI*signed((floor(lerp(atan(p.y, p.x), -PI, PI)*n) + .5)/n);
46
+ vec2 pp = vec2(cos(theta), sin(theta));
47
+ return dot(pp, p - radius*pp);
48
+ }
49
+
50
+ float donut(vec3 p, vec3 center, vec3 direction, float innerRadius, float outerRadius, float rotation, float sections, float sides) {
51
+ p = transformToZAxis(p, center, direction, 0.0);
52
+
53
+ vec3 xy = normalize(vec3(p.xy, 0));
54
+ float angle = mod(atan(xy.y, xy.x), 2.*PI);
55
+
56
+ angle = 2.*PI*round(sections*angle/(2.*PI))/sections;
57
+ innerRadius *= sqrt(PI/(sections*tan(PI/sections)));
58
+ outerRadius *= sqrt(PI/(sides*tan(PI/sides)));
59
+ return polygon(rotate(vec2(dot(p, vec3(cos(angle), sin(angle), 0)) - innerRadius, p.z), rotation + PI/4.), outerRadius, sides);
60
+ }
61
+
62
+ float boxFrame(vec3 p, vec3 center, vec3 direction, vec3 size, float thickness) {
63
+ float dBox = box(p, center, direction, size);
64
+
65
+ p = transformToZAxis(p, center, direction, 0.0);
66
+
67
+ vec3 o = 2.0*vec3(thickness, thickness, -1e9);
68
+ float dX = box(p, vec3(0), vec3(1, 0, 0), size.yzx - o);
69
+ float dY = box(p, vec3(0), vec3(0, 1, 0), size.zxy - o);
70
+ float dZ = box(p, vec3(0), vec3(0, 0, 1), size.xyz - o);
71
+
72
+ float dInner = min(dX, min(dY, dZ));
73
+ return max(dBox, -dInner);
74
+ }
75
+
76
+ // x is distance squared, y indicates which side of triangle p is on
77
+ // (relative to normal: positive if p is above, negative if below)
78
+ vec2 triangleSDFSquared(vec3 p, vec3 a, vec3 b, vec3 c) {
79
+ vec3 ab = b - a;
80
+ vec3 ca = a - c;
81
+ vec3 bc = c - b;
82
+
83
+ vec3 ap = p - a;
84
+ vec3 bp = p - b;
85
+ vec3 cp = p - c;
86
+
87
+ vec3 normal = cross(ab, -ca); // Negate to make cross(ab, ac), follows right-hand rule assuming abc is CCW
88
+
89
+ bool isInsideTriangle = all(greaterThan(barycentric(p, a, b, c), vec3(0)));
90
+
91
+ float dot2 = (vec3 v) => dot(v, v);
92
+
93
+ bool isInsideXYProjection = all(greaterThan(barycentric(p.xy, a.xy, b.xy, c.xy), vec3(0)));
94
+
95
+ float distSquared;
96
+ bool isAboveXYProjection;
97
+
98
+ // If p is above XY projection, then dist = dot(ap, normal) > 0 when normal points upwards
99
+ // and dist < 0 when it points downwards. sign(normal.z)*dist > 0.0 then tells us if p is above
100
+ if (isInsideTriangle) {
101
+ float dist = dot(ap, normalize(normal));
102
+ distSquared = dist*dist;
103
+ isAboveXYProjection = isInsideXYProjection && sign(normal.z)*dist > 0.0;
104
+ } else {
105
+ distSquared = min(min(
106
+ dot2(ap - ab*clamp(dot(ap, ab) / dot2(ab), 0.0, 1.0)),
107
+ dot2(bp - bc*clamp(dot(bp, bc) / dot2(bc), 0.0, 1.0))),
108
+ dot2(cp - ca*clamp(dot(cp, ca) / dot2(ca), 0.0, 1.0))
109
+ );
110
+ isAboveXYProjection = isInsideXYProjection && sign(normal.z)*dot(ap, normal) > 0.0;
111
+ }
112
+
113
+ return vec2(distSquared, isAboveXYProjection ? -1.0 : 1.0);
114
+ }
115
+
116
+ vec3 sdfNormal(vec3 p, float sdf(vec3), float h) {
117
+ vec2 o = vec2(1, -1)*h;
118
+ return normalize(
119
+ o.xyy*sdf(p + o.xyy) +
120
+ o.yyx*sdf(p + o.yyx) +
121
+ o.yxy*sdf(p + o.yxy) +
122
+ o.xxx*sdf(p + o.xxx)
123
+ );
124
+ }
125
+
126
+ `;