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 closed option generates geometry that fails subsequent CGAL boolean operations #31

Closed 46cv8 closed 2 years ago

46cv8 commented 2 years ago

Hey I first off just wanted to say thanks for providing such a great set of libraries for openscad, they truely open up possibilities that I had always longed for in openscad. I've run into some issues using the library. I'm not confident saying they are issues with the library per say, I suspect I am just trying to do something which perhaps it isn't quite designed for. I've managed to do a workaround but I thought I'de mention it anyways just incase there is some tip you have that I've missed.

Unfortunately I don't have a minimal example currently, but I can try and put one together if you are interested. What I'm finding is that when I use the path_extrude functionality with the closed option as shown in the torus knot example at the end of https://openhome.cc/eGossip/OpenSCAD/lib3x-path_extrude.html (except with my own custom path), and perform subsequent operations I get a CGAL error. I always try and use "AXIS_ANGLE" as it provides the smoothest line segment. I then introduce a twist to ensure that whatever geometry I have is as precisely aligned as possible to match at the start and the end. (this helps eliminate the problem in some cases however not in others)

If I then attempt to perform a difference operation to subtract portions from the path extruded mesh, it will preview fine, but rendering often fails with a message like this "ERROR: Rendering cancelled by exception Unauthorized intersections of constraints". I've also seen it spit out more specific CGAL errors but I don't have any present right now. When I searched for them online it seemed like perhaps the generated geometry might not have been entirely valid (something about polyhedron invalid or something). The work around for me is to basically never use closed, but instead to render the path as two chunks which overlap on both sides then perform a union. It doesn't generate as perfect a geometry but it works ok for my purposes.

I was curious if you were aware of any potential issue with the geometry not being valid being generated between the last segment and the first segment when the closed parameter is supplied. I'm happy to try and put together a minimal example if it would help.

JustinSDK commented 2 years ago

I think a minimal example is required to target what "CGAL errors" you encounter.

46cv8 commented 2 years ago

Ok here is one smallish example (it's not really minimal so I apologize) It results in an error "ERROR: The given mesh is not closed! Unable to convert to CGAL_Nef_Polyhedron. " when one attempts to compute the difference. If however one attempts to generate the two "torus knots" separately then they render fine, It is only when one attempts to computer the difference, union , intersection, etc that the above error emerges. I did fiddle with the twist parameter to ensure perfect alignment and sometimes it makes it able to complete the render successfully, other times it isn't sufficient and I need to slice up the torus knot into segments and render each segment separately without using the closed parameter. This workaround always works.

use <dotSCAD/src/shape_circle.scad>;
use <dotSCAD/src/path_extrude.scad>;
use <dotSCAD/src/util/degrees.scad>;
use <dotSCAD/src/__comm__/__frags.scad>;

function double_torus_knot(q, rad, dir, phi_step, min_phi=0, max_phi=-1) = 
    [
        for(phi = min_phi; phi < (max_phi == -1 ? 6.283185307179586*q[0] : max_phi); phi = phi + phi_step)
        let(
            phi_deg = degrees(phi),
            l0_deg = -dir[0]*phi_deg,
            l1_deg = phi_deg/q[0],
            l0_pos_x = rad[0]*cos(l0_deg),
            l0_pos_y = 0,
            l0_pos_z = rad[0]*sin(l0_deg),
            l1_pos_x = (l0_pos_x+rad[1])*cos(l1_deg)-l0_pos_y*sin(l1_deg),
            l1_pos_y = (l0_pos_x+rad[1])*sin(l1_deg)+l0_pos_y*cos(l1_deg),
            l1_pos_z = l0_pos_z
        )
        [l1_pos_x, 
         l1_pos_y, 
         l1_pos_z]
    ];

module torus_winding_mold(q, rad_p, rad_c, dir, phi_step, base_seg) {
    difference() {
        //*
        // this will render in cgal fine by itself if not part of a difference
        union() {
            wire_3_cross_section_radius = rad_c[2]+1*rad_c[1]+3*rad_c[0];
            wire_3_cross_section_pts = shape_circle(wire_3_cross_section_radius,$fn=base_seg*rad_p[1]);
            pts_3 = double_torus_knot([q[1]], [0,rad_p[2]], [dir[1],dir[2]], phi_step*rad_p[1], min_phi=-2*phi_step*rad_p[1]);
            path_extrude(
                wire_3_cross_section_pts,
                [each pts_3, pts_3[0]],
                closed = true,
                method = "AXIS_ANGLE"
            );
        }
        //*/
        //*
        // this will render in cgal fine by itself if not part of a difference
        rotate([0,0,40])
        union() {
            wire_2_cross_section_radius = rad_c[1]+2*rad_c[0];
            wire_2_cross_section_pts = shape_circle(wire_2_cross_section_radius,$fn=base_seg*rad_p[0]);
            pts_2 = double_torus_knot([q[1]], [rad_p[1],rad_p[2]], [dir[1],dir[2]], phi_step, min_phi=-4*phi_step);
            path_extrude(
                wire_2_cross_section_pts,
                [each pts_2, pts_2[0]],
                closed = true,
                method = "AXIS_ANGLE"
            );
        }
        //*/
    }
}

torus_winding_mold([12,18], [2-0.5,8-2,32-8], [0.5,1,4], [1,1], 6.283185307179586/20, 20);
JustinSDK commented 2 years ago

There're two problems.

One problem is that path_extrude has an extra section at the end. Its' fixed.

The other problem is that your pts_3 and pts_2 is too long so each path is overlapped. You have to fix your double_torus_knot.

Here's my quick workaround. Just see the " // remove overlapped points" parts.

use <dotSCAD/src/shape_circle.scad>;
use <dotSCAD/src/path_extrude.scad>;
use <dotSCAD/src/util/degrees.scad>;
use <dotSCAD/src/__comm__/__frags.scad>;

function double_torus_knot(q, rad, dir, phi_step, min_phi=0, max_phi=-1) = 
    [
        for(phi = min_phi; phi < (max_phi == -1 ? 6.283185307179586*q[0] : max_phi); phi = phi + phi_step)
        let(
            phi_deg = degrees(phi),
            l0_deg = -dir[0]*phi_deg,
            l1_deg = phi_deg/q[0],
            l0_pos_x = rad[0]*cos(l0_deg),
            l0_pos_y = 0,
            l0_pos_z = rad[0]*sin(l0_deg),
            l1_pos_x = (l0_pos_x+rad[1])*cos(l1_deg)-l0_pos_y*sin(l1_deg),
            l1_pos_y = (l0_pos_x+rad[1])*sin(l1_deg)+l0_pos_y*cos(l1_deg),
            l1_pos_z = l0_pos_z
        )
        [l1_pos_x, 
         l1_pos_y, 
         l1_pos_z]
    ];

module torus_winding_mold(q, rad_p, rad_c, dir, phi_step, base_seg) {
    difference() {
        //*
        // this will render in cgal fine by itself if not part of a difference
        union() {
            wire_3_cross_section_radius = rad_c[2]+1*rad_c[1]+3*rad_c[0];
            wire_3_cross_section_pts = shape_circle(wire_3_cross_section_radius,$fn=base_seg*rad_p[1]);
            pts_3 = double_torus_knot([q[1]], [0,rad_p[2]], [dir[1],dir[2]], phi_step*rad_p[1], min_phi=-2*phi_step*rad_p[1]);

            // remove overlapped points
            path = [for(i = [0:len(pts_3)-4]) pts_3[i]];

            path_extrude(
                wire_3_cross_section_pts,
                [each path, path[0]],
                closed = true,
                method = "AXIS_ANGLE"
            );
        }

        //*/
        //*
        // this will render in cgal fine by itself if not part of a difference
        translate([0, 0, .1])
        rotate([0,0,30])
        union() {
            wire_2_cross_section_radius = rad_c[1]+2*rad_c[0];
            wire_2_cross_section_pts = shape_circle(wire_2_cross_section_radius,$fn=base_seg*rad_p[0]);
            pts_2 = double_torus_knot([q[1]], [rad_p[1],rad_p[2]], [dir[1],dir[2]], phi_step, min_phi=-4*phi_step);

            // remove overlapped points
            path = [for(i = [0:len(pts_2) - 5]) pts_2[i]];

            path_extrude(
                wire_2_cross_section_pts,
                [each path, path[0]],
                closed = true,
                method = "AXIS_ANGLE"
            );
        }
        //*/
    }
}

torus_winding_mold([12,18], [2-0.5,8-2,32-8], [0.5,1,4], [1,1], 6.283185307179586/20, 20);

image

46cv8 commented 2 years ago

Whatever you did fixed all my issues as far as I can tell :) I apologize my minimal example did have overlap in the path I was attempting to extrude. This was because I had padded it so that when I sliced it up and then union'd it I wouldn't end up with wedges missing. It seems the issues with all my designs are gone now. If I run into any related issues I'll post them here, but it looks like you've fixed it. Awesome work man! Where do you take donations :) Looks like you take donations at https://cults3d.com/en/users/JustinSDK/creations is this correct?

JustinSDK commented 2 years ago

Yes, it's my account :)