discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Equivalent of offset for a list of points?

AM
Adrian Mariano
Thu, Apr 14, 2022 8:02 PM

Yes, it will always work for convex shapes with positive offset.  So given
the known design in this thread, can you honestly tell me it is "easy" to
know what offset will work without creating a self-intersection?

On Thu, Apr 14, 2022 at 6:59 AM nop head nop.head@gmail.com wrote:

I think it always works for convex shapes and a positive offset.

For concave shapes, or negative offsets, I think the offset has to be less
than half the width of the thinnest internal or external features. Not easy
to compute but easy to know when you are applying it to a known design.

On Thu, 14 Apr 2022 at 00:04, Adrian Mariano avm4@cornell.edu wrote:

Yes, that's correct, assuming by "distance between vertices" you mean the
global distance pairwise between all vertices, not the consecutive
difference between them.  In other words, there's no simple way to even
predict when it will work.

On Wed, Apr 13, 2022 at 5:42 PM nop head nop.head@gmail.com wrote:

Yes I suppose it only works if the offset is small compared to the
distance between vertices.

On Wed, 13 Apr 2022 at 22:02, Adrian Mariano avm4@cornell.edu wrote:

Sanjeev:

You are producing models that are invalid.  It is possible that
downstream processing (the 3d print slicer) can handle a bogus model.
Maybe.  Maybe not.  The STL you save will have self-intersecting faces.
Depending on how big the self-intersecting bits are it could be pretty hard
to deal with downstream.  My focus is on creating valid models.  The
point of running render (f6) is to demonstrate that the model is valid.  To
do this you have to include a second object, and usually you should have it
intersect your test object.  I suggest that if you post self-intersecting
models that you warn people that they are self-intersecting.  (Note that
f6 render is usually fast when your model contains just one polyhedron.)

How much 3d printing have you done?  I had an early experience where my
model just wouldn't stick to the build plate.  I ultimately discovered that
the problem was I had used the hull of a sphere to construct the model, and
due to the choice of $fn, the model was very slightly undersized.  I don't
know what you can get away with here.  Clearly 0.1mm is is going to be a
disaster.  It seems like 0.01mm is probably ok.  Again it may depend on
what the slicer does iwth your model.  So if your flaws/bumps on the bottom
of the model are very very small it's maybe OK.  I'd rather have a model
that's actually correct, though.  You could remove the extra stuff with an
intersection or difference...except no, you can't, because the model is
invalid.

You ignored my observation that you don't include the bottom of the
container.  Since your model is invalid you can't do a union.  How will you
add the bottom?  (Also union can be slow, so your run time numbers may
need refinement.)

I examined your code a bit and see what is happening and why it's
faster.  Offset requires computing an intersection of two offset lines.
You avoid that by simply shifting the point based on a normal computed from
some adjacent points.  You can also do the same thing in 2d and it should
be even faster.  It's not going to 3d that made it fast but using this type
of approximate offset.  It may in fact be a good approximation when the
angle step size is so very small, but of course, it's only going to give a
valid result if the offset is small enough.  I would say that if you want
to pursue the fastest possible invalid model that the way to do it would be
to use such an approximate 2d offset, working by layers.  Then you can skip
the two layers on the bottom of the inside so that the base is correct, and
the whole model is a single (invalid) polyhedron that you can render to a
(self-intersecting) STL.

Nophead:

You are doing the trivial part of offset and ignoring the hard part.
You also have a formulation that to me seems overly complicated.  Solving
for the tangent points of two circles appears to be a harder problem than
the intersection of two lines.  I would formulate offset as follows:  Given
two adjacent segments on the path, shift each one in its normal direction
by the offset distance.  If the resulting shifted segments intersect, then
you clip them at the intersection point.  If they don't, you extend them
somehow (arc, linear extension, whatever).  Ok.  That's the trivial part.

The problem now is that parts of the path generated this way are
invalid.  There are extra loops.  These extra loops cannot be detected
locally, only globally.
Image 1 below shows an example where there is a bogus loop in the red
offset to the yellow path.  I show two circles as per your description.
The segment between their intersection points is part of the result that is
obtained by that method.  But that segment is not actually part of the
answer, because that whole loop at the end is not part of the offset.

The second image shows two circles giving rise to a green segment.  The
green segment is not part of the offset---only part of it is.  To find
which part you need to intersect it with another segment that can only be
found globally.  It depends on what is happening on the other side of the
path.  It can be arbitrarily far away along the path.  I don't think
there's any way to avoid running a quadratic algorithm to find all the
self-intersections that result from the naive offset.  Then you have to
somehow decide which parts are valid.  In python you can probably find all
the self-intersections in n log n time using some kind of clever data
structure, but in OpenSCAD clever data structures are impossible, so brute
force is the only option.

My method for doing this is to check whether segments are too close to
the original curve.  There is a method that is more elegant and probably
more robust where you divide the offset into its polygon components and
compute the winding number of each component.  The problem I have with this
algorithm is that it seems difficult to keep track of the point order so
that I can map the offset points back to the original points.  My use of
the offset() function is almost always in constructing a polyhedron where I
need to use this point-point relationship.  The winding number method also
doesn't work if you have a path instead of a polygon.  For the model in
this thread my fast method that uses symmetry does an offset of just part
of the polygon, so it's not a closed path, and it's important to keep track
of that mapping to link the offset paths together.

On Wed, Apr 13, 2022 at 4:01 PM nop head nop.head@gmail.com wrote:

The problem is implementation.

Simply adding the offset as the radius for each vertex and passing it
to my rounded polygon function gives this:

[image: image.png]

I.e. it is correct for convex corners but concave ones need to detect
the intersection and remove the inner loop. That isn't hard and gives this:

[image: image.png]

Passing a negative offset also seems to just work. My rounded_polygon
function uses negative radii to indicate concave corners. So now it goes
wrong at the convex corners:

[image: image.png]

The same tangent intersection test fixes that as well.

[image: image.png]

So I think I have recreated Skeinforge's offset algorithm in OpenSCAD.
I don't know how robust my quick hack is as rounded_polygon was never
designed for doing offsets.

On Wed, 13 Apr 2022 at 18:17, Sanjeev Prabhakar <
sprabhakar2006@gmail.com> wrote:

I just tried this code on my macbook air m1
It is wonderfully fast.
0.1 step angle f6 render successfully completed in 26 sec

On Wed, 13 Apr 2022, 16:41 Sanjeev Prabhakar, <
sprabhakar2006@gmail.com> wrote:

there seems to be a syntax error in my last mail

correct one is here:

include <dependencies.scad>

function
surf_offset(prism,d)=[for(i=[0:len(prism)-1])[for(j=[0:len(prism[0])-1])let(j_plus=j<len(prism[0])-1?j+1:0,p0=prism[i][j],p1=i<len(prism)-1?prism[i][j_plus]:prism[i-1][j],p2=i<len(prism)-1?prism[i+1][j]:prism[i][j_plus],v1=p1-p0,v2=p2-p0,u1=uv(v1),u2=uv(v2),
p3=cross(u1,u2)*d )p0+p3]];

stages =200;

stage_height = 1.25;

rad = 50;

f1 = 25;

f2 = 25;

phase1 = 0;

phase2 = 180;

height_depth=5;

depth1 = 20;

depth2 = 20;

thickness = 2;

bottom_thickness = 3;

myslices = 5;

angle_step=0.1;

// generate outer points

points_base = [for (i = [0:1:stages]) let(f = pow(sin(i/stages *
120),2) * 7 + 1, var = 1 , a=((sin((i/stages360f)%360) * 0.5 + 0.5) *
(var * height_depth))) [for(j = [0:angle_step:360-angle_step]) [sin(j) *
(rad+a+(pauw(sin(j *f1+phase1),0.5)*0.5+0.5)depth1i/stages+(pauw(sin(j
*f2+phase2),0.5)*0.5+0.5)depth2(1-i/stages)), cos(j) *(rad+a+(pauw(sin(j
*f1+phase1),0.5)*0.5+0.5)depth1i/stages+(pauw(sin(j
*f2+phase2),0.5)0.5+0.5)depth2(1-i/stages)),istage_height]]];

points_base_e=surf_offset(points_base,2);

swp_prism_h(points_base,points_base_e);

function pauw(x,p)=sign(x)*abs(x)^p;


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Yes, it will always work for convex shapes with positive offset. So given the known design in this thread, can you honestly tell me it is "easy" to know what offset will work without creating a self-intersection? On Thu, Apr 14, 2022 at 6:59 AM nop head <nop.head@gmail.com> wrote: > I think it always works for convex shapes and a positive offset. > > For concave shapes, or negative offsets, I think the offset has to be less > than half the width of the thinnest internal or external features. Not easy > to compute but easy to know when you are applying it to a known design. > > On Thu, 14 Apr 2022 at 00:04, Adrian Mariano <avm4@cornell.edu> wrote: > >> Yes, that's correct, assuming by "distance between vertices" you mean the >> global distance pairwise between all vertices, not the consecutive >> difference between them. In other words, there's no simple way to even >> predict when it will work. >> >> On Wed, Apr 13, 2022 at 5:42 PM nop head <nop.head@gmail.com> wrote: >> >>> Yes I suppose it only works if the offset is small compared to the >>> distance between vertices. >>> >>> On Wed, 13 Apr 2022 at 22:02, Adrian Mariano <avm4@cornell.edu> wrote: >>> >>>> Sanjeev: >>>> >>>> You are producing models that are invalid. It is possible that >>>> downstream processing (the 3d print slicer) can handle a bogus model. >>>> Maybe. Maybe not. The STL you save will have self-intersecting faces. >>>> Depending on how big the self-intersecting bits are it could be pretty hard >>>> to deal with downstream. My focus is on creating valid models. The >>>> point of running render (f6) is to demonstrate that the model is valid. To >>>> do this you have to include a second object, and usually you should have it >>>> intersect your test object. I suggest that if you post self-intersecting >>>> models that you warn people that they are self-intersecting. (Note that >>>> f6 render is usually fast when your model contains just one polyhedron.) >>>> >>>> How much 3d printing have you done? I had an early experience where my >>>> model just wouldn't stick to the build plate. I ultimately discovered that >>>> the problem was I had used the hull of a sphere to construct the model, and >>>> due to the choice of $fn, the model was very slightly undersized. I don't >>>> know what you can get away with here. Clearly 0.1mm is is going to be a >>>> disaster. It seems like 0.01mm is probably ok. Again it may depend on >>>> what the slicer does iwth your model. So if your flaws/bumps on the bottom >>>> of the model are very very small it's maybe OK. I'd rather have a model >>>> that's actually correct, though. You could remove the extra stuff with an >>>> intersection or difference...except no, you can't, because the model is >>>> invalid. >>>> >>>> You ignored my observation that you don't include the bottom of the >>>> container. Since your model is invalid you can't do a union. How will you >>>> add the bottom? (Also union can be slow, so your run time numbers may >>>> need refinement.) >>>> >>>> I examined your code a bit and see what is happening and why it's >>>> faster. Offset requires computing an intersection of two offset lines. >>>> You avoid that by simply shifting the point based on a normal computed from >>>> some adjacent points. You can also do the same thing in 2d and it should >>>> be even faster. It's not going to 3d that made it fast but using this type >>>> of approximate offset. It may in fact be a good approximation when the >>>> angle step size is so very small, but of course, it's only going to give a >>>> valid result if the offset is small enough. I would say that if you want >>>> to pursue the fastest possible invalid model that the way to do it would be >>>> to use such an approximate 2d offset, working by layers. Then you can skip >>>> the two layers on the bottom of the inside so that the base is correct, and >>>> the whole model is a single (invalid) polyhedron that you can render to a >>>> (self-intersecting) STL. >>>> >>>> Nophead: >>>> >>>> You are doing the trivial part of offset and ignoring the hard part. >>>> You also have a formulation that to me seems overly complicated. Solving >>>> for the tangent points of two circles appears to be a harder problem than >>>> the intersection of two lines. I would formulate offset as follows: Given >>>> two adjacent segments on the path, shift each one in its normal direction >>>> by the offset distance. If the resulting shifted segments intersect, then >>>> you clip them at the intersection point. If they don't, you extend them >>>> somehow (arc, linear extension, whatever). Ok. That's the trivial part. >>>> >>>> The problem now is that parts of the path generated this way are >>>> invalid. There are extra loops. These extra loops cannot be detected >>>> locally, only globally. >>>> Image 1 below shows an example where there is a bogus loop in the red >>>> offset to the yellow path. I show two circles as per your description. >>>> The segment between their intersection points is part of the result that is >>>> obtained by that method. But that segment is not actually part of the >>>> answer, because that whole loop at the end is not part of the offset. >>>> >>>> The second image shows two circles giving rise to a green segment. The >>>> green segment is not part of the offset---only part of it is. To find >>>> which part you need to intersect it with another segment that can only be >>>> found globally. It depends on what is happening on the other side of the >>>> path. It can be arbitrarily far away along the path. I don't think >>>> there's any way to avoid running a quadratic algorithm to find all the >>>> self-intersections that result from the naive offset. Then you have to >>>> somehow decide which parts are valid. In python you can probably find all >>>> the self-intersections in n log n time using some kind of clever data >>>> structure, but in OpenSCAD clever data structures are impossible, so brute >>>> force is the only option. >>>> >>>> My method for doing this is to check whether segments are too close to >>>> the original curve. There is a method that is more elegant and probably >>>> more robust where you divide the offset into its polygon components and >>>> compute the winding number of each component. The problem I have with this >>>> algorithm is that it seems difficult to keep track of the point order so >>>> that I can map the offset points back to the original points. My use of >>>> the offset() function is almost always in constructing a polyhedron where I >>>> need to use this point-point relationship. The winding number method also >>>> doesn't work if you have a path instead of a polygon. For the model in >>>> this thread my fast method that uses symmetry does an offset of just part >>>> of the polygon, so it's not a closed path, and it's important to keep track >>>> of that mapping to link the offset paths together. >>>> >>>> >>>> On Wed, Apr 13, 2022 at 4:01 PM nop head <nop.head@gmail.com> wrote: >>>> >>>>> >The problem is implementation. >>>>> >>>>> Simply adding the offset as the radius for each vertex and passing it >>>>> to my rounded polygon function gives this: >>>>> >>>>> [image: image.png] >>>>> >>>>> I.e. it is correct for convex corners but concave ones need to detect >>>>> the intersection and remove the inner loop. That isn't hard and gives this: >>>>> >>>>> [image: image.png] >>>>> >>>>> Passing a negative offset also seems to just work. My rounded_polygon >>>>> function uses negative radii to indicate concave corners. So now it goes >>>>> wrong at the convex corners: >>>>> >>>>> [image: image.png] >>>>> >>>>> The same tangent intersection test fixes that as well. >>>>> >>>>> [image: image.png] >>>>> >>>>> So I think I have recreated Skeinforge's offset algorithm in OpenSCAD. >>>>> I don't know how robust my quick hack is as rounded_polygon was never >>>>> designed for doing offsets. >>>>> >>>>> >>>>> On Wed, 13 Apr 2022 at 18:17, Sanjeev Prabhakar < >>>>> sprabhakar2006@gmail.com> wrote: >>>>> >>>>>> I just tried this code on my macbook air m1 >>>>>> It is wonderfully fast. >>>>>> 0.1 step angle f6 render successfully completed in 26 sec >>>>>> >>>>>> On Wed, 13 Apr 2022, 16:41 Sanjeev Prabhakar, < >>>>>> sprabhakar2006@gmail.com> wrote: >>>>>> >>>>>>> there seems to be a syntax error in my last mail >>>>>>> >>>>>>> correct one is here: >>>>>>> >>>>>>> include <dependencies.scad> >>>>>>> >>>>>>> function >>>>>>> surf_offset(prism,d)=[for(i=[0:len(prism)-1])[for(j=[0:len(prism[0])-1])let(j_plus=j<len(prism[0])-1?j+1:0,p0=prism[i][j],p1=i<len(prism)-1?prism[i][j_plus]:prism[i-1][j],p2=i<len(prism)-1?prism[i+1][j]:prism[i][j_plus],v1=p1-p0,v2=p2-p0,u1=uv(v1),u2=uv(v2), >>>>>>> p3=cross(u1,u2)*d )p0+p3]]; >>>>>>> >>>>>>> stages =200; >>>>>>> >>>>>>> stage_height = 1.25; >>>>>>> >>>>>>> rad = 50; >>>>>>> >>>>>>> f1 = 25; >>>>>>> >>>>>>> f2 = 25; >>>>>>> >>>>>>> phase1 = 0; >>>>>>> >>>>>>> phase2 = 180; >>>>>>> >>>>>>> height_depth=5; >>>>>>> >>>>>>> depth1 = 20; >>>>>>> >>>>>>> depth2 = 20; >>>>>>> >>>>>>> thickness = 2; >>>>>>> >>>>>>> bottom_thickness = 3; >>>>>>> >>>>>>> myslices = 5; >>>>>>> >>>>>>> angle_step=0.1; >>>>>>> >>>>>>> // generate outer points >>>>>>> >>>>>>> points_base = [for (i = [0:1:stages]) let(f = pow(sin(i/stages * >>>>>>> 120),2) * 7 + 1, var = 1 , a=((sin((i/stages*360*f)%360) * 0.5 + 0.5) * >>>>>>> (var * height_depth))) [for(j = [0:angle_step:360-angle_step]) [sin(j) * >>>>>>> (rad+a+(pauw(sin(j *f1+phase1),0.5)*0.5+0.5)*depth1*i/stages+(pauw(sin(j >>>>>>> *f2+phase2),0.5)*0.5+0.5)*depth2*(1-i/stages)), cos(j) *(rad+a+(pauw(sin(j >>>>>>> *f1+phase1),0.5)*0.5+0.5)*depth1*i/stages+(pauw(sin(j >>>>>>> *f2+phase2),0.5)*0.5+0.5)*depth2*(1-i/stages)),i*stage_height]]]; >>>>>>> >>>>>>> points_base_e=surf_offset(points_base,2); >>>>>>> >>>>>>> swp_prism_h(points_base,points_base_e); >>>>>>> >>>>>>> function pauw(x,p)=sign(x)*abs(x)^p; >>>>>>> >>>>>>> >>>>>>>>> _______________________________________________ >>>>>> OpenSCAD mailing list >>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>>> >>>>> _______________________________________________ >>>>> OpenSCAD mailing list >>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>> >>>> _______________________________________________ >>>> OpenSCAD mailing list >>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>> >>> _______________________________________________ >>> OpenSCAD mailing list >>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>> >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >> > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
NH
nop head
Thu, Apr 14, 2022 10:40 PM

Well I guess about half the thickness of the protuberances or the gaps
between them. I don't model aesthetic objects or organic shapes. Mine are
purely functional, so generally a lot simpler geometrically.

There was some talk of exposing 2D operations as built in functions in
OpenSCAD, and even a PR IIRC. That would make stuff like this much easier
and faster and doesn't seem to break the language.

On Thu, 14 Apr 2022 at 21:03, Adrian Mariano avm4@cornell.edu wrote:

Yes, it will always work for convex shapes with positive offset.  So
given the known design in this thread, can you honestly tell me it is
"easy" to know what offset will work without creating a
self-intersection?

On Thu, Apr 14, 2022 at 6:59 AM nop head nop.head@gmail.com wrote:

I think it always works for convex shapes and a positive offset.

For concave shapes, or negative offsets, I think the offset has to be
less than half the width of the thinnest internal or external features. Not
easy to compute but easy to know when you are applying it to a known design.

On Thu, 14 Apr 2022 at 00:04, Adrian Mariano avm4@cornell.edu wrote:

Yes, that's correct, assuming by "distance between vertices" you mean
the global distance pairwise between all vertices, not the consecutive
difference between them.  In other words, there's no simple way to even
predict when it will work.

On Wed, Apr 13, 2022 at 5:42 PM nop head nop.head@gmail.com wrote:

Yes I suppose it only works if the offset is small compared to the
distance between vertices.

On Wed, 13 Apr 2022 at 22:02, Adrian Mariano avm4@cornell.edu wrote:

Sanjeev:

You are producing models that are invalid.  It is possible that
downstream processing (the 3d print slicer) can handle a bogus model.
Maybe.  Maybe not.  The STL you save will have self-intersecting faces.
Depending on how big the self-intersecting bits are it could be pretty hard
to deal with downstream.  My focus is on creating valid models.  The
point of running render (f6) is to demonstrate that the model is valid.  To
do this you have to include a second object, and usually you should have it
intersect your test object.  I suggest that if you post self-intersecting
models that you warn people that they are self-intersecting.  (Note that
f6 render is usually fast when your model contains just one polyhedron.)

How much 3d printing have you done?  I had an early experience where
my model just wouldn't stick to the build plate.  I ultimately discovered
that the problem was I had used the hull of a sphere to construct the
model, and due to the choice of $fn, the model was very slightly
undersized.  I don't know what you can get away with here.  Clearly 0.1mm
is is going to be a disaster.  It seems like 0.01mm is probably ok.  Again
it may depend on what the slicer does iwth your model.  So if your
flaws/bumps on the bottom of the model are very very small it's maybe OK.
I'd rather have a model that's actually correct, though.  You could remove
the extra stuff with an intersection or difference...except no, you can't,
because the model is invalid.

You ignored my observation that you don't include the bottom of the
container.  Since your model is invalid you can't do a union.  How will you
add the bottom?  (Also union can be slow, so your run time numbers may
need refinement.)

I examined your code a bit and see what is happening and why it's
faster.  Offset requires computing an intersection of two offset lines.
You avoid that by simply shifting the point based on a normal computed from
some adjacent points.  You can also do the same thing in 2d and it should
be even faster.  It's not going to 3d that made it fast but using this type
of approximate offset.  It may in fact be a good approximation when the
angle step size is so very small, but of course, it's only going to give a
valid result if the offset is small enough.  I would say that if you want
to pursue the fastest possible invalid model that the way to do it would be
to use such an approximate 2d offset, working by layers.  Then you can skip
the two layers on the bottom of the inside so that the base is correct, and
the whole model is a single (invalid) polyhedron that you can render to a
(self-intersecting) STL.

Nophead:

You are doing the trivial part of offset and ignoring the hard part.
You also have a formulation that to me seems overly complicated.  Solving
for the tangent points of two circles appears to be a harder problem than
the intersection of two lines.  I would formulate offset as follows:  Given
two adjacent segments on the path, shift each one in its normal direction
by the offset distance.  If the resulting shifted segments intersect, then
you clip them at the intersection point.  If they don't, you extend them
somehow (arc, linear extension, whatever).  Ok.  That's the trivial part.

The problem now is that parts of the path generated this way are
invalid.  There are extra loops.  These extra loops cannot be detected
locally, only globally.
Image 1 below shows an example where there is a bogus loop in the red
offset to the yellow path.  I show two circles as per your description.
The segment between their intersection points is part of the result that is
obtained by that method.  But that segment is not actually part of the
answer, because that whole loop at the end is not part of the offset.

The second image shows two circles giving rise to a green segment.
The green segment is not part of the offset---only part of it is.  To find
which part you need to intersect it with another segment that can only be
found globally.  It depends on what is happening on the other side of the
path.  It can be arbitrarily far away along the path.  I don't think
there's any way to avoid running a quadratic algorithm to find all the
self-intersections that result from the naive offset.  Then you have to
somehow decide which parts are valid.  In python you can probably find all
the self-intersections in n log n time using some kind of clever data
structure, but in OpenSCAD clever data structures are impossible, so brute
force is the only option.

My method for doing this is to check whether segments are too close to
the original curve.  There is a method that is more elegant and probably
more robust where you divide the offset into its polygon components and
compute the winding number of each component.  The problem I have with this
algorithm is that it seems difficult to keep track of the point order so
that I can map the offset points back to the original points.  My use of
the offset() function is almost always in constructing a polyhedron where I
need to use this point-point relationship.  The winding number method also
doesn't work if you have a path instead of a polygon.  For the model in
this thread my fast method that uses symmetry does an offset of just part
of the polygon, so it's not a closed path, and it's important to keep track
of that mapping to link the offset paths together.

On Wed, Apr 13, 2022 at 4:01 PM nop head nop.head@gmail.com wrote:

The problem is implementation.

Simply adding the offset as the radius for each vertex and passing it
to my rounded polygon function gives this:

[image: image.png]

I.e. it is correct for convex corners but concave ones need to detect
the intersection and remove the inner loop. That isn't hard and gives this:

[image: image.png]

Passing a negative offset also seems to just work. My rounded_polygon
function uses negative radii to indicate concave corners. So now it goes
wrong at the convex corners:

[image: image.png]

The same tangent intersection test fixes that as well.

[image: image.png]

So I think I have recreated Skeinforge's offset algorithm in
OpenSCAD. I don't know how robust my quick hack is as rounded_polygon was
never designed for doing offsets.

On Wed, 13 Apr 2022 at 18:17, Sanjeev Prabhakar <
sprabhakar2006@gmail.com> wrote:

I just tried this code on my macbook air m1
It is wonderfully fast.
0.1 step angle f6 render successfully completed in 26 sec

On Wed, 13 Apr 2022, 16:41 Sanjeev Prabhakar, <
sprabhakar2006@gmail.com> wrote:

there seems to be a syntax error in my last mail

correct one is here:

include <dependencies.scad>

function
surf_offset(prism,d)=[for(i=[0:len(prism)-1])[for(j=[0:len(prism[0])-1])let(j_plus=j<len(prism[0])-1?j+1:0,p0=prism[i][j],p1=i<len(prism)-1?prism[i][j_plus]:prism[i-1][j],p2=i<len(prism)-1?prism[i+1][j]:prism[i][j_plus],v1=p1-p0,v2=p2-p0,u1=uv(v1),u2=uv(v2),
p3=cross(u1,u2)*d )p0+p3]];

stages =200;

stage_height = 1.25;

rad = 50;

f1 = 25;

f2 = 25;

phase1 = 0;

phase2 = 180;

height_depth=5;

depth1 = 20;

depth2 = 20;

thickness = 2;

bottom_thickness = 3;

myslices = 5;

angle_step=0.1;

// generate outer points

points_base = [for (i = [0:1:stages]) let(f = pow(sin(i/stages *
120),2) * 7 + 1, var = 1 , a=((sin((i/stages360f)%360) * 0.5 + 0.5) *
(var * height_depth))) [for(j = [0:angle_step:360-angle_step]) [sin(j) *
(rad+a+(pauw(sin(j *f1+phase1),0.5)*0.5+0.5)depth1i/stages+(pauw(sin(j
*f2+phase2),0.5)*0.5+0.5)depth2(1-i/stages)), cos(j) *(rad+a+(pauw(sin(j
*f1+phase1),0.5)*0.5+0.5)depth1i/stages+(pauw(sin(j
*f2+phase2),0.5)0.5+0.5)depth2(1-i/stages)),istage_height]]];

points_base_e=surf_offset(points_base,2);

swp_prism_h(points_base,points_base_e);

function pauw(x,p)=sign(x)*abs(x)^p;


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Well I guess about half the thickness of the protuberances or the gaps between them. I don't model aesthetic objects or organic shapes. Mine are purely functional, so generally a lot simpler geometrically. There was some talk of exposing 2D operations as built in functions in OpenSCAD, and even a PR IIRC. That would make stuff like this much easier and faster and doesn't seem to break the language. On Thu, 14 Apr 2022 at 21:03, Adrian Mariano <avm4@cornell.edu> wrote: > Yes, it will always work for convex shapes with positive offset. So > given the known design in this thread, can you honestly tell me it is > "easy" to know what offset will work without creating a > self-intersection? > > > > On Thu, Apr 14, 2022 at 6:59 AM nop head <nop.head@gmail.com> wrote: > >> I think it always works for convex shapes and a positive offset. >> >> For concave shapes, or negative offsets, I think the offset has to be >> less than half the width of the thinnest internal or external features. Not >> easy to compute but easy to know when you are applying it to a known design. >> >> On Thu, 14 Apr 2022 at 00:04, Adrian Mariano <avm4@cornell.edu> wrote: >> >>> Yes, that's correct, assuming by "distance between vertices" you mean >>> the global distance pairwise between all vertices, not the consecutive >>> difference between them. In other words, there's no simple way to even >>> predict when it will work. >>> >>> On Wed, Apr 13, 2022 at 5:42 PM nop head <nop.head@gmail.com> wrote: >>> >>>> Yes I suppose it only works if the offset is small compared to the >>>> distance between vertices. >>>> >>>> On Wed, 13 Apr 2022 at 22:02, Adrian Mariano <avm4@cornell.edu> wrote: >>>> >>>>> Sanjeev: >>>>> >>>>> You are producing models that are invalid. It is possible that >>>>> downstream processing (the 3d print slicer) can handle a bogus model. >>>>> Maybe. Maybe not. The STL you save will have self-intersecting faces. >>>>> Depending on how big the self-intersecting bits are it could be pretty hard >>>>> to deal with downstream. My focus is on creating valid models. The >>>>> point of running render (f6) is to demonstrate that the model is valid. To >>>>> do this you have to include a second object, and usually you should have it >>>>> intersect your test object. I suggest that if you post self-intersecting >>>>> models that you warn people that they are self-intersecting. (Note that >>>>> f6 render is usually fast when your model contains just one polyhedron.) >>>>> >>>>> How much 3d printing have you done? I had an early experience where >>>>> my model just wouldn't stick to the build plate. I ultimately discovered >>>>> that the problem was I had used the hull of a sphere to construct the >>>>> model, and due to the choice of $fn, the model was very slightly >>>>> undersized. I don't know what you can get away with here. Clearly 0.1mm >>>>> is is going to be a disaster. It seems like 0.01mm is probably ok. Again >>>>> it may depend on what the slicer does iwth your model. So if your >>>>> flaws/bumps on the bottom of the model are very very small it's maybe OK. >>>>> I'd rather have a model that's actually correct, though. You could remove >>>>> the extra stuff with an intersection or difference...except no, you can't, >>>>> because the model is invalid. >>>>> >>>>> You ignored my observation that you don't include the bottom of the >>>>> container. Since your model is invalid you can't do a union. How will you >>>>> add the bottom? (Also union can be slow, so your run time numbers may >>>>> need refinement.) >>>>> >>>>> I examined your code a bit and see what is happening and why it's >>>>> faster. Offset requires computing an intersection of two offset lines. >>>>> You avoid that by simply shifting the point based on a normal computed from >>>>> some adjacent points. You can also do the same thing in 2d and it should >>>>> be even faster. It's not going to 3d that made it fast but using this type >>>>> of approximate offset. It may in fact be a good approximation when the >>>>> angle step size is so very small, but of course, it's only going to give a >>>>> valid result if the offset is small enough. I would say that if you want >>>>> to pursue the fastest possible invalid model that the way to do it would be >>>>> to use such an approximate 2d offset, working by layers. Then you can skip >>>>> the two layers on the bottom of the inside so that the base is correct, and >>>>> the whole model is a single (invalid) polyhedron that you can render to a >>>>> (self-intersecting) STL. >>>>> >>>>> Nophead: >>>>> >>>>> You are doing the trivial part of offset and ignoring the hard part. >>>>> You also have a formulation that to me seems overly complicated. Solving >>>>> for the tangent points of two circles appears to be a harder problem than >>>>> the intersection of two lines. I would formulate offset as follows: Given >>>>> two adjacent segments on the path, shift each one in its normal direction >>>>> by the offset distance. If the resulting shifted segments intersect, then >>>>> you clip them at the intersection point. If they don't, you extend them >>>>> somehow (arc, linear extension, whatever). Ok. That's the trivial part. >>>>> >>>>> The problem now is that parts of the path generated this way are >>>>> invalid. There are extra loops. These extra loops cannot be detected >>>>> locally, only globally. >>>>> Image 1 below shows an example where there is a bogus loop in the red >>>>> offset to the yellow path. I show two circles as per your description. >>>>> The segment between their intersection points is part of the result that is >>>>> obtained by that method. But that segment is not actually part of the >>>>> answer, because that whole loop at the end is not part of the offset. >>>>> >>>>> The second image shows two circles giving rise to a green segment. >>>>> The green segment is not part of the offset---only part of it is. To find >>>>> which part you need to intersect it with another segment that can only be >>>>> found globally. It depends on what is happening on the other side of the >>>>> path. It can be arbitrarily far away along the path. I don't think >>>>> there's any way to avoid running a quadratic algorithm to find all the >>>>> self-intersections that result from the naive offset. Then you have to >>>>> somehow decide which parts are valid. In python you can probably find all >>>>> the self-intersections in n log n time using some kind of clever data >>>>> structure, but in OpenSCAD clever data structures are impossible, so brute >>>>> force is the only option. >>>>> >>>>> My method for doing this is to check whether segments are too close to >>>>> the original curve. There is a method that is more elegant and probably >>>>> more robust where you divide the offset into its polygon components and >>>>> compute the winding number of each component. The problem I have with this >>>>> algorithm is that it seems difficult to keep track of the point order so >>>>> that I can map the offset points back to the original points. My use of >>>>> the offset() function is almost always in constructing a polyhedron where I >>>>> need to use this point-point relationship. The winding number method also >>>>> doesn't work if you have a path instead of a polygon. For the model in >>>>> this thread my fast method that uses symmetry does an offset of just part >>>>> of the polygon, so it's not a closed path, and it's important to keep track >>>>> of that mapping to link the offset paths together. >>>>> >>>>> >>>>> On Wed, Apr 13, 2022 at 4:01 PM nop head <nop.head@gmail.com> wrote: >>>>> >>>>>> >The problem is implementation. >>>>>> >>>>>> Simply adding the offset as the radius for each vertex and passing it >>>>>> to my rounded polygon function gives this: >>>>>> >>>>>> [image: image.png] >>>>>> >>>>>> I.e. it is correct for convex corners but concave ones need to detect >>>>>> the intersection and remove the inner loop. That isn't hard and gives this: >>>>>> >>>>>> [image: image.png] >>>>>> >>>>>> Passing a negative offset also seems to just work. My rounded_polygon >>>>>> function uses negative radii to indicate concave corners. So now it goes >>>>>> wrong at the convex corners: >>>>>> >>>>>> [image: image.png] >>>>>> >>>>>> The same tangent intersection test fixes that as well. >>>>>> >>>>>> [image: image.png] >>>>>> >>>>>> So I think I have recreated Skeinforge's offset algorithm in >>>>>> OpenSCAD. I don't know how robust my quick hack is as rounded_polygon was >>>>>> never designed for doing offsets. >>>>>> >>>>>> >>>>>> On Wed, 13 Apr 2022 at 18:17, Sanjeev Prabhakar < >>>>>> sprabhakar2006@gmail.com> wrote: >>>>>> >>>>>>> I just tried this code on my macbook air m1 >>>>>>> It is wonderfully fast. >>>>>>> 0.1 step angle f6 render successfully completed in 26 sec >>>>>>> >>>>>>> On Wed, 13 Apr 2022, 16:41 Sanjeev Prabhakar, < >>>>>>> sprabhakar2006@gmail.com> wrote: >>>>>>> >>>>>>>> there seems to be a syntax error in my last mail >>>>>>>> >>>>>>>> correct one is here: >>>>>>>> >>>>>>>> include <dependencies.scad> >>>>>>>> >>>>>>>> function >>>>>>>> surf_offset(prism,d)=[for(i=[0:len(prism)-1])[for(j=[0:len(prism[0])-1])let(j_plus=j<len(prism[0])-1?j+1:0,p0=prism[i][j],p1=i<len(prism)-1?prism[i][j_plus]:prism[i-1][j],p2=i<len(prism)-1?prism[i+1][j]:prism[i][j_plus],v1=p1-p0,v2=p2-p0,u1=uv(v1),u2=uv(v2), >>>>>>>> p3=cross(u1,u2)*d )p0+p3]]; >>>>>>>> >>>>>>>> stages =200; >>>>>>>> >>>>>>>> stage_height = 1.25; >>>>>>>> >>>>>>>> rad = 50; >>>>>>>> >>>>>>>> f1 = 25; >>>>>>>> >>>>>>>> f2 = 25; >>>>>>>> >>>>>>>> phase1 = 0; >>>>>>>> >>>>>>>> phase2 = 180; >>>>>>>> >>>>>>>> height_depth=5; >>>>>>>> >>>>>>>> depth1 = 20; >>>>>>>> >>>>>>>> depth2 = 20; >>>>>>>> >>>>>>>> thickness = 2; >>>>>>>> >>>>>>>> bottom_thickness = 3; >>>>>>>> >>>>>>>> myslices = 5; >>>>>>>> >>>>>>>> angle_step=0.1; >>>>>>>> >>>>>>>> // generate outer points >>>>>>>> >>>>>>>> points_base = [for (i = [0:1:stages]) let(f = pow(sin(i/stages * >>>>>>>> 120),2) * 7 + 1, var = 1 , a=((sin((i/stages*360*f)%360) * 0.5 + 0.5) * >>>>>>>> (var * height_depth))) [for(j = [0:angle_step:360-angle_step]) [sin(j) * >>>>>>>> (rad+a+(pauw(sin(j *f1+phase1),0.5)*0.5+0.5)*depth1*i/stages+(pauw(sin(j >>>>>>>> *f2+phase2),0.5)*0.5+0.5)*depth2*(1-i/stages)), cos(j) *(rad+a+(pauw(sin(j >>>>>>>> *f1+phase1),0.5)*0.5+0.5)*depth1*i/stages+(pauw(sin(j >>>>>>>> *f2+phase2),0.5)*0.5+0.5)*depth2*(1-i/stages)),i*stage_height]]]; >>>>>>>> >>>>>>>> points_base_e=surf_offset(points_base,2); >>>>>>>> >>>>>>>> swp_prism_h(points_base,points_base_e); >>>>>>>> >>>>>>>> function pauw(x,p)=sign(x)*abs(x)^p; >>>>>>>> >>>>>>>> >>>>>>>>>> _______________________________________________ >>>>>>> OpenSCAD mailing list >>>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>>>> >>>>>> _______________________________________________ >>>>>> OpenSCAD mailing list >>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>>> >>>>> _______________________________________________ >>>>> OpenSCAD mailing list >>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>> >>>> _______________________________________________ >>>> OpenSCAD mailing list >>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>> >>> _______________________________________________ >>> OpenSCAD mailing list >>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>> >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >> > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
SP
Sanjeev Prabhakar
Fri, Apr 15, 2022 1:32 AM

Printed this beautiful artifact.

Printed this beautiful artifact.
RB
roald.baudoux@brutele.be
Sat, Apr 16, 2022 9:59 AM

Congrats, Sanjeev, I haven’t had the time to print it myself yet!

@Jordan Brown: thank you for the idea of the 0% infill. But can you define a bottom thickness that way too?

Congrats, Sanjeev, I haven’t had the time to print it myself yet!\ \ @Jordan Brown: thank you for the idea of the 0% infill. But can you define a bottom thickness that way too?
AM
Adrian Mariano
Sat, Apr 16, 2022 11:55 AM

Roald, I don't see a message from Jordan (maybe it was private?) but
it occurred to me that at least with my slicer (PruseSlicer) you can
use "vase mode" where you give it a solid shape and it produces a
container with a single extrusion thickness.  Such a container will be
somewhat flexible, not very strong.  But you won't need any offset
operation, since you don't have to remove the interior, so the model
would be very easy and fast to construct.  Also the container will
have a smooth outside finish with no z-seam, because it is printed as
a continuous spiral.

Here's a tutorial explaining vase mode in PrusaSlicer:
https://the3dprinterbee.com/prusaslicer-spiral-vase-mode/

It will give you a base and sides and open on the top, and by tweaking
settings you can get the wall to be about 1.5 times the nozzle size
(so a .4 mm nozzle can produce a 0.6 mm wall.).  I assume other
slicers also have a feature like this.  (Or you can switch to
PrusaSlicer, or slic3r from which it derives---you can use the slicer
with any printer.)

On Sat, Apr 16, 2022 at 6:00 AM roald.baudoux@brutele.be wrote:

Congrats, Sanjeev, I haven’t had the time to print it myself yet!

@Jordan Brown: thank you for the idea of the 0% infill. But can you define a bottom thickness that way too?


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Roald, I don't see a message from Jordan (maybe it was private?) but it occurred to me that at least with my slicer (PruseSlicer) you can use "vase mode" where you give it a solid shape and it produces a container with a single extrusion thickness. Such a container will be somewhat flexible, not very strong. But you won't need any offset operation, since you don't have to remove the interior, so the model would be very easy and fast to construct. Also the container will have a smooth outside finish with no z-seam, because it is printed as a continuous spiral. Here's a tutorial explaining vase mode in PrusaSlicer: https://the3dprinterbee.com/prusaslicer-spiral-vase-mode/ It will give you a base and sides and open on the top, and by tweaking settings you can get the wall to be about 1.5 times the nozzle size (so a .4 mm nozzle can produce a 0.6 mm wall.). I assume other slicers also have a feature like this. (Or you can switch to PrusaSlicer, or slic3r from which it derives---you can use the slicer with any printer.) On Sat, Apr 16, 2022 at 6:00 AM <roald.baudoux@brutele.be> wrote: > > Congrats, Sanjeev, I haven’t had the time to print it myself yet! > > @Jordan Brown: thank you for the idea of the 0% infill. But can you define a bottom thickness that way too? > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org
FH
Father Horton
Sat, Apr 16, 2022 12:00 PM

Cura has vase mode too.

Cura has vase mode too.
SP
Sanjeev Prabhakar
Sat, Apr 16, 2022 1:35 PM

would suggest to use the code I sent earlier.

The workflow I used is as following:

Separately create stl files for base and complex artifact.

Go to freecad
Import both the files in freecad

Select both and export as stl.

Use your 3d printing software to create gcode

That's it

It was not a big challenge and the vas looks great,  right now I am using
it as a pen stand as my printer is not so big and had to scale down to 50%.

Creating both the stl took less than a minute for me

On Sat, 16 Apr 2022, 15:30 , roald.baudoux@brutele.be wrote:

Congrats, Sanjeev, I haven’t had the time to print it myself yet!

@Jordan Brown: thank you for the idea of the 0% infill. But can you define
a bottom thickness that way too?


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

would suggest to use the code I sent earlier. The workflow I used is as following: Separately create stl files for base and complex artifact. Go to freecad Import both the files in freecad Select both and export as stl. Use your 3d printing software to create gcode That's it It was not a big challenge and the vas looks great, right now I am using it as a pen stand as my printer is not so big and had to scale down to 50%. Creating both the stl took less than a minute for me On Sat, 16 Apr 2022, 15:30 , <roald.baudoux@brutele.be> wrote: > Congrats, Sanjeev, I haven’t had the time to print it myself yet! > > @Jordan Brown: thank you for the idea of the 0% infill. But can you define > a bottom thickness that way too? > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
RB
roald.baudoux@brutele.be
Sat, Apr 16, 2022 2:08 PM

I have used this strategy consisting in the generation of two separate stls on previous designs but I prefer to avoid it whenever possible.

I have used this strategy consisting in the generation of two separate stls on previous designs but I prefer to avoid it whenever possible.
J
jon
Sat, Apr 16, 2022 2:17 PM

Most slicers allow you to specify the number of top, bottom, and side
layers.  There is no need to go through something this complex.  It is
very simple.

On 4/16/2022 9:35 AM, Sanjeev Prabhakar wrote:

would suggest to use the code I sent earlier.

The workflow I used is as following:

Separately create stl files for base and complex artifact.

Go to freecad
Import both the files in freecad

Select both and export as stl.

Use your 3d printing software to create gcode

That's it

It was not a big challenge and the vas looks great,  right now I am
using it as a pen stand as my printer is not so big and had to scale
down to 50%.

Creating both the stl took less than a minute for me

On Sat, 16 Apr 2022, 15:30 , roald.baudoux@brutele.be wrote:

 Congrats, Sanjeev, I haven’t had the time to print it myself yet!

 @Jordan Brown: thank you for the idea of the 0% infill. But can
 you define a bottom thickness that way too?
Most slicers allow you to specify the number of top, bottom, and side layers.  There is no need to go through something this complex.  It is very simple. On 4/16/2022 9:35 AM, Sanjeev Prabhakar wrote: > would suggest to use the code I sent earlier. > > The workflow I used is as following: > > Separately create stl files for base and complex artifact. > > Go to freecad > Import both the files in freecad > > Select both and export as stl. > > Use your 3d printing software to create gcode > > That's it > > It was not a big challenge and the vas looks great,  right now I am > using it as a pen stand as my printer is not so big and had to scale > down to 50%. > > Creating both the stl took less than a minute for me > > On Sat, 16 Apr 2022, 15:30 , <roald.baudoux@brutele.be> wrote: > > Congrats, Sanjeev, I haven’t had the time to print it myself yet! > > @Jordan Brown: thank you for the idea of the 0% infill. But can > you define a bottom thickness that way too? > >
RW
Raymond West
Sat, Apr 16, 2022 2:42 PM

In cura, probably Prusa/superslicer/others, you can select walls,
infill, top, bottom independently of each other and set any or all of
them to 0. Vase mode (spiralize outer contour) does not give the 'blobs'
you may get with stepped level changes, but will only be the thickness
of the value you set in the line thickness, and the nozzle size. If you
select bottom and walls, you may get the blobs but wall thickness can be
however you want, and it may well be watertight and strong.

On 16/04/2022 10:59, roald.baudoux@brutele.be wrote:

Congrats, Sanjeev, I haven’t had the time to print it myself yet!

@Jordan Brown: thank you for the idea of the 0% infill. But can you
define a bottom thickness that way too?


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

In cura, probably Prusa/superslicer/others, you can select walls, infill, top, bottom independently of each other and set any or all of them to 0. Vase mode (spiralize outer contour) does not give the 'blobs' you may get with stepped level changes, but will only be the thickness of the value you set in the line thickness, and the nozzle size. If you select bottom and walls, you may get the blobs but wall thickness can be however you want, and it may well be watertight and strong. On 16/04/2022 10:59, roald.baudoux@brutele.be wrote: > > Congrats, Sanjeev, I haven’t had the time to print it myself yet! > > @Jordan Brown: thank you for the idea of the 0% infill. But can you > define a bottom thickness that way too? > > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org