mirror of
https://github.com/gumyr/build123d.git
synced 2025-12-06 02:30:55 -08:00
Add->add, restored context logging and validation
This commit is contained in:
parent
cb05ee97b6
commit
92979fc29c
21 changed files with 210 additions and 239 deletions
|
|
@ -60,7 +60,7 @@ if "show" in locals():
|
|||
|
||||
b = Box(1, 2, 3)
|
||||
with BuildPart() as bp:
|
||||
Add(b)
|
||||
add(b)
|
||||
|
||||
if "show" in locals():
|
||||
show(bp)
|
||||
|
|
@ -70,7 +70,7 @@ if "show" in locals():
|
|||
b = Box(1, 2, 3) + Cylinder(0.75, 2.5)
|
||||
|
||||
with BuildPart() as bp:
|
||||
Add(b)
|
||||
add(b)
|
||||
Cylinder(0.4, 6, mode=Mode.SUBTRACT)
|
||||
|
||||
c = bp.part - Plane.YZ * Cylinder(0.2, 6)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ if "show" in locals():
|
|||
b = Rectangle(1, 2) + Circle(0.75)
|
||||
|
||||
with BuildSketch() as sk:
|
||||
Add(b)
|
||||
add(b)
|
||||
Circle(0.1, mode=Mode.SUBTRACT)
|
||||
|
||||
c = sk.sketch - Pos(0, 0.5) * Circle(0.2)
|
||||
|
|
|
|||
|
|
@ -75,9 +75,9 @@ with BuildLine() as extension_lines:
|
|||
(logo_width, -ext_line_length - font_height * 0.1),
|
||||
)
|
||||
with Locations(l1 @ 0.5):
|
||||
Add(*arrow_left.line)
|
||||
add(*arrow_left.line)
|
||||
with Locations(l2 @ 0.5):
|
||||
Add(*arrow_left.line, rotation=180.0)
|
||||
add(*arrow_left.line, rotation=180.0)
|
||||
Line(l1 @ 0.5, l1 @ 0.5 + Vector(dim_line_length, 0))
|
||||
Line(l2 @ 0.5, l2 @ 0.5 - Vector(dim_line_length, 0))
|
||||
|
||||
|
|
@ -87,12 +87,12 @@ with BuildSketch() as build:
|
|||
(l1 @ 0.5 + l2 @ 0.5) / 2
|
||||
- Vector((build_vertices[-1].X + build_vertices[0].X) / 2, 0)
|
||||
):
|
||||
Add(build_text.sketch)
|
||||
add(build_text.sketch)
|
||||
# add the customizable text to the build text sketch
|
||||
with Locations(
|
||||
(l1 @ 1 + l2 @ 1) / 2 - Vector((cust_vertices[-1].X + cust_vertices[0].X), 1.4)
|
||||
):
|
||||
Add(cust_text.sketch)
|
||||
add(cust_text.sketch)
|
||||
|
||||
cmpd = Compound.make_compound(
|
||||
[three_d.part, two.sketch, one.line, build.sketch, extension_lines.line]
|
||||
|
|
|
|||
|
|
@ -64,9 +64,9 @@ with BuildLine() as extension_lines:
|
|||
(logo_width, -ext_line_length - font_height * 0.1),
|
||||
)
|
||||
with Locations(l1 @ 0.5):
|
||||
Add(*arrow_left.line)
|
||||
add(*arrow_left.line)
|
||||
with Locations(l2 @ 0.5):
|
||||
Add(*arrow_left.line, rotation=180.0)
|
||||
add(*arrow_left.line, rotation=180.0)
|
||||
Line(l1 @ 0.5, l1 @ 0.5 + Vector(dim_line_length, 0))
|
||||
Line(l2 @ 0.5, l2 @ 0.5 - Vector(dim_line_length, 0))
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ with BuildSketch() as build:
|
|||
(l1 @ 0.5 + l2 @ 0.5) / 2
|
||||
- Vector((build_vertices[-1].X + build_vertices[0].X) / 2, 0)
|
||||
):
|
||||
Add(build_text.sketch)
|
||||
add(build_text.sketch)
|
||||
|
||||
# logo = Assembly(None, name="logo")
|
||||
# logo.add(one.wires()[0], name="one")
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ with BuildSketch() as minute_indicator:
|
|||
with BuildSketch() as clock_face:
|
||||
Circle(clock_radius)
|
||||
with PolarLocations(0, 60):
|
||||
Add(minute_indicator.sketch, mode=Mode.SUBTRACT)
|
||||
add(minute_indicator.sketch, mode=Mode.SUBTRACT)
|
||||
with PolarLocations(clock_radius * 0.875, 12):
|
||||
SlotOverall(clock_radius * 0.05, clock_radius * 0.025, mode=Mode.SUBTRACT)
|
||||
for hour in range(1, 13):
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ with BuildPart() as box_builder:
|
|||
box = box_builder.part
|
||||
|
||||
with BuildPart() as lid_builder:
|
||||
Add(box_plan.sketch)
|
||||
add(box_plan.sketch)
|
||||
extrude(amount=pocket_t / 2 + bottom_t)
|
||||
with BuildSketch() as pocket:
|
||||
offset(box_plan.sketch, amount=-(wall_t - lip_t), mode=Mode.ADD)
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ with BuildPart() as box_builder:
|
|||
fillet(*plan.vertices(), radius=card_width / 15)
|
||||
extrude(amount=wall / 2)
|
||||
with BuildSketch(box_builder.faces().sort_by(Axis.Z)[-1]) as walls:
|
||||
Add(plan.sketch)
|
||||
add(plan.sketch)
|
||||
offset(plan.sketch, amount=-wall, mode=Mode.SUBTRACT)
|
||||
extrude(amount=deck / 2)
|
||||
with BuildSketch(box_builder.faces().sort_by(Axis.Z)[-1]) as inset_walls:
|
||||
|
|
@ -130,11 +130,11 @@ with BuildPart() as box_builder:
|
|||
|
||||
with BuildPart() as lid_builder:
|
||||
with BuildSketch() as outset_walls:
|
||||
Add(plan.sketch)
|
||||
add(plan.sketch)
|
||||
offset(plan.sketch, amount=-(wall - gap) / 2, mode=Mode.SUBTRACT)
|
||||
extrude(amount=deck / 2)
|
||||
with BuildSketch(lid_builder.faces().sort_by(Axis.Z)[-1]) as top:
|
||||
Add(plan.sketch)
|
||||
add(plan.sketch)
|
||||
extrude(amount=wall / 2)
|
||||
with BuildSketch(lid_builder.faces().sort_by(Axis.Z)[-1]):
|
||||
holes = GridLocations(
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ from build123d.build_common import *
|
|||
from build123d.build_line import *
|
||||
from build123d.build_sketch import *
|
||||
from build123d.build_part import *
|
||||
from build123d.build_generic import *
|
||||
from build123d.geometry import *
|
||||
from build123d.topology import *
|
||||
from build123d.build_enums import ApproxOption
|
||||
from build123d.build_enums import *
|
||||
from build123d.importers import *
|
||||
from build123d.operations_generic import *
|
||||
from build123d.operations_part import *
|
||||
|
|
@ -41,7 +40,6 @@ __all__ = [
|
|||
"Unit",
|
||||
"Until",
|
||||
# Builders,
|
||||
"Add",
|
||||
"HexLocations",
|
||||
"PolarLocations",
|
||||
"Locations",
|
||||
|
|
@ -129,6 +127,7 @@ __all__ = [
|
|||
# Other functions
|
||||
"polar",
|
||||
# Operations
|
||||
"add",
|
||||
"bounding_box",
|
||||
"chamfer",
|
||||
"extrude",
|
||||
|
|
|
|||
|
|
@ -41,10 +41,13 @@ from build123d.build_enums import Align, Mode, Select
|
|||
from build123d.geometry import Axis, Location, Plane, Vector, VectorLike
|
||||
from build123d.topology import (
|
||||
Compound,
|
||||
Curve,
|
||||
Edge,
|
||||
Face,
|
||||
Part,
|
||||
Shape,
|
||||
ShapeList,
|
||||
Sketch,
|
||||
Solid,
|
||||
Vertex,
|
||||
Wire,
|
||||
|
|
@ -75,6 +78,25 @@ FT = 12 * IN
|
|||
THOU = IN / 1000
|
||||
|
||||
|
||||
operations_apply_to = {
|
||||
"add": ["BuildPart", "BuildSketch", "BuildLine"],
|
||||
"bounding_box": ["BuildPart", "BuildSketch", "BuildLine"],
|
||||
"chamfer": ["BuildPart", "BuildSketch"],
|
||||
"extrude": ["BuildPart"],
|
||||
"fillet": ["BuildPart", "BuildSketch"],
|
||||
"loft": ["BuildPart"],
|
||||
"make_face": ["BuildSketch"],
|
||||
"make_hull": ["BuildSketch"],
|
||||
"mirror": ["BuildPart", "BuildSketch", "BuildLine"],
|
||||
"offset": ["BuildPart", "BuildSketch", "BuildLine"],
|
||||
"revolve": ["BuildPart"],
|
||||
"scale": ["BuildPart", "BuildSketch", "BuildLine"],
|
||||
"section": ["BuildPart"],
|
||||
"split": ["BuildPart", "BuildSketch", "BuildLine"],
|
||||
"sweep": ["BuildPart"],
|
||||
}
|
||||
|
||||
|
||||
class Builder(ABC):
|
||||
"""Builder
|
||||
|
||||
|
|
@ -194,33 +216,19 @@ class Builder(ABC):
|
|||
return NotImplementedError # pragma: no cover
|
||||
|
||||
@classmethod
|
||||
def _get_context(cls, caller=None) -> Self:
|
||||
def _get_context(cls, caller: Union[Builder, str] = None, log: bool = True) -> Self:
|
||||
"""Return the instance of the current builder"""
|
||||
result = cls._current.get(None)
|
||||
context_name = "None" if result is None else type(result).__name__
|
||||
|
||||
current_builder = inspect.currentframe().f_back.f_locals.get("self", None)
|
||||
|
||||
if not isinstance(current_builder, Builder):
|
||||
current_builder = None
|
||||
|
||||
if not isinstance(caller, Builder):
|
||||
caller = None
|
||||
|
||||
# print(f"{result=}, {current_builder=}, {caller=}")
|
||||
if current_builder is None:
|
||||
logger.info("Context requested by None")
|
||||
if log:
|
||||
if isinstance(caller, (Part, Sketch, Curve, Wire)):
|
||||
caller_name = caller.__class__.__name__
|
||||
elif isinstance(caller, str):
|
||||
caller_name = caller
|
||||
else:
|
||||
logger.info(
|
||||
"Context requested by %s",
|
||||
type(current_builder).__name__,
|
||||
)
|
||||
|
||||
if caller is not None and result is None:
|
||||
if hasattr(caller, "_applies_to"):
|
||||
raise RuntimeError(
|
||||
f"No valid context found, use one of {caller._applies_to}"
|
||||
)
|
||||
raise RuntimeError("No valid context found-common")
|
||||
caller_name = "None"
|
||||
logger.info("%s context requested by %s", context_name, caller_name)
|
||||
|
||||
return result
|
||||
|
||||
|
|
@ -322,14 +330,23 @@ class Builder(ABC):
|
|||
objects = [objects]
|
||||
|
||||
if (
|
||||
validating_class is not None
|
||||
and not self._tag() in validating_class._applies_to
|
||||
isinstance(validating_class, (Part, Sketch, Curve, Wire))
|
||||
and self._tag() not in validating_class._applies_to
|
||||
):
|
||||
raise RuntimeError(
|
||||
f"{self.__class__.__name__} doesn't have a "
|
||||
f"{validating_class.__class__.__name__} object or operation "
|
||||
f"({validating_class.__class__.__name__} applies to {validating_class._applies_to})"
|
||||
)
|
||||
elif (
|
||||
isinstance(validating_class, str)
|
||||
and self.__class__.__name__ not in operations_apply_to[validating_class]
|
||||
):
|
||||
raise RuntimeError(
|
||||
f"{self.__class__.__name__} doesn't have a "
|
||||
f"{validating_class} object or operation "
|
||||
f"({validating_class} applies to {operations_apply_to[validating_class]})"
|
||||
)
|
||||
# Check for valid object inputs
|
||||
for obj in objects:
|
||||
if obj is None:
|
||||
|
|
|
|||
|
|
@ -1,144 +0,0 @@
|
|||
"""
|
||||
BuildGeneric
|
||||
|
||||
name: build_generic.py
|
||||
by: Gumyr
|
||||
date: July 12th 2022
|
||||
|
||||
desc:
|
||||
This python module is a library of generic classes used by other
|
||||
build123d builders.
|
||||
|
||||
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.
|
||||
|
||||
"""
|
||||
import logging
|
||||
from typing import Union
|
||||
|
||||
from build123d.build_common import Builder, LocationList, WorkplaneList, validate_inputs
|
||||
from build123d.build_enums import Mode
|
||||
from build123d.build_line import BuildLine
|
||||
from build123d.build_part import BuildPart
|
||||
from build123d.build_sketch import BuildSketch
|
||||
from build123d.geometry import Axis, Rotation, RotationLike
|
||||
from build123d.topology import Compound, Edge, Face, Solid, Wire
|
||||
|
||||
logging.getLogger("build123d").addHandler(logging.NullHandler())
|
||||
logger = logging.getLogger("build123d")
|
||||
|
||||
|
||||
#
|
||||
# Objects
|
||||
#
|
||||
|
||||
|
||||
class Add(Compound):
|
||||
"""Generic Object: Add Object to Part or Sketch
|
||||
|
||||
Add an object to the builder.
|
||||
|
||||
if BuildPart:
|
||||
Edges and Wires are added to pending_edges. Compounds of Face are added to
|
||||
pending_faces. Solids or Compounds of Solid are combined into the part.
|
||||
elif BuildSketch:
|
||||
Edges and Wires are added to pending_edges. Compounds of Face are added to sketch.
|
||||
elif BuildLine:
|
||||
Edges and Wires are added to line.
|
||||
|
||||
Args:
|
||||
objects (Union[Edge, Wire, Face, Solid, Compound]): sequence of objects to add
|
||||
rotation (Union[float, RotationLike], optional): rotation angle for sketch,
|
||||
rotation about each axis for part. Defaults to None.
|
||||
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
|
||||
"""
|
||||
|
||||
_applies_to = [BuildPart._tag(), BuildSketch._tag(), BuildLine._tag()]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*objects: Union[Edge, Wire, Face, Solid, Compound],
|
||||
rotation: Union[float, RotationLike] = None,
|
||||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: Builder = Builder._get_context(self)
|
||||
if context is None:
|
||||
raise RuntimeError("Add must have an active builder context")
|
||||
validate_inputs(context, self, objects)
|
||||
|
||||
if isinstance(context, BuildPart):
|
||||
if rotation is None:
|
||||
rotation = Rotation(0, 0, 0)
|
||||
elif isinstance(rotation, float):
|
||||
raise ValueError("Float values of rotation are not valid for BuildPart")
|
||||
elif isinstance(rotation, tuple):
|
||||
rotation = Rotation(*rotation)
|
||||
|
||||
objects = [obj.moved(rotation) for obj in objects]
|
||||
new_edges = [obj for obj in objects if isinstance(obj, Edge)]
|
||||
new_wires = [obj for obj in objects if isinstance(obj, Wire)]
|
||||
new_faces = [obj for obj in objects if isinstance(obj, Face)]
|
||||
new_solids = [obj for obj in objects if isinstance(obj, Solid)]
|
||||
for compound in filter(lambda o: isinstance(o, Compound), objects):
|
||||
new_edges.extend(compound.get_type(Edge))
|
||||
new_wires.extend(compound.get_type(Wire))
|
||||
new_faces.extend(compound.get_type(Face))
|
||||
new_solids.extend(compound.get_type(Solid))
|
||||
for new_wire in new_wires:
|
||||
new_edges.extend(new_wire.edges())
|
||||
|
||||
# Add the pending Edges in one group
|
||||
if not LocationList._get_context():
|
||||
raise RuntimeError("There is no active Locations context")
|
||||
located_edges = [
|
||||
edge.moved(location)
|
||||
for edge in new_edges
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_pending(*located_edges)
|
||||
new_objects = located_edges
|
||||
|
||||
# Add to pending Faces batched by workplane
|
||||
for workplane in WorkplaneList._get_context().workplanes:
|
||||
faces_per_workplane = []
|
||||
for location in LocationList._get_context().locations:
|
||||
for face in new_faces:
|
||||
faces_per_workplane.append(face.moved(location))
|
||||
context._add_to_pending(*faces_per_workplane, face_plane=workplane)
|
||||
new_objects.extend(faces_per_workplane)
|
||||
|
||||
# Add to context Solids
|
||||
located_solids = [
|
||||
solid.moved(location)
|
||||
for solid in new_solids
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*located_solids, mode=mode)
|
||||
new_objects.extend(located_solids)
|
||||
|
||||
elif isinstance(context, (BuildLine, BuildSketch)):
|
||||
rotation_angle = rotation if isinstance(rotation, (int, float)) else 0.0
|
||||
new_objects = []
|
||||
for obj in objects:
|
||||
new_objects.extend(
|
||||
[
|
||||
obj.rotate(Axis.Z, rotation_angle).moved(location)
|
||||
for location in LocationList._get_context().local_locations
|
||||
]
|
||||
)
|
||||
context._add_to_context(*new_objects, mode=mode)
|
||||
|
||||
super().__init__(Compound.make_compound(new_objects).wrapped)
|
||||
|
|
@ -63,7 +63,7 @@ class BuildLine(Builder):
|
|||
|
||||
@staticmethod
|
||||
def _tag() -> str:
|
||||
return BuildLine
|
||||
return "BuildLine"
|
||||
|
||||
@property
|
||||
def _obj(self):
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class BuildPart(Builder):
|
|||
|
||||
@staticmethod
|
||||
def _tag() -> str:
|
||||
return BuildPart
|
||||
return "BuildPart"
|
||||
|
||||
@property
|
||||
def _obj(self):
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class BaseLineObject(Wire):
|
|||
curve: Union[Edge, Wire],
|
||||
mode: Mode = Mode.ADD,
|
||||
):
|
||||
context: BuildLine = BuildLine._get_context(self)
|
||||
context: BuildLine = BuildLine._get_context(self, log=False)
|
||||
|
||||
if context is not None:
|
||||
context._add_to_context(*curve.edges(), mode=mode)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class BasePartObject(Part):
|
|||
align_offset.append(-bbox.max.to_tuple()[i])
|
||||
solid.move(Location(Vector(*align_offset)))
|
||||
|
||||
context: BuildPart = BuildPart._get_context(self)
|
||||
context: BuildPart = BuildPart._get_context(self, log=False)
|
||||
if context is None:
|
||||
new_solids = [solid]
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class BaseSketchObject(Sketch):
|
|||
align_offset.append(-bbox.max.to_tuple()[i])
|
||||
obj.move(Location(Vector(*align_offset)))
|
||||
|
||||
context: BuildSketch = BuildSketch._get_context(self)
|
||||
context: BuildSketch = BuildSketch._get_context(self, log=False)
|
||||
if context is None:
|
||||
new_faces = obj.faces()
|
||||
|
||||
|
|
|
|||
|
|
@ -29,11 +29,19 @@ license:
|
|||
import copy
|
||||
import logging
|
||||
from typing import Union
|
||||
from build123d.build_enums import Mode, Kind, Keep
|
||||
|
||||
from build123d.build_common import Builder, LocationList, WorkplaneList, validate_inputs
|
||||
from build123d.build_enums import Keep, Kind, Mode
|
||||
from build123d.build_line import BuildLine
|
||||
from build123d.build_part import BuildPart
|
||||
from build123d.build_sketch import BuildSketch
|
||||
from build123d.geometry import (
|
||||
Axis,
|
||||
Location,
|
||||
Matrix,
|
||||
Plane,
|
||||
Rotation,
|
||||
RotationLike,
|
||||
Vector,
|
||||
)
|
||||
from build123d.topology import (
|
||||
|
|
@ -41,9 +49,9 @@ from build123d.topology import (
|
|||
Curve,
|
||||
Edge,
|
||||
Face,
|
||||
Matrix,
|
||||
Part,
|
||||
Plane,
|
||||
Matrix,
|
||||
Shape,
|
||||
Sketch,
|
||||
Solid,
|
||||
|
|
@ -51,15 +59,101 @@ from build123d.topology import (
|
|||
Wire,
|
||||
)
|
||||
|
||||
from build123d.build_common import Builder, validate_inputs
|
||||
|
||||
logging.getLogger("build123d").addHandler(logging.NullHandler())
|
||||
logger = logging.getLogger("build123d")
|
||||
|
||||
|
||||
#
|
||||
# Operations
|
||||
#
|
||||
def add(
|
||||
*objects: Union[Edge, Wire, Face, Solid, Compound],
|
||||
rotation: Union[float, RotationLike] = None,
|
||||
mode: Mode = Mode.ADD,
|
||||
) -> Compound:
|
||||
"""Generic Object: Add Object to Part or Sketch
|
||||
|
||||
Add an object to a builder.
|
||||
|
||||
if BuildPart:
|
||||
Edges and Wires are added to pending_edges. Compounds of Face are added to
|
||||
pending_faces. Solids or Compounds of Solid are combined into the part.
|
||||
elif BuildSketch:
|
||||
Edges and Wires are added to pending_edges. Compounds of Face are added to sketch.
|
||||
elif BuildLine:
|
||||
Edges and Wires are added to line.
|
||||
|
||||
Args:
|
||||
objects (Union[Edge, Wire, Face, Solid, Compound]): sequence of objects to add
|
||||
rotation (Union[float, RotationLike], optional): rotation angle for sketch,
|
||||
rotation about each axis for part. Defaults to None.
|
||||
mode (Mode, optional): combine mode. Defaults to Mode.ADD.
|
||||
"""
|
||||
context: Builder = Builder._get_context(None)
|
||||
if context is None:
|
||||
raise RuntimeError("Add must have an active builder context")
|
||||
validate_inputs(context, None, objects)
|
||||
|
||||
if isinstance(context, BuildPart):
|
||||
if rotation is None:
|
||||
rotation = Rotation(0, 0, 0)
|
||||
elif isinstance(rotation, float):
|
||||
raise ValueError("Float values of rotation are not valid for BuildPart")
|
||||
elif isinstance(rotation, tuple):
|
||||
rotation = Rotation(*rotation)
|
||||
|
||||
objects = [obj.moved(rotation) for obj in objects]
|
||||
new_edges = [obj for obj in objects if isinstance(obj, Edge)]
|
||||
new_wires = [obj for obj in objects if isinstance(obj, Wire)]
|
||||
new_faces = [obj for obj in objects if isinstance(obj, Face)]
|
||||
new_solids = [obj for obj in objects if isinstance(obj, Solid)]
|
||||
for compound in filter(lambda o: isinstance(o, Compound), objects):
|
||||
new_edges.extend(compound.get_type(Edge))
|
||||
new_wires.extend(compound.get_type(Wire))
|
||||
new_faces.extend(compound.get_type(Face))
|
||||
new_solids.extend(compound.get_type(Solid))
|
||||
for new_wire in new_wires:
|
||||
new_edges.extend(new_wire.edges())
|
||||
|
||||
# Add the pending Edges in one group
|
||||
if not LocationList._get_context():
|
||||
raise RuntimeError("There is no active Locations context")
|
||||
located_edges = [
|
||||
edge.moved(location)
|
||||
for edge in new_edges
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_pending(*located_edges)
|
||||
new_objects = located_edges
|
||||
|
||||
# Add to pending Faces batched by workplane
|
||||
for workplane in WorkplaneList._get_context().workplanes:
|
||||
faces_per_workplane = []
|
||||
for location in LocationList._get_context().locations:
|
||||
for face in new_faces:
|
||||
faces_per_workplane.append(face.moved(location))
|
||||
context._add_to_pending(*faces_per_workplane, face_plane=workplane)
|
||||
new_objects.extend(faces_per_workplane)
|
||||
|
||||
# Add to context Solids
|
||||
located_solids = [
|
||||
solid.moved(location)
|
||||
for solid in new_solids
|
||||
for location in LocationList._get_context().locations
|
||||
]
|
||||
context._add_to_context(*located_solids, mode=mode)
|
||||
new_objects.extend(located_solids)
|
||||
|
||||
elif isinstance(context, (BuildLine, BuildSketch)):
|
||||
rotation_angle = rotation if isinstance(rotation, (int, float)) else 0.0
|
||||
new_objects = []
|
||||
for obj in objects:
|
||||
new_objects.extend(
|
||||
[
|
||||
obj.rotate(Axis.Z, rotation_angle).moved(location)
|
||||
for location in LocationList._get_context().local_locations
|
||||
]
|
||||
)
|
||||
context._add_to_context(*new_objects, mode=mode)
|
||||
|
||||
return Compound.make_compound(new_objects)
|
||||
|
||||
|
||||
def bounding_box(
|
||||
|
|
@ -76,8 +170,8 @@ def bounding_box(
|
|||
objects (Shape): sequence of objects
|
||||
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
|
||||
"""
|
||||
context: Builder = Builder._get_context(None)
|
||||
validate_inputs(context, None, objects)
|
||||
context: Builder = Builder._get_context("bounding_box")
|
||||
validate_inputs(context, "bounding_box", objects)
|
||||
|
||||
if (not objects and context is None) or (
|
||||
not objects and context is not None and not context._obj
|
||||
|
|
@ -141,12 +235,13 @@ def chamfer(
|
|||
target (Union[Face, Sketch, Solid, Part], optional): object to chamfer. Defaults to None.
|
||||
|
||||
Raises:
|
||||
ValueError: no objects provided
|
||||
ValueError: objects must be Edges
|
||||
ValueError: objects must be Vertices
|
||||
ValueError: missing target object
|
||||
"""
|
||||
context: Builder = Builder._get_context(None)
|
||||
validate_inputs(context, None, objects)
|
||||
context: Builder = Builder._get_context("chamfer")
|
||||
validate_inputs(context, "chamfer", objects)
|
||||
|
||||
if (not objects and context is None) or (
|
||||
not objects and context is not None and not context._obj
|
||||
|
|
@ -204,8 +299,8 @@ def fillet(
|
|||
ValueError: objects must be Vertices
|
||||
ValueError: missing target object
|
||||
"""
|
||||
context: Builder = Builder._get_context(None)
|
||||
validate_inputs(context, None, objects)
|
||||
context: Builder = Builder._get_context("fillet")
|
||||
validate_inputs(context, "fillet", objects)
|
||||
|
||||
if (not objects and context is None) or (
|
||||
not objects and context is not None and not context._obj
|
||||
|
|
@ -262,8 +357,8 @@ def mirror(
|
|||
Raises:
|
||||
ValueError: missing objects
|
||||
"""
|
||||
context: Builder = Builder._get_context(None)
|
||||
validate_inputs(context, None, objects)
|
||||
context: Builder = Builder._get_context("mirror")
|
||||
validate_inputs(context, "mirror", objects)
|
||||
|
||||
if not objects:
|
||||
if context is None:
|
||||
|
|
@ -314,8 +409,8 @@ def offset(
|
|||
ValueError: missing objects
|
||||
ValueError: Invalid object type
|
||||
"""
|
||||
context: Builder = Builder._get_context(None)
|
||||
validate_inputs(context, None, objects)
|
||||
context: Builder = Builder._get_context("offset")
|
||||
validate_inputs(context, "offset", objects)
|
||||
|
||||
if not objects:
|
||||
if context is None:
|
||||
|
|
@ -404,8 +499,8 @@ def scale(
|
|||
Raises:
|
||||
ValueError: missing objects
|
||||
"""
|
||||
context: Builder = Builder._get_context(None)
|
||||
validate_inputs(context, None, objects)
|
||||
context: Builder = Builder._get_context("scale")
|
||||
validate_inputs(context, "scale", objects)
|
||||
|
||||
if not objects:
|
||||
if context is None:
|
||||
|
|
@ -491,8 +586,8 @@ def split(
|
|||
)
|
||||
)
|
||||
|
||||
context: Builder = Builder._get_context(None)
|
||||
validate_inputs(context, None, objects)
|
||||
context: Builder = Builder._get_context("split")
|
||||
validate_inputs(context, "split", objects)
|
||||
|
||||
if not objects:
|
||||
if context is None:
|
||||
|
|
|
|||
|
|
@ -89,8 +89,8 @@ def extrude(
|
|||
Returns:
|
||||
Part: extruded object
|
||||
"""
|
||||
context: BuildPart = BuildPart._get_context(None)
|
||||
validate_inputs(context, None, to_extrude)
|
||||
context: BuildPart = BuildPart._get_context("extrude")
|
||||
validate_inputs(context, "extrude", to_extrude)
|
||||
|
||||
to_extrude_faces: list[Face]
|
||||
|
||||
|
|
@ -173,7 +173,8 @@ def loft(
|
|||
clean (bool, optional): Remove extraneous internal structure. Defaults to True.
|
||||
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
|
||||
"""
|
||||
context: BuildPart = BuildPart._get_context(None)
|
||||
context: BuildPart = BuildPart._get_context("loft")
|
||||
validate_inputs(context, "loft", sections)
|
||||
|
||||
if not sections:
|
||||
loft_wires = [face.outer_wire() for face in context.pending_faces]
|
||||
|
|
@ -222,7 +223,8 @@ def revolve(
|
|||
Raises:
|
||||
ValueError: Invalid axis of revolution
|
||||
"""
|
||||
context: BuildPart = BuildPart._get_context(None)
|
||||
context: BuildPart = BuildPart._get_context("revolve")
|
||||
validate_inputs(context, "revolve", profiles)
|
||||
|
||||
# Make sure we account for users specifying angles larger than 360 degrees, and
|
||||
# for OCCT not assuming that a 0 degree revolve means a 360 degree revolve
|
||||
|
|
@ -279,7 +281,8 @@ def section(
|
|||
clean (bool, optional): Remove extraneous internal structure. Defaults to True.
|
||||
mode (Mode, optional): combination mode. Defaults to Mode.INTERSECT.
|
||||
"""
|
||||
context: BuildPart = BuildPart._get_context(None)
|
||||
context: BuildPart = BuildPart._get_context("section")
|
||||
validate_inputs(context, "section", None)
|
||||
|
||||
if context is not None and obj is None:
|
||||
max_size = context.part.bounding_box().diagonal
|
||||
|
|
@ -341,7 +344,8 @@ def sweep(
|
|||
clean (bool, optional): Remove extraneous internal structure. Defaults to True.
|
||||
mode (Mode, optional): combination. Defaults to Mode.ADD.
|
||||
"""
|
||||
context: BuildPart = BuildPart._get_context(None)
|
||||
context: BuildPart = BuildPart._get_context("sweep")
|
||||
validate_inputs(context, "sweep", sections)
|
||||
|
||||
if path is None:
|
||||
path_wire = context.pending_edges_as_wire
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ def make_face(*edges: Edge, mode: Mode = Mode.ADD) -> Sketch:
|
|||
edges (Edge): sequence of perimeter edges
|
||||
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
|
||||
"""
|
||||
context: BuildSketch = BuildSketch._get_context(None)
|
||||
validate_inputs(context, None, edges)
|
||||
context: BuildSketch = BuildSketch._get_context("make_face")
|
||||
validate_inputs(context, "make_face", edges)
|
||||
|
||||
if edges:
|
||||
outer_edges = list(edges)
|
||||
|
|
@ -71,8 +71,8 @@ def make_hull(*edges: Edge, mode: Mode = Mode.ADD) -> Sketch:
|
|||
pending and sketch edges.
|
||||
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
|
||||
"""
|
||||
context: BuildSketch = BuildSketch._get_context(None)
|
||||
validate_inputs(context, None, edges)
|
||||
context: BuildSketch = BuildSketch._get_context("make_hull")
|
||||
validate_inputs(context, "make_hull", edges)
|
||||
|
||||
if edges:
|
||||
hull_edges = list(edges)
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ class TestValidateInputs(unittest.TestCase):
|
|||
with BuildPart() as p:
|
||||
Box(1, 1, 1)
|
||||
with BuildSketch():
|
||||
Add(p)
|
||||
add(p)
|
||||
|
||||
def test_no_sequence(self):
|
||||
with self.assertRaises(RuntimeError):
|
||||
|
|
|
|||
|
|
@ -67,28 +67,28 @@ class AddTests(unittest.TestCase):
|
|||
def test_add_to_line(self):
|
||||
# Add Edge
|
||||
with BuildLine() as test:
|
||||
Add(Edge.make_line((0, 0, 0), (1, 1, 1)))
|
||||
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))
|
||||
with BuildLine() as test:
|
||||
Add(wire.wires()[0])
|
||||
add(wire.wires()[0])
|
||||
self.assertEqual(len(test.line.edges()), 3)
|
||||
|
||||
def test_add_to_sketch(self):
|
||||
with BuildSketch() as test:
|
||||
Add(Face.make_rect(10, 10))
|
||||
add(Face.make_rect(10, 10))
|
||||
self.assertAlmostEqual(test.sketch.area, 100, 5)
|
||||
|
||||
def test_add_to_part(self):
|
||||
# Add Solid
|
||||
with BuildPart() as test:
|
||||
Add(Solid.make_box(10, 10, 10))
|
||||
add(Solid.make_box(10, 10, 10))
|
||||
self.assertAlmostEqual(test.part.volume, 1000, 5)
|
||||
# Add Compound
|
||||
with BuildPart() as test:
|
||||
Add(
|
||||
add(
|
||||
Compound.make_compound(
|
||||
[
|
||||
Solid.make_box(10, 10, 10),
|
||||
|
|
@ -101,17 +101,17 @@ class AddTests(unittest.TestCase):
|
|||
with BuildLine() as wire:
|
||||
Polyline((0, 0, 0), (1, 1, 1), (2, 0, 0), (3, 1, 1))
|
||||
with BuildPart() as test:
|
||||
Add(wire.wires()[0])
|
||||
add(wire.wires()[0])
|
||||
self.assertEqual(len(test.pending_edges), 3)
|
||||
|
||||
def test_errors(self):
|
||||
with self.assertRaises(RuntimeError):
|
||||
Add(Edge.make_line((0, 0, 0), (1, 1, 1)))
|
||||
add(Edge.make_line((0, 0, 0), (1, 1, 1)))
|
||||
|
||||
def test_unsupported_builder(self):
|
||||
with self.assertRaises(TypeError):
|
||||
with _TestBuilder():
|
||||
Add(Edge.make_line((0, 0, 0), (1, 1, 1)))
|
||||
add(Edge.make_line((0, 0, 0), (1, 1, 1)))
|
||||
|
||||
def test_local_global_locations(self):
|
||||
"""Check that add is using a local location list"""
|
||||
|
|
@ -124,7 +124,7 @@ class AddTests(unittest.TestCase):
|
|||
extrude(amount=10)
|
||||
topf = mainp.faces().sort_by(Axis.Z)[-1]
|
||||
with BuildSketch(topf):
|
||||
Add(vertwalls.sketch)
|
||||
add(vertwalls.sketch)
|
||||
extrude(amount=15)
|
||||
|
||||
self.assertEqual(len(mainp.solids()), 1)
|
||||
|
|
@ -135,7 +135,7 @@ class AddTests(unittest.TestCase):
|
|||
]
|
||||
with BuildPart(Plane.XY, Plane.YZ) as multiple:
|
||||
with Locations((1, 1), (-1, -1)) as locs:
|
||||
Add(*faces)
|
||||
add(*faces)
|
||||
self.assertEqual(len(multiple.pending_faces), 16)
|
||||
|
||||
|
||||
|
|
@ -283,7 +283,7 @@ class ChamferTests(unittest.TestCase):
|
|||
self.assertAlmostEqual(test.sketch.area, 200 - 4 * 0.5, 5)
|
||||
|
||||
def test_errors(self):
|
||||
with self.assertRaises(ValueError):
|
||||
with self.assertRaises(RuntimeError):
|
||||
with BuildLine():
|
||||
chamfer(length=1)
|
||||
|
||||
|
|
@ -321,7 +321,7 @@ class FilletTests(unittest.TestCase):
|
|||
self.assertAlmostEqual(test.sketch.area, 200 - 4 + pi, 5)
|
||||
|
||||
def test_errors(self):
|
||||
with self.assertRaises(ValueError):
|
||||
with self.assertRaises(RuntimeError):
|
||||
with BuildLine():
|
||||
fillet(radius=1)
|
||||
|
||||
|
|
|
|||
|
|
@ -120,11 +120,11 @@ class TestBuildOnPlanes(unittest.TestCase):
|
|||
def test_not_coplanar(self):
|
||||
with self.assertRaises(ValueError):
|
||||
with BuildSketch() as error:
|
||||
Add(Face.make_rect(1, 1, Plane.XY.offset(1)))
|
||||
add(Face.make_rect(1, 1, Plane.XY.offset(1)))
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
with BuildSketch() as error:
|
||||
Add(Face.make_rect(1, 1, Plane.XZ))
|
||||
add(Face.make_rect(1, 1, Plane.XZ))
|
||||
|
||||
def test_changing_geometry(self):
|
||||
with BuildSketch() as s:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue