Introducing new PEP8 compatible direct api

This commit is contained in:
Roger Maitland 2022-10-14 10:37:05 -04:00
parent c253b3bba1
commit 9599e8e1ef
32 changed files with 9197 additions and 7291 deletions

View file

@ -47,4 +47,4 @@ with BuildPart() as example:
exporters.export(example.part, "selector_after.svg", opt=svg_opts)
if "show_object" in locals():
show_object(example.part, name="part")
show_object(example.part.wrapped, name="part")

View file

@ -22,4 +22,4 @@ with bd.BuildPart() as bp:
# )
if "show_object" in locals():
show_object(bp.part, name="box on faces")
show_object(bp.part.wrapped, name="box on faces")

View file

@ -106,8 +106,8 @@ if False:
)
if "show_object" in locals():
show_object(one.line, name="one")
show_object(two.sketch, name="two")
show_object(three_d.part, name="three_d")
show_object(extension_lines.line, name="extension_lines")
show_object(build.sketch, name="build")
show_object(one.line.wrapped, name="one")
show_object(two.sketch.wrapped, name="two")
show_object(three_d.part.wrapped, name="three_d")
show_object(extension_lines.line.wrapped, name="extension_lines")
show_object(build.sketch.wrapped, name="build")

View file

@ -59,12 +59,12 @@ with BuildSketch() as centre_field:
if "show_object" in locals():
show_object(
[west_field.sketch, east_field.sketch, leaf.sketch],
[west_field.sketch.wrapped, east_field.sketch.wrapped, leaf.sketch.wrapped],
name="flag_red_parts",
options={"color": (255, 0, 0)},
)
show_object(
centre_field.sketch,
centre_field.sketch.wrapped,
name="flag_white_part",
options={"color": (255, 255, 255)},
)

View file

@ -16,4 +16,4 @@ with BuildPart() as pcb:
Extrude(amount=3)
if "show_object" in locals():
show_object(pcb.part)
show_object(pcb.part.wrapped)

View file

@ -53,4 +53,4 @@ with BuildSketch() as clock_face:
)
if "show_object" in locals():
show_object(clock_face.sketch, name="clock_face")
show_object(clock_face.sketch.wrapped, name="clock_face")

View file

@ -76,4 +76,4 @@ with BuildPart("XZ") as rail:
Extrude(amount=-height, mode=Mode.SUBTRACT)
if "show_object" in locals():
show_object(rail.part, name="rail")
show_object(rail.part.wrapped, name="rail")

View file

@ -101,9 +101,15 @@ with BuildPart() as key_cap:
Extrude(amount=3.5 * MM, mode=Mode.ADD)
if "show_object" in locals():
show_object(simple.part.translate((-15, 0, 0)), name="simple pending extrude")
show_object(both.part.translate((20, 10, 0)), name="simple both")
show_object(multiple.part.translate((0, -20, 0)), name="multiple pending extrude")
show_object(single_multiple.part.translate((0, 20, 0)), name="single multiple")
show_object(non_planar.part.translate((20, -10, 0)), name="non planar")
show_object(key_cap.part, name="key cap", options={"alpha": 0.7})
show_object(
simple.part.translate((-15, 0, 0)).wrapped, name="simple pending extrude"
)
show_object(both.part.translate((20, 10, 0)).wrapped, name="simple both")
show_object(
multiple.part.translate((0, -20, 0)).wrapped, name="multiple pending extrude"
)
show_object(
single_multiple.part.translate((0, 20, 0)).wrapped, name="single multiple"
)
show_object(non_planar.part.translate((20, -10, 0)).wrapped, name="non planar")
show_object(key_cap.part.wrapped, name="key cap", options={"alpha": 0.7})

View file

@ -63,6 +63,7 @@ with BuildPart() as handle:
if "show_object" in locals():
show_object(handle_path, name="handle_path")
show_object(sections, name="sections")
show_object(handle.part, name="handle", options=dict(alpha=0.6))
show_object(handle_path.wrapped, name="handle_path")
for i, section in enumerate(sections):
show_object(section.wrapped, name="section" + str(i))
show_object(handle.part.wrapped, name="handle", options=dict(alpha=0.6))

View file

@ -49,7 +49,7 @@ with Workplanes("XY"):
x_count=exchanger_diameter // tube_diameter,
y_count=exchanger_diameter // tube_diameter,
)
if l.position().Length < bundle_diameter / 2
if l.position().length < bundle_diameter / 2
]
tube_count = len(tube_locations)
print(f"{tube_count=}")
@ -71,19 +71,20 @@ with BuildPart() as heat_exchanger:
with Locations(*tube_locations):
Circle(radius=tube_diameter / 2 - tube_wall_thickness, mode=Mode.SUBTRACT)
Extrude(amount=plate_thickness)
half_volume_before_fillet = heat_exchanger.part.Volume()
half_volume_before_fillet = heat_exchanger.part.volume()
# Simulate welded tubes by adding a fillet to the outside radius of the tubes
Fillet(
*((heat_exchanger.edges() % GeomType.CIRCLE > SortBy.RADIUS) < Axis.Z)[
2 * tube_count : 3 * tube_count
],
*heat_exchanger.edges()
.filter_by_type(GeomType.CIRCLE)
.sort_by(SortBy.RADIUS)
.sort_by(Axis.Z, reverse=True)[2 * tube_count : 3 * tube_count],
radius=fillet_radius,
)
half_volume_after_fillet = heat_exchanger.part.Volume()
half_volume_after_fillet = heat_exchanger.part.volume()
Mirror(about="XY")
fillet_volume = 2 * (half_volume_after_fillet - half_volume_before_fillet)
print(f"{fillet_volume=}")
if "show_object" in locals():
show_object(heat_exchanger.part)
show_object(heat_exchanger.part.wrapped)

View file

@ -51,7 +51,7 @@ with BuildPart() as flush_counter_sink:
CounterSinkHole(radius=1, counter_sink_radius=1.5)
if "show_object" in locals():
show_object(thru_hole.part, name="though hole")
show_object(recessed_counter_bore.part, name="recessed counter bore")
show_object(recessed_counter_sink.part, name="recessed counter sink")
show_object(flush_counter_sink.part, name="flush counter sink")
show_object(thru_hole.part.wrapped, name="though hole")
show_object(recessed_counter_bore.part.wrapped, name="recessed counter bore")
show_object(recessed_counter_sink.part.wrapped, name="recessed counter sink")
show_object(flush_counter_sink.part.wrapped, name="flush counter sink")

View file

@ -48,4 +48,4 @@ with BuildPart() as pipes:
Fillet(*pipes.edges(Select.LAST), radius=0.2)
if "show_object" in locals():
show_object(pipes.part, name="intersecting pipes")
show_object(pipes.part.wrapped, name="intersecting pipes")

View file

@ -82,4 +82,4 @@ with BuildPart() as lego:
if "show_object" in locals():
show_object(lego.part, name="lego")
show_object(lego.part.wrapped, name="lego")

View file

@ -39,4 +39,4 @@ with BuildPart() as art:
Offset(openings=top_bottom, amount=0.5)
if "show_object" in locals():
show_object(art.part, name="art")
show_object(art.part.wrapped, name="art")

View file

@ -44,4 +44,4 @@ with BuildPart() as pillow_block:
# Render the part
if "show_object" in locals():
show_object(pillow_block.part)
show_object(pillow_block.part.wrapped)

View file

@ -41,4 +41,4 @@ with BuildLine() as roller_coaster:
Spline(screw @ 1, (-100, 30, 10), powerup @ 0, tangents=(screw % 1, powerup % 0))
if "show_object" in locals():
show_object(roller_coaster.line, name="roller_coaster")
show_object(roller_coaster.line.wrapped, name="roller_coaster")

View file

@ -43,13 +43,12 @@ with BuildPart() as vase:
Polyline(
l5 @ 1,
l5 @ 1 + Vector(0, 1),
(0, (l5 @ 1).y + 1),
(0, (l5 @ 1).Y + 1),
l1 @ 0,
)
BuildFace()
Revolve(axis=Axis.Y)
# Offset(openings=vase.faces().filter_by_axis(Axis.Y)[-1], amount=-1)
Offset(openings=(vase.faces() | Axis.Y) >> Axis.Y, amount=-1)
Offset(openings=vase.faces().filter_by_axis(Axis.Y)[-1], amount=-1)
top_edges = (
vase.edges().filter_by_position(Axis.Y, 60, 62).filter_by_type(GeomType.CIRCLE)
)
@ -58,6 +57,6 @@ with BuildPart() as vase:
if "show_object" in locals():
# show_object(outline.line, name="outline")
# show_object(profile.sketch, name="profile")
show_object(vase.part, name="vase")
# show_object(outline.line.wrapped, name="outline")
# show_object(profile.sketch.wrapped, name="profile")
show_object(vase.part.wrapped, name="vase")

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,165 @@
"""
build123d ENUMs
name: build_enums.py
by: Gumyr
date: Oct 11th 2022
desc:
A collection of enums used throughout build123d.
license:
Copyright 2022 Gumyr
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from __future__ import annotations
from enum import Enum, auto
class Select(Enum):
"""Selector scope - all or last operation"""
ALL = auto()
LAST = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class Kind(Enum):
"""Offset corner transition"""
ARC = auto()
INTERSECTION = auto()
TANGENT = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class Keep(Enum):
"""Split options"""
TOP = auto()
BOTTOM = auto()
BOTH = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class Mode(Enum):
"""Combination Mode"""
ADD = auto()
SUBTRACT = auto()
INTERSECT = auto()
REPLACE = auto()
PRIVATE = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class Transition(Enum):
"""Sweep discontinuity handling option"""
RIGHT = auto()
ROUND = auto()
TRANSFORMED = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class FontStyle(Enum):
"""Text Font Styles"""
REGULAR = auto()
BOLD = auto()
ITALIC = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class Halign(Enum):
"""Text Horizontal Alignment"""
CENTER = auto()
LEFT = auto()
RIGHT = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class Valign(Enum):
"""Text Vertical Alignment"""
CENTER = auto()
TOP = auto()
BOTTOM = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class Until(Enum):
"""Extrude limit"""
NEXT = auto()
LAST = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class SortBy(Enum):
"""Sorting criteria"""
LENGTH = auto()
RADIUS = auto()
AREA = auto()
VOLUME = auto()
DISTANCE = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"
class GeomType(Enum):
"""CAD geometry object type"""
PLANE = auto()
CYLINDER = auto()
CONE = auto()
SPHERE = auto()
TORUS = auto()
BEZIER = auto()
BSPLINE = auto()
REVOLUTION = auto()
EXTRUSION = auto()
OFFSET = auto()
LINE = auto()
CIRCLE = auto()
ELLIPSE = auto()
HYPERBOLA = auto()
PARABOLA = auto()
OTHER = auto()
def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}>"

View file

@ -28,29 +28,31 @@ license:
"""
from typing import Union
import logging
from cadquery import Matrix
from .build_enums import Mode, Kind, Keep
from .direct_api import (
Edge,
Wire,
Vector,
Compound,
Location,
VectorLike,
ShapeList,
Face,
Plane,
PlaneLike,
Matrix,
Rotation,
RotationLike,
Shape,
Vertex,
Solid,
)
from build123d import (
BuildLine,
BuildSketch,
BuildPart,
Mode,
RotationLike,
Rotation,
Builder,
LocationList,
Kind,
Keep,
PlaneLike,
Shape,
Vertex,
Plane,
Compound,
Edge,
Wire,
Face,
Solid,
Location,
Vector,
validate_inputs,
)
@ -108,7 +110,7 @@ class Add(Compound):
new_solids.extend(compound.get_type(Solid))
new_objects = [obj for obj in objects if isinstance(obj, Edge)]
for new_wires in filter(lambda o: isinstance(o, Wire), objects):
new_objects.extend(new_wires.Edges())
new_objects.extend(new_wires.edges())
# Add to pending faces and edges
context._add_to_pending(*new_faces)
@ -140,7 +142,7 @@ class Add(Compound):
raise RuntimeError(
f"Add does not support builder {context.__class__.__name__}"
)
super().__init__(Compound.makeCompound(new_objects).wrapped)
super().__init__(Compound.make_compound(new_objects).wrapped)
#
@ -174,9 +176,9 @@ class BoundingBox(Compound):
for obj in objects:
if isinstance(obj, Vertex):
continue
bounding_box = obj.BoundingBox()
bounding_box = obj.bounding_box()
new_objects.append(
Solid.makeBox(
Solid.make_box(
bounding_box.xlen,
bounding_box.ylen,
bounding_box.zlen,
@ -184,14 +186,14 @@ class BoundingBox(Compound):
)
)
context._add_to_context(*new_objects, mode=mode)
super().__init__(Compound.makeCompound(new_objects).wrapped)
super().__init__(Compound.make_compound(new_objects).wrapped)
elif isinstance(context, BuildSketch):
new_faces = []
for obj in objects:
if isinstance(obj, Vertex):
continue
bounding_box = obj.BoundingBox()
bounding_box = obj.bounding_box()
vertices = [
(bounding_box.xmin, bounding_box.ymin),
(bounding_box.xmin, bounding_box.ymax),
@ -200,11 +202,13 @@ class BoundingBox(Compound):
(bounding_box.xmin, bounding_box.ymin),
]
new_faces.append(
Face.makeFromWires(Wire.makePolygon([Vector(v) for v in vertices]))
Face.make_from_wires(
Wire.make_polygon([Vector(v) for v in vertices])
)
)
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
else:
raise RuntimeError(
@ -239,12 +243,12 @@ class Chamfer(Compound):
elif isinstance(context, BuildSketch):
new_faces = []
for face in context.faces():
vertices_in_face = [v for v in face.Vertices() if v in objects]
vertices_in_face = [v for v in face.vertices() if v in objects]
if vertices_in_face:
new_faces.append(face.chamfer2D(length, vertices_in_face))
new_faces.append(face.chamfer_2d(length, vertices_in_face))
else:
new_faces.append(face)
new_sketch = Compound.makeCompound(new_faces)
new_sketch = Compound.make_compound(new_faces)
context._add_to_context(new_sketch, mode=Mode.REPLACE)
super().__init__(new_sketch.wrapped)
else:
@ -277,12 +281,12 @@ class Fillet(Compound):
elif isinstance(context, BuildSketch):
new_faces = []
for face in context.faces():
vertices_in_face = [v for v in face.Vertices() if v in objects]
vertices_in_face = [v for v in face.vertices() if v in objects]
if vertices_in_face:
new_faces.append(face.fillet2D(radius, vertices_in_face))
new_faces.append(face.fillet_2d(radius, vertices_in_face))
else:
new_faces.append(face)
new_sketch = Compound.makeCompound(new_faces)
new_sketch = Compound.make_compound(new_faces)
context._add_to_context(new_sketch, mode=Mode.REPLACE)
super().__init__(new_sketch.wrapped)
else:
@ -330,12 +334,12 @@ class Mirror(Compound):
[0.0, 0.0, 00.0, 1.0],
]
)
localized = [mirror_plane.toLocalCoords(o) for o in objects]
local_mirrored = [o.transformGeometry(scale_matrix) for o in localized]
mirrored = [mirror_plane.fromLocalCoords(o) for o in local_mirrored]
localized = [mirror_plane.to_local_coords(o) for o in objects]
local_mirrored = [o.transform_geometry(scale_matrix) for o in localized]
mirrored = [mirror_plane.from_local_coords(o) for o in local_mirrored]
context._add_to_context(*mirrored, mode=mode)
super().__init__(Compound.makeCompound(mirrored).wrapped)
super().__init__(Compound.make_compound(mirrored).wrapped)
class Offset(Compound):
@ -400,14 +404,14 @@ class Offset(Compound):
new_faces = []
for face in faces:
new_faces.append(
Face.makeFromWires(
face.outerWire().offset2D(amount, kind=kind.name.lower())[0]
Face.make_from_wires(
face.outer_wire().offset_2d(amount, kind=kind.name.lower())[0]
)
)
if edges:
if len(edges) == 1:
raise ValueError("At least two edges are required")
new_wires = Wire.assembleEdges(edges).offset2D(
new_wires = Wire.assemble_edges(edges).offset_2d(
amount, kind=kind.name.lower()
)
else:
@ -419,7 +423,7 @@ class Offset(Compound):
new_solids = []
for solid in solids:
if openings:
openings_in_this_solid = [o for o in openings if o in solid.Faces()]
openings_in_this_solid = [o for o in openings if o in solid.faces()]
else:
openings_in_this_solid = []
new_solids.append(
@ -430,7 +434,7 @@ class Offset(Compound):
new_objects = new_wires + new_faces + new_solids
context._add_to_context(*new_objects, mode=mode)
super().__init__(Compound.makeCompound(new_objects).wrapped)
super().__init__(Compound.make_compound(new_objects).wrapped)
class Scale(Compound):
@ -476,9 +480,9 @@ class Scale(Compound):
scale_matrix = Matrix(
[
[factor.x, 0.0, 0.0, 0.0],
[0.0, factor.y, 0.0, 0.0],
[0.0, 0.0, factor.z, 0.0],
[factor.X, 0.0, 0.0, 0.0],
[0.0, factor.Y, 0.0, 0.0],
[0.0, 0.0, factor.Z, 0.0],
[0.0, 0.0, 00.0, 1.0],
]
)
@ -487,12 +491,12 @@ class Scale(Compound):
current_location = obj.location()
obj_at_origin = obj.located(Location(Vector()))
new_objects.append(
obj_at_origin.transformGeometry(scale_matrix).locate(current_location)
obj_at_origin.transform_geometry(scale_matrix).locate(current_location)
)
context._add_to_context(*new_objects, mode=mode)
super().__init__(Compound.makeCompound(new_objects).wrapped)
super().__init__(Compound.make_compound(new_objects).wrapped)
class Split(Compound):
@ -522,8 +526,8 @@ class Split(Compound):
if keep == Keep.TOP
else Vector(-max_size, -max_size, -2 * max_size)
)
return bisect_plane.fromLocalCoords(
Solid.makeBox(2 * max_size, 2 * max_size, 2 * max_size).moved(
return bisect_plane.from_local_coords(
Solid.make_box(2 * max_size, 2 * max_size, 2 * max_size).moved(
Location(cutter_center)
)
)
@ -546,7 +550,7 @@ class Split(Compound):
new_objects = []
for obj in objects:
max_size = obj.BoundingBox().DiagonalLength
max_size = obj.bounding_box().diagonal_length
cutters = []
if keep == Keep.BOTH:
@ -557,4 +561,4 @@ class Split(Compound):
new_objects.append(obj.intersect(*cutters))
context._add_to_context(*new_objects, mode=mode)
super().__init__(Compound.makeCompound(new_objects).wrapped)
super().__init__(Compound.make_compound(new_objects).wrapped)

View file

@ -28,24 +28,20 @@ license:
import inspect
from math import sin, cos, radians, sqrt
from typing import Union, Iterable
from build123d.build_common import (
from .build_enums import Select, Mode
from .direct_api import (
Edge,
Wire,
Vector,
Vertex,
Compound,
Location,
Builder,
VectorLike,
Select,
ShapeList,
Mode,
logger,
validate_inputs,
Face,
Plane,
PlaneLike,
)
from .build_common import Builder, logger, validate_inputs
class BuildLine(Builder):
@ -93,7 +89,7 @@ class BuildLine(Builder):
ShapeList[Wire]: Wires extracted
"""
if select == Select.ALL:
wire_list = Wire.combine(self.line.Edges())
wire_list = Wire.combine(self.line.edges())
elif select == Select.LAST:
wire_list = Wire.combine(self.last_edges)
return ShapeList(wire_list)
@ -119,7 +115,7 @@ class BuildLine(Builder):
new_edges.extend(compound.get_type(Edge))
new_wires.extend(compound.get_type(Wire))
for wire in new_wires:
new_edges.extend(wire.Edges())
new_edges.extend(wire.edges())
if new_edges:
logger.debug(
"Add %d Edge(s) into line with Mode=%s", len(new_edges), mode
@ -129,12 +125,12 @@ class BuildLine(Builder):
if self.line:
self.line = self.line.fuse(*new_edges)
else:
self.line = Compound.makeCompound(new_edges)
self.line = Compound.make_compound(new_edges)
elif mode == Mode.REPLACE:
self.line = Compound.makeCompound(new_edges)
self.line = Compound.make_compound(new_edges)
self.last_edges = new_edges
self.last_vertices = list(
set(v for e in self.last_edges for v in e.Vertices())
set(v for e in self.last_edges for v in e.vertices())
)
@classmethod
@ -181,7 +177,7 @@ class CenterArc(Edge):
points = []
if abs(arc_size) >= 360:
arc = Edge.makeCircle(
arc = Edge.make_circle(
radius,
center,
angle1=start_angle,
@ -211,7 +207,7 @@ class CenterArc(Edge):
sin(radians(start_angle + arc_size)),
)
)
arc = Edge.makeThreePointArc(*points)
arc = Edge.make_three_point_arc(*points)
context._add_to_context(arc, mode=mode)
super().__init__(arc.wrapped)
@ -246,10 +242,10 @@ class Helix(Wire):
):
context: BuildLine = BuildLine._get_context()
validate_inputs(self, context)
helix = Wire.makeHelix(
helix = Wire.make_helix(
pitch, height, radius, Vector(center), Vector(direction), arc_size, lefhand
)
context._add_to_context(*helix.Edges(), mode=mode)
context._add_to_context(*helix.edges(), mode=mode)
super().__init__(helix.wrapped)
@ -274,7 +270,7 @@ class Line(Edge):
lines_pts = [Vector(p) for p in pts]
new_edge = Edge.makeLine(lines_pts[0], lines_pts[1])
new_edge = Edge.make_line(lines_pts[0], lines_pts[1])
context._add_to_context(new_edge, mode=mode)
super().__init__(new_edge.wrapped)
@ -308,11 +304,11 @@ class PolarLine(Edge):
if angle is not None:
x_val = cos(radians(angle)) * length
y_val = sin(radians(angle)) * length
new_edge = Edge.makeLine(
new_edge = Edge.make_line(
Vector(start), Vector(start) + Vector(x_val, y_val, 0)
)
elif direction is not None:
new_edge = Edge.makeLine(
new_edge = Edge.make_line(
Vector(start), Vector(start) + Vector(direction).normalized() * length
)
else:
@ -346,11 +342,11 @@ class Polyline(Wire):
lines_pts = [Vector(p) for p in pts]
new_edges = [
Edge.makeLine(lines_pts[i], lines_pts[i + 1])
Edge.make_line(lines_pts[i], lines_pts[i + 1])
for i in range(len(lines_pts) - 1)
]
if close and (new_edges[0] @ 0 - new_edges[-1] @ 1).Length > 1e-5:
new_edges.append(Edge.makeLine(new_edges[-1] @ 1, new_edges[0] @ 0))
if close and (new_edges[0] @ 0 - new_edges[-1] @ 1).length > 1e-5:
new_edges.append(Edge.make_line(new_edges[-1] @ 1, new_edges[0] @ 0))
context._add_to_context(*new_edges, mode=mode)
super().__init__(Wire.combine(new_edges)[0].wrapped)
@ -384,7 +380,7 @@ class RadiusArc(Edge):
end = Vector(end_point)
# Calculate the sagitta from the radius
length = end.sub(start).Length / 2.0
length = end.sub(start).length / 2.0
try:
sagitta = abs(radius) - sqrt(radius**2 - length**2)
except ValueError as exception:
@ -429,14 +425,14 @@ class SagittaArc(Edge):
sagitta_vector = (end - start).normalized() * abs(sagitta)
if sagitta > 0:
sagitta_vector.x, sagitta_vector.y = (
-sagitta_vector.y,
sagitta_vector.x,
sagitta_vector.X, sagitta_vector.Y = (
-sagitta_vector.Y,
sagitta_vector.X,
) # Rotate sagitta_vector +90 deg
else:
sagitta_vector.x, sagitta_vector.y = (
sagitta_vector.y,
-sagitta_vector.x,
sagitta_vector.X, sagitta_vector.Y = (
sagitta_vector.Y,
-sagitta_vector.X,
) # Rotate sagitta_vector -90 deg
sag_point = mid_point + sagitta_vector
@ -481,7 +477,7 @@ class Spline(Edge):
else:
scalars = tangent_scalars
spline = Edge.makeSpline(
spline = Edge.make_spline(
[p if isinstance(p, Vector) else Vector(*p) for p in spline_pts],
tangents=[
t * s if isinstance(t, Vector) else Vector(*t) * s
@ -503,7 +499,7 @@ class TangentArc(Edge):
Args:
pts (VectorLike): sequence of two points
tangent (VectorLike): tanget to constrain arc
tangent (VectorLike): tangent to constrain arc
tangent_from_first (bool, optional): apply tangent to first point. Note, applying
tangent to end point will flip the orientation of the arc. Defaults to True.
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
@ -527,7 +523,7 @@ class TangentArc(Edge):
arc_tangent = Vector(tangent)
point_indices = (0, -1) if tangent_from_first else (-1, 0)
arc = Edge.makeTangentArc(
arc = Edge.make_tangent_arc(
arc_pts[point_indices[0]], arc_tangent, arc_pts[point_indices[1]]
)
@ -554,6 +550,6 @@ class ThreePointArc(Edge):
if len(pts) != 3:
raise ValueError("ThreePointArc requires three points")
points = [Vector(p) for p in pts]
arc = Edge.makeThreePointArc(*points)
arc = Edge.make_three_point_arc(*points)
context._add_to_context(arc, mode=mode)
super().__init__(arc.wrapped)

View file

@ -34,31 +34,32 @@ from warnings import warn
from math import radians, tan, sqrt
from typing import Union, Iterable
from OCP.gp import gp_Pln, gp_Lin
from build123d.build_common import (
from .build_enums import Mode, Until, Select, Transition
from .direct_api import (
Edge,
Face,
Wire,
Vector,
Compound,
Solid,
Plane,
Shell,
Compound,
Location,
VectorLike,
Builder,
Mode,
PlaneLike,
Select,
ShapeList,
Until,
Location,
Face,
Plane,
PlaneLike,
Axis,
Transition,
Rotation,
RotationLike,
Shell,
)
from .build_common import (
Builder,
logger,
validate_inputs,
Rotation,
LocationList,
WorkplaneList,
Location,
)
@ -84,7 +85,7 @@ class BuildPart(Builder):
@property
def pending_edges_as_wire(self) -> Wire:
"""Return a wire representation of the pending edges"""
return Wire.assembleEdges(self.pending_edges)
return Wire.assemble_edges(self.pending_edges)
def __init__(
self,
@ -114,7 +115,7 @@ class BuildPart(Builder):
ShapeList[Solid]: Solids extracted
"""
if select == Select.ALL:
solid_list = self.part.Solids()
solid_list = self.part.solids()
elif select == Select.LAST:
solid_list = self.last_solids
return ShapeList(solid_list)
@ -179,7 +180,7 @@ class BuildPart(Builder):
new_edges.append(obj)
elif isinstance(obj, Compound):
new_edges.extend(obj.get_type(Edge))
new_edges.extend([w.Edges() for w in obj.get_type(Wire)])
new_edges.extend([w.edges() for w in obj.get_type(Wire)])
new_faces.extend(obj.get_type(Face))
new_solids.extend(obj.get_type(Solid))
else:
@ -191,10 +192,10 @@ class BuildPart(Builder):
new_solids.extend(new_faces)
new_faces = []
pre_vertices = set() if self.part is None else set(self.part.Vertices())
pre_edges = set() if self.part is None else set(self.part.Edges())
pre_faces = set() if self.part is None else set(self.part.Faces())
pre_solids = set() if self.part is None else set(self.part.Solids())
pre_vertices = set() if self.part is None else set(self.part.vertices())
pre_edges = set() if self.part is None else set(self.part.edges())
pre_faces = set() if self.part is None else set(self.part.faces())
pre_solids = set() if self.part is None else set(self.part.solids())
if new_solids:
logger.debug(
@ -219,7 +220,7 @@ class BuildPart(Builder):
raise RuntimeError("Nothing to intersect with")
self.part = self.part.intersect(*new_solids).clean()
elif mode == Mode.REPLACE:
self.part = Compound.makeCompound(list(new_solids)).clean()
self.part = Compound.make_compound(list(new_solids)).clean()
logger.info(
"Completed integrating %d object(s) into part with Mode=%s",
@ -227,18 +228,18 @@ class BuildPart(Builder):
mode,
)
post_vertices = set() if self.part is None else set(self.part.Vertices())
post_edges = set() if self.part is None else set(self.part.Edges())
post_faces = set() if self.part is None else set(self.part.Faces())
post_solids = set() if self.part is None else set(self.part.Solids())
post_vertices = set() if self.part is None else set(self.part.vertices())
post_edges = set() if self.part is None else set(self.part.edges())
post_faces = set() if self.part is None else set(self.part.faces())
post_solids = set() if self.part is None else set(self.part.solids())
self.last_vertices = list(post_vertices - pre_vertices)
self.last_edges = list(post_edges - pre_edges)
self.last_faces = list(post_faces - pre_faces)
self.last_solids = list(post_solids - pre_solids)
for plane in WorkplaneList._get_context().workplanes:
global_faces = [plane.fromLocalCoords(face) for face in new_faces]
global_edges = [plane.fromLocalCoords(edge) for edge in new_edges]
global_faces = [plane.from_local_coords(face) for face in new_faces]
global_edges = [plane.from_local_coords(edge) for edge in new_edges]
self._add_to_pending(*global_edges)
self._add_to_pending(*global_faces, face_plane=plane)
@ -290,16 +291,16 @@ class CounterBoreHole(Compound):
new_solids = []
for location in LocationList._get_context().locations:
hole_depth = (
context.part.fuse(Solid.makeBox(1, 1, 1).locate(location))
.BoundingBox()
.DiagonalLength
context.part.fuse(Solid.make_box(1, 1, 1).locate(location))
.bounding_box()
.diagonal_length
if not depth
else depth
)
new_solids.append(
Solid.makeCylinder(radius, hole_depth, (0, 0, 0), (0, 0, -1))
Solid.make_cylinder(radius, hole_depth, (0, 0, 0), (0, 0, -1))
.fuse(
Solid.makeCylinder(
Solid.make_cylinder(
counter_bore_radius,
counter_bore_depth + hole_depth,
(0, 0, -counter_bore_depth),
@ -309,7 +310,7 @@ class CounterBoreHole(Compound):
.locate(location)
)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
class CounterSinkHole(Compound):
@ -344,17 +345,17 @@ class CounterSinkHole(Compound):
for location in LocationList._get_context().locations:
hole_depth = (
context.part.fuse(Solid.makeBox(1, 1, 1).locate(location))
.BoundingBox()
.DiagonalLength
context.part.fuse(Solid.make_box(1, 1, 1).locate(location))
.bounding_box()
.diagonal_length
if not depth
else depth
)
cone_height = counter_sink_radius / tan(radians(counter_sink_angle / 2.0))
new_solids = [
Solid.makeCylinder(radius, hole_depth, (0, 0, 0), (0, 0, -1))
Solid.make_cylinder(radius, hole_depth, (0, 0, 0), (0, 0, -1))
.fuse(
Solid.makeCone(
Solid.make_cone(
counter_sink_radius,
0.0,
cone_height,
@ -363,14 +364,14 @@ class CounterSinkHole(Compound):
)
)
.fuse(
Solid.makeCylinder(
Solid.make_cylinder(
counter_sink_radius, hole_depth, (0, 0, 0), (0, 0, 1)
)
)
.locate(location)
]
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
class Extrude(Compound):
@ -429,31 +430,31 @@ class Extrude(Compound):
if until:
# Determine the maximum dimensions of faces and part
if len(faces) > 1:
f_bb = Face.fuse(*faces).BoundingBox()
f_bb = Face.fuse(*faces).bounding_box()
else:
f_bb = faces[0].BoundingBox()
p_bb = context.part.BoundingBox()
f_bb = faces[0].bounding_box()
p_bb = context.part.bounding_box()
scene_xlen = max(f_bb.xmax, p_bb.xmax) - min(f_bb.xmin, p_bb.xmin)
scene_ylen = max(f_bb.ymax, p_bb.ymax) - min(f_bb.ymin, p_bb.ymin)
scene_zlen = max(f_bb.zmax, p_bb.zmax) - min(f_bb.zmin, p_bb.zmin)
max_dimension = sqrt(scene_xlen**2 + scene_ylen**2 + scene_zlen**2)
# Extract faces for later use
part_faces = context.part.Faces()
part_faces = context.part.faces()
for face, plane in zip(faces, face_planes):
for direction in [1, -1] if both else [1]:
if amount:
new_solids.append(
Solid.extrudeLinear(
Solid.extrude_linear(
face,
plane.zDir * amount * direction,
plane.z_dir * amount * direction,
taper,
)
)
else:
# Extrude the face into a solid
extruded_face = Solid.extrudeLinear(
face, plane.zDir * max_dimension, taper
extruded_face = Solid.extrude_linear(
face, plane.z_dir * max_dimension, taper
)
# Intersect the part's faces with this extruded solid
trim_faces_raw = [pf.intersect(extruded_face) for pf in part_faces]
@ -463,45 +464,45 @@ class Extrude(Compound):
if isinstance(face, Face):
trim_faces.append(face)
elif isinstance(face, Compound):
trim_faces.extend(face.Faces())
trim_faces.extend(face.faces())
# Remove faces with a normal perpendicular to the direction of extrusion
# as these will have no volume.
trim_faces = [
f
for f in trim_faces
if f.normalAt(f.Center()).dot(plane.zDir) != 0.0
if f.normal_at(f.center()).dot(plane.z_dir) != 0.0
]
# Group the faces into surfaces
trim_shells = Shell.makeShell(trim_faces).Shells()
trim_shells = Shell.make_shell(trim_faces).shells()
# Determine which surface is "next" or "last"
surface_dirs = []
for trim_shell in trim_shells:
face_directions = Vector(0, 0, 0)
for trim_face in trim_shell.Faces():
face_directions = face_directions + trim_face.normalAt(
trim_face.Center()
for trim_face in trim_shell.faces():
face_directions = face_directions + trim_face.normal_at(
trim_face.center()
)
surface_dirs.append(face_directions.getAngle(plane.zDir))
surface_dirs.append(face_directions.get_angle(plane.z_dir))
if until == Until.NEXT:
surface_index = surface_dirs.index(max(surface_dirs))
else:
surface_index = surface_dirs.index(min(surface_dirs))
# Refine the trim faces to just those on the selected surface
trim_faces = trim_shells[surface_index].Faces()
trim_faces = trim_shells[surface_index].faces()
# Extrude the part faces back towards the face
trim_objects = [
Solid.extrudeLinear(
f, plane.zDir * max_dimension * -1.0, -taper
Solid.extrude_linear(
f, plane.z_dir * max_dimension * -1.0, -taper
)
for f in trim_faces
]
for trim_object, trim_face in zip(trim_objects, trim_faces):
if not trim_object.isValid():
if not trim_object.is_valid():
warn(
message=f"Part face with area {trim_face.Area()} "
message=f"Part face with area {trim_face.area()} "
f"creates an invalid extrusion",
category=Warning,
)
@ -517,7 +518,7 @@ class Extrude(Compound):
new_solids.append(new_object)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
class Hole(Compound):
@ -552,20 +553,20 @@ class Hole(Compound):
for location in LocationList._get_context().locations:
hole_depth = (
2
* context.part.fuse(Solid.makeBox(1, 1, 1).locate(location))
.BoundingBox()
.DiagonalLength
* context.part.fuse(Solid.make_box(1, 1, 1).locate(location))
.bounding_box()
.diagonal_length
if not depth
else depth
)
hole_start = (0, 0, hole_depth / 2) if not depth else (0, 0, 0)
new_solids.append(
Solid.makeCylinder(
Solid.make_cylinder(
radius, hole_depth, hole_start, (0, 0, -1), 360
).locate(location)
)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
class Loft(Solid):
@ -590,16 +591,16 @@ class Loft(Solid):
self.mode = mode
if not sections:
loft_wires = [face.outerWire() for face in context.pending_faces]
loft_wires = [face.outer_wire() for face in context.pending_faces]
context.pending_faces = []
else:
loft_wires = [section.outerWire() for section in sections]
new_solid = Solid.makeLoft(loft_wires, ruled)
loft_wires = [section.outer_wire() for section in sections]
new_solid = Solid.make_loft(loft_wires, ruled)
# Try to recover an invalid loft
if not new_solid.isValid():
new_solid = Solid.makeSolid(
Shell.makeShell(new_solid.Faces() + list(sections))
if not new_solid.is_valid():
new_solid = Solid.make_solid(
Shell.make_shell(new_solid.faces() + list(sections))
).clean()
if not new_solid.isValid():
raise RuntimeError("Failed to create valid loft")
@ -656,14 +657,14 @@ class Revolve(Compound):
for profile in profiles:
# axis origin must be on the same plane as profile
face_occt_pln = gp_Pln(
profile.Center().toPnt(), profile.normalAt(profile.Center()).toDir()
profile.center().to_pnt(), profile.normal_at(profile.center()).to_dir()
)
if not face_occt_pln.Contains(axis.position.toPnt(), 1e-5):
if not face_occt_pln.Contains(axis.position.to_pnt(), 1e-5):
raise ValueError(
"axis origin must be on the same plane as the face to revolve"
)
if not face_occt_pln.Contains(
gp_Lin(axis.position.toPnt(), axis.direction.toDir()), 1e-5, 1e-5
gp_Lin(axis.position.to_pnt(), axis.direction.to_dir()), 1e-5, 1e-5
):
raise ValueError(
"axis must be in the same plane as the face to revolve"
@ -683,7 +684,7 @@ class Revolve(Compound):
)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
class Section(Compound):
@ -711,7 +712,7 @@ class Section(Compound):
self.height = height
self.mode = mode
max_size = context.part.BoundingBox().DiagonalLength
max_size = context.part.bounding_box().diagonal_length
section_planes = (
section_by if section_by else WorkplaneList._get_context().workplanes
@ -727,17 +728,17 @@ class Section(Compound):
for section_plane in section_planes
]
planes = [
Face.makePlane(
Face.make_plane(
2 * max_size,
2 * max_size,
basePnt=plane.origin + plane.zDir * height,
dir=plane.zDir,
base_pnt=plane.origin + plane.z_dir * height,
dir=plane.z_dir,
)
for plane in section_planes
]
context._add_to_context(*planes, faces_to_pending=False, mode=mode)
super().__init__(Compound.makeCompound(planes).wrapped)
super().__init__(Compound.make_compound(planes).wrapped)
class Sweep(Compound):
@ -784,7 +785,7 @@ class Sweep(Compound):
if path is None:
path_wire = context.pending_edges_as_wire
else:
path_wire = Wire.assembleEdges([path]) if isinstance(path, Edge) else path
path_wire = Wire.assemble_edges([path]) if isinstance(path, Edge) else path
if sections:
section_list = sections
@ -795,14 +796,14 @@ class Sweep(Compound):
if binormal is None and normal is not None:
binormal_mode = Vector(normal)
elif isinstance(binormal, Edge):
binormal_mode = Wire.assembleEdges([binormal])
binormal_mode = Wire.assemble_edges([binormal])
else:
binormal_mode = binormal
new_solids = []
for location in LocationList._get_context().locations:
if multisection:
sections = [section.outerWire() for section in section_list]
sections = [section.outer_wire() for section in section_list]
new_solid = Solid.sweep_multi(
sections, path_wire, True, is_frenet, binormal_mode
).moved(location)
@ -819,7 +820,7 @@ class Sweep(Compound):
new_solids.append(new_solid)
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
#
@ -869,7 +870,7 @@ class Box(Compound):
-height / 2 if centered[2] else 0,
)
new_solids = [
Solid.makeBox(
Solid.make_box(
length,
width,
height,
@ -879,7 +880,7 @@ class Box(Compound):
for location in LocationList._get_context().locations
]
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
class Cone(Compound):
@ -927,7 +928,7 @@ class Cone(Compound):
-height / 2 if centered[2] else 0,
)
new_solids = [
Solid.makeCone(
Solid.make_cone(
bottom_radius,
top_radius,
height,
@ -938,7 +939,7 @@ class Cone(Compound):
for location in LocationList._get_context().locations
]
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
class Cylinder(Compound):
@ -983,7 +984,7 @@ class Cylinder(Compound):
-height / 2 if centered[2] else 0,
)
new_solids = [
Solid.makeCylinder(
Solid.make_cylinder(
radius,
height,
center_offset,
@ -993,7 +994,7 @@ class Cylinder(Compound):
for location in LocationList._get_context().locations
]
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
class Sphere(Compound):
@ -1041,7 +1042,7 @@ class Sphere(Compound):
0 if centered[2] else radius,
)
new_solids = [
Solid.makeSphere(
Solid.make_sphere(
radius,
center_offset,
(0, 0, 1),
@ -1052,7 +1053,7 @@ class Sphere(Compound):
for location in LocationList._get_context().locations
]
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
class Torus(Compound):
@ -1101,7 +1102,7 @@ class Torus(Compound):
0 if centered[2] else minor_radius,
)
new_solids = [
Solid.makeTorus(
Solid.make_torus(
major_radius,
minor_radius,
center_offset,
@ -1112,7 +1113,7 @@ class Torus(Compound):
for location in LocationList._get_context().locations
]
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)
class Wedge(Compound):
@ -1160,8 +1161,10 @@ class Wedge(Compound):
self.mode = mode
new_solids = [
Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax).moved(location * rotate)
Solid.make_wedge(dx, dy, dz, xmin, zmin, xmax, zmax).moved(
location * rotate
)
for location in LocationList._get_context().locations
]
context._add_to_context(*new_solids, mode=mode)
super().__init__(Compound.makeCompound(new_solids).wrapped)
super().__init__(Compound.make_compound(new_solids).wrapped)

View file

@ -10,7 +10,7 @@ desc:
TODO:
- add center to arrays
- bug: offset2D doesn't work on a Wire made from a single Edge
- bug: offset_2d doesn't work on a Wire made from a single Edge
Instead of existing constraints how about constraints that return locations
on objects:
@ -42,25 +42,24 @@ import inspect
from math import pi, sin, cos, tan, radians
from typing import Union
from cadquery.hull import find_hull
from build123d.build_common import (
from .build_enums import Mode, FontStyle, Halign, Valign
from .direct_api import (
Edge,
Face,
Wire,
Vector,
Location,
Compound,
Location,
VectorLike,
Builder,
Mode,
ShapeList,
FontStyle,
Halign,
Valign,
Face,
Plane,
PlaneLike,
)
from .build_common import (
Builder,
logger,
validate_inputs,
LocationList,
Plane,
PlaneLike,
WorkplaneList,
)
@ -130,9 +129,9 @@ class BuildSketch(Builder):
new_edges.extend(compound.get_type(Edge))
new_wires.extend(compound.get_type(Wire))
pre_vertices = set() if self.sketch is None else set(self.sketch.Vertices())
pre_edges = set() if self.sketch is None else set(self.sketch.Edges())
pre_faces = set() if self.sketch is None else set(self.sketch.Faces())
pre_vertices = set() if self.sketch is None else set(self.sketch.vertices())
pre_edges = set() if self.sketch is None else set(self.sketch.edges())
pre_faces = set() if self.sketch is None else set(self.sketch.faces())
if new_faces:
logger.debug(
"Attempting to integrate %d Face(s) into sketch with Mode=%s",
@ -141,7 +140,7 @@ class BuildSketch(Builder):
)
if mode == Mode.ADD:
if self.sketch is None:
self.sketch = Compound.makeCompound(new_faces)
self.sketch = Compound.make_compound(new_faces)
else:
self.sketch = self.sketch.fuse(*new_faces).clean()
elif mode == Mode.SUBTRACT:
@ -153,7 +152,7 @@ class BuildSketch(Builder):
raise RuntimeError("No sketch to intersect with")
self.sketch = self.sketch.intersect(*new_faces).clean()
elif mode == Mode.REPLACE:
self.sketch = Compound.makeCompound(new_faces).clean()
self.sketch = Compound.make_compound(new_faces).clean()
logger.debug(
"Completed integrating %d Face(s) into sketch with Mode=%s",
@ -162,16 +161,16 @@ class BuildSketch(Builder):
)
post_vertices = (
set() if self.sketch is None else set(self.sketch.Vertices())
set() if self.sketch is None else set(self.sketch.vertices())
)
post_edges = set() if self.sketch is None else set(self.sketch.Edges())
post_faces = set() if self.sketch is None else set(self.sketch.Faces())
post_edges = set() if self.sketch is None else set(self.sketch.edges())
post_faces = set() if self.sketch is None else set(self.sketch.faces())
self.last_vertices = list(post_vertices - pre_vertices)
self.last_edges = list(post_edges - pre_edges)
self.last_faces = list(post_faces - pre_faces)
self.pending_edges.extend(
new_edges + [e for w in new_wires for e in w.Edges()]
new_edges + [e for w in new_wires for e in w.edges()]
)
@classmethod
@ -207,7 +206,7 @@ class BuildFace(Face):
self.mode = mode
outer_edges = edges if edges else context.pending_edges
pending_face = Face.makeFromWires(Wire.combine(outer_edges)[0])
pending_face = Face.make_from_wires(Wire.combine(outer_edges)[0])
context._add_to_context(pending_face, mode)
context.pending_edges = ShapeList()
super().__init__(pending_face.wrapped)
@ -231,7 +230,7 @@ class BuildHull(Face):
self.mode = mode
hull_edges = edges if edges else context.pending_edges
pending_face = Face.makeFromWires(find_hull(hull_edges))
pending_face = Face.make_from_wires(find_hull(hull_edges))
context._add_to_context(pending_face, mode)
context.pending_edges = ShapeList()
super().__init__(pending_face.wrapped)
@ -270,15 +269,15 @@ class Circle(Compound):
0 if centered[0] else radius,
0 if centered[1] else radius,
)
face = Face.makeFromWires(Wire.makeCircle(radius, (0, 0, 0), (0, 0, 1))).locate(
Location(center_offset)
)
face = Face.make_from_wires(
Wire.make_circle(radius, (0, 0, 0), (0, 0, 1))
).locate(Location(center_offset))
new_faces = [
face.moved(location)
for location in LocationList._get_context().local_locations
]
context._add_to_context(*new_faces, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
class Ellipse(Compound):
@ -311,17 +310,17 @@ class Ellipse(Compound):
self.centered = centered
self.mode = mode
face = Face.makeFromWires(
Wire.makeEllipse(
face = Face.make_from_wires(
Wire.make_ellipse(
x_radius,
y_radius,
center=Vector(),
normal=Vector(0, 0, 1),
xDir=Vector(1, 0, 0),
x_dir=Vector(1, 0, 0),
rotation_angle=0,
)
)
bounding_box = face.BoundingBox()
bounding_box = face.bounding_box()
center_offset = Vector(
0 if centered[0] else bounding_box.xlen / 2,
0 if centered[1] else bounding_box.ylen / 2,
@ -336,7 +335,7 @@ class Ellipse(Compound):
]
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
class Polygon(Compound):
@ -367,8 +366,8 @@ class Polygon(Compound):
self.mode = mode
poly_pts = [Vector(p) for p in pts]
face = Face.makeFromWires(Wire.makePolygon(poly_pts))
bounding_box = face.BoundingBox()
face = Face.make_from_wires(Wire.make_polygon(poly_pts))
bounding_box = face.bounding_box()
center_offset = Vector(
0 if centered[0] else bounding_box.xlen / 2,
0 if centered[1] else bounding_box.ylen / 2,
@ -382,7 +381,7 @@ class Polygon(Compound):
]
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
class Rectangle(Compound):
@ -415,8 +414,8 @@ class Rectangle(Compound):
self.centered = centered
self.mode = mode
face = Face.makePlane(height, width)
bounding_box = face.BoundingBox()
face = Face.make_plane(height, width)
bounding_box = face.bounding_box()
center_offset = Vector(
0 if centered[0] else bounding_box.xlen / 2,
0 if centered[1] else bounding_box.ylen / 2,
@ -431,7 +430,7 @@ class Rectangle(Compound):
]
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
class RegularPolygon(Compound):
@ -471,8 +470,8 @@ class RegularPolygon(Compound):
)
for i in range(side_count + 1)
]
face = Face.makeFromWires(Wire.makePolygon(pts))
bounding_box = face.BoundingBox()
face = Face.make_from_wires(Wire.make_polygon(pts))
bounding_box = face.bounding_box()
center_offset = Vector(
0 if centered[0] else bounding_box.xlen / 2,
0 if centered[1] else bounding_box.ylen / 2,
@ -487,7 +486,7 @@ class RegularPolygon(Compound):
]
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
class SlotArc(Compound):
@ -517,8 +516,8 @@ class SlotArc(Compound):
self.rotation = rotation
self.mode = mode
arc = arc if isinstance(arc, Wire) else Wire.assembleEdges([arc])
face = Face.makeFromWires(arc.offset2D(height / 2)[0]).rotate(
arc = arc if isinstance(arc, Wire) else Wire.assemble_edges([arc])
face = Face.make_from_wires(arc.offset_2d(height / 2)[0]).rotate(
(0, 0, 0), (0, 0, 1), rotation
)
new_faces = [
@ -527,7 +526,7 @@ class SlotArc(Compound):
]
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
class SlotCenterPoint(Compound):
@ -566,13 +565,13 @@ class SlotCenterPoint(Compound):
self.mode = mode
half_line = point_v - center_v
face = Face.makeFromWires(
face = Face.make_from_wires(
Wire.combine(
[
Edge.makeLine(point_v, center_v),
Edge.makeLine(center_v, center_v - half_line),
Edge.make_line(point_v, center_v),
Edge.make_line(center_v, center_v - half_line),
]
)[0].offset2D(height / 2)[0]
)[0].offset_2d(height / 2)[0]
).rotate((0, 0, 0), (0, 0, 1), rotation)
new_faces = [
face.moved(location)
@ -580,7 +579,7 @@ class SlotCenterPoint(Compound):
]
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
class SlotCenterToCenter(Compound):
@ -611,13 +610,13 @@ class SlotCenterToCenter(Compound):
self.rotation = rotation
self.mode = mode
face = Face.makeFromWires(
Wire.assembleEdges(
face = Face.make_from_wires(
Wire.assemble_edges(
[
Edge.makeLine(Vector(-center_separation / 2, 0, 0), Vector()),
Edge.makeLine(Vector(), Vector(+center_separation / 2, 0, 0)),
Edge.make_line(Vector(-center_separation / 2, 0, 0), Vector()),
Edge.make_line(Vector(), Vector(+center_separation / 2, 0, 0)),
]
).offset2D(height / 2)[0]
).offset_2d(height / 2)[0]
).rotate((0, 0, 0), (0, 0, 1), rotation)
new_faces = [
face.moved(location)
@ -625,7 +624,7 @@ class SlotCenterToCenter(Compound):
]
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
class SlotOverall(Compound):
@ -655,13 +654,13 @@ class SlotOverall(Compound):
self.rotation = rotation
self.mode = mode
face = Face.makeFromWires(
Wire.assembleEdges(
face = Face.make_from_wires(
Wire.assemble_edges(
[
Edge.makeLine(Vector(-width / 2 + height / 2, 0, 0), Vector()),
Edge.makeLine(Vector(), Vector(+width / 2 - height / 2, 0, 0)),
Edge.make_line(Vector(-width / 2 + height / 2, 0, 0), Vector()),
Edge.make_line(Vector(), Vector(+width / 2 - height / 2, 0, 0)),
]
).offset2D(height / 2)[0]
).offset_2d(height / 2)[0]
).rotate((0, 0, 0), (0, 0, 1), rotation)
new_faces = [
face.moved(location)
@ -669,7 +668,7 @@ class SlotOverall(Compound):
]
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
class Text(Compound):
@ -722,16 +721,16 @@ class Text(Compound):
self.rotation = rotation
self.mode = mode
text_string = Compound.make2DText(
txt,
fontsize,
font,
font_path,
font_style.name.lower(),
halign.name.lower(),
valign.name.lower(),
position_on_path,
path,
text_string = Compound.make_2d_text(
txt=txt,
fontsize=fontsize,
font=font,
font_path=font_path,
font_style=font_style.name.lower(),
halign=halign.name.lower(),
valign=valign.name.lower(),
position_on_path=position_on_path,
text_path=path,
).rotate(Vector(), Vector(0, 0, 1), rotation)
new_compounds = [
text_string.moved(location)
@ -740,7 +739,7 @@ class Text(Compound):
new_faces = [face for compound in new_compounds for face in compound]
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)
class Trapezoid(Compound):
@ -800,8 +799,8 @@ class Trapezoid(Compound):
pts.append(Vector(width / 2 - reduction_right, height / 2))
pts.append(Vector(-width / 2 + reduction_left, height / 2))
pts.append(pts[0])
face = Face.makeFromWires(Wire.makePolygon(pts))
bounding_box = face.BoundingBox()
face = Face.make_from_wires(Wire.make_polygon(pts))
bounding_box = face.bounding_box()
center_offset = Vector(
0 if centered[0] else bounding_box.xlen / 2,
0 if centered[1] else bounding_box.ylen / 2,
@ -815,4 +814,4 @@ class Trapezoid(Compound):
]
for face in new_faces:
context._add_to_context(face, mode=mode)
super().__init__(Compound.makeCompound(new_faces).wrapped)
super().__init__(Compound.make_compound(new_faces).wrapped)

7843
src/build123d/direct_api.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,180 @@
from typing import Dict, Any, List
from json import dumps
from IPython.display import Javascript
from .exporters.vtk import toString
from .shapes import Shape
from ..assembly import Assembly
from .assembly import toJSON
DEFAULT_COLOR = [1, 0.8, 0, 1]
TEMPLATE_RENDER = """
function render(data, parent_element, ratio){{
// Initial setup
const renderWindow = vtk.Rendering.Core.vtkRenderWindow.newInstance();
const renderer = vtk.Rendering.Core.vtkRenderer.newInstance({{ background: [1, 1, 1 ] }});
renderWindow.addRenderer(renderer);
// iterate over all children children
for (var el of data){{
var trans = el.position;
var rot = el.orientation;
var rgba = el.color;
var shape = el.shape;
// load the inline data
var reader = vtk.IO.XML.vtkXMLPolyDataReader.newInstance();
const textEncoder = new TextEncoder();
reader.parseAsArrayBuffer(textEncoder.encode(shape));
// setup actor,mapper and add
const mapper = vtk.Rendering.Core.vtkMapper.newInstance();
mapper.setInputConnection(reader.getOutputPort());
mapper.setResolveCoincidentTopologyToPolygonOffset();
mapper.setResolveCoincidentTopologyPolygonOffsetParameters(0.5,100);
const actor = vtk.Rendering.Core.vtkActor.newInstance();
actor.setMapper(mapper);
// set color and position
actor.getProperty().setColor(rgba.slice(0,3));
actor.getProperty().setOpacity(rgba[3]);
actor.rotateZ(rot[2]*180/Math.PI);
actor.rotateY(rot[1]*180/Math.PI);
actor.rotateX(rot[0]*180/Math.PI);
actor.setPosition(trans);
renderer.addActor(actor);
}};
renderer.resetCamera();
const openglRenderWindow = vtk.Rendering.OpenGL.vtkRenderWindow.newInstance();
renderWindow.addView(openglRenderWindow);
// Add output to the "parent element"
var container;
var dims;
if(typeof(parent_element.appendChild) !== "undefined"){{
container = document.createElement("div");
parent_element.appendChild(container);
dims = parent_element.getBoundingClientRect();
}}else{{
container = parent_element.append("<div/>").children("div:last-child").get(0);
dims = parent_element.get(0).getBoundingClientRect();
}};
openglRenderWindow.setContainer(container);
// handle size
if (ratio){{
openglRenderWindow.setSize(dims.width, dims.width*ratio);
}}else{{
openglRenderWindow.setSize(dims.width, dims.height);
}};
// Interaction setup
const interact_style = vtk.Interaction.Style.vtkInteractorStyleManipulator.newInstance();
const manips = {{
rot: vtk.Interaction.Manipulators.vtkMouseCameraTrackballRotateManipulator.newInstance(),
pan: vtk.Interaction.Manipulators.vtkMouseCameraTrackballPanManipulator.newInstance(),
zoom1: vtk.Interaction.Manipulators.vtkMouseCameraTrackballZoomManipulator.newInstance(),
zoom2: vtk.Interaction.Manipulators.vtkMouseCameraTrackballZoomManipulator.newInstance(),
roll: vtk.Interaction.Manipulators.vtkMouseCameraTrackballRollManipulator.newInstance(),
}};
manips.zoom1.setControl(true);
manips.zoom2.setScrollEnabled(true);
manips.roll.setShift(true);
manips.pan.setButton(2);
for (var k in manips){{
interact_style.addMouseManipulator(manips[k]);
}};
const interactor = vtk.Rendering.Core.vtkRenderWindowInteractor.newInstance();
interactor.setView(openglRenderWindow);
interactor.initialize();
interactor.bindEvents(container);
interactor.setInteractorStyle(interact_style);
// Orientation marker
const axes = vtk.Rendering.Core.vtkAnnotatedCubeActor.newInstance();
axes.setXPlusFaceProperty({{text: '+X'}});
axes.setXMinusFaceProperty({{text: '-X'}});
axes.setYPlusFaceProperty({{text: '+Y'}});
axes.setYMinusFaceProperty({{text: '-Y'}});
axes.setZPlusFaceProperty({{text: '+Z'}});
axes.setZMinusFaceProperty({{text: '-Z'}});
const orientationWidget = vtk.Interaction.Widgets.vtkOrientationMarkerWidget.newInstance({{
actor: axes,
interactor: interactor }});
orientationWidget.setEnabled(true);
orientationWidget.setViewportCorner(vtk.Interaction.Widgets.vtkOrientationMarkerWidget.Corners.BOTTOM_LEFT);
orientationWidget.setViewportSize(0.2);
}};
"""
TEMPLATE = (
TEMPLATE_RENDER
+ """
new Promise(
function(resolve, reject)
{{
if (typeof(require) !== "undefined" ){{
require.config({{
"paths": {{"vtk": "https://unpkg.com/vtk"}},
}});
require(["vtk"], resolve, reject);
}} else if ( typeof(vtk) === "undefined" ){{
var script = document.createElement("script");
script.onload = resolve;
script.onerror = reject;
script.src = "https://unpkg.com/vtk.js";
document.head.appendChild(script);
}} else {{ resolve() }};
}}
).then(() => {{
var parent_element = {element};
var data = {data};
render(data, parent_element, {ratio});
}});
"""
)
def display(shape):
payload: List[Dict[str, Any]] = []
if isinstance(shape, Shape):
payload.append(
dict(
shape=toString(shape),
color=DEFAULT_COLOR,
position=[0, 0, 0],
orientation=[0, 0, 0],
)
)
elif isinstance(shape, Assembly):
payload = toJSON(shape)
else:
raise ValueError(f"Type {type(shape)} is not supported")
code = TEMPLATE.format(data=dumps(payload), element="element", ratio=0.5)
return Javascript(code)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -43,12 +43,12 @@ class TestCommonOperations(unittest.TestCase):
def test_matmul(self):
self.assertTupleAlmostEquals(
(Edge.makeLine((0, 0, 0), (1, 1, 1)) @ 0.5).toTuple(), (0.5, 0.5, 0.5), 5
(Edge.make_line((0, 0, 0), (1, 1, 1)) @ 0.5).to_tuple(), (0.5, 0.5, 0.5), 5
)
def test_mod(self):
self.assertTupleAlmostEquals(
(Wire.makeCircle(10, (0, 0, 0), (0, 0, 1)) % 0.5).toTuple(), (0, -1, 0), 5
(Wire.make_circle(10, (0, 0, 0), (0, 0, 1)) % 0.5).to_tuple(), (0, -1, 0), 5
)
@ -63,28 +63,28 @@ class TestRotation(unittest.TestCase):
def test_init(self):
thirty_by_three = Rotation(30, 30, 30)
box_vertices = Solid.makeBox(1, 1, 1).moved(thirty_by_three).Vertices()
box_vertices = Solid.make_box(1, 1, 1).moved(thirty_by_three).vertices()
self.assertTupleAlmostEquals(
box_vertices[0].toTuple(), (0.5, -0.4330127, 0.75), 5
box_vertices[0].to_tuple(), (0.5, -0.4330127, 0.75), 5
)
self.assertTupleAlmostEquals(box_vertices[1].toTuple(), (0.0, 0.0, 0.0), 7)
self.assertTupleAlmostEquals(box_vertices[1].to_tuple(), (0.0, 0.0, 0.0), 7)
self.assertTupleAlmostEquals(
box_vertices[2].toTuple(), (0.0669872, 0.191987, 1.399519), 5
box_vertices[2].to_tuple(), (0.0669872, 0.191987, 1.399519), 5
)
self.assertTupleAlmostEquals(
box_vertices[3].toTuple(), (-0.4330127, 0.625, 0.6495190), 5
box_vertices[3].to_tuple(), (-0.4330127, 0.625, 0.6495190), 5
)
self.assertTupleAlmostEquals(
box_vertices[4].toTuple(), (1.25, 0.2165063, 0.625), 5
box_vertices[4].to_tuple(), (1.25, 0.2165063, 0.625), 5
)
self.assertTupleAlmostEquals(
box_vertices[5].toTuple(), (0.75, 0.649519, -0.125), 5
box_vertices[5].to_tuple(), (0.75, 0.649519, -0.125), 5
)
self.assertTupleAlmostEquals(
box_vertices[6].toTuple(), (0.816987, 0.841506, 1.274519), 5
box_vertices[6].to_tuple(), (0.816987, 0.841506, 1.274519), 5
)
self.assertTupleAlmostEquals(
box_vertices[7].toTuple(), (0.3169872, 1.2745190, 0.52451905), 5
box_vertices[7].to_tuple(), (0.3169872, 1.2745190, 0.52451905), 5
)
@ -106,14 +106,14 @@ class TestShapeList(unittest.TestCase):
self.assertTrue(isinstance(edges, ShapeList))
self.assertEqual(len(edges), 4)
if axis == Axis.X:
self.assertLessEqual(faces[0].Center().x, faces[1].Center().x)
self.assertLessEqual(edges[0].Center().x, edges[-1].Center().x)
self.assertLessEqual(faces[0].center().x, faces[1].center().x)
self.assertLessEqual(edges[0].center().x, edges[-1].center().x)
elif axis == Axis.Y:
self.assertLessEqual(faces[0].Center().y, faces[1].Center().y)
self.assertLessEqual(edges[0].Center().y, edges[-1].Center().y)
self.assertLessEqual(faces[0].center().y, faces[1].center().y)
self.assertLessEqual(edges[0].center().y, edges[-1].center().y)
elif axis == Axis.Z:
self.assertLessEqual(faces[0].Center().z, faces[1].Center().z)
self.assertLessEqual(edges[0].Center().z, edges[-1].Center().z)
self.assertLessEqual(faces[0].center().z, faces[1].center().z)
self.assertLessEqual(edges[0].center().z, edges[-1].center().z)
def test_filter_by_position(self):
"""test the filter and sorting of Faces and Edges by position"""
@ -138,24 +138,24 @@ class TestShapeList(unittest.TestCase):
self.assertEqual(len(edges), 4 * sum(inclusive) + 4)
if axis == Axis.X:
self.assertLessEqual(
faces[0].Center().x, faces[-1].Center().x
faces[0].center().x, faces[-1].center().x
)
self.assertLessEqual(
edges[0].Center().x, edges[-1].Center().x
edges[0].center().x, edges[-1].center().x
)
elif axis == Axis.Y:
self.assertLessEqual(
faces[0].Center().y, faces[-1].Center().y
faces[0].center().y, faces[-1].center().y
)
self.assertLessEqual(
edges[0].Center().y, edges[-1].Center().y
edges[0].center().y, edges[-1].center().y
)
elif axis == Axis.Z:
self.assertLessEqual(
faces[0].Center().z, faces[-1].Center().z
faces[0].center().z, faces[-1].center().z
)
self.assertLessEqual(
edges[0].Center().z, edges[-1].Center().z
edges[0].center().z, edges[-1].center().z
)
def test_filter_by_type(self):
@ -173,22 +173,22 @@ class TestShapeList(unittest.TestCase):
with BuildPart() as test:
Wedge(1, 1, 1, 0, 0, 0.5, 0.5)
faces = test.faces().sort_by(SortBy.AREA)
self.assertEqual(faces[0].Area(), 0.25)
self.assertEqual(faces[-1].Area(), 1)
self.assertEqual(faces[0].area(), 0.25)
self.assertEqual(faces[-1].area(), 1)
with self.subTest(sort_by=SortBy.LENGTH):
with BuildPart() as test:
Wedge(1, 1, 1, 0, 0, 0.5, 0.5)
edges = test.edges().sort_by(SortBy.LENGTH)
self.assertEqual(edges[0].Length(), 0.5)
self.assertAlmostEqual(edges[-1].Length(), 1.2247448713915892, 7)
self.assertEqual(edges[0].length(), 0.5)
self.assertAlmostEqual(edges[-1].length(), 1.2247448713915892, 7)
with self.subTest(sort_by=SortBy.DISTANCE):
with BuildPart() as test:
Box(1, 1, 1, centered=(False, True, True))
faces = test.faces().sort_by(SortBy.DISTANCE)
self.assertAlmostEqual(faces[0].Center().Length, 0, 7)
self.assertAlmostEqual(faces[-1].Center().Length, 1, 7)
self.assertAlmostEqual(faces[0].center().length, 0, 7)
self.assertAlmostEqual(faces[-1].center().length, 1, 7)
with self.subTest(sort_by=SortBy.VOLUME):
with BuildPart() as test:
@ -196,8 +196,8 @@ class TestShapeList(unittest.TestCase):
with Locations((0, 0, 10)):
Box(2, 2, 2)
solids = test.solids().sort_by(SortBy.VOLUME)
self.assertAlmostEqual(solids[0].Volume(), 1, 7)
self.assertAlmostEqual(solids[-1].Volume(), 8, 7)
self.assertAlmostEqual(solids[0].volume(), 1, 7)
self.assertAlmostEqual(solids[-1].volume(), 8, 7)
with self.subTest(sort_by=SortBy.RADIUS):
with BuildPart() as test:
@ -212,22 +212,22 @@ class TestShapeList(unittest.TestCase):
with BuildPart() as test:
Box(1, 1, 1)
edges = test.edges() > Axis.X
self.assertEqual(edges[0].Center().x, -0.5)
self.assertEqual(edges[-1].Center().x, 0.5)
self.assertEqual(edges[0].center().X, -0.5)
self.assertEqual(edges[-1].center().X, 0.5)
with self.subTest(sort_by="Y"):
with BuildPart() as test:
Box(1, 1, 1)
edges = test.edges() > Axis.Y
self.assertEqual(edges[0].Center().y, -0.5)
self.assertEqual(edges[-1].Center().y, 0.5)
self.assertEqual(edges[0].center().Y, -0.5)
self.assertEqual(edges[-1].center().Y, 0.5)
with self.subTest(sort_by="Z"):
with BuildPart() as test:
Box(1, 1, 1)
edges = test.edges() > Axis.Z
self.assertEqual(edges[0].Center().z, -0.5)
self.assertEqual(edges[-1].Center().z, 0.5)
self.assertEqual(edges[0].center().Z, -0.5)
self.assertEqual(edges[-1].center().Z, 0.5)
def test_vertices(self):
with BuildPart() as test:
@ -286,17 +286,17 @@ class TestWorkplanes(unittest.TestCase):
def test_named(self):
with Workplanes("XY") as test:
self.assertTupleAlmostEquals(
test.workplanes[0].origin.toTuple(), (0, 0, 0), 5
test.workplanes[0].origin.to_tuple(), (0, 0, 0), 5
)
self.assertTupleAlmostEquals(
test.workplanes[0].zDir.toTuple(), (0, 0, 1), 5
test.workplanes[0].z_dir.to_tuple(), (0, 0, 1), 5
)
def test_locations(self):
with Workplanes("XY"):
with Locations((0, 0, 1), (0, 0, 2)) as l:
with Workplanes(*l.locations) as w:
origins = [p.origin.toTuple() for p in w.workplanes]
origins = [p.origin.to_tuple() for p in w.workplanes]
self.assertTupleAlmostEquals(origins[0], (0, 0, 1), 5)
self.assertTupleAlmostEquals(origins[1], (0, 0, 2), 5)
self.assertEqual(len(origins), 2)
@ -350,7 +350,7 @@ class TestLocations(unittest.TestCase):
def test_no_centering(self):
with BuildSketch():
with GridLocations(4, 4, 2, 2, centered=(False, False)) as l:
pts = [loc.toTuple()[0] for loc in l.locations]
pts = [loc.to_tuple()[0] for loc in l.locations]
self.assertTupleAlmostEquals(pts[0], (0, 0, 0), 5)
self.assertTupleAlmostEquals(pts[1], (0, 4, 0), 5)
self.assertTupleAlmostEquals(pts[2], (4, 0, 0), 5)
@ -359,7 +359,7 @@ class TestLocations(unittest.TestCase):
def test_centering(self):
with BuildSketch():
with GridLocations(4, 4, 2, 2, centered=(True, True)) as l:
pts = [loc.toTuple()[0] for loc in l.locations]
pts = [loc.to_tuple()[0] for loc in l.locations]
self.assertTupleAlmostEquals(pts[0], (-2, -2, 0), 5)
self.assertTupleAlmostEquals(pts[1], (-2, 2, 0), 5)
self.assertTupleAlmostEquals(pts[2], (2, -2, 0), 5)

View file

@ -28,8 +28,7 @@ license:
import unittest
from math import pi
from build123d import *
from cadquery import Compound, Vector, Edge, Face, Solid, Wire, Location
from build123d import LocationList, Builder
from build123d import Builder, LocationList
def _assertTupleAlmostEquals(self, expected, actual, places, msg=None):
@ -68,8 +67,8 @@ class AddTests(unittest.TestCase):
def test_add_to_line(self):
# Add Edge
with BuildLine() as test:
Add(Edge.makeLine((0, 0, 0), (1, 1, 1)))
self.assertTupleAlmostEquals((test.wires()[0] @ 1).toTuple(), (1, 1, 1), 5)
Add(Edge.make_line((0, 0, 0), (1, 1, 1)))
self.assertTupleAlmostEquals((test.wires()[0] @ 1).to_tuple(), (1, 1, 1), 5)
# Add Wire
with BuildLine() as wire:
Polyline((0, 0, 0), (1, 1, 1), (2, 0, 0), (3, 1, 1))
@ -79,25 +78,25 @@ class AddTests(unittest.TestCase):
def test_add_to_sketch(self):
with BuildSketch() as test:
Add(Face.makePlane(10, 10))
self.assertAlmostEqual(test.sketch.Area(), 100, 5)
Add(Face.make_plane(10, 10))
self.assertAlmostEqual(test.sketch.area(), 100, 5)
def test_add_to_part(self):
# Add Solid
with BuildPart() as test:
Add(Solid.makeBox(10, 10, 10))
self.assertAlmostEqual(test.part.Volume(), 1000, 5)
Add(Solid.make_box(10, 10, 10))
self.assertAlmostEqual(test.part.volume(), 1000, 5)
# Add Compound
with BuildPart() as test:
Add(
Compound.makeCompound(
Compound.make_compound(
[
Solid.makeBox(10, 10, 10),
Solid.makeBox(5, 5, 5, pnt=(20, 20, 20)),
Solid.make_box(10, 10, 10),
Solid.make_box(5, 5, 5, pnt=(20, 20, 20)),
]
)
)
self.assertEqual(test.part.Volume(), 1125, 5)
self.assertEqual(test.part.volume(), 1125, 5)
# Add Wire
with BuildLine() as wire:
Polyline((0, 0, 0), (1, 1, 1), (2, 0, 0), (3, 1, 1))
@ -107,12 +106,12 @@ class AddTests(unittest.TestCase):
def test_errors(self):
with self.assertRaises(RuntimeError):
Add(Edge.makeLine((0, 0, 0), (1, 1, 1)))
Add(Edge.make_line((0, 0, 0), (1, 1, 1)))
def test_unsupported_builder(self):
with self.assertRaises(RuntimeError):
with TestBuilder():
Add(Edge.makeLine((0, 0, 0), (1, 1, 1)))
Add(Edge.make_line((0, 0, 0), (1, 1, 1)))
class TestOffset(unittest.TestCase):
@ -129,22 +128,22 @@ class TestOffset(unittest.TestCase):
Line(l @ 1, (1, 1))
Offset(amount=1)
BuildFace()
self.assertAlmostEqual(test.sketch.Area(), pi * 1.25 + 3, 5)
self.assertAlmostEqual(test.sketch.area(), pi * 1.25 + 3, 5)
def test_line_offset(self):
with BuildSketch() as test:
with BuildLine() as line:
l = Line((0, 0), (1, 0))
Line(l @ 1, (1, 1))
Offset(*line.line.Edges(), amount=1)
Offset(*line.line.edges(), amount=1)
BuildFace()
self.assertAlmostEqual(test.sketch.Area(), pi * 1.25 + 3, 5)
self.assertAlmostEqual(test.sketch.area(), pi * 1.25 + 3, 5)
def test_face_offset(self):
with BuildSketch() as test:
Rectangle(1, 1)
Offset(amount=1, kind=Kind.INTERSECTION)
self.assertAlmostEqual(test.sketch.Area(), 9, 5)
self.assertAlmostEqual(test.sketch.area(), 9, 5)
def test_box_offset(self):
with BuildPart() as test:
@ -153,7 +152,7 @@ class TestOffset(unittest.TestCase):
amount=1,
kind=Kind.INTERSECTION,
)
self.assertAlmostEqual(test.part.Volume(), 3**3 - 1**3, 5)
self.assertAlmostEqual(test.part.volume(), 3**3 - 1**3, 5)
def test_box_offset_with_opening(self):
with BuildPart() as test:
@ -163,7 +162,7 @@ class TestOffset(unittest.TestCase):
openings=test.faces() >> Axis.Z,
kind=Kind.INTERSECTION,
)
self.assertAlmostEqual(test.part.Volume(), 10**3 - 8**2 * 9, 5)
self.assertAlmostEqual(test.part.volume(), 10**3 - 8**2 * 9, 5)
class BoundingBoxTests(unittest.TestCase):
@ -176,7 +175,7 @@ class BoundingBoxTests(unittest.TestCase):
ears = (bb.vertices() > Axis.Y)[:-2]
with Locations(*ears):
Circle(7)
self.assertAlmostEqual(mickey.sketch.Area(), 586.1521145312807, 5)
self.assertAlmostEqual(mickey.sketch.area(), 586.1521145312807, 5)
"""Test Vertices"""
with BuildSketch() as test:
Rectangle(10, 10)
@ -187,11 +186,11 @@ class BoundingBoxTests(unittest.TestCase):
with BuildPart() as test:
Sphere(1)
BoundingBox(*test.solids())
self.assertAlmostEqual(test.part.Volume(), 8, 5)
self.assertAlmostEqual(test.part.volume(), 8, 5)
with BuildPart() as test:
Sphere(1)
BoundingBox(*test.vertices())
self.assertAlmostEqual(test.part.Volume(), (4 / 3) * pi, 5)
self.assertAlmostEqual(test.part.volume(), (4 / 3) * pi, 5)
def test_errors(self):
with self.assertRaises(RuntimeError):
@ -204,13 +203,13 @@ class ChamferTests(unittest.TestCase):
with BuildPart() as test:
Box(10, 10, 10)
Chamfer(*test.edges(), length=1)
self.assertLess(test.part.Volume(), 1000)
self.assertLess(test.part.volume(), 1000)
def test_sketch_chamfer(self):
with BuildSketch() as test:
Rectangle(10, 10)
Chamfer(*test.vertices(), length=1)
self.assertAlmostEqual(test.sketch.Area(), 100 - 4 * 0.5, 5)
self.assertAlmostEqual(test.sketch.area(), 100 - 4 * 0.5, 5)
with BuildSketch() as test:
with Locations((-10, 0), (10, 0)):
@ -219,7 +218,7 @@ class ChamferTests(unittest.TestCase):
*test.vertices().filter_by_position(Axis.X, minimum=0, maximum=20),
length=1
)
self.assertAlmostEqual(test.sketch.Area(), 200 - 4 * 0.5, 5)
self.assertAlmostEqual(test.sketch.area(), 200 - 4 * 0.5, 5)
def test_errors(self):
with self.assertRaises(RuntimeError):
@ -232,13 +231,13 @@ class FilletTests(unittest.TestCase):
with BuildPart() as test:
Box(10, 10, 10)
Fillet(*test.edges(), radius=1)
self.assertLess(test.part.Volume(), 1000)
self.assertLess(test.part.volume(), 1000)
def test_sketch_chamfer(self):
with BuildSketch() as test:
Rectangle(10, 10)
Fillet(*test.vertices(), radius=1)
self.assertAlmostEqual(test.sketch.Area(), 100 - 4 + pi, 5)
self.assertAlmostEqual(test.sketch.area(), 100 - 4 + pi, 5)
with BuildSketch() as test:
with Locations((-10, 0), (10, 0)):
@ -247,7 +246,7 @@ class FilletTests(unittest.TestCase):
*test.vertices().filter_by_position(Axis.X, minimum=0, maximum=20),
radius=1
)
self.assertAlmostEqual(test.sketch.Area(), 200 - 4 + pi, 5)
self.assertAlmostEqual(test.sketch.area(), 200 - 4 + pi, 5)
def test_errors(self):
with self.assertRaises(RuntimeError):
@ -261,7 +260,7 @@ class HexArrayTests(unittest.TestCase):
Rectangle(70, 70)
with HexLocations(20, 4, 3, centered=(True, True)):
Circle(5, mode=Mode.SUBTRACT)
self.assertAlmostEqual(test.sketch.Area(), 70**2 - 12 * 25 * pi, 5)
self.assertAlmostEqual(test.sketch.area(), 70**2 - 12 * 25 * pi, 5)
def test_error(self):
with self.assertRaises(ValueError):
@ -275,8 +274,8 @@ class HexArrayTests(unittest.TestCase):
class MirrorTests(unittest.TestCase):
def test_mirror_line(self):
edge = Edge.makeLine((1, 0, 0), (2, 0, 0))
wire = Wire.makeCircle(1, center=(5, 0, 0), normal=(0, 0, 1))
edge = Edge.make_line((1, 0, 0), (2, 0, 0))
wire = Wire.make_circle(1, center=(5, 0, 0), normal=(0, 0, 1))
with BuildLine() as test:
Mirror(edge, wire, about="YZ")
self.assertEqual(
@ -291,13 +290,13 @@ class MirrorTests(unittest.TestCase):
self.assertEqual(len(test.edges()), 2)
def test_mirror_sketch(self):
edge = Edge.makeLine((1, 0), (2, 0))
wire = Wire.makeCircle(1, center=(5, 0, 0), normal=(0, 0, 1))
face = Face.makePlane(2, 2, basePnt=(8, 0))
compound = Compound.makeCompound(
edge = Edge.make_line((1, 0), (2, 0))
wire = Wire.make_circle(1, center=(5, 0, 0), normal=(0, 0, 1))
face = Face.make_plane(2, 2, base_pnt=(8, 0))
compound = Compound.make_compound(
[
Face.makePlane(2, 2, basePnt=(8, 8)),
Face.makePlane(2, 2, basePnt=(8, -8)),
Face.make_plane(2, 2, base_pnt=(8, 8)),
Face.make_plane(2, 2, base_pnt=(8, -8)),
]
)
with BuildSketch() as test:
@ -317,7 +316,7 @@ class MirrorTests(unittest.TestCase):
)
def test_mirror_part(self):
cone = Solid.makeCone(2, 1, 2, pnt=(5, 4, 0))
cone = Solid.make_cone(2, 1, 2, pnt=(5, 4, 0))
with BuildPart() as test:
Mirror(cone, about="YZ")
self.assertEqual(
@ -330,25 +329,25 @@ class ScaleTests(unittest.TestCase):
with BuildLine() as test:
Line((0, 0), (1, 0))
Scale(by=2, mode=Mode.REPLACE)
self.assertAlmostEqual(test.edges()[0].Length(), 2.0, 5)
self.assertAlmostEqual(test.edges()[0].length(), 2.0, 5)
def test_sketch(self):
with BuildSketch() as test:
Rectangle(1, 1)
Scale(by=2, mode=Mode.REPLACE)
self.assertAlmostEqual(test.sketch.Area(), 4.0, 5)
self.assertAlmostEqual(test.sketch.area(), 4.0, 5)
def test_part(self):
with BuildPart() as test:
Box(1, 1, 1)
Scale(by=(2, 2, 2), mode=Mode.REPLACE)
self.assertAlmostEqual(test.part.Volume(), 8.0, 5)
self.assertAlmostEqual(test.part.volume(), 8.0, 5)
def test_external_object(self):
line = Edge.makeLine((0, 0), (1, 0))
line = Edge.make_line((0, 0), (1, 0))
with BuildLine() as test:
Scale(line, by=2)
self.assertAlmostEqual(test.edges()[0].Length(), 2.0, 5)
self.assertAlmostEqual(test.edges()[0].length(), 2.0, 5)
def test_error_checking(self):
with self.assertRaises(ValueError):
@ -370,13 +369,13 @@ class LocationsTests(unittest.TestCase):
with BuildPart():
with Locations(Location(Vector())):
self.assertTupleAlmostEquals(
LocationList._get_context().locations[0].toTuple()[0], (0, 0, 0), 5
LocationList._get_context().locations[0].to_tuple()[0], (0, 0, 0), 5
)
def test_errors(self):
with self.assertRaises(ValueError):
with BuildPart():
with Locations(Edge.makeLine((1, 0), (2, 0))):
with Locations(Edge.make_line((1, 0), (2, 0))):
pass

View file

@ -77,7 +77,7 @@ class BuildLineTests(unittest.TestCase):
TangentArc(l6 @ 1, l7 @ 0, tangent=l6 % 1)
Mirror(*outline.edges(), about="YZ")
BuildFace(*leaf.pending_edges)
self.assertAlmostEqual(leaf.sketch.Area(), 0.2741600685288115, 5)
self.assertAlmostEqual(leaf.sketch.area(), 0.2741600685288115, 5)
def test_three_d(self):
"""Test 3D lines with a helix"""
@ -98,32 +98,32 @@ class BuildLineTests(unittest.TestCase):
powerup @ 0,
tangents=(screw % 1, powerup % 0),
)
self.assertAlmostEqual(roller_coaster.wires()[0].Length(), 678.983628932414, 5)
self.assertAlmostEqual(roller_coaster.wires()[0].length(), 678.983628932414, 5)
def test_polar_line(self):
"""Test 2D and 3D polar lines"""
with BuildLine() as test:
PolarLine((0, 0), sqrt(2), 45)
self.assertTupleAlmostEquals((test.edges()[0] @ 1).toTuple(), (1, 1, 0), 5)
self.assertTupleAlmostEquals((test.edges()[0] @ 1).to_tuple(), (1, 1, 0), 5)
with BuildLine() as test:
PolarLine((0, 0), sqrt(2), direction=(1, 0, 1))
self.assertTupleAlmostEquals((test.edges()[0] @ 1).toTuple(), (1, 0, 1), 5)
self.assertTupleAlmostEquals((test.edges()[0] @ 1).to_tuple(), (1, 0, 1), 5)
def test_spline(self):
"""Test spline with no tangents"""
with BuildLine() as test:
Spline((0, 0), (1, 1), (2, 0))
self.assertTupleAlmostEquals((test.edges()[0] @ 1).toTuple(), (2, 0, 0), 5)
self.assertTupleAlmostEquals((test.edges()[0] @ 1).to_tuple(), (2, 0, 0), 5)
def test_center_arc(self):
"""Test center arc as arc and circle"""
with BuildLine() as arc:
CenterArc((0, 0), 10, 0, 180)
self.assertTupleAlmostEquals((arc.edges()[0] @ 1).toTuple(), (-10, 0, 0), 5)
self.assertTupleAlmostEquals((arc.edges()[0] @ 1).to_tuple(), (-10, 0, 0), 5)
with BuildLine() as arc:
CenterArc((0, 0), 10, 0, 360)
self.assertTupleAlmostEquals(
(arc.edges()[0] @ 0).toTuple(), (arc.edges()[0] @ 1).toTuple(), 5
(arc.edges()[0] @ 0).to_tuple(), (arc.edges()[0] @ 1).to_tuple(), 5
)
def test_polyline(self):
@ -131,16 +131,16 @@ class BuildLineTests(unittest.TestCase):
with BuildLine() as test:
Polyline((0, 0), (1, 0), (1, 1), (0, 1), close=True)
self.assertAlmostEqual(
(test.edges()[0] @ 0 - test.edges()[-1] @ 1).Length, 0, 5
(test.edges()[0] @ 0 - test.edges()[-1] @ 1).length, 0, 5
)
self.assertEqual(len(test.edges()), 4)
self.assertAlmostEqual(test.wires()[0].Length(), 4)
self.assertAlmostEqual(test.wires()[0].length(), 4)
def test_wires_select_last(self):
with BuildLine() as test:
Line((0, 0), (0, 1))
Polyline((1, 0), (1, 1), (0, 1), (0, 0))
self.assertAlmostEqual(test.wires(Select.LAST)[0].Length(), 3, 5)
self.assertAlmostEqual(test.wires(Select.LAST)[0].length(), 3, 5)
def test_error_conditions(self):
"""Test error handling"""
@ -165,7 +165,8 @@ class BuildLineTests(unittest.TestCase):
def test_obj_name(self):
with BuildLine() as test:
self.assertEqual(test._obj_name,"line")
self.assertEqual(test._obj_name, "line")
if __name__ == "__main__":
unittest.main()

View file

@ -29,7 +29,7 @@ import unittest
from math import pi, sin
from build123d import *
from build123d import LocationList, WorkplaneList
from cadquery import Compound, Plane, Vector, Edge, Location, Face, Wire
from build123d import Compound, Plane, Vector, Edge, Location, Face, Wire
def _assertTupleAlmostEquals(self, expected, actual, places, msg=None):
@ -103,20 +103,20 @@ class BuildPartTests(unittest.TestCase):
Box(20, 20, 20)
Sphere(10, mode=Mode.SUBTRACT)
self.assertTrue(isinstance(test._obj, Compound))
self.assertAlmostEqual(test.part.Volume(), 8000 - (4000 / 3) * pi, 5)
self.assertAlmostEqual(test.part.volume(), 8000 - (4000 / 3) * pi, 5)
def test_mode_intersect(self):
"""Note that a negative volume is created"""
with BuildPart() as test:
Box(20, 20, 20)
Sphere(10, mode=Mode.INTERSECT)
self.assertAlmostEqual(abs(test.part.Volume()), (4000 / 3) * pi, 5)
self.assertAlmostEqual(abs(test.part.volume()), (4000 / 3) * pi, 5)
def test_mode_replace(self):
with BuildPart() as test:
Box(10, 10, 10)
Sphere(10, mode=Mode.REPLACE)
self.assertAlmostEqual(test.part.Volume(), (4000 / 3) * pi, 5)
self.assertAlmostEqual(test.part.volume(), (4000 / 3) * pi, 5)
def test_add_pending_faces(self):
with BuildPart() as test:
@ -126,7 +126,7 @@ class BuildPartTests(unittest.TestCase):
with PolarLocations(10, 5):
Circle(2)
self.assertEqual(len(test.pending_faces), 30)
# self.assertEqual(sum([len(s.Faces()) for s in test.pending_faces]), 30)
# self.assertEqual(sum([len(s.faces()) for s in test.pending_faces]), 30)
def test_add_pending_edges(self):
with BuildPart() as test:
@ -144,7 +144,7 @@ class BuildPartTests(unittest.TestCase):
def test_named_plane(self):
with BuildPart("YZ") as test:
self.assertTupleAlmostEquals(
WorkplaneList._get_context().workplanes[0].zDir.toTuple(),
WorkplaneList._get_context().workplanes[0].z_dir.to_tuple(),
(1, 0, 0),
5,
)
@ -168,34 +168,34 @@ class TestCounterBoreHole(unittest.TestCase):
def test_fixed_depth(self):
with BuildPart() as test:
Box(10, 10, 10)
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].Center()):
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].center()):
CounterBoreHole(2, 3, 1, 5)
self.assertAlmostEqual(test.part.Volume(), 1000 - 4 * 4 * pi - 9 * pi, 5)
self.assertAlmostEqual(test.part.volume(), 1000 - 4 * 4 * pi - 9 * pi, 5)
def test_through_hole(self):
with BuildPart() as test:
Box(10, 10, 10)
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].Center()):
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].center()):
CounterBoreHole(2, 3, 1)
self.assertAlmostEqual(test.part.Volume(), 1000 - 4 * 9 * pi - 9 * pi, 5)
self.assertAlmostEqual(test.part.volume(), 1000 - 4 * 9 * pi - 9 * pi, 5)
class TestCounterSinkHole(unittest.TestCase):
def test_fixed_depth(self):
with BuildPart() as test:
Box(10, 10, 10)
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].Center()):
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].center()):
CounterSinkHole(2, 4, 5)
self.assertLess(test.part.Volume(), 1000, 5)
self.assertGreater(test.part.Volume(), 1000 - 16 * 5 * pi, 5)
self.assertLess(test.part.volume(), 1000, 5)
self.assertGreater(test.part.volume(), 1000 - 16 * 5 * pi, 5)
def test_through_hole(self):
with BuildPart() as test:
Box(10, 10, 10)
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].Center()):
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].center()):
CounterSinkHole(2, 4)
self.assertLess(test.part.Volume(), 1000, 5)
self.assertGreater(test.part.Volume(), 1000 - 16 * 10 * pi, 5)
self.assertLess(test.part.volume(), 1000, 5)
self.assertGreater(test.part.volume(), 1000 - 16 * 10 * pi, 5)
class TestExtrude(unittest.TestCase):
@ -208,15 +208,15 @@ class TestExtrude(unittest.TestCase):
with BuildPart() as test:
with BuildSketch() as f:
Rectangle(5, 5)
Extrude(*f.sketch.Faces(), amount=2.5, both=True)
self.assertAlmostEqual(test.part.Volume(), 125, 5)
Extrude(*f.sketch.faces(), amount=2.5, both=True)
self.assertAlmostEqual(test.part.volume(), 125, 5)
def test_extrude_both(self):
with BuildPart() as test:
with BuildSketch():
Rectangle(5, 5)
Extrude(amount=2.5, both=True)
self.assertAlmostEqual(test.part.Volume(), 125, 5)
self.assertAlmostEqual(test.part.volume(), 125, 5)
# def test_extrude_until(self):
# with BuildPart() as test:
@ -225,23 +225,23 @@ class TestExtrude(unittest.TestCase):
# with BuildSketch():
# Rectangle(1, 1)
# Extrude(until=Until.NEXT)
# self.assertAlmostEqual(test.part.Volume(), 10**3 - 8**3 + 1**2 * 8, 5)
# self.assertAlmostEqual(test.part.volume(), 10**3 - 8**3 + 1**2 * 8, 5)
class TestHole(unittest.TestCase):
def test_fixed_depth(self):
with BuildPart() as test:
Box(10, 10, 10)
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].Center()):
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].center()):
Hole(2, 5)
self.assertAlmostEqual(test.part.Volume(), 1000 - 4 * 5 * pi, 5)
self.assertAlmostEqual(test.part.volume(), 1000 - 4 * 5 * pi, 5)
def test_through_hole(self):
with BuildPart() as test:
Box(10, 10, 10)
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].Center()):
with Locations(test.faces().filter_by_axis(Axis.Z)[-1].center()):
Hole(2)
self.assertAlmostEqual(test.part.Volume(), 1000 - 4 * 10 * pi, 5)
self.assertAlmostEqual(test.part.volume(), 1000 - 4 * 10 * pi, 5)
class TestLoft(unittest.TestCase):
@ -253,14 +253,14 @@ class TestLoft(unittest.TestCase):
with BuildSketch():
Circle(10 * sin(i * pi / slice_count) + 5)
Loft()
self.assertLess(test.part.Volume(), 225 * pi * 30, 5)
self.assertGreater(test.part.Volume(), 25 * pi * 30, 5)
self.assertLess(test.part.volume(), 225 * pi * 30, 5)
self.assertGreater(test.part.volume(), 25 * pi * 30, 5)
sections = [
Face.makeFromWires(
Wire.assembleEdges(
Face.make_from_wires(
Wire.assemble_edges(
[
Edge.makeCircle(10 * sin(i * pi / slice_count) + 5).moved(
Edge.make_circle(10 * sin(i * pi / slice_count) + 5).moved(
Location(Vector(0, 0, i * 3))
)
]
@ -270,8 +270,8 @@ class TestLoft(unittest.TestCase):
]
with BuildPart() as test:
Loft(*sections)
self.assertLess(test.part.Volume(), 225 * pi * 30, 5)
self.assertGreater(test.part.Volume(), 25 * pi * 30, 5)
self.assertLess(test.part.volume(), 225 * pi * 30, 5)
self.assertGreater(test.part.volume(), 25 * pi * 30, 5)
class TestRevolve(unittest.TestCase):
@ -294,13 +294,13 @@ class TestRevolve(unittest.TestCase):
Polyline(
l5 @ 1,
l5 @ 1 + Vector(0, 1),
(0, (l5 @ 1).y + 1),
(0, (l5 @ 1).Y + 1),
l1 @ 0,
)
BuildFace()
Revolve(axis=Axis.Y)
self.assertLess(test.part.Volume(), 22**2 * pi * 50, 5)
self.assertGreater(test.part.Volume(), 144 * pi * 50, 5)
self.assertLess(test.part.volume(), 22**2 * pi * 50, 5)
self.assertGreater(test.part.volume(), 144 * pi * 50, 5)
def test_revolve_with_axis(self):
with BuildPart() as test:
@ -312,8 +312,8 @@ class TestRevolve(unittest.TestCase):
l4 = Line(l3 @ 1, l1 @ 0)
BuildFace()
Revolve(axis=Axis.X)
self.assertLess(test.part.Volume(), 244 * pi * 20, 5)
self.assertGreater(test.part.Volume(), 100 * pi * 20, 5)
self.assertLess(test.part.volume(), 244 * pi * 20, 5)
self.assertGreater(test.part.volume(), 100 * pi * 20, 5)
def test_invalid_axis_origin(self):
with BuildPart():
@ -335,14 +335,14 @@ class TestSection(unittest.TestCase):
with BuildPart() as test:
Sphere(10)
Section()
self.assertAlmostEqual(test.faces()[-1].Area(), 100 * pi, 5)
self.assertAlmostEqual(test.faces()[-1].area(), 100 * pi, 5)
def test_custom_plane(self):
with BuildPart() as test:
Sphere(10)
Section("XZ")
self.assertAlmostEqual(
test.faces().filter_by_axis(Axis.Y)[-1].Area(), 100 * pi, 5
test.faces().filter_by_axis(Axis.Y)[-1].area(), 100 * pi, 5
)
@ -351,7 +351,7 @@ class TestSplit(unittest.TestCase):
with BuildPart() as test:
Sphere(10)
Split(keep=Keep.TOP)
self.assertAlmostEqual(test.part.Volume(), (2 / 3) * 1000 * pi, 5)
self.assertAlmostEqual(test.part.volume(), (2 / 3) * 1000 * pi, 5)
def test_split_both(self):
with BuildPart() as test:
@ -363,7 +363,7 @@ class TestSplit(unittest.TestCase):
with BuildPart() as test:
Sphere(10)
Split(bisect_by="YZ", keep=Keep.TOP)
self.assertAlmostEqual(test.part.Volume(), (2 / 3) * 1000 * pi, 5)
self.assertAlmostEqual(test.part.volume(), (2 / 3) * 1000 * pi, 5)
class TestSweep(unittest.TestCase):
@ -374,7 +374,7 @@ class TestSweep(unittest.TestCase):
with BuildSketch():
Rectangle(2, 2)
Sweep()
self.assertAlmostEqual(test.part.Volume(), 40, 5)
self.assertAlmostEqual(test.part.volume(), 40, 5)
def test_multi_section(self):
segment_count = 6
@ -403,7 +403,7 @@ class TestSweep(unittest.TestCase):
Fillet(*section.vertices(), radius=0.2)
# Create the handle by sweeping along the path
Sweep(multisection=True)
self.assertAlmostEqual(handle.part.Volume(), 54.11246334691092, 5)
self.assertAlmostEqual(handle.part.volume(), 54.11246334691092, 5)
def test_passed_parameters(self):
with BuildLine() as path:
@ -412,14 +412,14 @@ class TestSweep(unittest.TestCase):
Rectangle(2, 2)
with BuildPart() as test:
Sweep(*section.faces(), path=path.wires()[0])
self.assertAlmostEqual(test.part.Volume(), 40, 5)
self.assertAlmostEqual(test.part.volume(), 40, 5)
class TestTorus(unittest.TestCase):
def test_simple_torus(self):
with BuildPart() as test:
Torus(100, 10)
self.assertAlmostEqual(test.part.Volume(), pi * 100 * 2 * pi * 100, 5)
self.assertAlmostEqual(test.part.volume(), pi * 100 * 2 * pi * 100, 5)
if __name__ == "__main__":

View file

@ -45,7 +45,7 @@ class BuildSketchTests(unittest.TestCase):
def test_obj_name(self):
with BuildSketch() as test:
Rectangle(10, 10)
self.assertEqual(test._obj_name,"sketch")
self.assertEqual(test._obj_name, "sketch")
def test_select_vertices(self):
"""Test vertices()"""
@ -78,20 +78,20 @@ class BuildSketchTests(unittest.TestCase):
l1 = Line((0, 0), (10, 0))
Line(l1 @ 1, (10, 10))
self.assertTupleAlmostEquals(
(test.consolidate_edges() @ 1).toTuple(), (10, 10, 0), 5
(test.consolidate_edges() @ 1).to_tuple(), (10, 10, 0), 5
)
def test_mode_intersect(self):
with BuildSketch() as test:
Circle(10)
Rectangle(10, 10, centered=(False, False), mode=Mode.INTERSECT)
self.assertAlmostEqual(test.sketch.Area(), 25 * pi, 5)
self.assertAlmostEqual(test.sketch.area(), 25 * pi, 5)
def test_mode_replace(self):
with BuildSketch() as test:
Circle(10)
Rectangle(10, 10, centered=(False, False), mode=Mode.REPLACE)
self.assertAlmostEqual(test.sketch.Area(), 100, 5)
self.assertAlmostEqual(test.sketch.area(), 100, 5)
class BuildSketchExceptions(unittest.TestCase):
@ -117,7 +117,7 @@ class BuildSketchObjects(unittest.TestCase):
self.assertEqual(c.radius, 20)
self.assertEqual(c.centered, (True, True))
self.assertEqual(c.mode, Mode.ADD)
self.assertAlmostEqual(test.sketch.Area(), pi * 20**2, 5)
self.assertAlmostEqual(test.sketch.area(), pi * 20**2, 5)
def test_ellipse(self):
with BuildSketch() as test:
@ -127,7 +127,7 @@ class BuildSketchObjects(unittest.TestCase):
self.assertEqual(e.rotation, 0)
self.assertEqual(e.centered, (True, True))
self.assertEqual(e.mode, Mode.ADD)
self.assertAlmostEqual(test.sketch.Area(), pi * 20 * 10, 5)
self.assertAlmostEqual(test.sketch.area(), pi * 20 * 10, 5)
def test_polygon(self):
with BuildSketch() as test:
@ -136,7 +136,7 @@ class BuildSketchObjects(unittest.TestCase):
self.assertEqual(p.rotation, 0)
self.assertEqual(p.centered, (True, True))
self.assertEqual(p.mode, Mode.ADD)
self.assertAlmostEqual(test.sketch.Area(), 0.5, 5)
self.assertAlmostEqual(test.sketch.area(), 0.5, 5)
def test_rectangle(self):
with BuildSketch() as test:
@ -146,7 +146,7 @@ class BuildSketchObjects(unittest.TestCase):
self.assertEqual(r.rotation, 0)
self.assertEqual(r.centered, (True, True))
self.assertEqual(r.mode, Mode.ADD)
self.assertAlmostEqual(test.sketch.Area(), 20 * 10, 5)
self.assertAlmostEqual(test.sketch.area(), 20 * 10, 5)
def test_regular_polygon(self):
with BuildSketch() as test:
@ -156,7 +156,7 @@ class BuildSketchObjects(unittest.TestCase):
self.assertEqual(r.rotation, 0)
self.assertEqual(r.centered, (True, True))
self.assertEqual(r.mode, Mode.ADD)
self.assertAlmostEqual(test.sketch.Area(), (3 * sqrt(3) / 2) * 2**2, 5)
self.assertAlmostEqual(test.sketch.area(), (3 * sqrt(3) / 2) * 2**2, 5)
def test_slot_arc(self):
with BuildSketch() as test:
@ -167,17 +167,17 @@ class BuildSketchObjects(unittest.TestCase):
self.assertEqual(s.height, 1)
self.assertEqual(s.rotation, 45)
self.assertEqual(s.mode, Mode.ADD)
self.assertAlmostEqual(test.sketch.Area(), 6.186450426893698, 5)
self.assertAlmostEqual(test.sketch.area(), 6.186450426893698, 5)
def test_slot_center_point(self):
with BuildSketch() as test:
s = SlotCenterPoint((0, 0), (2, 0), 2)
self.assertTupleAlmostEquals(s.center.toTuple(), (0, 0, 0), 5)
self.assertTupleAlmostEquals(s.point.toTuple(), (2, 0, 0), 5)
self.assertTupleAlmostEquals(s.center.to_tuple(), (0, 0, 0), 5)
self.assertTupleAlmostEquals(s.point.to_tuple(), (2, 0, 0), 5)
self.assertEqual(s.height, 2)
self.assertEqual(s.rotation, 0)
self.assertEqual(s.mode, Mode.ADD)
self.assertAlmostEqual(test.sketch.Area(), pi + 4 * 2, 5)
self.assertAlmostEqual(test.sketch.area(), pi + 4 * 2, 5)
def test_slot_center_to_center(self):
with BuildSketch() as test:
@ -186,7 +186,7 @@ class BuildSketchObjects(unittest.TestCase):
self.assertEqual(s.height, 2)
self.assertEqual(s.rotation, 0)
self.assertEqual(s.mode, Mode.ADD)
self.assertAlmostEqual(test.sketch.Area(), pi + 4 * 2, 5)
self.assertAlmostEqual(test.sketch.area(), pi + 4 * 2, 5)
def test_slot_overall(self):
with BuildSketch() as test:
@ -195,7 +195,7 @@ class BuildSketchObjects(unittest.TestCase):
self.assertEqual(s.height, 2)
self.assertEqual(s.rotation, 0)
self.assertEqual(s.mode, Mode.ADD)
self.assertAlmostEqual(test.sketch.Area(), pi + 4 * 2, 5)
self.assertAlmostEqual(test.sketch.area(), pi + 4 * 2, 5)
def test_text(self):
with BuildSketch() as test:
@ -222,7 +222,7 @@ class BuildSketchObjects(unittest.TestCase):
self.assertEqual(t.right_side_angle, 63.434948823)
self.assertEqual(t.rotation, 0)
self.assertEqual(t.mode, Mode.ADD)
self.assertAlmostEqual(test.sketch.Area(), 2 * (6 + 4) / 2, 5)
self.assertAlmostEqual(test.sketch.area(), 2 * (6 + 4) / 2, 5)
with self.assertRaises(ValueError):
with BuildSketch() as test:
@ -243,7 +243,7 @@ class BuildSketchObjects(unittest.TestCase):
with BuildSketch() as test:
with Locations((-10, 0), (10, 0)):
Circle(1)
self.assertAlmostEqual(sum([f.Area() for f in test.faces()]), 2 * pi, 5)
self.assertAlmostEqual(sum([f.area() for f in test.faces()]), 2 * pi, 5)
def test_hull(self):
"""Test hull from pending edges and passed edges"""
@ -253,15 +253,15 @@ class BuildSketchObjects(unittest.TestCase):
CenterArc((1, 1.5), 0.5, 0, 360)
Line((0.0, 2), (-1, 3.0))
BuildHull()
self.assertAlmostEqual(test.sketch.Area(), 7.258175622249558, 5)
self.assertAlmostEqual(test.sketch.area(), 7.258175622249558, 5)
with BuildSketch() as test:
with Locations((-10, 0)):
Circle(10)
with Locations((10, 0)):
Circle(7)
BuildHull(*test.edges())
self.assertAlmostEqual(test.sketch.Area(), 577.8808734698988, 5)
self.assertAlmostEqual(test.sketch.area(), 577.8808734698988, 5)
if __name__ == "__main__":
unittest.main()
unittest.main(failfast=True)