JustinSDK / dotSCAD

Reduce the burden of mathematics when playing OpenSCAD
https://openhome.cc/zh-tw/openscad/
GNU Lesser General Public License v3.0
784 stars 107 forks source link

path_extrude() behavior incorrect in latest release(s) but correct in v1.0.0 #5

Closed chill1n closed 5 years ago

chill1n commented 5 years ago

Instead of a regular thread, I see a gradually rotated thread for increasing z. With release v1.0.0 the behavior was correct, but with release v1.2.0 (and possibly older releases) the behavior is incorrect. The code that shows this behavior is:

include <dotSCAD/rotate_p.scad>; include <dotSCAD/polysections.scad>; include <dotSCAD/path_extrude.scad>;

r = 10; fa = 2;

shape_pts = [ // outer [0,0], [3, 1], [0, 2] ];

path_pts = [ for(a = [0:fa:360 5]) [-r cos(a), r * sin(a), a/100] ];

path_extrude( shape_pts, path_pts );

The correct output result is shown below.

Screenshot at 2019-05-30 02-53-54

The incorrect output result is shown below. Screenshot at 2019-05-30 02-57-31

JustinSDK commented 5 years ago

Actually, it's correct behavior of path_extrude in dotSCAD 1.1 or later because it takes different algorithm for rotating sections from path points.

If you want to extrude a shape along a path precisely, providing enough information about how to rotate sections is necessary. If you want to extrude a shape along a helix, helix_extrude is more suitable because it knows how to dig out necessary data for rotating sections precisely.

include <rotate_p.scad>;
include <polysections.scad>;
include <helix.scad>;
include <cross_sections.scad>;
include <helix_extrude.scad>;

r = 10;
fa = 2;

shape_pts = [
    [0,0],
    [-3, 1],
    [0, 2]
];

$fn = 360 / fa;

helix_extrude(
    shape_pts,
    radius = r,
    levels = 5,
    level_dist = 3.6,
    vt_dir = "SPI_UP"
);

helix1

If you have only points, what path_extrude can do is to guess data about rotations. The different algorithm will dig out different data. path_extrude in dotSCAD 1.0 just has the result you want but has an obvious bug in some situation. (See issue #3). The bug is fixed after v1.1.

If you still want to use path_extrude, one way to get what you want is using the twist parameter. For example:

include <rotate_p.scad>;
include <polysections.scad>;
include <path_extrude.scad>;

r = 10;
fa = 2;

shape_pts = [
    [0,0],
    [3, 1],
    [0, 2]
];

path_pts = [
    for(a = [0:fa:360 * 5])
    [-r * cos(a), r * sin(a), a/100]
];

path_extrude(
    shape_pts,
    path_pts,
    twist = 100 // adjust it
);

helix

In this situation, you provide information which path points do not provide. You think it's a bug in path_extrude after dotSCAD 1.1 because your brain has information that path points do not provide.

(Even though old algorithm of path_extrude has an obvious bug, it has some benefits in some situations. I'm still considering whether I should provide an option for users to switch these two algorithms in the later version.)

JustinSDK commented 5 years ago

I add a method parameter to path_extrude. It accepts two values, "AXIS_ANGLE" and "EULER_ANGLE".

include <rotate_p.scad>;
include <polysections.scad>;
include <path_extrude.scad>;

r = 10;
fa = 2;

shape_pts = [
    [0,0],
    [3, 1],
    [0, 2]
];

path_pts = [
    for(a = [0:fa:360 * 5])
    [-r * cos(a), r * sin(a), a/100]
];

path_extrude(
    shape_pts,
    path_pts,
    method = "AXIS_ANGLE"  // default
);

translate([r * 3, 0, 0]) path_extrude(
    shape_pts,
    path_pts,
    method = "EULER_ANGLE"
);

method

"AXIS_ANGLE" is the default value because "EULER_ANGLE" will generate an abrupt when the path is exactly vertical. It happened in (older) Blender, too.

"EULER_ANGLE", however, generates the same section at the same point. This means that you don't have to adjust sections if you want to extrude along a closed path. It's an advantage when extruding. For example:

include <shape_pentagram.scad>;
include <rotate_p.scad>;
include <polysections.scad>;
include <path_extrude.scad>;
include <torus_knot.scad>;

p = 2;
q = 3;
phi_step = 0.05;
star_radius = 0.5;

pts = torus_knot(p, q, phi_step);

shape_pentagram_pts = shape_pentagram(star_radius);

// not closed perfectly
translate([-8, 0, 0]) path_extrude(
    shape_pentagram_pts, 
    concat(pts, [pts[0]]), 
    closed = true,
    method = "AXIS_ANGLE"
);

// adjust it 
path_extrude(
    shape_pentagram_pts, 
    concat(pts, [pts[0]]), 
    closed = true,
    twist = 188,
    method = "AXIS_ANGLE"
);

// "EULER_ANGLE" is easy in this situation
translate([0, 8, 0]) path_extrude(
    shape_pentagram_pts, 
    concat(pts, [pts[0]]), 
    closed = true,
    method = "EULER_ANGLE"
);

method2

If no problem, the new parameter method will be documented in the next release.

chill1n commented 5 years ago

Could you explain what EULER/AXIS_ANGLE are actually doing in math terms? You seem to suggest that AXIS_ANGLE is yielding a non-function property, i.e. two same input values yielding different outputs.

On May 31, 2019 2:46:36 AM UTC, Justin Lin notifications@github.com wrote:

I add a method parameter to path_extrude. It accepts two values, "AXIS_ANGLE" and "EULER_ANGLE".

include <rotate_p.scad>;
include <polysections.scad>;
include <path_extrude.scad>;

r = 10;
fa = 2;

shape_pts = [
   [0,0],
   [3, 1],
   [0, 2]
];

path_pts = [
   for(a = [0:fa:360 * 5])
   [-r * cos(a), r * sin(a), a/100]
];

path_extrude(
   shape_pts,
   path_pts,
   method = "AXIS_ANGLE"  // default
);

translate([r * 3, 0, 0]) path_extrude(
   shape_pts,
   path_pts,
   method = "EULER_ANGLE"
);

method

"AXIS_ANGLE" is the default value because "EULER_ANGLE" may have an abrupt when the path is exactly vertical. It happened in (older) Blender, too.

"EULER_ANGLE", however, generates the same section at the same point. This means that you don't have to adjust sections if you want to extrude along a closed path. It's an advantage when extruding. For example:

include <shape_pentagram.scad>;
include <rotate_p.scad>;
include <polysections.scad>;
include <path_extrude.scad>;
include <torus_knot.scad>;

p = 2;
q = 3;
phi_step = 0.05;
star_radius = 0.5;

pts = torus_knot(p, q, phi_step);

shape_pentagram_pts = shape_pentagram(star_radius);

// not closed perfectly
translate([-8, 0, 0]) path_extrude(
   shape_pentagram_pts, 
   concat(pts, [pts[0]]), 
   closed = true,
   method = "AXIS_ANGLE"
);

// adjust it 
path_extrude(
   shape_pentagram_pts, 
   concat(pts, [pts[0]]), 
   closed = true,
   twist = 188,
   method = "AXIS_ANGLE"
);

// "EULER_ANGLE" is easy in this situation
translate([0, 8, 0]) path_extrude(
   shape_pentagram_pts, 
   concat(pts, [pts[0]]), 
   closed = true,
   method = "EULER_ANGLE"
);

method2

If no problem, the new parameter method will be documented in the next release.

-- You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub: https://github.com/JustinSDK/dotSCAD/issues/5#issuecomment-497555398

-- Sent from my Android device with K-9 Mail. Please excuse my brevity.

JustinSDK commented 5 years ago

"EULER_ANGLE" takes one vector between two points to dig out Euler angles required when rotating sections.

"AXIS_ANGLE" takes three points to determinate two vectors used for rotating sections. The sections are rotated by Quaternion. A simple figure is shown in issue 3 for explaining how it works.

"AXIS_ANGLE" is preferred because it doesn't generate an abrupt for most situations.

chill1n commented 5 years ago

Thanks for the suggestion. I agree with you that indeed (rotation) information is missing. This naturally leads to the questions what the default values that it assumes are, and how this missing information to the function (path_extrude) can be added so as to get the expected result?

On Thu, May 30, 2019 at 4:15 AM Justin Lin notifications@github.com wrote:

Actually, it's correct behavior of path_extrude in dotSCAD 1.1 or later because it takes different algorithm for rotating sections from path points.

If you want to extrude a shape along a path precisely, providing enough information about how to rotate sections is necessary. If you want to extrude a shape along a helix, helix_extrude is more suitable because it knows how to dig out necessary data for rotating sections precisely.

include ;include ;include ;include ;include ;

r = 10; fa = 2;

shape_pts = [ [0,0], [-3, 1], [0, 2] ];

$fn = 360 / fa;

helix_extrude( shape_pts, radius = r, levels = 5, level_dist = 3.6, vt_dir = "SPI_UP" );

[image: helix1] https://user-images.githubusercontent.com/3424741/58603509-9d409600-82c3-11e9-92c5-e3a57124629c.JPG

If you have only points, what path_extrude can do is to guess data about rotations. The different algorithm will dig out different data. path_extrude in dotSCAD 1.0 just has the result you want but has an obvious bug in some situation. (See issue #3 https://github.com/JustinSDK/dotSCAD/issues/3). The bug is fixed after v1.1.

If you still want to use path_extrude, one way to get what you want is using the twist parameter. For example:

include ;include ;include ;

r = 10; fa = 2;

shape_pts = [ [0,0], [3, 1], [0, 2] ];

path_pts = [ for(a = [0:fa:360 5]) [-r cos(a), r * sin(a), a/100] ];

path_extrude( shape_pts, path_pts, twist = 100 // adjust it );

[image: helix] https://user-images.githubusercontent.com/3424741/58603462-71251500-82c3-11e9-93df-9ca673c5e1f9.JPG

In this situation, you provide information which path points do not provide. You think it's a bug in path_extrude after dotSCAD 1.1 because your brain has information that path points do not provide.

(Even though old algorithm of path_extrude has an obvious bug, it has some benefits in some situations. I'm still considering whether I should provide an option for users to switch these two algorithms in the later version.)

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/JustinSDK/dotSCAD/issues/5?email_source=notifications&email_token=AC7BXHE5PVGYWNAYCS7EE7LPX42EDA5CNFSM4HQZLE62YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWREV5Q#issuecomment-497175286, or mute the thread https://github.com/notifications/unsubscribe-auth/AC7BXHFNMSIIOPZZEZWDP73PX42EDANCNFSM4HQZLE6Q .

JustinSDK commented 5 years ago

path_extrude is actually a workaround when you have/provide only path points.

If we have enough information (shape points, path points and angles basically), cross_sections can help us to generate sections. Once we have sections, polysections can help us.

chill1n commented 5 years ago

For a non-mathematician, like myself, this description is a bit too terse. Could you elaborate a bit on which two points you are referring to for EULER_ANGLE? The same question for the "three points" regarding AXIS_ANGLE.

By the way, I really like your openscad library. It's one of the best ones I've seen so far. I made some small changes to make it work properly with openscad-2019.05, which I will detail on later. Do you accept pull requests?

On Fri, May 31, 2019 at 10:24 AM Justin Lin notifications@github.com wrote:

"EULER_ANGLE takes one vector between two points to dig out Euler angles https://en.wikipedia.org/wiki/Euler_angles required when rotating sections.

AXIS_ANGLE takes three points to determinate two vectors used for rotating sections. The sections are rotated by Quaternion https://en.wikipedia.org/wiki/Quaternion. A simple figure is shown in issue 3 https://github.com/JustinSDK/dotSCAD/issues/3 for explaining how it works.

https://user-images.githubusercontent.com/3424741/56887837-30a86080-6aa5-11e9-8505-fa7fdace2de4.JPG

"AXIS_ANGLE " is preferred because it doesn't generate an abrupt for most situations.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/JustinSDK/dotSCAD/issues/5?email_source=notifications&email_token=AC7BXHABGM5LYL72OLD6B63PYDODJA5CNFSM4HQZLE62YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWUSBGY#issuecomment-497623195, or mute the thread https://github.com/notifications/unsubscribe-auth/AC7BXHGWAEOL2C6K3AIEHKDPYDODJANCNFSM4HQZLE6Q .

JustinSDK commented 5 years ago

Euler angle

euler

Axis angle

axis

If you still don't understand, I'll suggest you read the links I provided and read source code for details.

OpenSCAD-2019.05 was published just a few days ago so it's not urgent to leverage its new features in dotSCAD for compatibility.

For those annoy warnings when using OpenSCAD-2019.05 and dotSCAD 1.1/1.0, you may use dotSCAD 1.2. It's fixed.

The new version which works properly with OpenSCAD-2019.05 will be a target at dotSCAD 2.0. Please stay tuned.

chill1n commented 5 years ago

Thanks for the elaboration. This makes sense. Can you explain under which conditions the "closing mismatch" with AXIS_ANGLE occurs? I read some of the wikipedia documentation, and it's much clearer now.

Can't you avoid the problems of both proposed methods by another method that simply:

  1. uses the vector between point xn and x(n+1) as normal vector of the plane that contains the 2D polygon,
  2. uses a "minimal energy" rotation method to provide the third degree of freedom.

The first property is easy to implement. The second is a bit more difficult but I think that if you take any point p1 of the 2D polygon centered at xn, the rotational energy is minimized if the same polygon point around x(n+1) has minimal distance. This looks like a problem for which a closed-form solution exists, although I don't know one. You might know. Perhaps the second property can be implied by a smart implementation of the first property. Any thoughts?

On Sat, Jun 1, 2019 at 3:17 AM Justin Lin notifications@github.com wrote:

Euler angle

[image: euler] https://user-images.githubusercontent.com/3424741/58741530-82e5f400-844c-11e9-92db-a114e50eb9c9.JPG Axis angle

[image: axis] https://user-images.githubusercontent.com/3424741/58741535-9002e300-844c-11e9-99fa-06f72de06597.JPG

If you still don't understand, I'll suggest you read the links I provided and read source code for details.

OpenSCAD-2019.05 was published just a few days ago so it's not urgent to leverage its new features in dotSCAD for compatibility. I've fixed those annoy warnings when using OpenSCAD-2019.05 and dotSCAD 1.1/1.0.

The new version which works properly with OpenSCAD-2019.05 is a target at dotSCAD 2.0. Please stay tuned.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/JustinSDK/dotSCAD/issues/5?email_source=notifications&email_token=AC7BXHCUKZRJX325Y3HWQXLPYHEZ7A5CNFSM4HQZLE62YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWWVZJQ#issuecomment-497900710, or mute the thread https://github.com/notifications/unsubscribe-auth/AC7BXHDSJC4RUT72DDARNMLPYHEZ7ANCNFSM4HQZLE6Q .

JustinSDK commented 5 years ago

If you really know what the problem is when using Euler angles and what the axis-angle rotation is. You'll know when "closing mismatch" happens.

If you really think it's simple to use the methods you list and easy to implement, you may try it by yourself. I currently have no idea about how to improve the situation.

Actually, what you mentioned in the label 1 have been done in path_extrude . This shows a fact that you didn't read through all code in path_extrude and figure out what it does.

As for the label 2, the same conclusion as I've said before.

If you want to extrude a shape along a path precisely, providing enough information about how to rotate sections is necessary.

I think your biggest problem is, why do you think generating the same section at the same point is the correct behavior?

dotSCAD is an opensource library. If you come out with a solution to solve the problem, just do it. Don't just say that something is easy or simple to implement.