Skip to content

Commit a9c9a0e

Browse files
chriswmackeyChris Mackey
authored andcommitted
fix(plane): Ensure that Plane does not mess up bounding calculations
1 parent 404c0d7 commit a9c9a0e

File tree

7 files changed

+88
-15
lines changed

7 files changed

+88
-15
lines changed

ladybug_geometry/geometry3d/arc.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ def from_start_mid_end(cls, p1, m, p2, circle=False):
9090
return cls(Plane(plane.n, plane.xy_to_xyz(arc_2d.c), plane.x),
9191
arc_2d.r, arc_2d.a1, arc_2d.a2)
9292

93+
@classmethod
94+
def from_arc2d(cls, arc2d, z=0):
95+
"""Initialize a new Arc3D from an Arc2D and a z value.
96+
97+
Args:
98+
arc2d: An Arc2D to be used to generate the Arc3D.
99+
z: A number for the Z coordinate value of the arc.
100+
"""
101+
plane = Plane(o=Point3D(arc2d.c.x, arc2d.c.y, z))
102+
return cls(plane, arc2d.r, arc2d.a1, arc2d.a2)
103+
93104
@property
94105
def plane(self):
95106
"""A Plane in which the arc lies with an origin for the center of the arc."""
@@ -375,14 +386,18 @@ def _calculate_min_max(self):
375386
https://stackoverflow.com/questions/2592011/\
376387
bounding-boxes-for-circle-and-arcs-in-3d
377388
"""
378-
# TODO: get this method to respect the start and end points of the arc
379-
o, r = self._plane.o, self.radius
380-
ax = self._plane.n.angle(Vector3D(1, 0, 0))
381-
ay = self._plane.n.angle(Vector3D(0, 1, 0))
382-
az = self._plane.n.angle(Vector3D(0, 0, 1))
383-
r_vec = (math.sin(ax) * r, math.sin(ay) * r, math.sin(az) * r)
384-
self._min = Point3D(o.x - r_vec[0], o.y - r_vec[1], o.z - r_vec[2])
385-
self._max = Point3D(o.x + r_vec[0], o.y + r_vec[1], o.z + r_vec[2])
389+
if self.is_circle:
390+
o, r = self._plane.o, self.radius
391+
ax = self._plane.n.angle(Vector3D(1, 0, 0))
392+
ay = self._plane.n.angle(Vector3D(0, 1, 0))
393+
az = self._plane.n.angle(Vector3D(0, 0, 1))
394+
r_vec = (math.sin(ax) * r, math.sin(ay) * r, math.sin(az) * r)
395+
self._min = Point3D(o.x - r_vec[0], o.y - r_vec[1], o.z - r_vec[2])
396+
self._max = Point3D(o.x + r_vec[0], o.y + r_vec[1], o.z + r_vec[2])
397+
else: # get the min and max of the Arc2D
398+
min_pt2d, max_pt2d = self._arc2d.min, self._arc2d.max
399+
self._min = self.plane.xy_to_xyz(min_pt2d)
400+
self._max = self.plane.xy_to_xyz(max_pt2d)
386401

387402
@staticmethod
388403
def _plane_from_vertices(pt1, pt2, pt3):

ladybug_geometry/geometry3d/line.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"""3D Line Segment"""
33
from __future__ import division
44

5-
from .pointvector import Point3D
5+
from .pointvector import Point3D, Vector3D
66
from ._1d import Base1DIn3D
77

88

@@ -62,6 +62,18 @@ def from_array(cls, line_array):
6262
"""
6363
return LineSegment3D.from_end_points(*tuple(Point3D(*pt) for pt in line_array))
6464

65+
@classmethod
66+
def from_line_segment2d(cls, line2d, z=0):
67+
"""Initialize a new LineSegment3D from an LineSegment2D and a z value.
68+
69+
Args:
70+
line2d: A LineSegment2D to be used to generate the LineSegment3D.
71+
z: A number for the Z coordinate value of the line.
72+
"""
73+
base_p = Point3D(line2d.p.x, line2d.p.y, z)
74+
base_v = Vector3D(line2d.v.x, line2d.v.y, 0)
75+
return cls(base_p, base_v)
76+
6577
@property
6678
def p1(self):
6779
"""First point (same as p)."""

ladybug_geometry/geometry3d/plane.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class Plane(object):
3030
* y
3131
* altitude
3232
* azimuth
33+
* min
34+
* max
3335
"""
3436
__slots__ = ('_n', '_o', '_k', '_x', '_y', '_altitude', '_azimuth')
3537

@@ -155,6 +157,16 @@ def altitude(self):
155157
self._altitude = self.n.angle(Vector3D(0, 0, -1)) - math.pi / 2
156158
return self._altitude
157159

160+
@property
161+
def min(self):
162+
"""Returns the Plane origin."""
163+
return self._o
164+
165+
@property
166+
def max(self):
167+
"""Returns the Plane origin."""
168+
return self._o
169+
158170
def flip(self):
159171
"""Get a flipped version of this plane (facing the opposite direction)."""
160172
return Plane(self.n.reverse(), self.o, self.x)

ladybug_geometry/geometry3d/pointvector.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ def from_array(cls, array):
5959
"""
6060
return cls(array[0], array[1], array[2])
6161

62+
@classmethod
63+
def from_vector2d(cls, vector2d, z=0):
64+
"""Initialize a new Vector3D from an Vector2D and a z value.
65+
66+
Args:
67+
line2d: A Vector2D to be used to generate the Vector3D.
68+
z: A number for the Z coordinate value of the line.
69+
"""
70+
return cls(vector2d.x, vector2d.y, z)
71+
6272
@property
6373
def x(self):
6474
"""Get the X coordinate."""
@@ -395,6 +405,16 @@ class Point3D(Vector3D):
395405
"""
396406
__slots__ = ()
397407

408+
@classmethod
409+
def from_point2d(cls, point2d, z=0):
410+
"""Initialize a new Point3D from an Point2D and a z value.
411+
412+
Args:
413+
line2d: A Point2D to be used to generate the Point3D.
414+
z: A number for the Z coordinate value of the line.
415+
"""
416+
return cls(point2d.x, point2d.y, z)
417+
398418
@property
399419
def min(self):
400420
"""Always equal to the point itself.

ladybug_geometry/geometry3d/polyline.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ def from_array(cls, point_array):
7171
return Polyline3D(Point3D(*point) for point in point_array)
7272

7373
@classmethod
74-
def from_polyline2d(cls, polyline2d, plane=Plane()):
74+
def from_polyline2d(cls, polyline2d, plane=None):
7575
"""Create a closed Polyline3D from a Polyline2D and a plane.
7676
7777
Args:
7878
polyline2d: A Polyline2D object to be converted to a Polyline3D.
79-
plane: A Plane in which the Polyline2D sits.
79+
plane: A Plane in which the Polyline2D sits. If None, the WorldXY
80+
plane will be used.
8081
"""
82+
plane = Plane() if plane is None else plane
8183
return Polyline3D((plane.xy_to_xyz(pt) for pt in polyline2d.vertices),
8284
polyline2d.interpolated)
8385

ladybug_geometry/geometry3d/ray.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ def from_array(cls, ray_array):
3434
"""
3535
return Ray3D(Point3D(*ray_array[0]), Vector3D(*ray_array[1]))
3636

37+
@classmethod
38+
def from_ray2d(cls, ray2d, z=0):
39+
"""Initialize a new Ray3D from an Ray2D and a z value.
40+
41+
Args:
42+
line2d: A Ray2D to be used to generate the Ray3D.
43+
z: A number for the Z coordinate value of the line.
44+
"""
45+
base_p = Point3D(ray2d.p.x, ray2d.p.y, z)
46+
base_v = Vector3D(ray2d.v.x, ray2d.v.y, 0)
47+
return cls(base_p, base_v)
48+
3749
def reverse(self):
3850
"""Get a copy of this ray that is reversed."""
3951
return Ray3D(self.p, self.v.reverse())

tests/arc3d_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def test_arc3_init():
2727
assert arc.p2.y == pytest.approx(0, rel=1e-3)
2828
assert arc.p2.z == pytest.approx(2, rel=1e-3)
2929
assert arc.midpoint == Point3D(2, 1, 2)
30-
assert arc.min == Point3D(1, -1, 2)
30+
assert arc.min.x == pytest.approx(1, rel=1e-3)
31+
assert arc.min.z == pytest.approx(2, rel=1e-3)
3132
assert arc.max == Point3D(3, 1, 2)
3233
assert arc.length == pytest.approx(math.pi, rel=1e-3)
3334
assert arc.angle == pytest.approx(math.pi, rel=1e-3)
@@ -63,7 +64,7 @@ def test_arc3_init_radius():
6364
assert arc.is_inverted is True
6465

6566

66-
def test_arc3_init_reveresed():
67+
def test_arc3_init_reversed():
6768
"""Test the initialization of Arc3D objects with reversed direction."""
6869
pt = Point3D(2, 0, 2)
6970
arc = Arc3D(Plane(o=pt), 1, 1.5 * math.pi, 0.5 * math.pi)
@@ -80,8 +81,7 @@ def test_arc3_init_reveresed():
8081
assert arc.p2.z == pytest.approx(2, rel=1e-3)
8182
assert arc.midpoint.x == pytest.approx(3, rel=1e-3)
8283
assert arc.midpoint.y == pytest.approx(0, rel=1e-3)
83-
assert arc.min.x == pytest.approx(1, rel=1e-3)
84-
assert arc.min.y == pytest.approx(-1, rel=1e-3)
84+
assert arc.min.x == pytest.approx(2, rel=1e-3)
8585
assert arc.max.x == pytest.approx(3, rel=1e-3)
8686
assert arc.max.y == pytest.approx(1, rel=1e-3)
8787
assert arc.length == pytest.approx(math.pi, rel=1e-3)

0 commit comments

Comments
 (0)