diff --git a/examples/heat_exchanger.py b/examples/heat_exchanger.py index 0e98b4db..910d5502 100644 --- a/examples/heat_exchanger.py +++ b/examples/heat_exchanger.py @@ -48,7 +48,7 @@ tube_locations = [ l for l in HexLocations( - apothem=(tube_diameter + tube_spacing) / 2, + radius=(tube_diameter + tube_spacing) / 2, x_count=exchanger_diameter // tube_diameter, y_count=exchanger_diameter // tube_diameter, ) diff --git a/examples/heat_exchanger_algebra.py b/examples/heat_exchanger_algebra.py index 79984e3f..81a25279 100644 --- a/examples/heat_exchanger_algebra.py +++ b/examples/heat_exchanger_algebra.py @@ -19,7 +19,7 @@ tube_locations = [ l for l in HexLocations( - apothem=(tube_diameter + tube_spacing) / 2, + radius=(tube_diameter + tube_spacing) / 2, x_count=exchanger_diameter // tube_diameter, y_count=exchanger_diameter // tube_diameter, ) diff --git a/src/build123d/build_common.py b/src/build123d/build_common.py index 9a7a815d..3fccefb3 100644 --- a/src/build123d/build_common.py +++ b/src/build123d/build_common.py @@ -49,7 +49,7 @@ import functools from abc import ABC, abstractmethod from itertools import product -from math import sqrt +from math import sqrt, cos, pi from typing import Any, Callable, Iterable, Optional, Union, TypeVar from typing_extensions import Self, ParamSpec, Concatenate @@ -860,20 +860,30 @@ class HexLocations(LocationList): """Location Context: Hex Array Creates a context of hexagon array of locations for Part or Sketch. When creating - hex locations for an array of circles, set `apothem` to the radius of the circle + hex locations for an array of circles, set `radius` to the radius of the circle plus one half the spacing between the circles. Args: - apothem (float): radius of the inscribed circle - xCount (int): number of points ( > 0 ) - yCount (int): number of points ( > 0 ) + radius (float): distance from origin to vertices (major), or + optionally from the origin to side (minor or apothem) + with major_radius = False + x_count (int): number of points ( > 0 ) + y_count (int): number of points ( > 0 ) + major_radius (bool): If True the radius is the major radius, else the + radius is the minor radius (also known as inscribed radius). + Defaults to False. align (Union[Align, tuple[Align, Align]], optional): align min, center, or max of object. Defaults to (Align.CENTER, Align.CENTER). Attributes: - apothem (float): radius of the inscribed circle - xCount (int): number of points ( > 0 ) - yCount (int): number of points ( > 0 ) + radius (float): distance from origin to vertices (major), or + optionally from the origin to side (minor or apothem) + with major_radius = False + apothem (float): radius of the inscribed circle, also known as minor radius + x_count (int): number of points ( > 0 ) + y_count (int): number of points ( > 0 ) + major_radius (bool): If True the radius is the major radius, else the + radius is the minor radius (also known as inscribed radius). align (Union[Align, tuple[Align, Align]]): align min, center, or max of object. diagonal (float): major radius local_locations (list{Location}): locations relative to workplane @@ -884,23 +894,32 @@ class HexLocations(LocationList): def __init__( self, - apothem: float, + radius: float, x_count: int, y_count: int, + major_radius: bool = False, align: Union[Align, tuple[Align, Align]] = (Align.CENTER, Align.CENTER), ): # pylint: disable=too-many-locals - diagonal = 4 * apothem / sqrt(3) + if major_radius: + diagonal = 2 * radius + apothem = radius * cos(pi / 6) + else: + diagonal = 4 * radius / sqrt(3) + apothem = radius + 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 ") + self.radius = radius self.apothem = apothem self.diagonal = diagonal self.x_count = x_count self.y_count = y_count + self.major_radius = major_radius self.align = tuplify(align, 2) # Generate the raw coordinates relative to bottom left point diff --git a/tests/test_build_common.py b/tests/test_build_common.py index b6261664..a4c6e0e7 100644 --- a/tests/test_build_common.py +++ b/tests/test_build_common.py @@ -320,6 +320,16 @@ def test_hex_no_centering(self): for position in positions: self.assertTrue(position.X <= 0 and position.Y <= 0) + def test_hex_major_radius(self): + hex = RegularPolygon(1, 6) + with BuildSketch() as s: + with HexLocations(1, 3, 3, major_radius=True) as hloc: + add(hex) + self.assertAlmostEqual(s.sketch.face().area, hex.area * 9, 7) + self.assertAlmostEqual(hloc.radius, 1, 7) + self.assertAlmostEqual(hloc.diagonal, 2, 7) + self.assertAlmostEqual(hloc.apothem, 3**0.5 / 2, 7) + def test_centering(self): with BuildSketch(): with GridLocations(4, 4, 2, 2, align=(Align.CENTER, Align.CENTER)) as l: