error when using parameter in if expression

4 views
Skip to first unread message

Angelo Tartanian

unread,
Mar 19, 2021, 11:42:48 AM3/19/21
to Curv

Hi all,

I’m a new user of Curv. Loving the pure functional direction and the SDF basis for geometry.

The following code is giving me trouble. I expect the use_chamfer parameter to allow me to select between two different boolean functions, but instead I get an error. If I replace

fun = if use_chamfer chamfer(bevel_distance).union else smooth(.02).union;

with

fun = if true chamfer(bevel_distance).union else smooth(.02).union;

the code compiles.

Here’s the full code:

let
    cross = {bevel_distance = .02, use_chamfer = false} ->
        let
            cross_bar = cylinder({d: 1, h:3});
            cross_bars = [
                cross_bar,
                cross_bar >> rotate({angle: 90*deg, axis:[1, 0, 0]}),
                cross_bar >> rotate({angle: 90*deg, axis:[0,1,0]})
            ];
            fun = if use_chamfer chamfer(bevel_distance).union else smooth(.02).union;
        in
            cross_bars.[0] >> into fun [cross_bars.[1]] >> into fun [cross_bars.[2]];
in
    parametric
        'bevel distance' :: slider[.01,1] = .02;
        'use chamfer' :: checkbox = false;
    in
        cross {bevel_distance:'bevel distance', use_chamfer:'use chamfer'}


Doug Moen

unread,
Mar 19, 2021, 1:59:12 PM3/19/21
to Curv
You have encountered a limitation of Curv's GPU compiler. I don't have a workaround. Right now, checkbox parameters are only really useful for making conditional *numeric* expressions. Conditional functions and conditional shapes don't work yet, if the condition contains a parameter declared using 'parametric'.

The root cause relates to the way that 'parametric' shapes are compiled into GLSL shader code (which runs on the GPU). The 'use chamfer' checkbox is a boolean parameter that is updated every frame, and the shader checks the value of this parameter in each frame. So the definition
    fun = if use_chamfer chamfer(bevel_distance).union else smooth(.02).union
must be evaluated inside the shader. But this is an if expression that returns a function value, and in GLSL, there is no concept of a 'function value' or 'function pointer'. So there's no direct translation for this if expression in GLSL, and that is the problem that the GPU compiler has run into. Instead of fixing this problem by transforming the code, or producing a clear error message, it is getting confused and giving an opaque error message.

The planned fix is going to be a new shape compiler that compiles these expressions by rearranging the surrounding code. It requires a rewrite of the GPU compiler because I need a better IR (intermediate representation). I'm working on a roadmap; my aspiration is to have this working "sometime this year".

Doug Moen
--
You received this message because you are subscribed to the Google Groups "Curv" group.
To unsubscribe from this group and stop receiving emails from it, send an email to curv+uns...@googlegroups.com.

Angelo Tartanian

unread,
Mar 19, 2021, 4:16:50 PM3/19/21
to Curv

hmmm ok. so a workaround for this would be to create a single function that switched between the two methods inside the function i guess.

like this

let
    chamfer_min[a, b, r] =
        let e = max[r - abs(a - b), 0];
        in min[a, b] - e*.5;

    mychamfersmoothunion = r -> chamfer_int -> smooth_int -> [s1,s2] ->
                    make_shape {
                        dist p =
                            let a = s1.dist p;
                                b = s2.dist p;
                            in chamfer_min[a, b, r] * chamfer_int + smooth_min[a, b, r] * smooth_int;
                        colour p =
                            let d1 = s1.dist p;
                                d2 = s2.dist p;
                            in if (d2 <= 0 || d2 <= d1) s2.colour p else s1.colour p;
                        bbox = [ min[s1.bbox@MIN, s2.bbox@MIN],
                                 max[s1.bbox@MAX, s2.bbox@MAX] ];
                        is_2d = s1.is_2d && s2.is_2d;
                        is_3d = s1.is_3d && s2.is_3d;
                    };

    cross = {bevel_distance = .02, use_chamfer = false} ->
        let
            cross_bar = cylinder({d: 1, h:3});
            cross_bars = [
                cross_bar,
                cross_bar >> rotate({angle: 90*deg, axis:[1, 0, 0]}),
                cross_bar >> rotate({angle: 90*deg, axis:[0,1,0]})
            ];

            use_chamfer_int = (bit use_chamfer);
            use_smooth_int = (bit (not use_chamfer));

             myunion = mychamfersmoothunion bevel_distance use_chamfer_int use_smooth_int;
        in
            cross_bars.[0] >> into myunion [cross_bars.[1]] >> into myunion [cross_bars.[2]];
in
    parametric
        'bevel distance' :: slider[.01,1] = .02;
        'use chamfer' :: checkbox = false;
    in
        cross {bevel_distance:'bevel distance', use_chamfer:'use chamfer'}
Reply all
Reply to author
Forward
0 new messages