Added improved non-clay text handling along with consistent origin and rotation API

This commit is contained in:
Zachary Levy
2026-04-19 18:28:42 -07:00
parent 30b72128b2
commit 7a21d6f253
12 changed files with 1157 additions and 388 deletions

View File

@@ -24,32 +24,41 @@ struct main0_in
float4 f_params [[user(locn2)]];
float4 f_params2 [[user(locn3)]];
uint f_kind_flags [[user(locn4)]];
float f_rotation [[user(locn5), flat]];
};
static inline __attribute__((always_inline))
float2 apply_rotation(thread const float2& p, thread const float& angle)
{
float cr = cos(-angle);
float sr = sin(-angle);
return float2x2(float2(cr, sr), float2(-sr, cr)) * p;
}
static inline __attribute__((always_inline))
float sdRoundedBox(thread const float2& p, thread const float2& b, thread float4& r)
{
float2 _56;
float2 _61;
if (p.x > 0.0)
{
_56 = r.xy;
_61 = r.xy;
}
else
{
_56 = r.zw;
_61 = r.zw;
}
r.x = _56.x;
r.y = _56.y;
float _73;
r.x = _61.x;
r.y = _61.y;
float _78;
if (p.y > 0.0)
{
_73 = r.x;
_78 = r.x;
}
else
{
_73 = r.y;
_78 = r.y;
}
r.x = _73;
r.x = _78;
float2 q = (abs(p) - b) + float2(r.x);
return (fast::min(fast::max(q.x, q.y), 0.0) + length(fast::max(q, float2(0.0)))) - r.x;
}
@@ -142,16 +151,23 @@ fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> tex [[textur
float4 r = float4(in.f_params.zw, in.f_params2.xy);
soft = fast::max(in.f_params2.z, 1.0);
float stroke_px = in.f_params2.w;
float2 param = in.f_local_or_uv;
float2 param_1 = b;
float4 param_2 = r;
float _453 = sdRoundedBox(param, param_1, param_2);
d = _453;
float2 p_local = in.f_local_or_uv;
if (in.f_rotation != 0.0)
{
float2 param = p_local;
float param_1 = in.f_rotation;
p_local = apply_rotation(param, param_1);
}
float2 param_2 = p_local;
float2 param_3 = b;
float4 param_4 = r;
float _491 = sdRoundedBox(param_2, param_3, param_4);
d = _491;
if ((flags & 1u) != 0u)
{
float param_3 = d;
float param_4 = stroke_px;
d = sdf_stroke(param_3, param_4);
float param_5 = d;
float param_6 = stroke_px;
d = sdf_stroke(param_5, param_6);
}
}
else
@@ -161,14 +177,14 @@ fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> tex [[textur
float radius = in.f_params.x;
soft = fast::max(in.f_params.y, 1.0);
float stroke_px_1 = in.f_params.z;
float2 param_5 = in.f_local_or_uv;
float param_6 = radius;
d = sdCircle(param_5, param_6);
float2 param_7 = in.f_local_or_uv;
float param_8 = radius;
d = sdCircle(param_7, param_8);
if ((flags & 1u) != 0u)
{
float param_7 = d;
float param_8 = stroke_px_1;
d = sdf_stroke(param_7, param_8);
float param_9 = d;
float param_10 = stroke_px_1;
d = sdf_stroke(param_9, param_10);
}
}
else
@@ -178,15 +194,22 @@ fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> tex [[textur
float2 ab = in.f_params.xy;
soft = fast::max(in.f_params.z, 1.0);
float stroke_px_2 = in.f_params.w;
float2 param_9 = in.f_local_or_uv;
float2 param_10 = ab;
float _511 = sdEllipse(param_9, param_10);
d = _511;
float2 p_local_1 = in.f_local_or_uv;
if (in.f_rotation != 0.0)
{
float2 param_11 = p_local_1;
float param_12 = in.f_rotation;
p_local_1 = apply_rotation(param_11, param_12);
}
float2 param_13 = p_local_1;
float2 param_14 = ab;
float _560 = sdEllipse(param_13, param_14);
d = _560;
if ((flags & 1u) != 0u)
{
float param_11 = d;
float param_12 = stroke_px_2;
d = sdf_stroke(param_11, param_12);
float param_15 = d;
float param_16 = stroke_px_2;
d = sdf_stroke(param_15, param_16);
}
}
else
@@ -197,10 +220,10 @@ fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> tex [[textur
float2 b_1 = in.f_params.zw;
float width = in.f_params2.x;
soft = fast::max(in.f_params2.y, 1.0);
float2 param_13 = in.f_local_or_uv;
float2 param_14 = a;
float2 param_15 = b_1;
d = sdSegment(param_13, param_14, param_15) - (width * 0.5);
float2 param_17 = in.f_local_or_uv;
float2 param_18 = a;
float2 param_19 = b_1;
d = sdSegment(param_17, param_18, param_19) - (width * 0.5);
}
else
{
@@ -218,26 +241,18 @@ fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> tex [[textur
{
angle += 6.283185482025146484375;
}
float ang_start = start_rad;
float ang_end = end_rad;
if (ang_start < 0.0)
{
ang_start += 6.283185482025146484375;
}
if (ang_end < 0.0)
{
ang_end += 6.283185482025146484375;
}
float _615;
float ang_start = mod(start_rad, 6.283185482025146484375);
float ang_end = mod(end_rad, 6.283185482025146484375);
float _654;
if (ang_end > ang_start)
{
_615 = float((angle >= ang_start) && (angle <= ang_end));
_654 = float((angle >= ang_start) && (angle <= ang_end));
}
else
{
_615 = float((angle >= ang_start) || (angle <= ang_end));
_654 = float((angle >= ang_start) || (angle <= ang_end));
}
float in_arc = _615;
float in_arc = _654;
if (abs(ang_end - ang_start) >= 6.282185077667236328125)
{
in_arc = 1.0;
@@ -262,9 +277,9 @@ fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> tex [[textur
d = (length(p) * cos(bn)) - radius_1;
if ((flags & 1u) != 0u)
{
float param_16 = d;
float param_17 = stroke_px_3;
d = sdf_stroke(param_16, param_17);
float param_20 = d;
float param_21 = stroke_px_3;
d = sdf_stroke(param_20, param_21);
}
}
}
@@ -272,9 +287,9 @@ fragment main0_out main0(main0_in in [[stage_in]], texture2d<float> tex [[textur
}
}
}
float param_18 = d;
float param_19 = soft;
float alpha = sdf_alpha(param_18, param_19);
float param_22 = d;
float param_23 = soft;
float alpha = sdf_alpha(param_22, param_23);
out.out_color = float4(in.f_color.xyz, in.f_color.w * alpha);
return out;
}

View File

@@ -15,7 +15,8 @@ struct Primitive
float4 bounds;
uint color;
uint kind_flags;
float2 _pad;
float rotation;
float _pad;
float4 params;
float4 params2;
};
@@ -25,7 +26,8 @@ struct Primitive_1
float4 bounds;
uint color;
uint kind_flags;
float2 _pad;
float rotation;
float _pad;
float4 params;
float4 params2;
};
@@ -42,6 +44,7 @@ struct main0_out
float4 f_params [[user(locn2)]];
float4 f_params2 [[user(locn3)]];
uint f_kind_flags [[user(locn4)]];
float f_rotation [[user(locn5)]];
float4 gl_Position [[position]];
};
@@ -52,7 +55,7 @@ struct main0_in
float4 v_color [[attribute(2)]];
};
vertex main0_out main0(main0_in in [[stage_in]], constant Uniforms& _12 [[buffer(0)]], const device Primitives& _70 [[buffer(1)]], uint gl_InstanceIndex [[instance_id]])
vertex main0_out main0(main0_in in [[stage_in]], constant Uniforms& _12 [[buffer(0)]], const device Primitives& _72 [[buffer(1)]], uint gl_InstanceIndex [[instance_id]])
{
main0_out out = {};
if (_12.mode == 0u)
@@ -62,17 +65,19 @@ vertex main0_out main0(main0_in in [[stage_in]], constant Uniforms& _12 [[buffer
out.f_params = float4(0.0);
out.f_params2 = float4(0.0);
out.f_kind_flags = 0u;
out.f_rotation = 0.0;
out.gl_Position = _12.projection * float4(in.v_position * _12.dpi_scale, 0.0, 1.0);
}
else
{
Primitive p;
p.bounds = _70.primitives[int(gl_InstanceIndex)].bounds;
p.color = _70.primitives[int(gl_InstanceIndex)].color;
p.kind_flags = _70.primitives[int(gl_InstanceIndex)].kind_flags;
p._pad = _70.primitives[int(gl_InstanceIndex)]._pad;
p.params = _70.primitives[int(gl_InstanceIndex)].params;
p.params2 = _70.primitives[int(gl_InstanceIndex)].params2;
p.bounds = _72.primitives[int(gl_InstanceIndex)].bounds;
p.color = _72.primitives[int(gl_InstanceIndex)].color;
p.kind_flags = _72.primitives[int(gl_InstanceIndex)].kind_flags;
p.rotation = _72.primitives[int(gl_InstanceIndex)].rotation;
p._pad = _72.primitives[int(gl_InstanceIndex)]._pad;
p.params = _72.primitives[int(gl_InstanceIndex)].params;
p.params2 = _72.primitives[int(gl_InstanceIndex)].params2;
float2 corner = in.v_position;
float2 world_pos = mix(p.bounds.xy, p.bounds.zw, corner);
float2 center = (p.bounds.xy + p.bounds.zw) * 0.5;
@@ -81,6 +86,7 @@ vertex main0_out main0(main0_in in [[stage_in]], constant Uniforms& _12 [[buffer
out.f_params = p.params;
out.f_params2 = p.params2;
out.f_kind_flags = p.kind_flags;
out.f_rotation = p.rotation;
out.gl_Position = _12.projection * float4(world_pos * _12.dpi_scale, 0.0, 1.0);
}
return out;

View File

@@ -6,6 +6,7 @@ layout(location = 1) in vec2 f_local_or_uv;
layout(location = 2) in vec4 f_params;
layout(location = 3) in vec4 f_params2;
layout(location = 4) flat in uint f_kind_flags;
layout(location = 5) flat in float f_rotation;
// --- Output ---
layout(location = 0) out vec4 out_color;
@@ -82,6 +83,15 @@ float sdf_stroke(float d, float stroke_width) {
return abs(d) - stroke_width * 0.5;
}
// Rotate a 2D point by the negative of the given angle (inverse rotation).
// Used to rotate the sampling frame opposite to the shape's rotation so that
// the SDF evaluates correctly for the rotated shape.
vec2 apply_rotation(vec2 p, float angle) {
float cr = cos(-angle);
float sr = sin(-angle);
return mat2(cr, sr, -sr, cr) * p;
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
@@ -113,11 +123,16 @@ void main() {
soft = max(f_params2.z, 1.0);
float stroke_px = f_params2.w;
d = sdRoundedBox(f_local_or_uv, b, r);
vec2 p_local = f_local_or_uv;
if (f_rotation != 0.0) {
p_local = apply_rotation(p_local, f_rotation);
}
d = sdRoundedBox(p_local, b, r);
if ((flags & 1u) != 0u) d = sdf_stroke(d, stroke_px);
}
else if (kind == 2u) {
// Circle
// Circle — rotationally symmetric, no rotation needed
float radius = f_params.x;
soft = max(f_params.y, 1.0);
float stroke_px = f_params.z;
@@ -131,11 +146,16 @@ void main() {
soft = max(f_params.z, 1.0);
float stroke_px = f_params.w;
d = sdEllipse(f_local_or_uv, ab);
vec2 p_local = f_local_or_uv;
if (f_rotation != 0.0) {
p_local = apply_rotation(p_local, f_rotation);
}
d = sdEllipse(p_local, ab);
if ((flags & 1u) != 0u) d = sdf_stroke(d, stroke_px);
}
else if (kind == 4u) {
// Segment (capsule line)
// Segment (capsule line) — no rotation (excluded)
vec2 a = f_params.xy; // already in local physical pixels
vec2 b = f_params.zw;
float width = f_params2.x;
@@ -144,7 +164,7 @@ void main() {
d = sdSegment(f_local_or_uv, a, b) - width * 0.5;
}
else if (kind == 5u) {
// Ring / Arc
// Ring / Arc — rotation handled by CPU angle offset, no shader rotation
float inner = f_params.x;
float outer = f_params.y;
float start_rad = f_params.z;
@@ -157,10 +177,8 @@ void main() {
// Angular clip
float angle = atan(f_local_or_uv.y, f_local_or_uv.x);
if (angle < 0.0) angle += 2.0 * PI;
float ang_start = start_rad;
float ang_end = end_rad;
if (ang_start < 0.0) ang_start += 2.0 * PI;
if (ang_end < 0.0) ang_end += 2.0 * PI;
float ang_start = mod(start_rad, 2.0 * PI);
float ang_end = mod(end_rad, 2.0 * PI);
float in_arc = (ang_end > ang_start)
? ((angle >= ang_start && angle <= ang_end) ? 1.0 : 0.0) : ((angle >= ang_start || angle <= ang_end) ? 1.0 : 0.0);
@@ -169,7 +187,7 @@ void main() {
d = in_arc > 0.5 ? d_ring : 1e30;
}
else if (kind == 6u) {
// Regular N-gon
// Regular N-gon — has its own rotation in params, no Primitive.rotation used
float radius = f_params.x;
float rotation = f_params.y;
float sides = f_params.z;

View File

@@ -11,6 +11,7 @@ layout(location = 1) out vec2 f_local_or_uv;
layout(location = 2) out vec4 f_params;
layout(location = 3) out vec4 f_params2;
layout(location = 4) flat out uint f_kind_flags;
layout(location = 5) flat out float f_rotation;
// ---------- Uniforms (single block — avoids spirv-cross reordering on Metal) ----------
layout(set = 1, binding = 0) uniform Uniforms {
@@ -24,7 +25,8 @@ struct Primitive {
vec4 bounds; // 0-15: min_x, min_y, max_x, max_y
uint color; // 16-19: packed u8x4 (unpack with unpackUnorm4x8)
uint kind_flags; // 20-23: kind | (flags << 8)
vec2 _pad; // 24-31: padding
float rotation; // 24-27: shader self-rotation in radians
float _pad; // 28-31: alignment padding
vec4 params; // 32-47: shape params part 1
vec4 params2; // 48-63: shape params part 2
};
@@ -42,6 +44,7 @@ void main() {
f_params = vec4(0.0);
f_params2 = vec4(0.0);
f_kind_flags = 0u;
f_rotation = 0.0;
gl_Position = projection * vec4(v_position * dpi_scale, 0.0, 1.0);
} else {
@@ -57,6 +60,7 @@ void main() {
f_params = p.params;
f_params2 = p.params2;
f_kind_flags = p.kind_flags;
f_rotation = p.rotation;
gl_Position = projection * vec4(world_pos * dpi_scale, 0.0, 1.0);
}