Comments (6)
Yup, these issues are definitely helping me decide where to start on documentation π
Best practices aren't really nailed down yet, but if I'm designing with the standard library, lambda-shape
will be fairly rare β I'll only use it when I need a shape that isn't already available.
All of the standard transforms use remap-shape
, which works as follows:
Let's say you have a shape, which is conceptually a function f(x, y, z)
. remap-shape
changes the function into f'(x', y', z')
, taking the function and the modified coordinates x'
, y'
, z'
, then returning f'
.
For example, move
can be defined as
(define-public (move shape delta)
(remap-shape (shape x y z)
(- x (.x delta))
(- y (.y delta))
(- z (.z delta))))
This says that given a function shape(x, y, z)
, return a new function that actually calls shape(x - dx, y - dy, z - dz)
instead.
This is a higher-order function, which addresses your second question:
None of these functions know anything about x
, y
, or z
β they're just placeholders, arguments to the function. All the function knows is how to go from x
to x'
(in this case, x - dx
), and so on. This is how move
(and all of the transforms in the standard library) can manipulate coordinates systems without explicitly knowing about them.
from libfive.
Thank you for your patience. I don't know how I wasn't seeing that lambda-shape and remap-shape both return a shape, and so the transformation functions return shapes too. Fingers crossed, I think this might finally be clicking ;)
from libfive.
This is a bit subtle, but I think I can explain what's going on β it's a difference in how the standard library handles the shape coordinate systems.
If you look at how the standard library function move
is called, notice that the only use of the remapped x = abs x
is in the call to mybox
. move
treats its first argument as an opaque shape, so there's no way for it to know about the remapping of x
(which is as designed).
(let ((x (abs x)))
(move
(mybox x y z 0.2 0.2 0.2)
#[0.3 0 -0.6])
)
Compare that to how you call translate
:
(let ((x (abs x)))
(translate x y z 0.3 0 0.6 (lambda (x y z)
(mybox x y z 0.2 0.2 0.2)
)))
The abs x
that's passed into translate
is then passed onto the continuation that you provide, so the translation also operates on the remapped space.
If you want to use the standard library functions, the general coordinate-remapping function is called remap-shape
. Using it in this example, we could do something like this:
(let ((b (lambda-shape (x y z)
(move (mybox x y z 0.2 0.2 0.2)
#[0.3 0 -0.6]))))
(remap-shape (b x y z) (abs x) y z))
Or, using even more of the standard library,
(let ((b (move (box #[-0.2 -0.2 -0.2] #[0.2 0.2 0.2])
#[0.3 0 -0.6])))
(remap-shape (b x y z) (abs x) y z))
Taking it one step further, this is actually how move
is implemented, so we could do the move + reflection in a single remap-shape
:
(let ((b (box #[-0.2 -0.2 -0.2] #[0.2 0.2 0.2])))
(remap-shape (b x y z)
(- (abs x) 0.3)
y
(+ z 0.6)))
Does this make sense?
In general, shapes will work nicely with the standard library if they're of the form
(define (my-shape a b c)
...
(lambda-shape (x y z)
...something with x, y, z, a, b, c
)
)
because that encapsulates the coordinate system, which is what the stdlib expects.
You should be cautious if you see explicit x y z
terms that aren't near one of the shape
functions (lambda-shape
, define-shape
, or remap-shape
), because it's a sign that you're breaking this encapsulation.
from libfive.
OK -- consider me learning about remap-shape
here as a dozen self-upvotes for my other issue about writing a fuller introduction at some point ;) (Shouldn't it at least be in the shape reference or something?)
I'm taking it from this example that generally, scenes should be broken into as many lambda-shape
s as possible, rather than putting all the shapes inside a single one joined by min
like I've been doing? Is this an optimization thing?
If it isn't too much trouble to ask -- how are functions like move
able to get ahold of the x,y,z argument values outside of the let
that I wrapped them in? I can see that I'm not passing them to the move
call, but if that's the case, how is it having any effect on them at all, as far as the things inside are concerned? (And why does it work differently when remap-shape
does it, compared to when let
does it?) I'm able to do what I need with remap-shape
, I'm just curious to have a better understanding of exactly how the stuff I'm writing behavesβ¦
from libfive.
I'm sorry to keep pestering you with questions about this, but:
The way I used to do things, I had (for example) a function IDOLheadtilt
:
(define (IDOLheadtilt x y z then)
(translate x y z 0.085 0 0 (lambda (x y z)
(rot3dY x y z 0.1 (lambda (x y z)
(then x y z)
)) ))
)
;i.e.
(lambda-shape (x y z)
(IDOLheadtilt x y z (lambda (x y z)
(mytorus x y z 0.55 0.06)
))
)
so that I could apply the same transformation to various elements in various places, without having to 'nest' them (because they needed to be blended with other shapes which DIDN'T have that transformation applied to them, then finally be combined after that).
How can I rewrite that to use the native transform functions? The closest I can intuit is
(define (IDOLheadtilt shape)
(remap-shape (shape x y z)
(sequence
(rotate-y 0.1)
(move #[0.085 0 0])
)
)
)
but that just gives me source expression failed to match any pattern in form (remap-shape...
Alternately: if you have any good recommendations for what I should be reading to get familiar with slightly more abstract uses of scheme, I'll take that as an answer :) Everything I've been able to find on my own is either too basic to cover stuff like this, or too complex for me to make sense of as a beginner... I'm right in that frustrating middle ground of having trouble closing that gap, unfortunately.
from libfive.
Using the stdlib functions to do this transformation would look like the following:
(define (IDOLheadtilt shape)
(sequence shape
(rotate-y 0.1)
(move #[0.085 0 0])
)
)
(IDOLheadtilt (lambda-shape (x y z) (mytorus x y z 1 0.5)))
If you want to move farther in the direction of the standard library, you would also factor out the x y z
from mytorus
by returning a lambda-shape
:
(define (mytorus ra rb)
(lambda-shape (x y z)
(-
(len2d
(-
(len2d x z)
ra
)
y
)
rb
)
)
)
Then, you could invoke IDOLheadtilt
as
(IDOLheadtilt (mytorus 1 0.5))
This is what I was referring to as "encapsulation" earlier β the explicit x y z
arguments are gone, and instead we're passing around shapes (where a "shape" is a compiled three-argument function, e.g. what you get from lambda-shape
).
SICP is the classic Scheme book, but it's a pretty substantial read; the section on higher-order functions is most relevant here.
from libfive.
Related Issues (20)
- Where to look for complete Guile docs HOT 1
- Help understanding why libfive_tree_render_mesh would return nullptr HOT 7
- Output final optimized tree as JSON? HOT 1
- nodejs bindings HOT 4
- frac function? Edit: Solved! Use mod 1! HOT 4
- I cant see a Window when opening Libfive Studio HOT 2
- Stream meshing immediately to STL? HOT 2
- Compiling on Fedore FC37 HOT 1
- Problem to compile libfive on FC37 HOT 8
- Embed Libfive in an application - general SDF Question HOT 4
- Feature request: Separate bounds/resolutions for different objects in the same scene? HOT 2
- Return multiple shapes without using union, in Python? HOT 4
- New url for inspekt3d HOT 2
- Double free or corruption error when trying to use libfive HOT 3
- Expose normals in libfive_mesh HOT 2
- Does Mesh::render consume the libfive_tree? HOT 3
- Unioning nothing at the top level causes things to mesh very slow HOT 6
- koffi and ffi-napi both segfault when meshing non-trivial models HOT 6
- Publish new release and set soversion for building shared libraries
- I've packaged this for OpenSuse
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from libfive.