Skip to content

Commit

Permalink
fix(tolerance): Ensuring values are valid when <= tolerance
Browse files Browse the repository at this point in the history
I made a pretty grave mistake throughout several uses of tolerance in the geometry library in that I only had values equal when they were < tolerance instead of <= tolerance.  This created lots of issues when I wanted to use 0 tolerance for a few things and it s now fixed.

There are also a few minor style changes that were made for better consistency with soon-to-come honeybee-core edits.
  • Loading branch information
chriswmackey authored and Chris Mackey committed Jul 16, 2019
1 parent df60161 commit eaa55b6
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 42 deletions.
2 changes: 1 addition & 1 deletion ladybug_geometry/geometry2d/polygon.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ def remove_colinear_vertices(self, tolerance):
for i, _v in enumerate(self.vertices):
_a = self[i - 2].determinant(self[i - 1]) + self[i - 1].determinant(_v) + \
_v.determinant(self[i - 2])
if abs(_a) > tolerance:
if abs(_a) >= tolerance:
new_vertices.append(self[i - 1])
return Polygon2D(new_vertices)

Expand Down
2 changes: 1 addition & 1 deletion ladybug_geometry/geometry3d/_1d.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def is_colinear(self, line_ray, tolerance, angle_tolerance):
if not self.is_parallel(line_ray, angle_tolerance):
return False
_close_pt = closest_point3d_on_line3d_infinite(self.p, line_ray)
if self.p.distance_to_point(_close_pt) > tolerance:
if self.p.distance_to_point(_close_pt) >= tolerance:
return False
return True

Expand Down
5 changes: 1 addition & 4 deletions ladybug_geometry/geometry3d/_2d.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ def max(self):

@property
def center(self):
"""A Point3D for the center of the bounding box around this geometry.
For a Surface3D, this point will always lie within the plane of the surface.
"""
"""A Point3D for the center of the bounding box around this geometry."""
if self._center is None:
min, max = self.min, self.max
self._center = Point3D(
Expand Down
27 changes: 13 additions & 14 deletions ladybug_geometry/geometry3d/face.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,11 @@ def from_rectangle(cls, base, height, base_plane=None):
return face

@classmethod
def from_regular_polygon(cls, number_of_sides, radius=1, base_plane=None):
def from_regular_polygon(cls, side_count, radius=1, base_plane=None):
"""Initialize Face3D from regular polygon parameters and a base_plane.
Args:
number_of_sides: An integer for the number of sides on the regular
side_count: An integer for the number of sides on the regular
polgygon. This number must be greater than 2.
radius: A number indicating the distance from the polygon's center
where the vertices of the polygon will lie.
Expand All @@ -242,7 +242,7 @@ def from_regular_polygon(cls, number_of_sides, radius=1, base_plane=None):
base_plane = Plane(Vector3D(0, 0, 1), Point3D(0, 0, 0))

# create the regular polygon face
_polygon2d = Polygon2D.from_regular_polygon(number_of_sides, radius)
_polygon2d = Polygon2D.from_regular_polygon(side_count, radius)
_vert3d = tuple(base_plane.xy_to_xyz(_v) for _v in _polygon2d.vertices)
_face = cls(_vert3d, base_plane, enforce_right_hand=False)

Expand Down Expand Up @@ -652,7 +652,7 @@ def validate_planarity(self, tolerance, raise_exception=True):
True if planar within the tolerance. False if not planar.
"""
for _v in self.vertices:
if self._plane.distance_to_point(_v) > tolerance:
if self._plane.distance_to_point(_v) >= tolerance:
if raise_exception is True:
raise AttributeError(
'Vertex {} is out of plane with its parent face.\nDistance '
Expand Down Expand Up @@ -842,7 +842,7 @@ def project_point(self, point):

def get_mesh_grid(self, x_dim, y_dim=None, offset=None, flip=False,
generate_centroids=True):
"""Get a gridded Mesh3D from over this face.
"""Get a gridded Mesh3D over this face.
This method generates a mesh grid over the domain of the face
and then removes any vertices that do not lie within it.
Expand Down Expand Up @@ -907,12 +907,12 @@ def get_mesh_grid(self, x_dim, y_dim=None, offset=None, flip=False,

return grid_mesh3d

def countour_by_number(self, number_of_contours, direction_vector=Vector3D(0, 0, 1),
def countour_by_number(self, contour_count, direction_vector=Vector3D(0, 0, 1),
flip_side=False):
"""Generate a list of LineSegment3D objects contouring the face.
Args:
number_of_contours: A positive integer for the number of contours
contour_count: A positive integer for the number of contours
to generate over the face.
direction_vector: A Vector3D for the direction along which contours
are generated. Default is Z-Axis, which generates horizontal contours.
Expand All @@ -925,7 +925,7 @@ def countour_by_number(self, number_of_contours, direction_vector=Vector3D(0, 0,
diagonal = LineSegment3D.from_end_points(self.min + tol_pt, self.max - tol_pt)
diagonal = diagonal.flip() if flip_side is False else diagonal
contours = []
for pt in diagonal.subdivide_evenly(number_of_contours)[:-1]:
for pt in diagonal.subdivide_evenly(contour_count)[:-1]:
result = self.intersect_plane(Plane(plane_normal, pt))
if result is not None:
contours.extend(result)
Expand Down Expand Up @@ -957,12 +957,12 @@ def countour_by_distance_between(self, distance, direction_vector=Vector3D(0, 0,
contours.extend(result)
return contours

def countour_fins_by_number(self, number_of_fins, depth, offset=0, angle=0,
def countour_fins_by_number(self, fin_count, depth, offset=0, angle=0,
contour_vector=Vector3D(0, 0, 1), flip_side=False):
"""Generate a list of Fac3D objects over this face (like louvers or fins).
Args:
number_of_fins: A positive integer for the number of fins to generate.
fin_count: A positive integer for the number of fins to generate.
depth: A number for the depth to extrude the fins.
offset: A number for the distance to offset fins from this face.
Default is 0 for no offset.
Expand All @@ -975,7 +975,7 @@ def countour_fins_by_number(self, number_of_fins, depth, offset=0, angle=0,
Setting to True will start contours on the bottom or left.
"""
extru_vec = self._get_fin_extrusion_vector(depth, angle, contour_vector)
contours = self.countour_by_number(number_of_fins, contour_vector, flip_side)
contours = self.countour_by_number(fin_count, contour_vector, flip_side)
return self._get_extrusion_fins(contours, extru_vec, offset)

def countour_fins_by_distance_between(self, distance, depth, offset=0, angle=0,
Expand All @@ -985,7 +985,6 @@ def countour_fins_by_distance_between(self, distance, depth, offset=0, angle=0,
Args:
distance: A number for the approximate distance between each contour.
The actual distance will be computed from
depth: A number for the depth to extrude the fins.
offset: A number for the distance to offset fins from this face.
Default is 0 for no offset.
Expand Down Expand Up @@ -1180,7 +1179,7 @@ def extract_rectangle(self, tolerance):
# perform checks on the face to see if a rectangle is extractable
if self.has_holes:
return None
if abs(self.normal.x) < tolerance and abs(self.normal.y) < tolerance:
if abs(self.normal.x) <= tolerance and abs(self.normal.y) <= tolerance:
# face lies within a horizontal plane; we cannot distinguish top and bottom
return None
clean_face = self.remove_colinear_vertices(tolerance)
Expand Down Expand Up @@ -1451,7 +1450,7 @@ def _remove_colinear(self, pts_3d, pts_2d, tolerance):
for i, _v in enumerate(pts_2d):
_a = pts_2d[i - 2].determinant(pts_2d[i - 1]) + \
pts_2d[i - 1].determinant(_v) + _v.determinant(pts_2d[i - 2])
if abs(_a) > tolerance:
if abs(_a) >= tolerance:
new_vertices.append(pts_3d[i - 1])
return new_vertices

Expand Down
4 changes: 2 additions & 2 deletions ladybug_geometry/geometry3d/line.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def is_horizontal(edge, tolerance):
tolerance: The maximum difference between the z values of the start and
end coordinates at which the line segment is considered horizontal.
"""
return abs(edge.v.z) < tolerance
return abs(edge.v.z) <= tolerance

def is_vertical(edge, tolerance):
"""Test whether this line segment is vertical within a certain tolerance.
Expand All @@ -87,7 +87,7 @@ def is_vertical(edge, tolerance):
tolerance: The maximum difference between the x and y values of the start
and end coordinates at which the line segment is considered horizontal.
"""
return abs(edge.v.x) < tolerance and abs(edge.v.y) < tolerance
return abs(edge.v.x) <= tolerance and abs(edge.v.y) <= tolerance

def flip(self):
"""Get a copy of this line segment that is flipped."""
Expand Down
6 changes: 3 additions & 3 deletions ladybug_geometry/geometry3d/plane.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,9 @@ def is_coplanar_tolerance(self, plane, tolerance, angle_tolerance):
Returns:
True if plane is coplanar. False if it is not coplanar.
"""
if self.n.angle(plane.n) < angle_tolerance or \
self.n.angle(plane.n.reverse()) < angle_tolerance:
return self.distance_to_point(plane.o) < tolerance
if self.n.angle(plane.n) <= angle_tolerance or \
self.n.angle(plane.n.reverse()) <= angle_tolerance:
return self.distance_to_point(plane.o) <= tolerance
return False

def duplicate(self):
Expand Down
29 changes: 14 additions & 15 deletions ladybug_geometry/geometry3d/polyface.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,43 +201,43 @@ def from_faces_tolerance(cls, faces, tolerance):
return cls(vertices, face_indices)

@classmethod
def from_box(cls, length, width, height, base_plane=None):
def from_box(cls, width, depth, height, base_plane=None):
"""Initilize Polyface3D from parameters describing a box.
Initializing a polyface this way has the added benefit of having its
faces property quickly calculated.
Args:
length: A number for the length of the box (in the X direction).
width: A number for the width of the box (in the Y direction).
width: A number for the width of the box (in the X direction).
depth: A number for the depth of the box (in the Y direction).
height: A number for the height of the box (in the Z direction).
base_plane: A Plane object from which to generate the box.
If None, default is the WorldXY plane.
"""
assert isinstance(length, (float, int)), 'Box length must be a number.'
assert isinstance(width, (float, int)), 'Box width must be a number.'
assert isinstance(depth, (float, int)), 'Box depth must be a number.'
assert isinstance(height, (float, int)), 'Box height must be a number.'
if base_plane is not None:
assert isinstance(base_plane, Plane), \
'base_plane must be Plane. Got {}.'.format(type(base_plane))
else:
base_plane = Plane(Vector3D(0, 0, 1), Point3D())
_o = base_plane.o
_l_vec = base_plane.x * length
_w_vec = base_plane.y * width
_w_vec = base_plane.x * width
_d_vec = base_plane.y * depth
_h_vec = base_plane.n * height
_verts = (_o, _o + _w_vec, _o + _w_vec + _l_vec, _o + _l_vec,
_o + _h_vec, _o + _w_vec + _h_vec,
_o + _w_vec + _l_vec + _h_vec, _o + _l_vec + _h_vec)
_face_indices = ([(0, 1, 2, 3)], [(0, 4, 5, 1)], [(0, 3, 7, 4)],
[(2, 1, 5, 6)], [(6, 7, 3, 2)], [(7, 6, 5, 4)])
_verts = (_o, _o + _d_vec, _o + _d_vec + _w_vec, _o + _w_vec,
_o + _h_vec, _o + _d_vec + _h_vec,
_o + _d_vec + _w_vec + _h_vec, _o + _w_vec + _h_vec)
_face_indices = ([(0, 1, 2, 3)], [(2, 1, 5, 6)], [(6, 7, 3, 2)],
[(0, 3, 7, 4)], [(0, 4, 5, 1)], [(7, 6, 5, 4)])
_edge_indices = ((3, 0), (0, 1), (1, 2), (2, 3), (0, 4), (4, 5),
(5, 1), (3, 7), (7, 4), (6, 2), (5, 6), (6, 7))
polyface = cls(_verts, _face_indices, {'edge_indices': _edge_indices,
'edge_types': [1] * 12})
verts = tuple(tuple(_verts[i] for i in face[0]) for face in _face_indices)
polyface._faces = tuple(Face3D(v, enforce_right_hand=False) for v in verts)
polyface._volume = length * width * height
polyface._volume = width * depth * height
return polyface

@classmethod
Expand Down Expand Up @@ -461,9 +461,8 @@ def merge_overlapping_edges(self, tolerance, angle_tolerance):
Args:
tolerance: The minimum distance between a vertex and the boundary segments
at which point the vertex is considered colinear.
angle_tolerance: The max angle in radians that the direction between
this object and another can vary for them to be considered
parallel.
angle_tolerance: The max angle in radians that vertices are allowed to
differ from one another in order to consider them colinear.
"""
# get naked edges
naked_edges = list(self.naked_edges)
Expand Down
4 changes: 2 additions & 2 deletions tests/face3d_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,8 @@ def test_face3d_init_from_punched_geometry(self):
assert face.is_convex is False
assert face.is_self_intersecting is False

def test_is_equivalent(self):
"""Test the is_equivalent method."""
def test_is_geometrically_equivalent(self):
"""Test the is_geometrically_equivalent method."""
plane_1 = Plane(Vector3D(0, 0, 1))
plane_2 = Plane(Vector3D(0, 0, -1))
pts_1 = (Point3D(0, 0), Point3D(2, 0), Point3D(2, 2), Point3D(0, 2))
Expand Down

0 comments on commit eaa55b6

Please sign in to comment.