Coder Social home page Coder Social logo

Comments (6)

mkeeter avatar mkeeter commented on May 18, 2024 1

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.

omgitsraven avatar omgitsraven commented on May 18, 2024 1

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.

mkeeter avatar mkeeter commented on May 18, 2024

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.

omgitsraven avatar omgitsraven commented on May 18, 2024

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-shapes 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.

omgitsraven avatar omgitsraven commented on May 18, 2024

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.

mkeeter avatar mkeeter commented on May 18, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.