-
-
Notifications
You must be signed in to change notification settings - Fork 94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fillet extrusion of an SVG path #805
Comments
Hello @gumyr, Thanks for this analysis. The error root cause might also be how the SVG was generated. What is a bit unexpected for me is that the "blob" SVG is, if I am interpreting the SVG code right, composed of 8 bezier curves. And even if all the control points were placed on all the vertices, there would still be at least 8 vertices if I am counting right.
So ending with 6 vertices was a bit of a suprise to me. I have highlighted the vertices directly in the SVG and 2 vertices are in fact aligned with two others which, sort of, masks them: I will try to see what happens when removing these "spurious" vertices from the SVGs I have tested before. In the meantime, as you said, the SVG importer is something that is taken off the shelf. Should I try to file the issue on OCCT or somewhere else? |
I've already notified the author of the SVG importer and they are looking into it. I did remove those extra vertices and still got the problem. Possibly due to the location of the control points. |
It looks like there are 2 issues here... First, while OCP considers a bezier edge where the first/last control overlaps with the start/end point to be valid, it really does not handle it well at all: p1 = 0, 0
p2 = 5, 5
p3 = 10, 0
e = Edge.make_bezier(p1, p2, p3, p3)
print(f"{e.is_valid() = }")
print(f"{e.tangent_at(0) = }")
print(f"{e.tangent_at(1) = }")
I'm assuming that something similar is happening internally within the filleting operation for it to throw the Second, the shape itself may not be "nice" enough for OCP to deal with. With the following function we can bypass the above issue by nudging the coincident control points ever so slightly in the tangent direction: def fix_edge(e: Edge):
epsilon_d = 1e-9
epsilon_t = 1e-9
if e.geom_type == GeomType.BEZIER:
bezier = e._geom_adaptor().Bezier()
start, *controls, end = [
Vector(bezier.Pole(i + 1)) for i in range(bezier.NbPoles())
]
coincident_at_start = (start - controls[0]).length < epsilon_d
coincident_at_end = (end - controls[-1]).length < epsilon_d
if coincident_at_start and coincident_at_end:
return Edge.make_line(start, end)
else:
if coincident_at_start:
controls[0] += e.tangent_at(0 + epsilon_t) * epsilon_t
if coincident_at_end:
controls[-1] -= e.tangent_at(1 - epsilon_t) * epsilon_t
return Edge.make_bezier(start, *controls, end)
else:
return e Now if we run [a minimal variation of] the code sample again... d = """
M 979.00,259.14
C 979.00,259.14 1016.00,259.14 1016.00,259.14
1192.00,436.00 1192.00,483.00 1192.00,483.00
1062.56,678.37 1048.26,689.66 1032.00,697.51
818.00,705.91 805.00,700.54 805.00,700.54
618.26,569.37 615.07,566.81 611.31,560.00
714.72,297.67 723.99,296.88 733.00,295.57
928.00,264.00 968.00,260.17 968.00,260.17
968.00,260.17 979.00,259.14 979.00,259.14
Z
"""
with BuildPart() as blob:
with BuildSketch():
for ocp_wire in ocpsvg.wires_from_svg_path(d):
wire = Wire(ocp_wire)
wire = Wire(map(fix_edge, wire.edges()))
add(wire)
make_face()
extrude(amount=100)
print(f"{blob.part.is_valid() = }")
fillet(blob.edges().group_by(Axis.Z)[-1], radius=1) We get |
Thanks both of you for these clarifications. I have added the So I have tried to found a minimal example to demonstrate this second issue without needing import logging
import ocpsvg
from build123d import *
logging.basicConfig(level=logging.INFO)
d = """
M 0.00,0.00
C 0.00,100.100 100.00,100.0 100.00,0.0
100.00,-100.00 0.00,-100.00 0.00,0.00
Z
"""
with BuildPart() as blob:
with BuildSketch():
for ocp_wire in ocpsvg.wires_from_svg_path(d):
wire = Wire(ocp_wire)
add(wire)
make_face()
extrude(amount=100)
fillet(blob.edges().group_by(Axis.Z)[-1], radius=0) This ends up with: Traceback (most recent call last):
File "/models/./blob3.py", line 24, in <module>
fillet(blob.edges().group_by(Axis.Z)[-1], radius=0)
File "/opt/build123d/lib/python3.12/site-packages/build123d/operations_generic.py", line 430, in fillet
new_part = target.fillet(radius, list(object_list))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/build123d/lib/python3.12/site-packages/build123d/topology.py", line 1002, in fillet
raise ValueError(
ValueError: Failed creating a fillet with radius of 0, try a smaller value or use max_fillet() to find the largest valid fillet radius I would have said at first that this was most probably due to the fact that there is a discontinuity at both vertices (well, I am not familiar enough with bezier curves to tell), but looking at the model without trying to apply @gumyr I am not quite sure what is the best course of action for the original issue, @snoyer is right:
I feel like I have opened pandora's box, sorry. EDIT: Looking at the definition of Bézier curves, there is no discontinuity in the shape as, at both vertices, the tangent vectors defining both Bezier curves are parallel ((0,100) and (0,-100)). |
@gautaz, your latest minimal example with the 2 bezier curves should work, see this screenshot with Maybe you need to update the the OCC kernel can be a bit funny with fillets, for example for this random path drawn in Inkscape: d = """
m 696.875,296.875
c 130.57365,-24.00515 280.94885,-73.9965 401.2901,8.80364 106.7063,84.65404 78.6898,235.8703 22.6071,340.41541 -86.8949,134.46831 -230.88529,-6.55489 -346.04657,25.37209
C 630.21748,698.41227 600.27391,533.70059 673.1101,441.67239 693.64812,396.89464 710.89482,346.26422 696.875,296.875
Z
""" I can get it to fillet for |
Hello @snoyer, I have just checked the version of ❯ docker compose exec build123d cat /opt/build123d/lib/python3.12/site-packages/ocpsvg-0.3.2.dist-info/METADATA
Metadata-Version: 2.1
Name: ocpsvg
Version: 0.3.2
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cadquery-ocp>=7.7.0
Requires-Dist: svgelements<2,>=1.9.1
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
# OCPSVG
- works at the [`OCP`](https://github.com/CadQuery/OCP) level
- uses [`svgelements`](https://github.com/meerk40t/svgelements) to:
- convert SVG path strings to and from `TopoDS_Edge`, `TopoDS_Wire`, and `TopoDS_Face` objects
- import `TopoDS_Wire` and `TopoDS_Face` objects from an SVG document
- can be used to add SVG functionality (import and export) to higher level API Is version |
Yes, that's the latest one and the one I used for the examples in my previous post |
The error message is just trying to be helpful - the complexities of OCCT Bezier and fillets are not fully accounted for. As described in the first tip here Tips, Best Practices and FAQ not everything that can be described can be built by OCCT. The art of this endeavour is working around limitations. |
@gumyr Just to be clear, I find
I totally agree with that but currently the message feels misleading to me. Another pain point is that the message tells to use
I also agree with this, I had already read the tips. |
99% of the time the problem is that the radius is too large. This problem is very strange and doesn't reflect what most users see.
If one searches in the docs for
The limitations in OCCT are not described anywhere but experienced users learn to avoid them after a time. build123d attempts to work around them and the docs provide general guidelines as how to avoid them but there is no way to list them all. CAD S/W is incredibly complex as it needs to work for arbitrary geometries which is very challenging so it's no wonder that OCCT has some (sometimes frustrating) limitations.
Sometimes it takes me a while to determine if a bug should be attributed to build123d or OCCT, I don't expect users to be able to do so. |
Thanks for all these explanations. As for As for the issue itself, FMU there is not much |
Hello,
For testing, I am using the following
test.svg
file:When trying to fillet the extrusion of this SVG with:
It fails with these logs:
I am probably doing something stupid but I have no idea what.
Would someone be kind enough to shed some light on what the issue is?
The text was updated successfully, but these errors were encountered: