libfive / libfive

Infrastructure for solid modeling
https://libfive.com
1.43k stars 152 forks source link

Standard format for "transform into then back out of"? (for transforming transformation reference points) #121

Open omgitsraven opened 6 years ago

omgitsraven commented 6 years ago

When I was implementing the twirl-n functions, I was thinking about how there should probably be some way of generalizing the fact that basically every function involving a shape-remap is probably going to involve a move in and out of a center, and three variants to change which axis it operates on...

My first thought was to write twirl-x normally, and then make twirl-y and twirl-z both call it only wrapped with a pair of (shape-remap (shape x y z) z y x) (or y x z) calls — this nearly worked, but it meant that the value passed to center would be aligned along the wrong axis too.

However, if there were something like sequence but that could take shape-remap calls (including their responses), and ideally also a variant of that that would call the opposite of each function in reverse order after reaching the end, then this would be much easier to work around; all of the other-axis variants in the library could be replaced with something like

(define twirl-y shape a r center
  (sequence-inout
    (twirl-x shape a r)
    (shape-remap (x y z) y x z)
    (move center)
  )
)

To be honest I'm not completely sure how right that example is -- I'm not sure I've got the order of operations right. But the principle should be clear, at least? This would also let people more easily apply arbitrary rotations to the transformation, instead of having to manually rotate the shape the other way before being transformed, and back afterward.

This isn't exactly urgent since it's just a convenience thing, but it seems like something that could greatly simplify working with (and implementing) transformations if it were possible. shape-remaps definitely suffer in terms of usability because they can't really be nested the way everything else can...

mkeeter commented 6 years ago

Thanks for the discussion!

@omgitsraven , I've added an alternate form of remap-shape, so now you can do (remap-shape shape (x y z) y x z), instead of (remap-shape (shape x y z) y x z).

It's a small difference, but means that remap-shape is now sequenceable:

(sequence
  (sphere 1)
  (remap-shape (x y z) (/ x 2) y z))

Does that accomplish what you're looking for?

@tir-kaval , feel free to open up a PR to do generalized-axis versions of functions.

I think that for generating -y and -z variants, @omgitsraven 's plan is a bit more efficient: we theoretically eliminate arithmetic identities, but with floating-point calculations, I can't be sure that axially-aligned generalized-axis calls would collapse down (i.e. we could end up with 0.99999999997*x + 1e-12*y - 1e-15*z, rather than just x).

omgitsraven commented 6 years ago

FYI, I've continued this train of thought here: https://github.com/libfive/libfive/pull/123

omgitsraven commented 6 years ago

OK, so I think I've finally realised that the proper approach for what I was trying to do is actually something like

(define (myscale shape)
  (remap-shape shape (x y z) (/ x 2) y z))
)
(myscale (sphere 1))

where I'd been trying to write it as something like

(define (myscale x y z)
  (/ x 2) y z
)
(remap-shape shape (x y z) (myscale x y z))

So my question now isn't urgent, since I do have a working way to deal with it, but just out of curiosity, I am still curious—exactly how does remap-shape work? Is it another macro like sequence? It doesn't look like anything else I've seen in scheme—where does the (x y z) being passed as its second argument come from? And is there a way to apply the results of another function as its last three arguments (like with values-from or something)?

mkeeter commented 6 years ago

Documentation is happening!

Right now, there's not a way to bring in another function, but it may be useful to have a vector form of remap-shape, e.g. (remap-shape shape (x y z) #[(/ x 2) y z]), since then it's easy to call other functions and use their results.