From d642920243239b8421ec2da385099a71cd796c5f Mon Sep 17 00:00:00 2001 From: Roger Maitland Date: Tue, 27 Sep 2022 19:10:34 -0400 Subject: [PATCH] Pylint changes - (mostly) not functional changes --- examples/build123d_logo.py | 8 +- examples/loft.py | 2 +- examples/vase.py | 2 +- src/build123d/__init__.py | 4 +- src/build123d/build_common.py | 275 +++++++++++++++++++++------------ src/build123d/build_generic.py | 10 +- src/build123d/build_line.py | 90 ++++------- src/build123d/build_part.py | 120 ++++++-------- src/build123d/build_sketch.py | 103 ++++-------- tests/build_common_tests.py | 12 +- tests/build_generic_tests.py | 33 ++-- 11 files changed, 307 insertions(+), 352 deletions(-) diff --git a/examples/build123d_logo.py b/examples/build123d_logo.py index 9333a53..54b67aa 100644 --- a/examples/build123d_logo.py +++ b/examples/build123d_logo.py @@ -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) diff --git a/examples/loft.py b/examples/loft.py index 5c2d621..7412a04 100644 --- a/examples/loft.py +++ b/examples/loft.py @@ -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(): diff --git a/examples/vase.py b/examples/vase.py index d029f32..2351fb7 100644 --- a/examples/vase.py +++ b/examples/vase.py @@ -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) diff --git a/src/build123d/__init__.py b/src/build123d/__init__.py index 7713724..2a865b7 100644 --- a/src/build123d/__init__.py +++ b/src/build123d/__init__.py @@ -23,11 +23,11 @@ __all__ = [ "Until", "Axis", "SortBy", - "Type", + "GeomType", # Classes "Rotation", "ShapeList", - "Builder", + # "Builder", "Add", "BoundingBox", "Chamfer", diff --git a/src/build123d/build_common.py b/src/build123d/build_common.py index 750448d..fc4fa09 100644 --- a/src/build123d/build_common.py +++ b/src/build123d/build_common.py @@ -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 diff --git a/src/build123d/build_generic.py b/src/build123d/build_generic.py index 7f9a5a7..f44696c 100644 --- a/src/build123d/build_generic.py +++ b/src/build123d/build_generic.py @@ -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") diff --git a/src/build123d/build_line.py b/src/build123d/build_line.py index 05203ac..8b2e032 100644 --- a/src/build123d/build_line.py +++ b/src/build123d/build_line.py @@ -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: diff --git a/src/build123d/build_part.py b/src/build123d/build_part.py index 9b17a0f..80dbd2e 100644 --- a/src/build123d/build_part.py +++ b/src/build123d/build_part.py @@ -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, ) diff --git a/src/build123d/build_sketch.py b/src/build123d/build_sketch.py index 4efb0fa..3f335f4 100644 --- a/src/build123d/build_sketch.py +++ b/src/build123d/build_sketch.py @@ -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) diff --git a/tests/build_common_tests.py b/tests/build_common_tests.py index ffbb227..61b74f0 100644 --- a/tests/build_common_tests.py +++ b/tests/build_common_tests.py @@ -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) diff --git a/tests/build_generic_tests.py b/tests/build_generic_tests.py index 57298f0..8bf1c2a 100644 --- a/tests/build_generic_tests.py +++ b/tests/build_generic_tests.py @@ -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 )