discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Z behavior of surface() with invert=true

JB
Jordan Brown
Sun, Aug 9, 2020 3:22 AM

Does anybody know what the Z behavior is of surface() with invert=true?

For my sample PNG, it seems to yield an object that ranges from about Z
= -77 to about Z = -20.  Huh what?

Does anybody know what the Z behavior is of surface() with invert=true? For my sample PNG, it seems to yield an object that ranges from about Z = -77 to about Z = -20.  Huh what?
JB
Jordan Brown
Sun, Aug 9, 2020 5:29 AM

On 8/8/2020 8:22 PM, Jordan Brown wrote:

Does anybody know what the Z behavior is of surface() with invert=true?

For my sample PNG, it seems to yield an object that ranges from about
Z = -77 to about Z = -20.  Huh what?

Somebody on the IRC channel helped me with this.  ("InPhase", but I
don't know who that is.)

With invert=false you get an object that ranges from Z=0 to Z=<max bright>, where 100 is pure white.
It appears that with invert=false you get an object that ranges from Z =
-max to Z = -min.  That is, it does not reach all the down to -100 if
you have no pure white, and does not reach all the way up to zero if you
have no pure black.

I'm going to experiment some more.

On 8/8/2020 8:22 PM, Jordan Brown wrote: > Does anybody know what the Z behavior is of surface() with invert=true? > > For my sample PNG, it seems to yield an object that ranges from about > Z = -77 to about Z = -20.  Huh what? Somebody on the IRC channel helped me with this.  ("InPhase", but I don't know who that is.) With invert=false you get an object that ranges from Z=0 to Z=<max bright>, where 100 is pure white. It appears that with invert=false you get an object that ranges from Z = -max to Z = -min.  That is, it does not reach all the down to -100 if you have no pure white, and does not reach all the way up to zero if you have no pure black. I'm going to experiment some more.
JB
Jordan Brown
Sun, Aug 9, 2020 10:38 PM

On 8/8/2020 10:29 PM, Jordan Brown wrote:

With invert=false you get an object that ranges from Z=0 to Z=<max bright>, where 100 is pure white.
It appears that with invert=false you get an object that ranges from Z
= -max to Z = -min.  That is, it does not reach all the down to -100
if you have no pure white, and does not reach all the way up to zero
if you have no pure black.

I've pretty much confirmed this behavior using constructed images with
specific values.


I'm playing with making lithophanes
https://en.wikipedia.org/wiki/Lithophane, where I want to precisely
control the minimum thickness, and want to be able to adjust the
thickness for the particular image.

Note that invert=false is always based at zero, while invert=true has
its limits based on the actual data.  Neither of them is right for a
lithophane.  You don't want the brightest spot to have zero thickness. 
You might (or might not) want the thickness to be directly related to
the input data.  You might want the thickness to be "absolute", that 0%
bright is a particular thickness and 100% bright is a particular
thickness, or you might want the brightest spot to have a minimum
thickness and the darkest spot to have a particular maximum thickness.

Some of that you can get by differencing or unioning.  However, photos
are very high-complexity objects, and CGAL render times can be
unreasonable as soon as you bring in a boolean operation.  (One that I
just did, for a 300x225 image, took 26 minutes to render.)

Some you can get with resize(), since it will take whatever the current
dimension is, and scale to reach a different dimension.  Thus if you
have data that spans 53 units, you can use resize to make it span 100
units, and you don't have to know the original range.

It is tempting to suggest a few additional features for surface().  I
might even be up for implementing them :-).

Some ideas, roughly in the order that I thought of them:

  • Z clipping.  Let the caller specify the minimum and maximum Z.  The
    minimum Z sets the base of the object (which might add more to the
    "natural" object, or might clip some off).  The maximum Z sets the
    top of the object (which might clip some off).  Using this feature
    would require that you know your particular image, but sometimes
    that's OK.
  • Z "margin".  I'm not sure exactly how to describe this.  It's
    unpleasantly asymmetrical, but in a lot of ways it's exactly what I
    want.  The idea would be that you could specify a number of units to
    add to the bottom of the range.  If your data runs from 20 to 77,
    specifying a margin of 5 would yield an object that is solid for its
    first 5 units, then has the minimum data point, and so on, up to a
    total height of 82.
  • Z scaling.  Let the caller specify a desired dynamic range.  Scale
    the data so that its minimum and maximum match that dynamic range. 
    (You can do this with resize(), but it might be more natural and
    faster here.)
  • Z translation.  If you're using the minimum and maximum from the
    data, translate one of those to a specified Z value (or just to the
    XY plane).  Let the program say "whatever the minimum value is, make
    it be Z=0".  (Or maybe maximum?)  This might be a variation on the Z
    scaling above.
  • Gamma correction.  All of those (and, for that matter, invert) are
    really just special cases of gamma correction - mapping an input
    brightness to an output brightness.  Maybe have a
    gamma=[v,v,v,v,...] that would supply points in a gamma correction
    curve; have the import operation map according to that curve.
  • X and Y clipping.  Only import the specified rectangle.
  • X and Y scaling.  Scale the image to the specified dimensions. 
    (Again, you can do this with resize.)
  • On a totally different subject:  allow input from an array, instead
    of from a file.  That would make it much easier to create sample
    cases, and might be useful as a replacement for simple variations on
    polyhedron().

All of these are things that you could do with an image manipulation
tool.  On the other hand, few image manipulation tools let you have
scripts that will do the same operations, over and over again, and these
are all "easy" transformations as you're processing the data.  It would
be nice to have a script that controls the entire pipeline, from the
image off the camera to the STL.

Many of these things could be done with OpenSCAD post-processing, but
sometimes it's unobvious and sometimes it's hard on CGAL.  Some are hard
to do because you don't necessarily know what the input data looks
like.  You can scale the dynamic range with resize(), but you still
don't know what its Z coordinate is.  (Hmm.  Maybe a "reposition"
operation that is like resize in that it looks at the actual object, but
it does whatever translate is required to move the minimum coordinate to
a specified coordinate.  Most general in that line would be an operation
that scales and translates an object to fit a specified bounding box.)

Any thoughts?

On 8/8/2020 10:29 PM, Jordan Brown wrote: > With invert=false you get an object that ranges from Z=0 to Z=<max > bright>, where 100 is pure white. > It appears that with invert=false you get an object that ranges from Z > = -max to Z = -min.  That is, it does not reach all the down to -100 > if you have no pure white, and does not reach all the way up to zero > if you have no pure black. I've pretty much confirmed this behavior using constructed images with specific values. --- I'm playing with making lithophanes <https://en.wikipedia.org/wiki/Lithophane>, where I want to precisely control the minimum thickness, and want to be able to adjust the thickness for the particular image. Note that invert=false is always based at zero, while invert=true has its limits based on the actual data.  Neither of them is right for a lithophane.  You don't want the brightest spot to have zero thickness.  You might (or might not) want the thickness to be directly related to the input data.  You might want the thickness to be "absolute", that 0% bright is a particular thickness and 100% bright is a particular thickness, or you might want the brightest spot to have a *minimum* thickness and the darkest spot to have a particular *maximum* thickness. Some of that you can get by differencing or unioning.  However, photos are very high-complexity objects, and CGAL render times can be unreasonable as soon as you bring in a boolean operation.  (One that I just did, for a 300x225 image, took 26 minutes to render.) Some you can get with resize(), since it will take whatever the current dimension is, and scale to reach a different dimension.  Thus if you have data that spans 53 units, you can use resize to make it span 100 units, and you don't have to know the original range. It is tempting to suggest a few additional features for surface().  I might even be up for implementing them :-). Some ideas, roughly in the order that I thought of them: * Z clipping.  Let the caller specify the minimum and maximum Z.  The minimum Z sets the base of the object (which might add more to the "natural" object, or might clip some off).  The maximum Z sets the top of the object (which might clip some off).  Using this feature would require that you know your particular image, but sometimes that's OK. * Z "margin".  I'm not sure exactly how to describe this.  It's unpleasantly asymmetrical, but in a lot of ways it's exactly what I want.  The idea would be that you could specify a number of units to add to the bottom of the range.  If your data runs from 20 to 77, specifying a margin of 5 would yield an object that is solid for its first 5 units, then has the minimum data point, and so on, up to a total height of 82. * Z scaling.  Let the caller specify a desired dynamic range.  Scale the data so that its minimum and maximum match that dynamic range.  (You can do this with resize(), but it might be more natural and faster here.) * Z translation.  If you're using the minimum and maximum from the data, translate one of those to a specified Z value (or just to the XY plane).  Let the program say "whatever the minimum value is, make it be Z=0".  (Or maybe maximum?)  This might be a variation on the Z scaling above. * Gamma correction.  All of those (and, for that matter, invert) are really just special cases of gamma correction - mapping an input brightness to an output brightness.  Maybe have a gamma=[v,v,v,v,...] that would supply points in a gamma correction curve; have the import operation map according to that curve. * X and Y clipping.  Only import the specified rectangle. * X and Y scaling.  Scale the image to the specified dimensions.  (Again, you can do this with resize.) * On a totally different subject:  allow input from an array, instead of from a file.  That would make it much easier to create sample cases, and might be useful as a replacement for simple variations on polyhedron(). All of these are things that you could do with an image manipulation tool.  On the other hand, few image manipulation tools let you have scripts that will do the same operations, over and over again, and these are all "easy" transformations as you're processing the data.  It would be nice to have a script that controls the entire pipeline, from the image off the camera to the STL. Many of these things could be done with OpenSCAD post-processing, but sometimes it's unobvious and sometimes it's hard on CGAL.  Some are hard to do because you don't necessarily know what the input data looks like.  You can scale the dynamic range with resize(), but you still don't know what its Z coordinate is.  (Hmm.  Maybe a "reposition" operation that is like resize in that it looks at the actual object, but it does whatever translate is required to move the minimum coordinate to a specified coordinate.  Most general in that line would be an operation that scales and translates an object to fit a specified bounding box.) Any thoughts?