mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-06 02:30:55 -08:00
Introducing new PEP8 compatible direct api
This commit is contained in:
parent
c253b3bba1
commit
9599e8e1ef
32 changed files with 9197 additions and 7291 deletions
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
165
src/build123d/build_enums.py
Normal file
165
src/build123d/build_enums.py
Normal 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}>"
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
7843
src/build123d/direct_api.py
Normal file
File diff suppressed because it is too large
Load diff
180
src/build123d/jupyter_tools.py
Normal file
180
src/build123d/jupyter_tools.py
Normal 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
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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__":
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue