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
}