Replaced Grid/HexLocations centered for align

This commit is contained in:
Roger Maitland 2023-01-26 13:44:13 -05:00
parent 0ee290c816
commit c8928a8e68
6 changed files with 61 additions and 42 deletions

View file

@ -61,7 +61,7 @@ class Hinge(Compound):
with BuildPart() as hinge_profile: with BuildPart() as hinge_profile:
with BuildSketch(): with BuildSketch():
for i, loc in enumerate( for i, loc in enumerate(
GridLocations(0, length / 5, 1, 5, centered=(False, False)) GridLocations(0, length / 5, 1, 5, align=(Align.MIN, Align.MIN))
): ):
if i % 2 == inner: if i % 2 == inner:
with Locations(loc): with Locations(loc):

View file

@ -55,8 +55,8 @@ tutorial is the joints and not the CAD operations to create objects, this code i
described in detail. described in detail.
.. literalinclude:: tutorial_joints.py .. literalinclude:: tutorial_joints.py
:lines: 27,32-132 :lines: 27,32-130
:emphasize-lines: 3-102 :emphasize-lines: 3-100
Once the two leaves have been created they will look as follows: Once the two leaves have been created they will look as follows:

View file

@ -34,6 +34,7 @@ from math import sqrt, pi
from typing import Iterable, Union from typing import Iterable, Union
import logging import logging
from build123d.build_enums import ( from build123d.build_enums import (
Align,
Select, Select,
Kind, Kind,
Keep, Keep,
@ -410,14 +411,14 @@ class LocationList:
class HexLocations(LocationList): class HexLocations(LocationList):
"""Location Context: Hex Array """Location Context: Hex Array
Creates a context of hexagon array of points. Creates a context of hexagon array of locations for Part or Sketch
Args: Args:
diagonal: tip to tip size of hexagon ( must be > 0) diagonal: tip to tip size of hexagon ( must be > 0)
xCount: number of points ( > 0 ) xCount: number of points ( > 0 )
yCount: number of points ( > 0 ) yCount: number of points ( > 0 )
centered: specify centering along each axis. align (tuple[Align, Align], optional): align min, center, or max of object.
offset (VectorLike): offset to apply to all locations. Defaults to (0,0). Defaults to (Align.CENTER, Align.CENTER).
Raises: Raises:
ValueError: Spacing and count must be > 0 ValueError: Spacing and count must be > 0
@ -428,15 +429,20 @@ class HexLocations(LocationList):
diagonal: float, diagonal: float,
x_count: int, x_count: int,
y_count: int, y_count: int,
centered: tuple[bool, bool] = (True, True), align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
offset: VectorLike = (0, 0),
): ):
x_spacing = 3 * diagonal / 4 x_spacing = 3 * diagonal / 4
y_spacing = diagonal * sqrt(3) / 2 y_spacing = diagonal * sqrt(3) / 2
if x_spacing <= 0 or y_spacing <= 0 or x_count < 1 or y_count < 1: if x_spacing <= 0 or y_spacing <= 0 or x_count < 1 or y_count < 1:
raise ValueError("Spacing and count must be > 0 ") raise ValueError("Spacing and count must be > 0 ")
points = [] # coordinates relative to bottom left point self.diagonal = diagonal
self.x_count = x_count
self.y_count = y_count
self.align = align
# Generate the raw coordinates relative to bottom left point
points = ShapeList()
for x_val in range(0, x_count, 2): for x_val in range(0, x_count, 2):
for y_val in range(y_count): for y_val in range(y_count):
points.append( points.append(
@ -446,15 +452,28 @@ class HexLocations(LocationList):
for y_val in range(y_count): for y_val in range(y_count):
points.append(Vector(x_spacing * x_val, y_spacing * y_val + y_spacing)) points.append(Vector(x_spacing * x_val, y_spacing * y_val + y_spacing))
# shift points down and left relative to origin if requested # Determine the minimum point and size of the array
offset = Vector(offset) sorted_points = [points.sort_by(Axis.X), points.sort_by(Axis.Y)]
if centered[0]: size = [
offset += Vector(-x_spacing * (x_count - 1) * 0.5, 0) sorted_points[0][-1].X - sorted_points[0][0].X,
if centered[1]: sorted_points[1][-1].Y - sorted_points[1][0].Y,
offset += Vector(0, -y_spacing * y_count * 0.5) ]
points = [x + offset for x in points] min_corner = Vector(sorted_points[0][0].X, sorted_points[1][0].Y)
# convert to locations and store the reference plane # Calculate the amount to offset the array to align it
align_offset = []
for i in range(2):
if align[i] == Align.MIN:
align_offset.append(0)
elif align[i] == Align.CENTER:
align_offset.append(-size[i] / 2)
elif align[i] == Align.MAX:
align_offset.append(-size[i])
# Align the points
points = [point + Vector(*align_offset) - min_corner for point in points]
# Convert to locations and store the reference plane
local_locations = [Location(point) for point in points] local_locations = [Location(point) for point in points]
self.local_locations = Locations._move_to_existing(local_locations) self.local_locations = Locations._move_to_existing(local_locations)
@ -470,7 +489,7 @@ class HexLocations(LocationList):
class PolarLocations(LocationList): class PolarLocations(LocationList):
"""Location Context: Polar Array """Location Context: Polar Array
Push a polar array of locations to Part or Sketch Creates a context of polar array of locations for Part or Sketch
Args: Args:
radius (float): array radius radius (float): array radius
@ -515,7 +534,7 @@ class PolarLocations(LocationList):
class Locations(LocationList): class Locations(LocationList):
"""Location Context: Push Points """Location Context: Push Points
Push sequence of locations to Part or Sketch Creates a context of locations for Part or Sketch
Args: Args:
pts (Union[VectorLike, Vertex, Location]): sequence of points to push pts (Union[VectorLike, Vertex, Location]): sequence of points to push
@ -571,15 +590,15 @@ class Locations(LocationList):
class GridLocations(LocationList): class GridLocations(LocationList):
"""Location Context: Rectangular Array """Location Context: Rectangular Array
Push a rectangular array of locations to Part or Sketch Creates a context of rectangular array of locations for Part or Sketch
Args: Args:
x_spacing (float): horizontal spacing x_spacing (float): horizontal spacing
y_spacing (float): vertical spacing y_spacing (float): vertical spacing
x_count (int): number of horizontal points x_count (int): number of horizontal points
y_count (int): number of vertical points y_count (int): number of vertical points
centered: specify centering along each axis. align (tuple[Align, Align], optional): align min, center, or max of object.
offset (VectorLike): offset to apply to all locations. Defaults to (0,0). Defaults to (Align.CENTER, Align.CENTER).
Raises: Raises:
ValueError: Either x or y count must be greater than or equal to one. ValueError: Either x or y count must be greater than or equal to one.
@ -591,8 +610,7 @@ class GridLocations(LocationList):
y_spacing: float, y_spacing: float,
x_count: int, x_count: int,
y_count: int, y_count: int,
centered: tuple[bool, bool] = (True, True), align: tuple[Align, Align] = (Align.CENTER, Align.CENTER),
offset: VectorLike = (0, 0),
): ):
if x_count < 1 or y_count < 1: if x_count < 1 or y_count < 1:
raise ValueError( raise ValueError(
@ -602,15 +620,17 @@ class GridLocations(LocationList):
self.y_spacing = y_spacing self.y_spacing = y_spacing
self.x_count = x_count self.x_count = x_count
self.y_count = y_count self.y_count = y_count
self.centered = centered self.align = align
self.offset = Vector(offset)
center_x_offset = ( size = [x_spacing * (x_count - 1), y_spacing * (y_count - 1)]
x_spacing * (x_count - 1) / 2 if centered[0] else 0 + self.offset.X align_offset = []
) for i in range(2):
center_y_offset = ( if align[i] == Align.MIN:
y_spacing * (y_count - 1) / 2 if centered[1] else 0 + self.offset.Y align_offset.append(0)
) elif align[i] == Align.CENTER:
align_offset.append(-size[i] / 2)
elif align[i] == Align.MAX:
align_offset.append(-size[i])
# Create the list of local locations # Create the list of local locations
local_locations = [] local_locations = []
@ -618,8 +638,8 @@ class GridLocations(LocationList):
local_locations.append( local_locations.append(
Location( Location(
Vector( Vector(
i * x_spacing - center_x_offset, i * x_spacing + align_offset[0],
j * y_spacing - center_y_offset, j * y_spacing + align_offset[1],
) )
) )
) )

View file

@ -10,7 +10,6 @@ desc:
TODO: TODO:
- add TwistExtrude, ProjectText - add TwistExtrude, ProjectText
- add centered to wedge
license: license:
@ -256,8 +255,8 @@ class BasePartObject(Compound):
Args: Args:
solid (Solid): object to create solid (Solid): object to create
rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0). rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0).
centered (tuple[bool, bool, bool], optional): center about axes. align (tuple[Align, Align, Align], optional): align min, center, or max of object.
Defaults to (True, True, True). Defaults to (Align.CENTER, Align.CENTER, Align.CENTER).
mode (Mode, optional): combination mode. Defaults to Mode.ADD. mode (Mode, optional): combination mode. Defaults to Mode.ADD.
""" """

View file

@ -375,7 +375,7 @@ class TestBuilderExit(unittest.TestCase):
class TestLocations(unittest.TestCase): class TestLocations(unittest.TestCase):
def test_no_centering(self): def test_no_centering(self):
with BuildSketch(): with BuildSketch():
with GridLocations(4, 4, 2, 2, centered=(False, False)) as l: with GridLocations(4, 4, 2, 2, align=(Align.MIN, Align.MIN)) as l:
pts = [loc.to_tuple()[0] for loc in l.locations] pts = [loc.to_tuple()[0] for loc in l.locations]
self.assertTupleAlmostEquals(pts[0], (0, 0, 0), 5) self.assertTupleAlmostEquals(pts[0], (0, 0, 0), 5)
self.assertTupleAlmostEquals(pts[1], (0, 4, 0), 5) self.assertTupleAlmostEquals(pts[1], (0, 4, 0), 5)
@ -384,7 +384,7 @@ class TestLocations(unittest.TestCase):
def test_centering(self): def test_centering(self):
with BuildSketch(): with BuildSketch():
with GridLocations(4, 4, 2, 2, centered=(True, True)) as l: with GridLocations(4, 4, 2, 2, align=(Align.CENTER, Align.CENTER)) as l:
pts = [loc.to_tuple()[0] for loc in l.locations] pts = [loc.to_tuple()[0] for loc in l.locations]
self.assertTupleAlmostEquals(pts[0], (-2, -2, 0), 5) self.assertTupleAlmostEquals(pts[0], (-2, -2, 0), 5)
self.assertTupleAlmostEquals(pts[1], (-2, 2, 0), 5) self.assertTupleAlmostEquals(pts[1], (-2, 2, 0), 5)

View file

@ -274,17 +274,17 @@ class HexArrayTests(unittest.TestCase):
def test_hexarray_in_sketch(self): def test_hexarray_in_sketch(self):
with BuildSketch() as test: with BuildSketch() as test:
Rectangle(70, 70) Rectangle(70, 70)
with HexLocations(20, 4, 3, centered=(True, True)): with HexLocations(20, 4, 3, align=(Align.CENTER, Align.CENTER)):
Circle(5, mode=Mode.SUBTRACT) Circle(5, mode=Mode.SUBTRACT)
self.assertAlmostEqual(test.sketch.area, 70**2 - 12 * 25 * pi, 5) self.assertAlmostEqual(test.sketch.area, 70**2 - 12 * 25 * pi, 5)
def test_error(self): def test_error(self):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
with BuildSketch(): with BuildSketch():
with HexLocations(20, 0, 3, centered=(True, True)): with HexLocations(20, 0, 3, align=(Align.CENTER, Align.CENTER)):
pass pass
with BuildSketch(): with BuildSketch():
with HexLocations(20, 1, -3, centered=(True, True)): with HexLocations(20, 1, -3, align=(Align.CENTER, Align.CENTER)):
pass pass