Pylint changes - (mostly) not functional changes

This commit is contained in:
Roger Maitland 2022-09-27 19:10:34 -04:00
parent b50d6d5fe1
commit d642920243
11 changed files with 307 additions and 352 deletions

View file

@ -29,13 +29,13 @@ import cadquery as cq
with BuildSketch() as logo_text:
Text("123d", fontsize=10, valign=Valign.BOTTOM)
font_height = (logo_text.vertices() >> Axis.Y).y
font_height = (logo_text.vertices() >> Axis.Y).Y
with BuildSketch() as build_text:
Text("build", fontsize=5, halign=Halign.CENTER)
build_bb = BoundingBox(build_text.sketch, mode=Mode.PRIVATE)
build_vertices = build_bb.vertices() > Axis.X
build_width = build_vertices[-1].x - build_vertices[0].x
build_width = build_vertices[-1].X - build_vertices[0].X
with BuildLine() as one:
l1 = Line((font_height * 0.3, 0), (font_height * 0.3, font_height))
@ -50,7 +50,7 @@ with BuildPart() as three_d:
with BuildSketch():
Text("3d", fontsize=10, valign=Valign.BOTTOM)
Extrude(amount=font_height * 0.3)
logo_width = (three_d.vertices() >> Axis.X).x
logo_width = (three_d.vertices() >> Axis.X).X
with BuildLine() as arrow_left:
t1 = TangentArc((0, 0), (1, 0.75), tangent=(1, 0))
@ -75,7 +75,7 @@ with BuildLine() as extension_lines:
with BuildSketch() as build:
with Locations(
(l1 @ 0.5 + l2 @ 0.5) / 2
- cq.Vector((build_vertices[-1].x + build_vertices[0].x) / 2, 0)
- cq.Vector((build_vertices[-1].X + build_vertices[0].X) / 2, 0)
):
Add(build_text.sketch)

View file

@ -35,7 +35,7 @@ with BuildPart() as art:
with BuildSketch() as slice:
Circle(10 * sin(i * pi / slice_count) + 5)
Loft()
top_bottom = art.faces().filter_by_type(Type.PLANE)
top_bottom = art.faces().filter_by_type(GeomType.PLANE)
Offset(openings=top_bottom, amount=0.5)
if "show_object" in locals():

View file

@ -51,7 +51,7 @@ with BuildPart() as vase:
# Offset(openings=vase.faces().filter_by_axis(Axis.Y)[-1], amount=-1)
Offset(openings=(vase.faces() | Axis.Y) >> Axis.Y, amount=-1)
top_edges = (
vase.edges().filter_by_position(Axis.Y, 60, 62).filter_by_type(Type.CIRCLE)
vase.edges().filter_by_position(Axis.Y, 60, 62).filter_by_type(GeomType.CIRCLE)
)
Fillet(*top_edges, radius=0.25)
Fillet(vase.edges() << Axis.Y, radius=0.5)

View file

@ -23,11 +23,11 @@ __all__ = [
"Until",
"Axis",
"SortBy",
"Type",
"GeomType",
# Classes
"Rotation",
"ShapeList",
"Builder",
# "Builder",
"Add",
"BoundingBox",
"Chamfer",

View file

@ -36,6 +36,7 @@ from abc import ABC, abstractmethod
from math import radians, sqrt, pi
from typing import Iterable, Union
from enum import Enum, auto
import logging
from cadquery import (
Edge,
Wire,
@ -47,7 +48,7 @@ from cadquery import (
Shape,
Vertex,
Plane,
Matrix,
Shell,
)
from cadquery.occ_impl.shapes import VectorLike
from OCP.gp import gp_Pnt, gp_Ax1, gp_Dir, gp_Trsf
@ -55,7 +56,6 @@ from OCP.BRepTools import BRepTools
from OCP.TopAbs import TopAbs_ShapeEnum
from OCP.TopoDS import TopoDS_Iterator
import cq_warehouse.extensions
import logging
# Create a build123d logger to distinguish these logs from application logs.
# If the user doesn't configure logging, all build123d logs will be discarded.
@ -66,28 +66,12 @@ logger = logging.getLogger("build123d")
# logging.basicConfig(
# filename="myapp.log",
# level=logging.INFO,
# format="%(name)s-%(levelname)s %(asctime)s - [%(filename)s:%(lineno)s - %(funcName)20s() ] - %(message)s",
# format="%(name)s-%(levelname)s %(asctime)s - [%(filename)s:%(lineno)s - \
# %(funcName)20s() ] - %(message)s",
# )
# Where using %(name)s in the log format will distinguish between user and build123d library logs
# Monkey patch Vertex to give it the x,y, and z property
def _vertex_x(self: Vertex):
return self.X
def _vertex_y(self: Vertex):
return self.Y
def _vertex_z(self: Vertex):
return self.Z
Vertex.x = property(_vertex_x)
Vertex.y = property(_vertex_y)
Vertex.z = property(_vertex_z)
# Monkey patch Vector to give it the X,Y, and Z property
def _vector_x(self: Vector):
return self.x
@ -117,12 +101,12 @@ Vertex.__eq__ = vertex_eq_
#
# Operators
#
def __matmul__custom(e: Union[Edge, Wire], p: float):
return e.positionAt(p)
def __matmul__custom(wire_edge: Union[Edge, Wire], position: float):
return wire_edge.positionAt(position)
def __mod__custom(e: Union[Edge, Wire], p: float):
return e.tangentAt(p)
def __mod__custom(wire_edge: Union[Edge, Wire], position: float):
return wire_edge.tangentAt(position)
Edge.__matmul__ = __matmul__custom
@ -132,8 +116,19 @@ Wire.__mod__ = __mod__custom
def compound_get_type(
self: Compound, obj_type: Union[Edge, Face, Solid]
) -> list[Union[Edge, Face, Solid]]:
self: Compound, obj_type: Union[Edge, Wire, Face, Solid]
) -> list[Union[Edge, Wire, Face, Solid]]:
"""get_type
Extract the objects of the given type from a Compound. Note that this
isn't the same as Faces() etc. which will extract Faces from Solids.
Args:
obj_type (Union[Edge, Face, Solid]): Object types to extract
Returns:
list[Union[Edge, Face, Solid]]: Extracted objects
"""
iterator = TopoDS_Iterator()
iterator.Initialize(self.wrapped)
@ -175,7 +170,7 @@ class Select(Enum):
LAST = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
class Kind(Enum):
@ -186,7 +181,7 @@ class Kind(Enum):
TANGENT = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
class Keep(Enum):
@ -197,7 +192,7 @@ class Keep(Enum):
BOTH = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
class Mode(Enum):
@ -210,7 +205,7 @@ class Mode(Enum):
PRIVATE = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
class Transition(Enum):
@ -221,7 +216,7 @@ class Transition(Enum):
TRANSFORMED = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
class FontStyle(Enum):
@ -232,7 +227,7 @@ class FontStyle(Enum):
ITALIC = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
class Halign(Enum):
@ -243,7 +238,7 @@ class Halign(Enum):
RIGHT = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
class Valign(Enum):
@ -254,7 +249,7 @@ class Valign(Enum):
BOTTOM = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
class Until(Enum):
@ -264,7 +259,7 @@ class Until(Enum):
LAST = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
class SortBy(Enum):
@ -277,11 +272,11 @@ class SortBy(Enum):
DISTANCE = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
class Type(Enum):
"""CAD object type"""
class GeomType(Enum):
"""CAD geometry object type"""
PLANE = auto()
CYLINDER = auto()
@ -301,12 +296,15 @@ class Type(Enum):
OTHER = auto()
def __repr__(self):
return "<%s.%s>" % (self.__class__.__name__, self.name)
return f"<{self.__class__.__name__}.{self.name}>"
def validate_inputs(validating_class, builder_context, objects=[]):
def validate_inputs(validating_class, builder_context, objects: Shape = None):
"""Validate that objects/operations and parameters apply"""
if not objects:
objects = []
# Check for builder / object matches
if not builder_context:
builder_dict = {
@ -319,9 +317,9 @@ def validate_inputs(validating_class, builder_context, objects=[]):
f"{validating_class.__class__.__name__} doesn't have an active builder, "
f"did you miss a with {builder_dict[validating_class.__module__]}:"
)
elif not (
builder_context.__module__ == validating_class.__module__
or validating_class.__module__ == "build123d.build_generic"
if not (
validating_class.__module__
in [builder_context.__module__, "build123d.build_generic"]
):
raise RuntimeError(
f"{builder_context.__class__.__name__} doesn't have a "
@ -360,13 +358,13 @@ class Rotation(Location):
self.about_z = about_z
# Compute rotation matrix.
rx = gp_Trsf()
rx.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), radians(about_x))
ry = gp_Trsf()
ry.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)), radians(about_y))
rz = gp_Trsf()
rz.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), radians(about_z))
super().__init__(Location(rx * ry * rz).wrapped)
rot_x = gp_Trsf()
rot_x.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(1, 0, 0)), radians(about_x))
rot_y = gp_Trsf()
rot_y.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)), radians(about_y))
rot_z = gp_Trsf()
rot_z.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), radians(about_z))
super().__init__(Location(rot_x * rot_y * rot_z).wrapped)
#:TypeVar("RotationLike"): Three tuple of angles about x, y, z or Rotation
@ -381,17 +379,20 @@ class Axis:
@classmethod
@property
def X(self) -> Axis:
def X(cls) -> Axis:
"""X Axis"""
return Axis((0, 0, 0), (1, 0, 0))
@classmethod
@property
def Y(self) -> Axis:
def Y(cls) -> Axis:
"""Y Axis"""
return Axis((0, 0, 0), (0, 1, 0))
@classmethod
@property
def Z(self) -> Axis:
def Z(cls) -> Axis:
"""Z Axis"""
return Axis((0, 0, 0), (0, 0, 1))
def __init__(self, origin: VectorLike, direction: VectorLike):
@ -484,8 +485,9 @@ class Axis:
def is_opposite(self, other: Axis, angular_tolerance: float = 1e-5) -> bool:
"""are axes opposite
Returns True if the direction of this and another axis are parallel with opposite orientation.
That is, if the angle between the two axes is equal to 180° within the angular_tolerance.
Returns True if the direction of this and another axis are parallel with
opposite orientation. That is, if the angle between the two axes is equal
to 180° within the angular_tolerance.
Args:
other (Axis): axis to compare to
@ -582,8 +584,8 @@ class ShapeList(list):
def filter_by_position(
self,
axis: Axis,
min: float,
max: float,
minimum: float,
maximum: float,
inclusive: tuple[bool, bool] = (True, True),
):
"""filter by position
@ -593,36 +595,48 @@ class ShapeList(list):
Args:
axis (Axis): axis to sort by
min (float): minimum value
max (float): maximum value
inclusive (tuple[bool, bool], optional): include min,max values. Defaults to (True, True).
minimum (float): minimum value
maximum (float): maximum value
inclusive (tuple[bool, bool], optional): include min,max values.
Defaults to (True, True).
Returns:
ShapeList: filtered object list
"""
if inclusive == (True, True):
objects = filter(
lambda o: min <= axis.to_plane().toLocalCoords(o).Center().z <= max,
lambda o: minimum
<= axis.to_plane().toLocalCoords(o).Center().z
<= maximum,
self,
)
elif inclusive == (True, False):
objects = filter(
lambda o: min <= axis.to_plane().toLocalCoords(o).Center().z < max, self
lambda o: minimum
<= axis.to_plane().toLocalCoords(o).Center().z
< maximum,
self,
)
elif inclusive == (False, True):
objects = filter(
lambda o: min < axis.to_plane().toLocalCoords(o).Center().z <= max, self
lambda o: minimum
< axis.to_plane().toLocalCoords(o).Center().z
<= maximum,
self,
)
elif inclusive == (False, False):
objects = filter(
lambda o: min < axis.to_plane().toLocalCoords(o).Center().z < max, self
lambda o: minimum
< axis.to_plane().toLocalCoords(o).Center().z
< maximum,
self,
)
return ShapeList(objects).sort_by(axis)
def filter_by_type(
self,
type: Type,
geom_type: GeomType,
):
"""filter by type
@ -635,7 +649,7 @@ class ShapeList(list):
Returns:
ShapeList: filtered list of objects
"""
result = filter(lambda o: o.geomType() == type.name, self)
result = filter(lambda o: o.geomType() == geom_type.name, self)
return ShapeList(result)
def sort_by(self, sort_by: Union[Axis, SortBy] = Axis.Z, reverse: bool = False):
@ -714,9 +728,9 @@ class ShapeList(list):
"""Filter by axis operator"""
return self.filter_by_axis(axis)
def __mod__(self, type: Type):
"""Filter by type operator"""
return self.filter_by_type(type)
def __mod__(self, geom_type: GeomType):
"""Filter by geometry type operator"""
return self.filter_by_type(geom_type)
def _vertices(self: Shape) -> ShapeList[Vertex]:
@ -767,7 +781,7 @@ class Builder(ABC):
mode (Mode, optional): combination mode. Defaults to Mode.ADD.
"""
"""Context variable used to by Objects and Operations to link to current builder instance"""
# Context variable used to by Objects and Operations to link to current builder instance
_current: contextvars.ContextVar["Builder"] = contextvars.ContextVar(
"Builder._current"
)
@ -779,6 +793,7 @@ class Builder(ABC):
self._parent = None
self.last_vertices = []
self.last_edges = []
self.workplane_generator = None
def __enter__(self):
"""Upon entering record the parent and a token to restore contextvars"""
@ -789,19 +804,19 @@ class Builder(ABC):
# Push an initial plane and point
self.workplane_generator = Workplanes(self.initial_plane).__enter__()
logger.info(f"Entering {type(self).__name__} with mode={self.mode}")
logger.info("Entering %s with mode=%s", type(self).__name__, self.mode)
return self
def __exit__(self, exception_type, exception_value, traceback):
"""Upon exiting restore context and send object to parent"""
self._current.reset(self._reset_tok)
logger.info(f"Exiting {type(self).__name__}")
logger.info("Exiting %s", type(self).__name__)
# Pop the initial plane and point
self.workplane_generator.__exit__(None, None, None)
if self._parent is not None:
logger.debug(f"Transferring line to {type(self._parent).__name__}")
logger.debug("Transferring line to %s", type(self._parent).__name__)
if isinstance(self._obj, Iterable):
self._parent._add_to_context(*self._obj, mode=self.mode)
else:
@ -836,6 +851,59 @@ class Builder(ABC):
"""
return cls._current.get(None)
def vertices(self, select: Select = Select.ALL) -> ShapeList[Vertex]:
"""Return Vertices from Part
Return either all or the vertices created during the last operation.
Args:
select (Select, optional): Vertex selector. Defaults to Select.ALL.
Returns:
VertexList[Vertex]: Vertices extracted
"""
vertex_list = []
if select == Select.ALL:
for edge in self._obj.Edges():
vertex_list.extend(edge.Vertices())
elif select == Select.LAST:
vertex_list = self.last_vertices
return ShapeList(set(vertex_list))
def edges(self, select: Select = Select.ALL) -> ShapeList[Edge]:
"""Return Edges from Part
Return either all or the edges created during the last operation.
Args:
select (Select, optional): Edge selector. Defaults to Select.ALL.
Returns:
ShapeList[Edge]: Edges extracted
"""
if select == Select.ALL:
edge_list = self._obj.Edges()
elif select == Select.LAST:
edge_list = self.last_edges
return ShapeList(edge_list)
def faces(self, select: Select = Select.ALL) -> ShapeList[Face]:
"""Return Faces from Sketch
Return either all or the faces created during the last operation.
Args:
select (Select, optional): Face selector. Defaults to Select.ALL.
Returns:
ShapeList[Face]: Faces extracted
"""
if select == Select.ALL:
face_list = self._obj.Faces()
elif select == Select.LAST:
face_list = self.last_faces
return ShapeList(face_list)
class LocationList:
@ -853,13 +921,13 @@ class LocationList:
def __enter__(self):
"""Upon entering create a token to restore contextvars"""
self._reset_tok = self._current.set(self)
logger.info(f"{type(self).__name__} is pushing {len(self.locations)} points")
logger.info("%s is pushing %d points", type(self).__name__, len(self.locations))
return self
def __exit__(self, exception_type, exception_value, traceback):
"""Upon exiting restore context"""
self._current.reset(self._reset_tok)
logger.info(f"{type(self).__name__} is popping {len(self.locations)} points")
logger.info("%s is popping %d points", type(self).__name__, len(self.locations))
@classmethod
def _get_context(cls):
@ -885,37 +953,39 @@ class HexLocations(LocationList):
def __init__(
self,
diagonal: float,
xCount: int,
yCount: int,
x_count: int,
y_count: int,
centered: tuple[bool, bool] = (True, True),
):
xSpacing = 3 * diagonal / 4
ySpacing = diagonal * sqrt(3) / 2
if xSpacing <= 0 or ySpacing <= 0 or xCount < 1 or yCount < 1:
x_spacing = 3 * diagonal / 4
y_spacing = diagonal * sqrt(3) / 2
if x_spacing <= 0 or y_spacing <= 0 or x_count < 1 or y_count < 1:
raise ValueError("Spacing and count must be > 0 ")
points = [] # coordinates relative to bottom left point
for x in range(0, xCount, 2):
for y in range(yCount):
points.append(Vector(xSpacing * x, ySpacing * y + ySpacing / 2))
for x in range(1, xCount, 2):
for y in range(yCount):
points.append(Vector(xSpacing * x, ySpacing * y + ySpacing))
for x_val in range(0, x_count, 2):
for y_val in range(y_count):
points.append(
Vector(x_spacing * x_val, y_spacing * y_val + y_spacing / 2)
)
for x_val in range(1, x_count, 2):
for y_val in range(y_count):
points.append(Vector(x_spacing * x_val, y_spacing * y_val + y_spacing))
# shift points down and left relative to origin if requested
offset = Vector()
if centered[0]:
offset += Vector(-xSpacing * (xCount - 1) * 0.5, 0)
offset += Vector(-x_spacing * (x_count - 1) * 0.5, 0)
if centered[1]:
offset += Vector(0, -ySpacing * yCount * 0.5)
offset += Vector(0, -y_spacing * y_count * 0.5)
points = [x + offset for x in points]
# convert to locations and store the reference plane
self.locations = []
self.planes = []
for plane in WorkplaneList._get_context().workplanes:
for pt in points:
self.locations.append(Location(plane) * Location(pt))
for point in points:
self.locations.append(Location(plane) * Location(point))
self.planes.append(plane)
super().__init__(self.locations, self.planes)
@ -980,19 +1050,19 @@ class Locations(LocationList):
self.locations = []
self.planes = []
for plane in WorkplaneList._get_context().workplanes:
for pt in pts:
if isinstance(pt, Location):
self.locations.append(Location(plane) * pt)
elif isinstance(pt, Vector):
self.locations.append(Location(plane) * Location(pt))
elif isinstance(pt, Vertex):
for point in pts:
if isinstance(point, Location):
self.locations.append(Location(plane) * point)
elif isinstance(point, Vector):
self.locations.append(Location(plane) * Location(point))
elif isinstance(point, Vertex):
self.locations.append(
Location(plane) * Location(Vector(pt.toTuple()))
Location(plane) * Location(Vector(point.toTuple()))
)
elif isinstance(pt, tuple):
self.locations.append(Location(plane) * Location(Vector(pt)))
elif isinstance(point, tuple):
self.locations.append(Location(plane) * Location(Vector(point)))
else:
raise ValueError(f"Locations doesn't accept type {type(pt)}")
raise ValueError(f"Locations doesn't accept type {type(point)}")
self.planes.append(plane)
super().__init__(self.locations, self.planes)
@ -1062,13 +1132,14 @@ class WorkplaneList:
def __init__(self, planes: list[Plane]):
self._reset_tok = None
self.workplanes = planes
self.point_generator = None
def __enter__(self):
"""Upon entering create a token to restore contextvars"""
self._reset_tok = self._current.set(self)
self.point_generator = Locations((0, 0, 0)).__enter__()
logger.info(
f"{type(self).__name__} is pushing {len(self.workplanes)} workplanes"
"%s is pushing %d workplanes", type(self).__name__, len(self.workplanes)
)
return self
@ -1077,7 +1148,7 @@ class WorkplaneList:
self._current.reset(self._reset_tok)
self.point_generator.__exit__(None, None, None)
logger.info(
f"{type(self).__name__} is popping {len(self.workplanes)} workplanes"
"%s is popping %d workplanes", type(self).__name__, len(self.workplanes)
)
@classmethod

View file

@ -26,8 +26,9 @@ license:
limitations under the License.
"""
import inspect
from typing import Union
import logging
from cadquery import Matrix
from build123d import (
BuildLine,
BuildSketch,
@ -35,16 +36,11 @@ from build123d import (
Mode,
RotationLike,
Rotation,
Axis,
Builder,
LocationList,
Kind,
Keep,
PlaneLike,
Matrix,
validate_inputs,
)
from cadquery import (
Shape,
Vertex,
Plane,
@ -55,8 +51,8 @@ from cadquery import (
Solid,
Location,
Vector,
validate_inputs,
)
import logging
logging.getLogger("build123d").addHandler(logging.NullHandler())
logger = logging.getLogger("build123d")

View file

@ -28,26 +28,21 @@ license:
import inspect
from math import sin, cos, radians, sqrt
from typing import Union, Iterable
# from .occ_impl.geom import Vector, Matrix, Plane, Location, BoundBox
# from .occ_impl.shapes import (
# Shape,
# Vertex,
# Edge,
# Wire,
# Face,
# Shell,
# Solid,
# Compound,
# VectorLike,
# )
from cadquery import Edge, Wire, Vector, Vertex
from cadquery.occ_impl.shapes import VectorLike
# import cq_warehouse.extensions
from build123d.build_common import *
from build123d.build_common import (
Edge,
Wire,
Vector,
Vertex,
Compound,
Location,
Builder,
VectorLike,
Select,
ShapeList,
Mode,
logger,
validate_inputs,
)
class BuildLine(Builder):
@ -72,42 +67,6 @@ class BuildLine(Builder):
self.locations: list[Location] = [Location(Vector())]
super().__init__(mode)
def vertices(self, select: Select = Select.ALL) -> ShapeList[Vertex]:
"""Return Vertices from Line
Return either all or the vertices created during the last operation.
Args:
select (Select, optional): Vertex selector. Defaults to Select.ALL.
Returns:
VertexList[Vertex]: Vertices extracted
"""
vertex_list = []
if select == Select.ALL:
for edge in self.line.Edges():
vertex_list.extend(edge.Vertices())
elif select == Select.LAST:
vertex_list = self.last_vertices
return ShapeList(set(vertex_list))
def edges(self, select: Select = Select.ALL) -> ShapeList[Edge]:
"""Return Edges from Line
Return either all or the edges created during the last operation.
Args:
select (Select, optional): Edge selector. Defaults to Select.ALL.
Returns:
ShapeList[Edge]: Edges extracted
"""
if select == Select.ALL:
edge_list = self.line.Edges()
elif select == Select.LAST:
edge_list = self.last_edges
return ShapeList(edge_list)
def wires(self, select: Select = Select.ALL) -> ShapeList[Wire]:
"""Return Wires from Line
@ -149,7 +108,9 @@ class BuildLine(Builder):
for wire in new_wires:
new_edges.extend(wire.Edges())
if new_edges:
logger.debug(f"Add {len(new_edges)} Edge(s) into line with Mode={mode}")
logger.debug(
"Add %d Edge(s) into line with Mode=%s", len(new_edges), mode
)
if mode == Mode.ADD:
if self.line:
@ -167,7 +128,8 @@ class BuildLine(Builder):
def _get_context(cls) -> "BuildLine":
"""Return the instance of the current builder"""
logger.info(
f"Context requested by {type(inspect.currentframe().f_back.f_locals['self']).__name__}"
"Context requested by %s",
type(inspect.currentframe().f_back.f_locals["self"]).__name__,
)
return cls._current.get(None)
@ -331,9 +293,11 @@ class PolarLine(Edge):
context: BuildLine = BuildLine._get_context()
validate_inputs(self, context)
if angle is not None:
x = cos(radians(angle)) * length
y = sin(radians(angle)) * length
new_edge = Edge.makeLine(Vector(start), Vector(start) + Vector(x, y, 0))
x_val = cos(radians(angle)) * length
y_val = sin(radians(angle)) * length
new_edge = Edge.makeLine(
Vector(start), Vector(start) + Vector(x_val, y_val, 0)
)
elif direction is not None:
new_edge = Edge.makeLine(
Vector(start), Vector(start) + Vector(direction).normalized() * length
@ -410,10 +374,10 @@ class RadiusArc(Edge):
length = end.sub(start).Length / 2.0
try:
sagitta = abs(radius) - sqrt(radius**2 - length**2)
except ValueError as e:
except ValueError as exception:
raise ValueError(
"Arc radius is not large enough to reach the end point."
) from e
) from exception
# Return a sagitta arc
if radius > 0:

View file

@ -31,23 +31,34 @@ license:
"""
import inspect
from warnings import warn
from math import radians, tan
from typing import Union
from cadquery import (
from math import radians, tan, sqrt
from typing import Union, Iterable
from OCP.gp import gp_Pln, gp_Lin
from build123d.build_common import (
Edge,
Face,
Wire,
Vector,
Location,
Vertex,
Compound,
Solid,
Plane,
Shell,
VectorLike,
Builder,
Mode,
PlaneLike,
Select,
ShapeList,
Until,
Axis,
Transition,
RotationLike,
logger,
validate_inputs,
Rotation,
LocationList,
WorkplaneList,
)
from cadquery.occ_impl.shapes import VectorLike
from build123d.build_common import *
from OCP.gp import gp_Pln, gp_Lin
class BuildPart(Builder):
@ -92,59 +103,6 @@ class BuildPart(Builder):
self.last_solids = []
super().__init__(mode, initial_plane)
def vertices(self, select: Select = Select.ALL) -> ShapeList[Vertex]:
"""Return Vertices from Part
Return either all or the vertices created during the last operation.
Args:
select (Select, optional): Vertex selector. Defaults to Select.ALL.
Returns:
VertexList[Vertex]: Vertices extracted
"""
vertex_list = []
if select == Select.ALL:
for edge in self.part.Edges():
vertex_list.extend(edge.Vertices())
elif select == Select.LAST:
vertex_list = self.last_vertices
return ShapeList(set(vertex_list))
def edges(self, select: Select = Select.ALL) -> ShapeList[Edge]:
"""Return Edges from Part
Return either all or the edges created during the last operation.
Args:
select (Select, optional): Edge selector. Defaults to Select.ALL.
Returns:
ShapeList[Edge]: Edges extracted
"""
if select == Select.ALL:
edge_list = self.part.Edges()
elif select == Select.LAST:
edge_list = self.last_edges
return ShapeList(edge_list)
def faces(self, select: Select = Select.ALL) -> ShapeList[Face]:
"""Return Faces from Part
Return either all or the faces created during the last operation.
Args:
select (Select, optional): Face selector. Defaults to Select.ALL.
Returns:
ShapeList[Face]: Faces extracted
"""
if select == Select.ALL:
face_list = self.part.Faces()
elif select == Select.LAST:
face_list = self.last_faces
return ShapeList(face_list)
def solids(self, select: Select = Select.ALL) -> ShapeList[Solid]:
"""Return Solids from Part
@ -174,13 +132,15 @@ class BuildPart(Builder):
localized_obj = obj.moved(loc)
if isinstance(obj, Face):
logger.debug(
f"Adding localized Face to pending_faces at {localized_obj.location()}"
"Adding localized Face to pending_faces at %s",
localized_obj.location(),
)
self.pending_faces.append(localized_obj)
self.pending_face_planes.append(plane)
else:
logger.debug(
f"Adding localized Edge to pending_edges at {localized_obj.location()}"
"Adding localized Edge to pending_edges at %s",
localized_obj.location(),
)
self.pending_edges.append(localized_obj)
self.pending_edge_planes.append(plane)
@ -241,8 +201,9 @@ class BuildPart(Builder):
if new_solids:
logger.debug(
f"Attempting to integrate {len(new_solids)} object(s) into part"
f" with Mode={mode}"
"Attempting to integrate %d object(s) into part with Mode=%s",
len(new_solids),
mode,
)
if mode == Mode.ADD:
if self.part is None:
@ -264,8 +225,9 @@ class BuildPart(Builder):
self.part = Compound.makeCompound(list(new_solids)).clean()
logger.info(
f"Completed integrating {len(new_solids)} object(s) into part"
f" with Mode={mode}"
"Completed integrating %d object(s) into part with Mode=%s",
len(new_solids),
mode,
)
post_vertices = set() if self.part is None else set(self.part.Vertices())
@ -284,7 +246,8 @@ class BuildPart(Builder):
def _get_context(cls) -> "BuildPart":
"""Return the instance of the current builder"""
logger.info(
f"Context requested by {type(inspect.currentframe().f_back.f_locals['self']).__name__}"
"Context requested by %s",
type(inspect.currentframe().f_back.f_locals["self"]).__name__,
)
return cls._current.get(None)
@ -436,7 +399,7 @@ class Extrude(Compound):
if to_extrude:
list_context = LocationList._get_context()
faces = [to_extrude.moved(loc) for loc in list_context.locations]
face_planes = [plane for plane in list_context.planes]
face_planes = list_context.planes
else:
faces = context.pending_faces
face_planes = context.pending_face_planes
@ -458,12 +421,12 @@ class Extrude(Compound):
part_faces = context.part.Faces()
for face, plane in zip(faces, face_planes):
for dir in [1, -1] if both else [1]:
for direction in [1, -1] if both else [1]:
if amount:
new_solids.append(
Solid.extrudeLinear(
face,
plane.zDir * amount * dir,
plane.zDir * amount * direction,
taper,
)
)
@ -493,10 +456,12 @@ class Extrude(Compound):
# Determine which surface is "next" or "last"
surface_dirs = []
for s in trim_shells:
for trim_shell in trim_shells:
face_directions = Vector(0, 0, 0)
for f in s.Faces():
face_directions = face_directions + f.normalAt(f.Center())
for trim_face in trim_shell.Faces():
face_directions = face_directions + trim_face.normalAt(
trim_face.Center()
)
surface_dirs.append(face_directions.getAngle(plane.zDir))
if until == Until.NEXT:
surface_index = surface_dirs.index(max(surface_dirs))
@ -513,10 +478,11 @@ class Extrude(Compound):
)
for f in trim_faces
]
for o, f in zip(trim_objects, trim_faces):
if not o.isValid():
for trim_object, trim_face in zip(trim_objects, trim_faces):
if not trim_object.isValid():
warn(
message=f"Part face with area {f.Area()} creates an invalid extrusion",
message=f"Part face with area {trim_face.Area()} "
f"creates an invalid extrusion",
category=Warning,
)

View file

@ -41,24 +41,25 @@ license:
import inspect
from math import pi, sin, cos, tan, radians
from typing import Union
# from .hull import find_hull
# from .occ_impl.geom import Vector, Matrix, Plane, Location, BoundBox
# from .occ_impl.shapes import (
# Shape,
# Vertex,
# Edge,
# Wire,
# Face,
# Shell,
# Solid,
# Compound,
# VectorLike,
# )
from cadquery.hull import find_hull
from cadquery import Edge, Face, Wire, Vector, Location, Vertex, Compound
from cadquery.occ_impl.shapes import VectorLike
from build123d.build_common import *
from build123d.build_common import (
Edge,
Face,
Wire,
Vector,
Location,
Compound,
VectorLike,
Builder,
Mode,
ShapeList,
FontStyle,
Halign,
Valign,
logger,
validate_inputs,
LocationList,
)
class BuildSketch(Builder):
@ -85,59 +86,6 @@ class BuildSketch(Builder):
self.last_faces = []
super().__init__(mode)
def vertices(self, select: Select = Select.ALL) -> ShapeList[Vertex]:
"""Return Vertices from Sketch
Return either all or the vertices created during the last operation.
Args:
select (Select, optional): Vertex selector. Defaults to Select.ALL.
Returns:
VertexList[Vertex]: Vertices extracted
"""
vertex_list = []
if select == Select.ALL:
for edge in self.sketch.Edges():
vertex_list.extend(edge.Vertices())
elif select == Select.LAST:
vertex_list = self.last_vertices
return ShapeList(set(vertex_list))
def edges(self, select: Select = Select.ALL) -> ShapeList[Edge]:
"""Return Edges from Sketch
Return either all or the edges created during the last operation.
Args:
select (Select, optional): Edge selector. Defaults to Select.ALL.
Returns:
ShapeList[Edge]: Edges extracted
"""
if select == Select.ALL:
edge_list = self.sketch.Edges()
elif select == Select.LAST:
edge_list = self.last_edges
return ShapeList(edge_list)
def faces(self, select: Select = Select.ALL) -> ShapeList[Face]:
"""Return Faces from Sketch
Return either all or the faces created during the last operation.
Args:
select (Select, optional): Face selector. Defaults to Select.ALL.
Returns:
ShapeList[Face]: Faces extracted
"""
if select == Select.ALL:
face_list = self.sketch.Faces()
elif select == Select.LAST:
face_list = self.last_faces
return ShapeList(face_list)
def consolidate_edges(self) -> Union[Wire, list[Wire]]:
"""Unify pending edges into one or more Wires"""
wires = Wire.combine(self.pending_edges)
@ -179,8 +127,9 @@ class BuildSketch(Builder):
pre_faces = set() if self.sketch is None else set(self.sketch.Faces())
if new_faces:
logger.debug(
f"Attempting to integrate {len(new_faces)} Face(s) into sketch"
f" with Mode={mode}"
"Attempting to integrate %d Face(s) into sketch with Mode=%s",
len(new_faces),
mode,
)
if mode == Mode.ADD:
if self.sketch is None:
@ -198,9 +147,10 @@ class BuildSketch(Builder):
elif mode == Mode.REPLACE:
self.sketch = Compound.makeCompound(new_faces).clean()
logger.info(
f"Completed integrating {len(new_faces)} Face(s) into sketch"
f" with Mode={mode}"
logger.debug(
"Completed integrating %d Face(s) into sketch with Mode=%s",
len(new_faces),
mode,
)
post_vertices = (
@ -220,7 +170,8 @@ class BuildSketch(Builder):
def _get_context(cls) -> "BuildSketch":
"""Return the instance of the current builder"""
logger.info(
f"Context requested by {type(inspect.currentframe().f_back.f_locals['self']).__name__}"
"Context requested by %s",
type(inspect.currentframe().f_back.f_locals["self"]).__name__,
)
return cls._current.get(None)

View file

@ -53,10 +53,6 @@ class TestCommonOperations(unittest.TestCase):
class TestProperties(unittest.TestCase):
def test_vertex_properties(self):
v = Vertex.makeVertex(1, 2, 3)
self.assertTupleAlmostEquals((v.x, v.y, v.z), (1, 2, 3), 5)
def test_vector_properties(self):
v = Vector(1, 2, 3)
self.assertTupleAlmostEquals((v.X, v.Y, v.Z), (1, 2, 3), 5)
@ -168,8 +164,8 @@ class TestShapeList(unittest.TestCase):
Box(2, 2, 2)
objects = test.faces()
objects.extend(test.edges())
self.assertEqual(len(objects.filter_by_type(Type.PLANE)), 6)
self.assertEqual(len(objects.filter_by_type(Type.LINE)), 12)
self.assertEqual(len(objects.filter_by_type(GeomType.PLANE)), 6)
self.assertEqual(len(objects.filter_by_type(GeomType.LINE)), 12)
def test_sort_by_type(self):
"""test sorting by different attributes"""
@ -206,7 +202,9 @@ class TestShapeList(unittest.TestCase):
with self.subTest(sort_by=SortBy.RADIUS):
with BuildPart() as test:
Cone(1, 0.5, 2)
edges = test.edges().filter_by_type(Type.CIRCLE).sort_by(SortBy.RADIUS)
edges = (
test.edges().filter_by_type(GeomType.CIRCLE).sort_by(SortBy.RADIUS)
)
self.assertEqual(edges[0].radius(), 0.5)
self.assertEqual(edges[-1].radius(), 1)

View file

@ -29,7 +29,7 @@ import unittest
from math import pi
from build123d import *
from cadquery import Compound, Vector, Edge, Face, Solid, Wire, Location
from build123d import LocationList
from build123d import LocationList, Builder
def _assertTupleAlmostEquals(self, expected, actual, places, msg=None):
@ -216,7 +216,8 @@ class ChamferTests(unittest.TestCase):
with Locations((-10, 0), (10, 0)):
Rectangle(10, 10)
Chamfer(
*test.vertices().filter_by_position(Axis.X, min=0, max=20), length=1
*test.vertices().filter_by_position(Axis.X, minimum=0, maximum=20),
length=1
)
self.assertAlmostEqual(test.sketch.Area(), 200 - 4 * 0.5, 5)
@ -242,7 +243,10 @@ class FilletTests(unittest.TestCase):
with BuildSketch() as test:
with Locations((-10, 0), (10, 0)):
Rectangle(10, 10)
Fillet(*test.vertices().filter_by_position(Axis.X, min=0, max=20), radius=1)
Fillet(
*test.vertices().filter_by_position(Axis.X, minimum=0, maximum=20),
radius=1
)
self.assertAlmostEqual(test.sketch.Area(), 200 - 4 + pi, 5)
def test_errors(self):
@ -275,9 +279,11 @@ class MirrorTests(unittest.TestCase):
wire = Wire.makeCircle(1, center=(5, 0, 0), normal=(0, 0, 1))
with BuildLine() as test:
Mirror(edge, wire, about="YZ")
self.assertEqual(len(test.edges().filter_by_position(Axis.X, min=0, max=10)), 0)
self.assertEqual(
len(test.edges().filter_by_position(Axis.X, min=-10, max=0)), 2
len(test.edges().filter_by_position(Axis.X, minimum=0, maximum=10)), 0
)
self.assertEqual(
len(test.edges().filter_by_position(Axis.X, minimum=-10, maximum=0)), 2
)
with BuildLine() as test:
Line((1, 0), (2, 0))
@ -297,14 +303,17 @@ class MirrorTests(unittest.TestCase):
with BuildSketch() as test:
Mirror(edge, wire, face, compound, about="YZ")
self.assertEqual(
len(test.pending_edges.filter_by_position(Axis.X, min=0, max=10)), 0
)
self.assertEqual(len(test.faces().filter_by_position(Axis.X, min=0, max=10)), 0)
self.assertEqual(
len(test.pending_edges.filter_by_position(Axis.X, min=-10, max=0)), 2
len(test.pending_edges.filter_by_position(Axis.X, minimum=0, maximum=10)), 0
)
self.assertEqual(
len(test.faces().filter_by_position(Axis.X, min=-10, max=0)), 3
len(test.faces().filter_by_position(Axis.X, minimum=0, maximum=10)), 0
)
self.assertEqual(
len(test.pending_edges.filter_by_position(Axis.X, minimum=-10, maximum=0)),
2,
)
self.assertEqual(
len(test.faces().filter_by_position(Axis.X, minimum=-10, maximum=0)), 3
)
def test_mirror_part(self):
@ -312,7 +321,7 @@ class MirrorTests(unittest.TestCase):
with BuildPart() as test:
Mirror(cone, about="YZ")
self.assertEqual(
len(test.solids().filter_by_position(Axis.X, min=-10, max=0)), 1
len(test.solids().filter_by_position(Axis.X, minimum=-10, maximum=0)), 1
)