Ensure all shapes are unique

This commit is contained in:
Andras Schmelczer 2026-06-10 22:29:40 +01:00
parent c2f61a2f7e
commit 4d94fdeec8
7 changed files with 135 additions and 108 deletions

View file

@ -15,8 +15,6 @@ class CircleBase extends EmptyDrawable {
} }
} }
// Suffixing the GLSL names with a per-factory-invocation id lets multiple
// circle types (e.g. different colors) coexist in one compiled shader.
let _id = 0; let _id = 0;
/** /**

View file

@ -19,6 +19,8 @@ class DropletBase extends EmptyDrawable {
} }
} }
let _id = 0;
/** /**
* @category Drawable * @category Drawable
*/ */
@ -27,28 +29,28 @@ export const DropletFactory = (color: vec3 | vec4 | number): typeof DropletBase
public static descriptor: DrawableDescriptor = { public static descriptor: DrawableDescriptor = {
sdf: { sdf: {
shader: ` shader: `
uniform vec2 froms[DROPLET_COUNT]; uniform vec2 dropletFroms${_id}[DROPLET_COUNT${_id}];
uniform vec2 toFromDeltas[DROPLET_COUNT]; uniform vec2 dropletToFromDeltas${_id}[DROPLET_COUNT${_id}];
uniform float fromRadii[DROPLET_COUNT]; uniform float dropletFromRadii${_id}[DROPLET_COUNT${_id}];
uniform float toRadii[DROPLET_COUNT]; uniform float dropletToRadii${_id}[DROPLET_COUNT${_id}];
float dropletMinDistance(vec2 target, out vec4 color) { float dropletMinDistance${_id}(vec2 target, out vec4 color) {
color = ${codeForColorAccess(color)}; color = ${codeForColorAccess(color)};
float minDistance = 1000.0; float minDistance = 1000.0;
for (int i = 0; i < DROPLET_COUNT; i++) { for (int i = 0; i < DROPLET_COUNT${_id}; i++) {
vec2 targetFromDelta = target - froms[i]; vec2 targetFromDelta = target - dropletFroms${_id}[i];
float h = clamp( float h = clamp(
dot(targetFromDelta, toFromDeltas[i]) dot(targetFromDelta, dropletToFromDeltas${_id}[i])
/ max(dot(toFromDeltas[i], toFromDeltas[i]), 0.00000001), / max(dot(dropletToFromDeltas${_id}[i], dropletToFromDeltas${_id}[i]), 0.00000001),
0.0, 1.0 0.0, 1.0
); );
float currentDistance = -mix( float currentDistance = -mix(
fromRadii[i], toRadii[i], h dropletFromRadii${_id}[i], dropletToRadii${_id}[i], h
) + distance( ) + distance(
targetFromDelta, toFromDeltas[i] * h targetFromDelta, dropletToFromDeltas${_id}[i] * h
); );
minDistance = min(minDistance, currentDistance); minDistance = min(minDistance, currentDistance);
@ -57,15 +59,15 @@ export const DropletFactory = (color: vec3 | vec4 | number): typeof DropletBase
return minDistance; return minDistance;
} }
`, `,
distanceFunctionName: 'dropletMinDistance', distanceFunctionName: `dropletMinDistance${_id}`,
}, },
propertyUniformMapping: { propertyUniformMapping: {
from: 'froms', from: `dropletFroms${_id}`,
toFromDelta: 'toFromDeltas', toFromDelta: `dropletToFromDeltas${_id}`,
fromRadius: 'fromRadii', fromRadius: `dropletFromRadii${_id}`,
toRadius: 'toRadii', toRadius: `dropletToRadii${_id}`,
}, },
uniformCountMacroName: 'DROPLET_COUNT', uniformCountMacroName: `DROPLET_COUNT${_id}`,
shaderCombinationSteps: [0, 1, 4, 16, 32], shaderCombinationSteps: [0, 1, 4, 16, 32],
empty: new Droplet(vec2.create(), vec2.create(), 0, 0), empty: new Droplet(vec2.create(), vec2.create(), 0, 0),
}; };
@ -96,5 +98,7 @@ export const DropletFactory = (color: vec3 | vec4 | number): typeof DropletBase
} }
} }
_id++;
return Droplet; return Droplet;
}; };

View file

@ -15,6 +15,8 @@ class HexagonBase extends EmptyDrawable {
} }
} }
let _id = 0;
/** /**
* @category Drawable * @category Drawable
*/ */
@ -23,16 +25,16 @@ export const HexagonFactory = (color: vec3 | vec4 | number): typeof HexagonBase
public static descriptor: DrawableDescriptor = { public static descriptor: DrawableDescriptor = {
sdf: { sdf: {
shader: ` shader: `
uniform vec2 hexagonCenters[HEXAGON_COUNT]; uniform vec2 hexagonCenters${_id}[HEXAGON_COUNT${_id}];
uniform float hexagonSize[HEXAGON_COUNT]; uniform float hexagonSize${_id}[HEXAGON_COUNT${_id}];
float hexagonMinDistance(vec2 target, out vec4 color) { float hexagonMinDistance${_id}(vec2 target, out vec4 color) {
color = ${codeForColorAccess(color)}; color = ${codeForColorAccess(color)};
float minDistance = 1000.0; float minDistance = 1000.0;
for (int i = 0; i < HEXAGON_COUNT; i++) { for (int i = 0; i < HEXAGON_COUNT${_id}; i++) {
const vec3 k = vec3(-0.866025404,0.5,0.577350269); const vec3 k = vec3(-0.866025404,0.5,0.577350269);
float r = hexagonSize[i]; float r = hexagonSize${_id}[i];
vec2 p = abs(target - hexagonCenters[i]); vec2 p = abs(target - hexagonCenters${_id}[i]);
float cosa = 0.8660; float cosa = 0.8660;
float sina = 0.5; float sina = 0.5;
p = vec2(cosa * p.x - sina * p.y, sina * p.x + cosa * p.y); p = vec2(cosa * p.x - sina * p.y, sina * p.x + cosa * p.y);
@ -45,13 +47,13 @@ export const HexagonFactory = (color: vec3 | vec4 | number): typeof HexagonBase
return minDistance; return minDistance;
} }
`, `,
distanceFunctionName: 'hexagonMinDistance', distanceFunctionName: `hexagonMinDistance${_id}`,
}, },
propertyUniformMapping: { propertyUniformMapping: {
center: 'hexagonCenters', center: `hexagonCenters${_id}`,
radius: 'hexagonSize', radius: `hexagonSize${_id}`,
}, },
uniformCountMacroName: 'HEXAGON_COUNT', uniformCountMacroName: `HEXAGON_COUNT${_id}`,
shaderCombinationSteps: [0, 1, 2, 3, 8, 16], shaderCombinationSteps: [0, 1, 2, 3, 8, 16],
empty: new Hexagon(vec2.create(), 0), empty: new Hexagon(vec2.create(), 0),
}; };
@ -68,5 +70,7 @@ export const HexagonFactory = (color: vec3 | vec4 | number): typeof HexagonBase
} }
} }
_id++;
return Hexagon; return Hexagon;
}; };

View file

@ -19,6 +19,8 @@ class InvertedTunnelBase extends EmptyDrawable {
} }
} }
let _id = 0;
/** /**
* Providing a noise texture is required for this drawable. * Providing a noise texture is required for this drawable.
* *
@ -31,10 +33,10 @@ export const InvertedTunnelFactory = (
public static descriptor: DrawableDescriptor = { public static descriptor: DrawableDescriptor = {
sdf: { sdf: {
shader: ` shader: `
uniform vec2 froms[INVERTED_TUNNEL_COUNT]; uniform vec2 invertedTunnelFroms${_id}[INVERTED_TUNNEL_COUNT${_id}];
uniform vec2 toFromDeltas[INVERTED_TUNNEL_COUNT]; uniform vec2 invertedTunnelToFromDeltas${_id}[INVERTED_TUNNEL_COUNT${_id}];
uniform float fromRadii[INVERTED_TUNNEL_COUNT]; uniform float invertedTunnelFromRadii${_id}[INVERTED_TUNNEL_COUNT${_id}];
uniform float toRadii[INVERTED_TUNNEL_COUNT]; uniform float invertedTunnelToRadii${_id}[INVERTED_TUNNEL_COUNT${_id}];
// Other drawables share this declaration; the include guard keeps // Other drawables share this declaration; the include guard keeps
// the combined shader from declaring it twice. // the combined shader from declaring it twice.
@ -43,6 +45,10 @@ export const InvertedTunnelFactory = (
uniform sampler2D noiseTexture; uniform sampler2D noiseTexture;
#endif #endif
// Shared between every inverted-tunnel variant; the include guard
// keeps the combined shader from defining it twice.
#ifndef INVERTED_TUNNEL_TERRAIN_DECLARED
#define INVERTED_TUNNEL_TERRAIN_DECLARED
#ifdef WEBGL2_IS_AVAILABLE #ifdef WEBGL2_IS_AVAILABLE
float invertedTunnelTerrain(float h) { float invertedTunnelTerrain(float h) {
return texture( return texture(
@ -58,24 +64,25 @@ export const InvertedTunnelFactory = (
)[0] - 0.5; )[0] - 0.5;
} }
#endif #endif
#endif
float invertedTunnelMinDistance(vec2 target, out vec4 color) { float invertedTunnelMinDistance${_id}(vec2 target, out vec4 color) {
color = ${codeForColorAccess(color)}; color = ${codeForColorAccess(color)};
float minDistance = -1000.0; float minDistance = -1000.0;
for (int i = 0; i < INVERTED_TUNNEL_COUNT; i++) { for (int i = 0; i < INVERTED_TUNNEL_COUNT${_id}; i++) {
vec2 targetFromDelta = target - froms[i]; vec2 targetFromDelta = target - invertedTunnelFroms${_id}[i];
float h = dot(targetFromDelta, toFromDeltas[i]) float h = dot(targetFromDelta, invertedTunnelToFromDeltas${_id}[i])
/ max(dot(toFromDeltas[i], toFromDeltas[i]), 0.00000001); / max(dot(invertedTunnelToFromDeltas${_id}[i], invertedTunnelToFromDeltas${_id}[i]), 0.00000001);
float clampedH = clamp(h, 0.0, 1.0); float clampedH = clamp(h, 0.0, 1.0);
float currentDistance = -mix( float currentDistance = -mix(
fromRadii[i], toRadii[i], clampedH invertedTunnelFromRadii${_id}[i], invertedTunnelToRadii${_id}[i], clampedH
) + distance( ) + distance(
targetFromDelta, toFromDeltas[i] * clampedH targetFromDelta, invertedTunnelToFromDeltas${_id}[i] * clampedH
) - invertedTunnelTerrain(h) / 12.0; ) - invertedTunnelTerrain(h) / 12.0;
minDistance = max(minDistance, -currentDistance); minDistance = max(minDistance, -currentDistance);
@ -85,15 +92,15 @@ export const InvertedTunnelFactory = (
} }
`, `,
isInverted: true, isInverted: true,
distanceFunctionName: 'invertedTunnelMinDistance', distanceFunctionName: `invertedTunnelMinDistance${_id}`,
}, },
propertyUniformMapping: { propertyUniformMapping: {
from: 'froms', from: `invertedTunnelFroms${_id}`,
toFromDelta: 'toFromDeltas', toFromDelta: `invertedTunnelToFromDeltas${_id}`,
fromRadius: 'fromRadii', fromRadius: `invertedTunnelFromRadii${_id}`,
toRadius: 'toRadii', toRadius: `invertedTunnelToRadii${_id}`,
}, },
uniformCountMacroName: 'INVERTED_TUNNEL_COUNT', uniformCountMacroName: `INVERTED_TUNNEL_COUNT${_id}`,
shaderCombinationSteps: [0, 1, 4, 16, 32], shaderCombinationSteps: [0, 1, 4, 16, 32],
empty: new InvertedTunnel(vec2.create(), vec2.create(), 0, 0), empty: new InvertedTunnel(vec2.create(), vec2.create(), 0, 0),
}; };
@ -124,5 +131,7 @@ export const InvertedTunnelFactory = (
} }
} }
_id++;
return InvertedTunnel; return InvertedTunnel;
}; };

View file

@ -8,6 +8,8 @@ interface NoisyPolygonBase extends PolygonBase {
randomOffset: number; randomOffset: number;
} }
let _id = 0;
/** /**
* @category Drawable * @category Drawable
*/ */
@ -19,10 +21,10 @@ export const NoisyPolygonFactory = (
public static descriptor: DrawableDescriptor = { public static descriptor: DrawableDescriptor = {
sdf: { sdf: {
shader: ` shader: `
uniform vec2 noisyPolygon${vertexCount}Vertices[NOISY_POLYGON${vertexCount}_COUNT * ${vertexCount}]; uniform vec2 noisyPolygon${vertexCount}Vertices${_id}[NOISY_POLYGON${vertexCount}_COUNT${_id} * ${vertexCount}];
uniform vec2 noisyPolygon${vertexCount}Centers[NOISY_POLYGON${vertexCount}_COUNT]; uniform vec2 noisyPolygon${vertexCount}Centers${_id}[NOISY_POLYGON${vertexCount}_COUNT${_id}];
uniform float noisyPolygon${vertexCount}Lengths[NOISY_POLYGON${vertexCount}_COUNT]; uniform float noisyPolygon${vertexCount}Lengths${_id}[NOISY_POLYGON${vertexCount}_COUNT${_id}];
uniform float noisyPolygon${vertexCount}Randoms[NOISY_POLYGON${vertexCount}_COUNT]; uniform float noisyPolygon${vertexCount}Randoms${_id}[NOISY_POLYGON${vertexCount}_COUNT${_id}];
// Other drawables (and other vertex-count variants of this one) // Other drawables (and other vertex-count variants of this one)
// share these declarations; the include guards keep the combined // share these declarations; the include guards keep the combined
@ -45,7 +47,7 @@ export const NoisyPolygonFactory = (
#endif #endif
#endif #endif
vec2 noisyPolygon${vertexCount}LineDistance(vec2 target, vec2 from, vec2 to) { vec2 noisyPolygon${vertexCount}LineDistance${_id}(vec2 target, vec2 from, vec2 to) {
vec2 targetFromDelta = target - from; vec2 targetFromDelta = target - from;
vec2 toFromDelta = to - from; vec2 toFromDelta = to - from;
float h = clamp( float h = clamp(
@ -61,18 +63,18 @@ export const NoisyPolygonFactory = (
); );
} }
float noisyPolygon${vertexCount}MinDistance(vec2 target, out vec4 color) { float noisyPolygon${vertexCount}MinDistance${_id}(vec2 target, out vec4 color) {
color = ${codeForColorAccess(color)}; color = ${codeForColorAccess(color)};
float minDistance = 100.0; float minDistance = 100.0;
for (int j = 0; j < NOISY_POLYGON${vertexCount}_COUNT; j++) { for (int j = 0; j < NOISY_POLYGON${vertexCount}_COUNT${_id}; j++) {
vec2 startEnd = noisyPolygon${vertexCount}Vertices[j * ${vertexCount}]; vec2 startEnd = noisyPolygon${vertexCount}Vertices${_id}[j * ${vertexCount}];
vec2 vb = startEnd; vec2 vb = startEnd;
vec2 center = noisyPolygon${vertexCount}Centers[j]; vec2 center = noisyPolygon${vertexCount}Centers${_id}[j];
float l = noisyPolygon${vertexCount}Lengths[j]; float l = noisyPolygon${vertexCount}Lengths${_id}[j];
float randomOffset = noisyPolygon${vertexCount}Randoms[j]; float randomOffset = noisyPolygon${vertexCount}Randoms${_id}[j];
vec2 targetCenterDelta = target - center; vec2 targetCenterDelta = target - center;
float targetDistance = length(targetCenterDelta); float targetDistance = length(targetCenterDelta);
vec2 targetTangent = targetCenterDelta / clamp(targetDistance, 0.01, 1000.0); vec2 targetTangent = targetCenterDelta / clamp(targetDistance, 0.01, 1000.0);
@ -88,8 +90,8 @@ export const NoisyPolygonFactory = (
float s = 1.0; float s = 1.0;
for (int k = 1; k < ${vertexCount}; k++) { for (int k = 1; k < ${vertexCount}; k++) {
vec2 va = vb; vec2 va = vb;
vb = noisyPolygon${vertexCount}Vertices[j * ${vertexCount} + k]; vb = noisyPolygon${vertexCount}Vertices${_id}[j * ${vertexCount} + k];
vec2 ds = noisyPolygon${vertexCount}LineDistance(noisyTarget, va, vb); vec2 ds = noisyPolygon${vertexCount}LineDistance${_id}(noisyTarget, va, vb);
bvec3 cond = bvec3(noisyTarget.y >= va.y, noisyTarget.y < vb.y, ds.y > 0.0); bvec3 cond = bvec3(noisyTarget.y >= va.y, noisyTarget.y < vb.y, ds.y > 0.0);
if (all(cond) || all(not(cond))) { if (all(cond) || all(not(cond))) {
@ -99,7 +101,7 @@ export const NoisyPolygonFactory = (
d = min(d, ds.x); d = min(d, ds.x);
} }
vec2 ds = noisyPolygon${vertexCount}LineDistance(noisyTarget, vb, startEnd); vec2 ds = noisyPolygon${vertexCount}LineDistance${_id}(noisyTarget, vb, startEnd);
bvec3 cond = bvec3(noisyTarget.y >= vb.y, noisyTarget.y < startEnd.y, ds.y > 0.0); bvec3 cond = bvec3(noisyTarget.y >= vb.y, noisyTarget.y < startEnd.y, ds.y > 0.0);
if (all(cond) || all(not(cond))) { if (all(cond) || all(not(cond))) {
s *= -1.0; s *= -1.0;
@ -112,15 +114,15 @@ export const NoisyPolygonFactory = (
return minDistance; return minDistance;
} }
`, `,
distanceFunctionName: `noisyPolygon${vertexCount}MinDistance`, distanceFunctionName: `noisyPolygon${vertexCount}MinDistance${_id}`,
}, },
propertyUniformMapping: { propertyUniformMapping: {
length: `noisyPolygon${vertexCount}Lengths`, length: `noisyPolygon${vertexCount}Lengths${_id}`,
random: `noisyPolygon${vertexCount}Randoms`, random: `noisyPolygon${vertexCount}Randoms${_id}`,
center: `noisyPolygon${vertexCount}Centers`, center: `noisyPolygon${vertexCount}Centers${_id}`,
vertices: `noisyPolygon${vertexCount}Vertices`, vertices: `noisyPolygon${vertexCount}Vertices${_id}`,
}, },
uniformCountMacroName: `NOISY_POLYGON${vertexCount}_COUNT`, uniformCountMacroName: `NOISY_POLYGON${vertexCount}_COUNT${_id}`,
shaderCombinationSteps: [0, 1, 2, 3, 8, 16], shaderCombinationSteps: [0, 1, 2, 3, 8, 16],
empty: new NoisyPolygon( empty: new NoisyPolygon(
new Array(vertexCount).fill(vec2.create()) new Array(vertexCount).fill(vec2.create())
@ -183,5 +185,7 @@ export const NoisyPolygonFactory = (
} }
} }
_id++;
return NoisyPolygon as any; return NoisyPolygon as any;
}; };

View file

@ -14,6 +14,8 @@ export class PolygonBase extends EmptyDrawable {
} }
} }
let _id = 0;
/** /**
* @category Drawable * @category Drawable
*/ */
@ -25,9 +27,9 @@ export const PolygonFactory = (
public static descriptor: DrawableDescriptor = { public static descriptor: DrawableDescriptor = {
sdf: { sdf: {
shader: ` shader: `
uniform vec2 polygon${vertexCount}Vertices[POLYGON${vertexCount}_COUNT * ${vertexCount}]; uniform vec2 polygon${vertexCount}Vertices${_id}[POLYGON${vertexCount}_COUNT${_id} * ${vertexCount}];
vec2 polygon${vertexCount}LineDistance(vec2 target, vec2 from, vec2 to) { vec2 polygon${vertexCount}LineDistance${_id}(vec2 target, vec2 from, vec2 to) {
vec2 targetFromDelta = target - from; vec2 targetFromDelta = target - from;
vec2 toFromDelta = to - from; vec2 toFromDelta = to - from;
float h = clamp( float h = clamp(
@ -43,21 +45,21 @@ export const PolygonFactory = (
); );
} }
float polygon${vertexCount}MinDistance(vec2 target, out vec4 color) { float polygon${vertexCount}MinDistance${_id}(vec2 target, out vec4 color) {
color = ${codeForColorAccess(color)}; color = ${codeForColorAccess(color)};
float minDistance = 100.0; float minDistance = 100.0;
for (int j = 0; j < POLYGON${vertexCount}_COUNT; j++) { for (int j = 0; j < POLYGON${vertexCount}_COUNT${_id}; j++) {
vec2 startEnd = polygon${vertexCount}Vertices[j * ${vertexCount}]; vec2 startEnd = polygon${vertexCount}Vertices${_id}[j * ${vertexCount}];
vec2 vb = startEnd; vec2 vb = startEnd;
float d = 10000.0; float d = 10000.0;
float s = 1.0; float s = 1.0;
for (int k = 1; k < ${vertexCount}; k++) { for (int k = 1; k < ${vertexCount}; k++) {
vec2 va = vb; vec2 va = vb;
vb = polygon${vertexCount}Vertices[j * ${vertexCount} + k]; vb = polygon${vertexCount}Vertices${_id}[j * ${vertexCount} + k];
vec2 ds = polygon${vertexCount}LineDistance(target, va, vb); vec2 ds = polygon${vertexCount}LineDistance${_id}(target, va, vb);
bvec3 cond = bvec3(target.y >= va.y, target.y < vb.y, ds.y > 0.0); bvec3 cond = bvec3(target.y >= va.y, target.y < vb.y, ds.y > 0.0);
if (all(cond) || all(not(cond))) { if (all(cond) || all(not(cond))) {
@ -67,7 +69,7 @@ export const PolygonFactory = (
d = min(d, ds.x); d = min(d, ds.x);
} }
vec2 ds = polygon${vertexCount}LineDistance(target, vb, startEnd); vec2 ds = polygon${vertexCount}LineDistance${_id}(target, vb, startEnd);
bvec3 cond = bvec3(target.y >= vb.y, target.y < startEnd.y, ds.y > 0.0); bvec3 cond = bvec3(target.y >= vb.y, target.y < startEnd.y, ds.y > 0.0);
if (all(cond) || all(not(cond))) { if (all(cond) || all(not(cond))) {
s *= -1.0; s *= -1.0;
@ -80,13 +82,13 @@ export const PolygonFactory = (
return minDistance; return minDistance;
} }
`, `,
distanceFunctionName: `polygon${vertexCount}MinDistance`, distanceFunctionName: `polygon${vertexCount}MinDistance${_id}`,
}, },
propertyUniformMapping: { propertyUniformMapping: {
vertices: `polygon${vertexCount}Vertices`, vertices: `polygon${vertexCount}Vertices${_id}`,
}, },
objectCountScaler: 1 / vertexCount, objectCountScaler: 1 / vertexCount,
uniformCountMacroName: `POLYGON${vertexCount}_COUNT`, uniformCountMacroName: `POLYGON${vertexCount}_COUNT${_id}`,
shaderCombinationSteps: [0, 1, 2, 3, 8, 16], shaderCombinationSteps: [0, 1, 2, 3, 8, 16],
empty: new Polygon(new Array(vertexCount).fill(vec2.create())), empty: new Polygon(new Array(vertexCount).fill(vec2.create())),
}; };
@ -178,5 +180,7 @@ export const PolygonFactory = (
} }
} }
_id++;
return Polygon; return Polygon;
}; };

View file

@ -16,6 +16,8 @@ class RotatedRectangleBase extends EmptyDrawable {
} }
} }
let _id = 0;
/** /**
* @category Drawable * @category Drawable
*/ */
@ -27,23 +29,23 @@ export const RotatedRectangleFactory = (
sdf: { sdf: {
// Source: https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm // Source: https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
shader: ` shader: `
uniform vec2 rotatedRectangleTopCenters[ROTATED_RECTANGLE_COUNT]; uniform vec2 rotatedRectangleTopCenters${_id}[ROTATED_RECTANGLE_COUNT${_id}];
uniform vec2 rotatedRectangleBottomCenters[ROTATED_RECTANGLE_COUNT]; uniform vec2 rotatedRectangleBottomCenters${_id}[ROTATED_RECTANGLE_COUNT${_id}];
uniform float rotatedRectangleWidths[ROTATED_RECTANGLE_COUNT]; uniform float rotatedRectangleWidths${_id}[ROTATED_RECTANGLE_COUNT${_id}];
float rotatedRectangleMinDistance(vec2 target, out vec4 color) { float rotatedRectangleMinDistance${_id}(vec2 target, out vec4 color) {
color = ${codeForColorAccess(color)}; color = ${codeForColorAccess(color)};
float minDistance = 1000.0; float minDistance = 1000.0;
for (int i = 0; i < ROTATED_RECTANGLE_COUNT; i++) { for (int i = 0; i < ROTATED_RECTANGLE_COUNT${_id}; i++) {
vec2 top = rotatedRectangleTopCenters[i]; vec2 top = rotatedRectangleTopCenters${_id}[i];
vec2 bottom = rotatedRectangleBottomCenters[i]; vec2 bottom = rotatedRectangleBottomCenters${_id}[i];
float height = length(bottom - top); float height = length(bottom - top);
vec2 d = height > 0.00000001 ? (bottom - top) / height : vec2(0.0, 1.0); vec2 d = height > 0.00000001 ? (bottom - top) / height : vec2(0.0, 1.0);
vec2 q = (target - (top + bottom) * 0.5); vec2 q = (target - (top + bottom) * 0.5);
q = mat2(d.x, -d.y, d.y, d.x) * q; q = mat2(d.x, -d.y, d.y, d.x) * q;
q = abs(q) - vec2(height, rotatedRectangleWidths[i]) * 0.5; q = abs(q) - vec2(height, rotatedRectangleWidths${_id}[i]) * 0.5;
float dist = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0); float dist = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0);
minDistance = min(minDistance, dist); minDistance = min(minDistance, dist);
} }
@ -51,20 +53,20 @@ export const RotatedRectangleFactory = (
return minDistance; return minDistance;
} }
`, `,
distanceFunctionName: 'rotatedRectangleMinDistance', distanceFunctionName: `rotatedRectangleMinDistance${_id}`,
}, },
propertyUniformMapping: { propertyUniformMapping: {
topCenter: 'rotatedRectangleTopCenters', topCenter: `rotatedRectangleTopCenters${_id}`,
bottomCenter: 'rotatedRectangleBottomCenters', bottomCenter: `rotatedRectangleBottomCenters${_id}`,
width: 'rotatedRectangleWidths', width: `rotatedRectangleWidths${_id}`,
}, },
uniformCountMacroName: 'ROTATED_RECTANGLE_COUNT', uniformCountMacroName: `ROTATED_RECTANGLE_COUNT${_id}`,
shaderCombinationSteps: [0, 1, 2, 3, 8, 16], shaderCombinationSteps: [0, 1, 2, 3, 8, 16],
empty: new RotatedRectangle(vec2.create(), vec2.create(), 0), empty: new RotatedRectangle(vec2.create(), vec2.create(), 0),
}; };
/** /**
* It is just an estimate by calculating a bounding circle * This is only an estimate, computed from a bounding circle.
* @param target * @param target
*/ */
public minDistance(target: vec2): number { public minDistance(target: vec2): number {
@ -91,5 +93,7 @@ export const RotatedRectangleFactory = (
} }
} }
_id++;
return RotatedRectangle; return RotatedRectangle;
}; };