builtin-programs/display/curve.folk
# Bezier implementation from https://www.shadertoy.com/view/XdVBWd
Wish the GPU compiles function "bboxBezier" {{vec2 p0 vec2 p1 vec2 p2 vec2 p3} vec4 {
// Exact BBox to a quadratic bezier
// extremes
vec2 mi = min(p0,p3);
vec2 ma = max(p0,p3);
vec2 k0 = -1.0*p0 + 1.0*p1;
vec2 k1 = 1.0*p0 - 2.0*p1 + 1.0*p2;
vec2 k2 = -1.0*p0 + 3.0*p1 - 3.0*p2 + 1.0*p3;
vec2 h = k1*k1 - k0*k2;
if( h.x>0.0 )
{
h.x = sqrt(h.x);
//float t = (-k1.x - h.x)/k2.x;
float t = k0.x/(-k1.x-h.x);
if( t>0.0 && t<1.0 )
{
float s = 1.0-t;
float q = s*s*s*p0.x + 3.0*s*s*t*p1.x + 3.0*s*t*t*p2.x + t*t*t*p3.x;
mi.x = min(mi.x,q);
ma.x = max(ma.x,q);
}
//t = (-k1.x + h.x)/k2.x;
t = k0.x/(-k1.x+h.x);
if( t>0.0 && t<1.0 )
{
float s = 1.0-t;
float q = s*s*s*p0.x + 3.0*s*s*t*p1.x + 3.0*s*t*t*p2.x + t*t*t*p3.x;
mi.x = min(mi.x,q);
ma.x = max(ma.x,q);
}
}
if( h.y>0.0)
{
h.y = sqrt(h.y);
//float t = (-k1.y - h.y)/k2.y;
float t = k0.y/(-k1.y-h.y);
if( t>0.0 && t<1.0 )
{
float s = 1.0-t;
float q = s*s*s*p0.y + 3.0*s*s*t*p1.y + 3.0*s*t*t*p2.y + t*t*t*p3.y;
mi.y = min(mi.y,q);
ma.y = max(ma.y,q);
}
//t = (-k1.y + h.y)/k2.y;
t = k0.y/(-k1.y+h.y);
if( t>0.0 && t<1.0 )
{
float s = 1.0-t;
float q = s*s*s*p0.y + 3.0*s*s*t*p1.y + 3.0*s*t*t*p2.y + t*t*t*p3.y;
mi.y = min(mi.y,q);
ma.y = max(ma.y,q);
}
}
return vec4( mi, ma );
}}
Wish the GPU compiles function sdSegmentSq {{vec2 p vec2 a vec2 b} float {
vec2 pa = p-a, ba = b-a;
float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
vec2 d = pa - ba*h;
return dot(d, d);
}}
Wish the GPU compiles function udBezier {{vec2 p0 vec2 p1 vec2 p2 vec2 p3 vec2 pos} vec2 {
const int kNum = 50;
vec2 res = vec2(1e10,0.0);
vec2 a = p0;
for( int i=1; i<kNum; i++ )
{
float t = float(i)/float(kNum-1);
float s = 1.0-t;
vec2 b = p0*s*s*s + p1*3.0*s*s*t + p2*3.0*s*t*t + p3*t*t*t;
float d = sdSegmentSq( pos, a, b );
if( d<res.x ) res = vec2(d,t);
a = b;
}
return vec2(sqrt(res.x),res.y);
}}
Wish the GPU compiles pipeline "curve" {
{ vec2 p0 vec2 p1 vec2 p2 vec2 p3 float thickness vec4 color} {
// Need to calculate the bounds of the curve
vec2 from = min(min(p0,p1),min(p2,p3));
vec2 to = max(max(p0,p1),max(p2,p3));
vec2 vertices[4] = vec2[4](
min(from, to) - thickness,
vec2(max(from.x, to.x) + thickness, min(from.y, to.y) - thickness),
vec2(min(from.x, to.x) - thickness, max(from.y, to.y) + thickness),
max(from, to) + thickness
);
return vec4(vertices[gl_VertexIndex], 0.0, 1.0);
} {fn sdSegmentSq fn udBezier} {
vec2 p = gl_FragCoord.xy;
float px = 2.0; // sharpness
float t = thickness;
float be = udBezier( p0, p1, p2, p3, p ).x;
float d = be;
vec4 col = mix( vec4(0.0), color, 1.0-smoothstep(t, t + px*1.5, d) );
// control points
//d = length(p0-p); col = mix( col, vec4(1.0, 0., 0., 1.), 1.0-smoothstep(4,4+px,d) );
//d = length(p1-p); col = mix( col, vec4(0., 1.0, 0., 1.), 1.0-smoothstep(4,4+px,d) );
//d = length(p2-p); col = mix( col, vec4(0., 0., 1.0, 1.), 1.0-smoothstep(4,4+px,d) );
//d = length(p3-p); col = mix( col, vec4(1.0), 1.0-smoothstep(4,4+px,d) );
return col;
}
}
When /someone/ wishes to draw a curve with /...options/ {
set p0 [dict get $options p0]
set p1 [dict get $options p1]
set p2 [dict get $options p2]
set p3 [dict get $options p3]
set thickness [dict get $options thickness]
set color [getColor [dict get $options color]]
set layer [dict_getdef $options layer 0]
Wish the GPU draws pipeline "curve" with arguments \
[list $p0 $p1 $p2 $p3 $thickness $color] \
layer $layer
}