gumyr / build123d

A python CAD programming library
Apache License 2.0
402 stars 75 forks source link

Inconsistent Location between Extrude and Sketch #87

Closed jdegenstein closed 1 year ago

jdegenstein commented 1 year ago

The part that is floating is 2x as high as I expected it to be. When I view wallsAndPosts.sketch it sits on top of the main part as expected, but when I extrude it -- it is in the position in the image. If I try and halve the offset to Plane.XY.offset(p_outerHeight/2) then I get this error: Standard_NullObject: BRep_Tool:: TopoDS_Vertex hasn't gp_Pnt.

image

from build123d import *

# parameter definitions
p_outerWidth = 100.0  # Outer width of box enclosure
p_outerLength = 150.0  # Outer length of box enclosure
p_outerHeight = 50.0  # Outer height of box enclosure

p_thickness = 3.0  # Thickness of the box walls
p_sideRadius = 10.0  # Radius for the curves around the sides of the box
#^ not fully parametric in side radius
p_topAndBottomChamfer = 2.0  # Chamfer for the top and bottom edges of the box

p_screwpostInset = 12.0  # How far in from the edges the screw posts should be place.
p_screwpostID = 4.0  # Inner Diameter of the screw post holes, should be roughly screw diameter not including threads
p_screwpostOD = 10.0  # Outer Diameter of the screw posts.\nDetermines overall thickness of the posts

p_boreDiameter = 8.0  # Diameter of the counterbore hole, if any
p_boreDepth = 1.0  # Depth of the counterbore hole, if
p_countersinkDiameter = 0.0  # Outer diameter of countersink. Should roughly match the outer diameter of the screw head
p_countersinkAngle = 90.0  # Countersink angle (complete angle between opposite sides, not from center to one side)
p_flipLid = True  # Whether to place the lid with the top facing down or not.
p_lipHeight = 1.0  # Height of lip on the underside of the lid.\nSits inside the box body for a snug fit.

with BuildSketch() as postXS:
    Circle(p_screwpostOD/2)
    Circle(p_screwpostID/2,mode=Mode.SUBTRACT)

with BuildSketch() as postReinf:
    Circle(p_screwpostInset)
    Circle(p_screwpostID/2,mode=Mode.SUBTRACT)
    Split(bisect_by=Plane.ZY)
    Split(bisect_by=Plane.XZ)

with BuildSketch() as vertwalls:
    Rectangle(p_outerWidth,p_outerLength,centered=(False,False))
    Fillet(*vertwalls.vertices(),radius=p_sideRadius)
    Offset(amount=-p_thickness,mode=Mode.SUBTRACT)

with BuildSketch() as main_sk:
    Add(vertwalls.sketch)
    locs = GridLocations(p_outerWidth-2*p_screwpostInset,
                         p_outerLength-2*p_screwpostInset,
                         2,
                         2,
                         centered=(False,False),
                         offset=(-p_screwpostInset,-p_screwpostInset)
                         )
    with locs:
        Add(postXS.sketch)
    for idx,l in enumerate(locs):
        #adding reinforcements
        r = postReinf.faces()[0]
        if idx == 0:
            ang = 0
        elif idx == 1:
            ang = 90
        elif idx == 2:
            ang = 270
        elif idx == 3:
            ang = 180
        r = r.rotate(angle=-ang,axis=Axis.Z)
        r.position = l.position
        Add(r)

with BuildPart() as mainp:
    Add(main_sk.sketch)
    Extrude(amount=p_outerHeight)
    with BuildSketch() as bot_sk:
        Rectangle(p_outerWidth,p_outerLength,centered=(False,False))
        Fillet(*bot_sk.vertices(),radius=p_sideRadius)
    Extrude(amount=-p_thickness)
    with BuildSketch(Plane.XY.offset(p_outerHeight)) as wallsAndPosts:
        Add(vertwalls.sketch)
        locs = GridLocations(p_outerWidth-2*p_screwpostInset,
                             p_outerLength-2*p_screwpostInset,
                             2,
                             2,
                             centered=(False,False),
                             offset=(-p_screwpostInset,-p_screwpostInset)
                             )
        with locs:
            Add(postXS.sketch)
    Extrude(amount=p_lipHeight)

show_object(mainp.part.wrapped,options=rand_color())
jdegenstein commented 1 year ago

I retested this with the latest commit a few hours ago and I no longer get the error: Standard_NullObject: BRep_Tool:: TopoDS_Vertex hasn't gp_Pnt that I was getting before. I still don't think the placement of the extrude is correct though, so I think this is half fixed, and luckily much easier to work around (since I can now manually correct the position and now I get valid geometry).

jdegenstein commented 1 year ago

Correction, I can manually correct the position, but I now get warnings.warn(f"Unable to clean {self}") which as I understand means that failed to clean. The issue is that it does not properly sew the 2nd body on top of first, leaving some gaps.

jdegenstein commented 1 year ago

OK, I have done some more testing and simplified the above lengthy example:

from build123d import *

with BuildSketch() as vertwalls:
    Rectangle(40,90)

with BuildPart() as mainp:
    with BuildSketch() as main_sk:
        Rectangle(50,100)
    Extrude(amount=10)
    topf = mainp.faces().sort_by(Axis.Z)[-1]
    with BuildSketch(topf) as vertwalls2:
        Add(vertwalls.sketch)
    Extrude(amount=15)

show_object(mainp.part)

image

I believe that Add does not properly respect the current location. With the above example, the "top face offset" is essentially applied twice leading to the floating box.

If we instead change BuildSketch to Workplanes we get this (also incorrect) result with the 2nd box starting from Plane.XY.

with BuildSketch() as vertwalls:
    Rectangle(40,90)

with BuildPart() as mainp:
    with BuildSketch() as main_sk:
        Rectangle(50,100)
    Extrude(amount=10)
    topf = mainp.faces().sort_by(Axis.Z)[-1]
    with Workplanes(topf) as vertwalls2:
        Add(vertwalls.sketch)
    Extrude(amount=15)

image

Finally if we eliminate the call to Add() completely then we get the correct result:

with BuildPart() as mainp:
    with BuildSketch() as main_sk:
        Rectangle(50,100)
    Extrude(amount=10)
    topf = mainp.faces().sort_by(Axis.Z)[-1]
    with BuildSketch(topf) as vertwalls:
        Rectangle(40,90)
    Extrude(amount=15)

image

gumyr commented 1 year ago

The problem seems to be left-over from prior changes to sketch local/global - Add should have used local_locations (which used to be called just locations) so this is the change:

-                        for location in LocationList._get_context().locations
+                        for location in LocationList._get_context().local_locations

Now:

with BuildSketch() as vertwalls:
    Rectangle(40, 90)

with BuildPart() as mainp:
    with BuildSketch() as main_sk:
        Rectangle(50, 100)
    Extrude(amount=10)
    topf = mainp.faces().sort_by(Axis.Z)[-1]
    with BuildSketch(topf) as vertwalls2:
        show_current_locations("In sketch")
        show_current_workplanes("")
        Add(vertwalls.sketch)
    Extrude(amount=15)

generates: image

and:

with BuildSketch() as vertwalls:
    Rectangle(40, 90)

with BuildPart() as mainp:
    with BuildSketch() as main_sk:
        Rectangle(50, 100)
    Extrude(amount=10)
    topf = mainp.faces().sort_by(Axis.Z)[-1]
    with Workplanes(topf) as vertwalls2:
        Add(vertwalls.sketch)
    Extrude(amount=15)

generates: image

Closing the issue.

gumyr commented 1 year ago

Workplane version doesn't seem to locate the face on the correct plane so the extrusions are different heights.

gumyr commented 1 year ago

The changes to Add for BuildPart (2a0d9bb2461bd8ae792a3fc99b3435c5124354bd) now results in identical results for the two cases. Add is now able to use Locations as part of this change.