Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

Request: Splines? #136

Open
lf94 opened this issue Sep 10, 2021 · 12 comments
Open

Request: Splines? #136

lf94 opened this issue Sep 10, 2021 · 12 comments

Comments

@lf94
Copy link
Contributor

lf94 commented Sep 10, 2021

Is your feature request related to a problem? Please describe.

I'd like to be able to specify a spline. Right now I'm trying to create an ergonomic controller, and I'm hitting limitations in CadQuery, which have led me here to Curv.

Describe the solution you'd like

Add a new function: spline list_of_points list_of_tangents. That's it!

Describe alternatives you've considered

None

Additional context

It may be necessary to add other sketching functions like hLine, vLine, ellipse, etc, like from CadQuery, to create enclosed shapes. https://cadquery.readthedocs.io/en/latest/apireference.html#d-operations

This is the design so far but it's lacking, as I want everything to be smooth:

1631231666

In the meantime I've been recreating a soroban design in curv to compare it to cadquery and I'm really loving what I'm seeing!

@A-G-D
Copy link
Contributor

A-G-D commented Sep 10, 2021

I'm curious what particular limitations of cadquery are you facing?

@doug-moen
Copy link
Member

Splines are possible. Exactly duplicating the CadQuery API is not possible, because CadQuery and Curv use quite different internal representations. Curv is a volumetric (V-rep) solid-modelling tool, meaning that the fundamental primitives are solid objects. CadQuery uses boundary representation (B-rep), meaning that the primitives describe the boundaries of solid objects. So it changes the set of primitives that are available, and it changes the way you design a model using those primitives.

We already have an ellipse primitive. Try curv -x 'ellipse[2,1]'

The CadQuery vLine, hLine etc appear to be used for specifying the boundary of a polygon, one face at a time. (I don't know CadQuery.) If so, then the equivalent in Curv is polygon, where you specify all of the vertices of a polygon at once. On the other hand, if vLine, hLine etc are stroked line primitives, where the strokes have thickness (like in SVG), then the corresponding 2D primitives are stroke and polyline.

The original paper describing the math behind Curv (signed distance fields or SDF) describes a spline primitive. See docs/papers in the Curv repo. But I never learned the math behind splines, so I've just never implemented it. Most of the Curv primitives are copied from code written by other SDF researchers. So it may just be a matter of finding SDF spline code on the internet and porting it to Curv. Sources I've used in the past include https://shadertoy.com and https://iquilezles.org

@lf94
Copy link
Contributor Author

lf94 commented Sep 10, 2021

Thank you very much @doug-moen !

Luckily I have looked into the math behind splines to implement my own spline in OpenSCAD:

// P = (1−t)³P1 + 3(1−t)²tP2 +3(1−t)t²P3 + t³P4
function bezier4(points) =
let (s = 1.0 / $fn)
[for(t = 0, i = 0; i < $fn + 1; t = t + s, i = i + 1)
       (pow(1 - t, 3)             * points[0])
+  (3 * pow(1 - t, 2) * pow(t, 1) * points[1])
+  (3 * pow(1 - t, 1) * pow(t, 2) * points[2])
+  (                    pow(t, 3) * points[3])
];

Well, "spline", it's for a 4-point bezier curve.

I'm curious, how would you model something like this (an ergonomic controller arm)?

I'm curious what particular limitations of cadquery are you facing?

In particular creating complex curved surfaces is not CadQuery's forté. While I can get pretty far using splines, arcs, and fillet, it gets increasingly hard for CadQuery to accept what I'm asking of it (it will simply say "cannot do B-rep operation" or something like that).

After literally playing with Curv for 1 hour last night and learning just from looking at examples and the docs I'm very impressed with how easy it was to pick up. It may have to do with how I have experience with functional PLs and OpenSCAD though (the latter I doubt because I feel the mental model is different compared to it).

@doug-moen
Copy link
Member

OpenSCAD uses boundary representation, which is different from Curv. Your OpenSCAD bezier4 function converts a spline into a collection of line segments, which you could feed into the Curv polygon or polyline function. But this is a polygonal approximation to a curved line, instead of directly representing a curved line (which is what you really want). Curv will encounter performance problems during preview if you subdivide the spline too much (don't try to feed 1000 vertexes to polygon, it will be too slow). Another approach is needed. This is the point where it is helpful to understand how to write SDF distance functions.

Here's the code for polygon, from lib/curv/std.curv in the repo:

// Finite polygon. May be nonconvex. Edges may cross, and the polygon may
// self intersect. Uses the "crossing number" method to determine which
// points are inside/outside the polygon. It doesn't matter if the vertexes
// are listed in clockwise or counterclockwise order.
// (http://geomalgorithms.com/a03-_inclusion.html)
// Exact distance field.
// From https://www.shadertoy.com/view/wdBXRW
// which is Copyright 2019 Inigo Quilez (The MIT Licence)
polygon v = make_shape {
  dist p =
    do
      local p = p@[X,Y];
      local num = count v;
      local d = dot[p-v@0, p-v@0];
      local s = 1;
      local j = num-1;
      for (i in 0..<num) (
        local e = v@j - v@i;
        local w = p - v@i;
        local b = w - e*clamp[dot[w,e]/dot[e,e], 0, 1];
        d := min[d, dot[b,b]];
        local cond = [p@Y >= v@i@Y, p@Y < v@j@Y, e@X*w@Y > e@Y*w@X];
        if (and cond || and(not cond)) s := -s;
        j := i;
      );
    in s * sqrt d;
  bbox = [[...min v,0], [...max v,0]];
  is_2d = true;
};

The dist function computes the distance from an arbitrary point [x,y] to the closest point on the boundary of the polygon. The result is positive if [x,y] is outside the polygon. It is 0 if the point is on the boundary. It is negative if [x,y] is inside the polygon. There are lots of tutorials explaining the SDF representation, here's the one I wrote: https://github.com/curv3d/curv/blob/master/docs/Theory.rst

The polygon code is magic, I didn't invent it, I just copied it from shadertoy.com and transliterated the code into Curv. The spline code needed for Curv will be magic in a similar way. I hope there is some SDF spline code on the internet that can be adopted.

@lf94
Copy link
Contributor Author

lf94 commented Sep 10, 2021

Excellent, I will look for an SDF spline then! Thank you for all the excellent help Doug. Your project is crazy awesome.

Edit: I'll leave this issue open until someone or me has implemented it.

@theohonohan
Copy link

Hi,

I'm not sure if this is the right place to mention this. I had a look at the current code for sweeping along Bezier curves. I am also aware of a 2D analytic SDF for quadratic Beziers: https://www.shadertoy.com/view/MlKcDD which seems like it could be mentioned here.

@lf94
Copy link
Contributor Author

lf94 commented Jan 27, 2023

Thank @theohonohan - I've tried such SDFs but they don't work well for the reason of needing to make a closed shape.

@lf94
Copy link
Contributor Author

lf94 commented Jan 27, 2023

It seems a few smart people have come along and figured out some potential SDFs for this! https://www.shadertoy.com/view/3dtBR4

@theohonohan
Copy link

@doug-moen posted some code which referred to Inigo Quilez. He founded ShaderToy and his site is a good place to start:
https://iquilezles.org/articles/distfunctions/
https://iquilezles.org/articles/distfunctions2d/

I am not sure what you mean by "the reason of needing to make a closed shape". It's hard not to end up with a closed contour when dealing with SDFs.
See https://iquilezles.org/articles/interiordistance/

@lf94
Copy link
Contributor Author

lf94 commented Jan 27, 2023

@theohonohan you can't just arbitrarily combine SDFs. There are "proper SDFs" that are necessary for further manipulations. An example is the offset manipulation. If you don't have a proper SDF, this will fail.

That example is not using bezier curves. I have studied those exact materials over a year ago and they were not sufficient to create proper SDFs for arbitrary closed shapes.

@theohonohan
Copy link

RIght, the third link does discuss the difficulty of making a valid SDF. But the link https://www.shadertoy.com/view/3dtBR4 which you posted just uses min() to intersect SDFs, which is what gave me the idea that you were OK with approximate results.

The problem is not that the resulting shape isn't "closed", though.

@doug-moen
Copy link
Member

Here's another one. 3 Bezier splines joined into a closed 2D shape.
https://www.shadertoy.com/view/llyXDV

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants