CadQuery / cadquery

A python parametric CAD scripting framework based on OCCT
https://cadquery.readthedocs.io
Other
2.93k stars 276 forks source link

Weird "Fillets requires that edges be selected" exception #1553

Open KizzyCode opened 3 months ago

KizzyCode commented 3 months ago

To Reproduce

I'm trying to build some gridfinity stuff, therefore I'm trying to to build the baseplate in CadQuery. However I'm getting some weird Fillets requires that edges be selected-exception.

(The following code are cells from a Jupyter-notebook, where each ##### marks a different cell)

rounding_diameter = 7_500
outer_width = 42_000

layer0_inner = 42_000 - ((2_150 + 700) * 2)
layer0_height = 700
layer0_rounding_diameter_inner = rounding_diameter - (outer_width - layer0_inner)

layer1_inner = 42_000 - (2_150 * 2)
layer1_height = 1_800
layer1_rounding_diameter_inner = rounding_diameter - (outer_width - layer1_inner)

layer2_inner = 42_000 - (2_150 * 2)
layer2_height = 1_900
layer2_rounding_diameter_inner = rounding_diameter - (outer_width - layer2_inner)

#####

from cadquery import Workplane

object = Workplane()\
    .box(outer_width, outer_width, layer0_height, centered=[True, True, False])\
    .faces(">Z")\
        .workplane(invert=True)\
        .box(layer0_inner, layer0_inner, layer0_height, centered=[True, True, False], combine="cut")\
    .edges("|Z and (>X or <X)")\
        .fillet(rounding_diameter / 2)\
    .edges("|Z")\
        .fillet(layer0_rounding_diameter_inner / 2)

display(object)

#####

object = object\
    .faces(">Z")\
        .workplane()\
        .box(outer_width, outer_width, layer1_height, centered=[True, True, False])\
    .faces(">Z")\
        .workplane(invert=True)\
        .box(layer1_inner, layer1_inner, layer1_height, centered=[True, True, False], combine="cut")\
    .edges("|Z and (>X or <X)")\
        .fillet(rounding_diameter / 2)\
    .edges("|Z")\
        .fillet(layer1_rounding_diameter_inner / 2)

display(object)

#####

object = object\
    .faces(">Z")\
        .workplane()\
        .box(outer_width, outer_width, layer2_height, centered=[True, True, False])\
    .faces(">Z")\
        .workplane(invert=True)\
        .box(layer2_inner, layer2_inner, layer2_height, centered=[True, True, False], combine="cut")\
    .edges("|Z and (>X or <X)")\
        .fillet(rounding_diameter / 2)\ # <--- THIS IS THE LINE TRIGGERING THE EXCEPTION
    .edges("|Z")\
        .fillet(layer2_rounding_diameter_inner / 2)

display(object)

The weird thing is: If I change the rounding_diameter / 2 to either rounding_diameter / 2.00001 OR rounding_diameter / 1.99999, it does not throw... for me this looks like some kind of floating point error; but then I'm a noob regarding CAD-kernels, and I'm pretty confused that it works with a deviation in both directions. Also I'm a newby with CadQuery, so please bear with me if I'm completely misunderstanding some concepts here 🙈

Backtrace

{
    "name": "StdFail_NotDone",
    "message": "BRep_API: command not done",
    "stack": "---------------------------------------------------------------------------
StdFail_NotDone                           Traceback (most recent call last)
Cell In[117], line 9
      1 object = object\\
      2     .faces(\">Z\")\\
      3         .workplane()\\
      4         .box(outer_width, outer_width, layer2_height, centered=[True, True, False])\\
      5     .faces(\">Z\")\\
      6         .workplane(invert=True)\\
      7         .box(layer2_inner, layer2_inner, layer2_height, centered=[True, True, False], combine=\"cut\")\\
      8     .edges(\"|Z and (>X or <X)\")\\
----> 9         .fillet(rounding_diameter / 2)\\
     10     .edges(\"|Z\")\\
     11         .fillet(layer2_rounding_diameter_inner / 2)
     13 display(object)

File /usr/local/lib/python3.11/dist-packages/cadquery/cq.py:1304, in Workplane.fillet(self, radius)
   1301 if len(edgeList) < 1:
   1302     raise ValueError(\"Fillets requires that edges be selected\")
-> 1304 s = solid.fillet(radius, edgeList)
   1305 return self.newObject([s.clean()])

File /usr/local/lib/python3.11/dist-packages/cadquery/occ_impl/shapes.py:2780, in Mixin3D.fillet(self, radius, edgeList)
   2777 for e in nativeEdges:
   2778     fillet_builder.Add(radius, e)
-> 2780 return self.__class__(fillet_builder.Shape())

StdFail_NotDone: BRep_API: command not done"
}

Environment

OS: VSCode dev container running Debian on M1Pro-MacBook Pro:

FROM mcr.microsoft.com/devcontainers/base:debian

ENV APT_PACKAGES libgl1 libnlopt0 libnlopt-cxx0 python3 python3-dev python3-nlopt python3-pip
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update \
    && apt-get upgrade --yes \
    && apt-get install --yes --no-install-recommends ${APT_PACKAGES} \
    && apt-get autoremove --yes \
    && apt-get clean

RUN pip install --break-system-packages \
    ipykernel
# Manually install top-level dependencies of `cadquery` except `nlopt`
RUN pip install --break-system-packages \
    casadi \
    ezdxf \
    multimethod==1.9.1 \
    nptyping==2.0.1 \
    path \
    typish \
    https://github.com/CadQuery/ocp-build-system/releases/download/7.7.2.0/cadquery_ocp-7.7.2-cp311-cp311-manylinux_2_35_aarch64.whl
# Install `cadquery` without automatic dependency resolution
RUN pip install --break-system-packages --no-deps \
    cadquery

Also see my attached workspace if it is any use: Gridfinity.zip

lorenzncode commented 3 months ago

It looks like you are creating rounded walls. In general, it's recommended to create this kind of shape in 2D. I don't know the root cause of the issue with your code, however, I'd suggest to try moving the fillet to 2D with something like this:

import cadquery as cq

def make_wall(w1, w2, d1, d2) -> cq.Sketch:
    return (
        cq.Sketch()
        .rect(w1, w1, tag="outer")
        .rect(w2, w2, mode="s", tag="inner")
        .vertices(tag="outer")
        .fillet(d1 / 2)
        .vertices(tag="inner")
        .fillet(d2 / 2)
    )

res = (
    cq.Workplane()
    .placeSketch(make_wall(50, 40, 10, 5))
    .extrude(10)
    .faces(">Z")
    .workplane()
    .placeSketch(make_wall(50, 45, 10, 5))
    .extrude(5)
)
KizzyCode commented 2 months ago

Thanks, will try it like this instead. Please feel free to close this if it is out-of-scope or probably a problem with the underlying CAD-kernel 😊

lorenzncode commented 2 months ago

Yes AFAICT it's a kernel issue. Here is a minimal example. It fails with same error as original code.:

import cadquery as cq

b0 = cq.Workplane().box(100, 100, 5).edges(">XY").fillet(10)

b1 = (
    b0.faces(">Z")
    .box(100, 100, 5, centered=[True, True, False])
    .edges(">XY")
    .fillet(10)
)

A workaround could be to set combine to False in b1, then union after the b1 fillet

b1 = (
    b0.faces(">Z")
    .box(100, 100, 5, centered=[True, True, False], combine=False)
    .edges(">XY")
    .fillet(10)
)

b1 = b1.union(b0)

... or move the fillet to 2D as in the previous example.