gumyr / build123d

A python CAD programming library
Apache License 2.0
576 stars 94 forks source link

Extrude faces from SVG sometimes changes direction when applying taper #553

Open qwelyt opened 9 months ago

qwelyt commented 9 months ago
import build123d as bd

svg = bd.import_svg("bd_test.svg")
# taper = 0
taper = 10

with bd.BuildPart() as prt:
    with bd.BuildSketch():
        for k in svg:
            with bd.BuildLine():
                bd.add(k)
            bd.make_face()
    bd.extrude(amount=10, taper=taper, dir=bd.Axis.Z.direction)

show(prt)

Depending on what shapes are in the imported SVG, some objects change direction when applying a taper.

This is without taper. Things extrude as expected. 20240218_005027

When applying a taper primitives like rect and cylinder change direction of the extrusion while path behaves like they should. 20240218_005046 bd_test

gumyr commented 9 months ago

Thanks for isolating the problem. After removing all of the Python code that I can, here is what happens:

from build123d import *
from ocp_vscode import show
from OCP.LocOpe import LocOpe_DPrism
from math import radians, cos

svg = import_svg("issue-553.svg")
face_to_extrude = -Face(svg[2])
taper = 10

prism_builder = LocOpe_DPrism(
    face_to_extrude.wrapped,
    10 / cos(radians(taper)),
    radians(taper),
)
new_solid = Solid(prism_builder.Shape())

show(face_to_extrude, new_solid)

image the extrude is in the wrong direction and this direction is independent of the face normal (same result with face_to_extrude = Face(svg[2]).

Need to figure out why LocOpe_DPrism is flipping the direction or avoid using this class at all.

gumyr commented 9 months ago

There are a couple solutions to this. One is this:

svg = import_svg("issue-553.svg")
profile = Face(svg[2])
taper = 10

taper_law = Law_Linear()
taper_law.Set(0.0, 1.0, 1.0, 1 - sin(radians(taper)))
outer = profile.outer_wire().wrapped

direction = Vector(0, 0, 10)
spine_p1 = profile.center()
spine_p2 = spine_p1 + Vector(direction)
pipe_shell_spine = Wire(Edge.make_line(spine_p1, spine_p2)).wrapped

extrude_builder = BRepOffsetAPI_MakePipeShell(pipe_shell_spine)
extrude_builder.Add(outer)
extrude_builder.SetLaw(outer, taper_law)
extrude_builder.Build()
extrude_builder.MakeSolid()
taper_solid = extrude_builder.Shape()

show(pipe_shell_spine, taper_solid)

where LocOpe_DPrism is replaced with BRepOffsetAPI_MakePipeShell with a "law" that implements a linear taper.

Alternatively, if I do this:

        if (
            False
            and direction.normalized() == profile.normal_at()
            and Plane(profile).z_dir.Z > 0
            and taper > 0
            and not profile.inner_wires()
        ):

i.e. just disable the use of LocOpe_DPrism in extrude_taper the objects will be created by lofting.

There is an open PR on an improvement to lofting that would allow for holes (but the PR isn't complete), so I'll have to think about which way to go here.