Skip to content
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

Add section to docs with further object placement examples #450

Open
jdegenstein opened this issue Jan 3, 2024 · 14 comments
Open

Add section to docs with further object placement examples #450

jdegenstein opened this issue Jan 3, 2024 · 14 comments
Labels
documentation Improvements or additions to documentation enhancement New feature or request

Comments

@jdegenstein
Copy link
Collaborator

jdegenstein commented Jan 3, 2024

Currently the intro examples touch on object placement, but there are more scenarios that need to be covered. I propose that another object placement examples section is added to the docs.

Some ideas:

  1. Reusing faces of an existing part
  2. Further example with PolarLocations illustrating the interaction between object position and radius
  3. More "3D" positioning examples with e.g. CounterBoreHoles
  4. Precursory knowledge building examples for a few joint types
  5. TTT T2-17-CARRIER_BASE example

I am looking for more input here, so please suggest more examples.

@gumyr gumyr added documentation Improvements or additions to documentation enhancement New feature or request labels Jan 5, 2024
@gumyr gumyr added this to the Post Release 1.0.0 milestone Jan 5, 2024
@jdegenstein
Copy link
Collaborator Author

  1. locate, located, move, moved, translate example(s)

@jdegenstein
Copy link
Collaborator Author

  1. illustrate several examples of applying rotation (object.orientation += (10, 20, 30)), with Locations(Rotation((10, 20, 30))):, add(something, rotation=)

@CePeU
Copy link

CePeU commented Jan 30, 2024

Algebra mode also

There are many ways to move an object. One of the lesser known ways is to just change it's position: box.position = (1,2,3)

box.position += (1,2,3) is a relative move

@proegssilb
Copy link

proegssilb commented Mar 16, 2024

The two main issues I've bumped into both depend on placing a work plane in an arbitrary location out in 3D space, and controlling its orientation. For example, using a sweep() to connect two different rectangles via a spline's path, but both rectangles are set at specific points, and are unrelated to each other.

With the ability to place a workplane in an arbitrary spot, it's easy enough to place a workplane at a variable spot; I arbitrarily choose this box's corner over at min-X/max-Y/mid-Z.

(EDIT: One of my examples is actually about locating a sketch at a specific position using coordinates from an existing sketch, but this is already documented. The matter still gets complicated when you stack rotating the second sketch's workplane.)

@proegssilb
Copy link

An issue I might run into: Placing a workplane at the result of projecting a line/shape onto another face. So, if you have two unrelated boxes (A and B), and A has a small hole in the center, projecting that hole's center point onto B, and then dropping a workplane on B's face, centered where that projected centerpoint lands.

@gumyr
Copy link
Owner

gumyr commented Mar 16, 2024

How about this example for arbitrary placement of sketches (@jdegenstein IIRC you have an similar example that might be better)?

with BuildPart() as twist:
    with BuildSketch() as x_section:
        Circle(1)
        with GridLocations(0, 2, 1, 2):
            Circle(0.75, mode=Mode.SUBTRACT)
    for i in range(1, 11):
        with Locations(Location((0, 0, i), (0, 0, i * 36))):
            add(x_section.sketch)
    x_sections = twist.pending_faces  # for display
    loft()

image

@proegssilb
Copy link

@gumyr For an example, I'd rather see as little going on as possible. Either just extrude a Rectangle into a box, or make a cone point at the plane's origin.

Second, how does this work when you rotate the plane?

Because of my prior experience with OpenSCAD, one of my instincts (good, bad, or otherwise) is to make global translations work in a predictable way independent of what the rotation is doing. This is partially because I have a hard time rotating coordinate systems in my head.

Suppose I'm making a simple rectangular duct. The base is centered at global (0, 0, 0), and the top needs to be 15mm above (+Z) and 7mm in front (+Y) of the centerpoint, oriented 34 degrees off from vertical (XZ plane, rotated). The easiest way (in my head) to set this up is one work plane at the origin, and another at (0, 7, 15), rotated one of (34, 124, 214, 304) degrees. Set up those two workplanes, draw your rectangles, use a curve in the YZ plane to set up a sweep between the two. Just need a clean way to set up that second work plane.

@gumyr
Copy link
Owner

gumyr commented Mar 16, 2024

@proegssilb you were too fast - this might address your previous question about projection:

Here a mounting plate for a flange is created (in Algebra mode) with the sketch relative to the flange. Key features are projected from the flange to the plate.

from build123d import *
from ocp_vscode import *
from bd_warehouse.flange import WeldNeckFlange

flange = Pos(1 * IN, 2 * IN, 3 * IN) * WeldNeckFlange(
    nps="12", flange_class=300, face_type="Ring"
)
# Initial sketch plane to be repositioned relative to center of flange
plate_sketch_plane = Plane.XY
pipe_hole = (
    flange.edges()
    .filter_by(GeomType.CIRCLE)
    .group_by(SortBy.RADIUS)[3]
    .group_by(Axis.Z)[0]
)
# Project the pipe hole from the flange onto the plate sketch plane
pipe_hole_projection = project(pipe_hole, workplane=plate_sketch_plane)
# Relocate the plate sketch plane to the flange center and rotate it
plate_sketch_plane = plate_sketch_plane.shift_origin(
    pipe_hole_projection.edge().arc_center
).rotated((0, 0, 15))
# Create the sketch for the plate with dimensions relative to the flange
plate_plan = (
    plate_sketch_plane * Pos(-8 * IN, 0, 0) * RectangleRounded(40 * IN, 30 * IN, 1 * IN)
)
plate = extrude(plate_plan, amount=-1 * IN)

# Project the flange's bolt circle to the plate sketch
flange_bolt_circle = (
    flange.edges()
    .filter_by(GeomType.CIRCLE)
    .group_by(SortBy.RADIUS)[2]
    .group_by(Axis.Z)[0]
)
bolt_circle_projection = project(flange_bolt_circle, workplane=plate_sketch_plane)
# Create some mounting slots relative to the flange
slots = (
    plate_sketch_plane
    * Pos(-18 * IN, 0, 0)
    * GridLocations(10 * IN, 20 * IN, 2, 2)
    * SlotOverall(4 * IN, 1 * IN)
)
# Make faces out of the projected edges
holes = [
    make_face(e) for e in bolt_circle_projection.edges() + pipe_hole_projection.edges()
]
# Cut holes in the plate
for face in slots + holes:
    plate -= extrude(face, amount=-1 * IN)

image

@proegssilb
Copy link

I can't read Algebra mode, but here's a distilled thing from an actual model I had to fumble my way through:

base_width = 40
base_height = 30
base_extr = 15

targ_loc = (15,0,25)
targ_rot = (0,34,0)
targ_width = 20
targ_height = 10
targ_extr = 5

with BuildPart() as plane_place_demo:
    with BuildSketch() as base_sketch:
        Rectangle(base_width, base_height)
    extrude(amount=base_extr)
    targ_plane = Plane(targ_loc).rotated(targ_rot)
    with BuildSketch(targ_plane):
        Rectangle(targ_width, targ_height)
    extrude(amount=targ_extr)

image

That should be pretty close to an intro example. Remove the extrusions, add an offset or two (does offsetting in negative mode work?), add a line & sweep, and you have a tutorial for ducts that I could have used while working on my 3D printer.

Hope this helps.

@gumyr
Copy link
Owner

gumyr commented Mar 16, 2024

How is this for an example of building a duct with three different operations for creating the Solid, extrude, sweep and loft?

from build123d import *
from ocp_vscode import *

with BuildPart() as duct:
    # Straight vertical section
    with BuildSketch() as duct_plan:
        Rectangle(30, 20)
        inside = offset(amount=-1, mode=Mode.SUBTRACT)
    extrude(amount=10)
    top = duct.faces().sort_by(Axis.Z)[-1]

    # Swept section
    with BuildLine(Plane.XZ):
        path1 = JernArc(
            start=top.center(),
            tangent=(0, 1),
            radius=50,
            arc_size=-45,
        )
    sweep(top, path=path1)

    # Lofted section
    with BuildLine(Plane.XZ):
        path2 = JernArc(
            start=path1 @ 1,
            tangent=path1 % 1,
            radius=50,
            arc_size=-45,
        )
    with BuildSketch(path2 ^ 0.0):  # Position & Orientation at beginning
        add(duct_plan.sketch)
    with BuildSketch(path2 ^ 0.5):  # Position & Orientation at middle
        add(duct_plan.sketch)
    with BuildSketch(path2 ^ 1.0):  # Position & Orientation at end
        add(duct_plan.sketch)
    loft(ruled=True)
    # loft doesn't support a hole, so make one
    with BuildSketch(path2 ^ 0.0):
        add(inside)
    with BuildSketch(path2 ^ 0.5):
        add(inside)
    with BuildSketch(path2 ^ 1.0):
        add(inside)
    loft(ruled=True, mode=Mode.SUBTRACT)

show_all()

image

@gumyr
Copy link
Owner

gumyr commented Mar 16, 2024

The more efficient way to create is a duct with a single sweep like this:

with BuildPart() as duct2:
    with BuildLine(Plane.XZ):
        l1 = Line((0, 0), (0, 10))
        l2 = JernArc(
            start=l1 @ 1,
            tangent=l1 % 1,
            radius=50,
            arc_size=-90,
        )
        l3 = Line(l2 @ 1, l2 @ 1 + (10, 0))
    with BuildSketch() as duct_plan:
        Rectangle(30, 20)
        inside = offset(amount=-1, mode=Mode.SUBTRACT)
    sweep()

image
but it doesn't show the other operations.

@gumyr
Copy link
Owner

gumyr commented Mar 16, 2024

# Suppose I'm making a simple rectangular duct. The base is centered at
# global (0, 0, 0), and the top needs to be 15mm above (+Z) and 7mm in front (+Y)
# of the centerpoint, oriented 34 degrees off from vertical (XZ plane, rotated).
with BuildPart() as duct3:
    with BuildSketch() as duct_plan1:
        Rectangle(30, 20)
    with BuildSketch(
        Plane(origin=(0, 7, 15), x_dir=(1, 0, 0), z_dir=(0, 1, 0)).rotated((34, 0, 0))
    ) as duct_plan2:
        add(duct_plan1.sketch)
    loft()

image

@gumyr
Copy link
Owner

gumyr commented Mar 16, 2024

Here is the flange example in Builder mode:

flange = WeldNeckFlange(nps="12", flange_class=300, face_type="Ring", mode=Mode.PRIVATE)
flange_projection = project(flange.edges(), workplane=Plane.XY)
pipe_edge: Edge = (
    flange_projection.edges().filter_by(GeomType.CIRCLE).group_by(SortBy.RADIUS)[3][0]
)

with BuildPart() as plate:
    with BuildSketch(
        Plane(origin=pipe_edge.arc_center, x_dir=Vector(1, 0, 0).rotate(Axis.Z, 15))
    ) as plate_plan:
        with Locations((-8 * IN, 0, 0)):
            RectangleRounded(40 * IN, 30 * IN, 1 * IN)
        make_face(pipe_edge, mode=Mode.SUBTRACT)
        for hole_edge in (
            flange_projection.edges()
            .filter_by(GeomType.CIRCLE)
            .group_by(SortBy.RADIUS)[0]
        ):
            make_face(hole_edge, mode=Mode.SUBTRACT)
        with Locations((-18 * IN, 0, 0)):
            with GridLocations(10 * IN, 20 * IN, 2, 2):
                SlotOverall(4 * IN, 1 * IN, mode=Mode.SUBTRACT)
    extrude(amount=1 * IN)

show(plate, Pos(0, 0, 4 * IN) * flange)

image

@proegssilb
Copy link

This is the example that I'd consider "simple enough" for documentation purposes. If the two boxes I showed are too simple or are visually confusing, then the comment I linked to is what I'd suggest using.

With tutorials & introductory examples, there's a balance between "having an interesting part" and "showing a specific idea without minimal noise". The idea that I had a hard time finding in the documentation was "how do I position a workplane at a specific location in 3D space, with a particular orientation". So I picked the most boring example that demonstrated that. Ducts are what I was working on when I discovered I had no idea how to do that thing, so they're a "complete part" that can still show the same concept.

With the flange in Builder mode, I now see where it constructs a plane at a variable spot. If, however, I was completely brand new to build123d, there would be a lot going on in that example, and it'd take me a while to sift through everything. Therefore, in my opinion, it makes a better tutorial than an Introductory Example. Whereas my boxes code was aiming for closer to an introductory example, with the assumption that a duct could be a tutorial.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants