diff --git a/CHANGELOG.md b/CHANGELOG.md index 83dd154837..e46cce2a88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Removed Grasshopper after-install plugin. Components should be installed via Rhino's Plugin Manager. * Removed `get_face_most_towards_beam` from `Joint` as not used anywhere. * Removed `get_face_most_ortho_to_beam` from `Joint` as not used anywhere. +* Removed `angle_vectors_projected` from `compas_timber.utils` since this has been upstreamed to core. ## [0.16.2] 2025-05-07 @@ -99,6 +100,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed * Fixed `AttributeError` when deserializing a model with Lap joints. +* Fixed a bug in `compas_timber.fabrication.Lap` where `ref_side_index` failed for `0` by checking for `None` instead. +* Fixed a bug in `compas_timber.fabrication.Lap` to handle the case when the vectors used to calculate the `inclination` angle are perpendicular. + ### Removed diff --git a/src/compas_timber/connections/l_lap.py b/src/compas_timber/connections/l_lap.py index 6f39640e44..266cab2b6a 100644 --- a/src/compas_timber/connections/l_lap.py +++ b/src/compas_timber/connections/l_lap.py @@ -1,3 +1,5 @@ +from compas.tolerance import TOL + from compas_timber.errors import BeamJoiningError from compas_timber.fabrication import JackRafterCut from compas_timber.fabrication import Lap @@ -64,8 +66,9 @@ def add_extensions(self): raise BeamJoiningError(self.elements, self, debug_info=str(ae), debug_geometries=geometries) except Exception as ex: raise BeamJoiningError(self.elements, self, debug_info=str(ex)) - self.main_beam.add_blank_extension(start_main, end_main, self.main_beam_guid) - self.cross_beam.add_blank_extension(start_cross, end_cross, self.cross_beam_guid) + tol = TOL.absolute + self.main_beam.add_blank_extension(start_main + tol, end_main + tol, self.main_beam_guid) + self.cross_beam.add_blank_extension(start_cross + tol, end_cross + tol, self.cross_beam_guid) def add_features(self): """Adds the required joint features to both beams. diff --git a/src/compas_timber/fabrication/lap.py b/src/compas_timber/fabrication/lap.py index 0fc290514d..207ead53aa 100644 --- a/src/compas_timber/fabrication/lap.py +++ b/src/compas_timber/fabrication/lap.py @@ -9,6 +9,7 @@ from compas.geometry import Point from compas.geometry import Polyhedron from compas.geometry import Vector +from compas.geometry import angle_vectors_projected from compas.geometry import angle_vectors_signed from compas.geometry import distance_point_plane from compas.geometry import distance_point_point @@ -21,7 +22,6 @@ from compas.tolerance import Tolerance from compas_timber.errors import FeatureApplicationError -from compas_timber.utils import angle_vectors_projected from .btlx import BTLxProcessing from .btlx import BTLxProcessingParams @@ -409,7 +409,7 @@ def from_volume_and_beam(cls, volume, beam, machining_limits=None, ref_side_inde raise ValueError("Volume must have 6 faces.") # get ref_side of the - if not ref_side_index: + if ref_side_index is None: ref_side_index = cls._get_optimal_ref_side_index(beam, volume) ref_side = beam.ref_sides[ref_side_index] @@ -441,11 +441,14 @@ def from_volume_and_beam(cls, volume, beam, machining_limits=None, ref_side_inde angle = angle_vectors_signed(-yyaxis, ref_side.xaxis, ref_side.normal, deg=True) # calculate the inclination of the lap - inclination = angle_vectors_projected(zzaxis, front_plane.normal, yyaxis) + if TOL.is_zero(yyaxis.dot(zzaxis)) or TOL.is_zero(yyaxis.dot(front_plane.normal)): # TODO: follow changes in compas.geometry and update this accordingly + inclination = angle_vectors_signed(zzaxis, ref_side.xaxis, ref_side.normal, deg=True) + else: + inclination = angle_vectors_projected(zzaxis, front_plane.normal, yyaxis, deg=True) inclination = 180 + inclination if inclination < 0 else inclination # calculate the slope of the lap - slope = angle_vectors_projected(-ref_side.normal, bottom_plane.normal, start_plane.normal) + slope = angle_vectors_projected(-ref_side.normal, bottom_plane.normal, start_plane.normal, deg=True) # calculate length, width and depth length = distance_point_plane(start_plane.point, end_plane) @@ -739,10 +742,10 @@ def _planes_from_params_and_beam(self, beam): tol = Tolerance() tol.absolute=1e-3 - if self.machining_limits["FaceLimitedStart"]: - start_frame = self._start_frame_from_params_and_beam(beam) - else: - start_frame = beam.ref_sides[4] + start_frame = self._start_frame_from_params_and_beam(beam) + + top_frame = beam.ref_sides[self.ref_side_index] # top should always be unlimited + top_frame.translate(top_frame.normal * TOL.absolute) if self.machining_limits["FaceLimitedEnd"]: end_frame = start_frame.translated(-start_frame.normal * self.length) @@ -750,14 +753,12 @@ def _planes_from_params_and_beam(self, beam): else: end_frame = beam.ref_sides[5] - top_frame = beam.ref_sides[self.ref_side_index] # top should always be unlimited - if self.machining_limits["FaceLimitedBottom"]: bottom_frame = Frame(start_frame.point, start_frame.zaxis, start_frame.yaxis) angle = angle_vectors_signed(top_frame.xaxis, -start_frame.xaxis, top_frame.yaxis) bottom_frame = bottom_frame.translated(bottom_frame.zaxis * (self.depth/math.sin(angle))) else: - bottom_frame = beam.ref_sides[4] + bottom_frame = beam.opp_side(self.ref_side_index) if self.machining_limits["FaceLimitedFront"]: front_frame = bottom_frame.rotated(math.radians(self.lead_angle), bottom_frame.xaxis, point=bottom_frame.point) diff --git a/src/compas_timber/fabrication/pocket.py b/src/compas_timber/fabrication/pocket.py index 14808dbf6f..ba621d21a1 100644 --- a/src/compas_timber/fabrication/pocket.py +++ b/src/compas_timber/fabrication/pocket.py @@ -10,6 +10,7 @@ from compas.geometry import Polyhedron from compas.geometry import Vector from compas.geometry import angle_vectors +from compas.geometry import angle_vectors_projected from compas.geometry import angle_vectors_signed from compas.geometry import dot_vectors from compas.geometry import intersection_plane_plane_plane @@ -19,7 +20,6 @@ from compas.tolerance import Tolerance from compas_timber.errors import FeatureApplicationError -from compas_timber.utils import angle_vectors_projected from .btlx import BTLxProcessing from .btlx import BTLxProcessingParams @@ -354,17 +354,17 @@ def from_volume_and_element(cls, volume, element, machining_limits=None, ref_sid yyaxis = Vector.from_start_end(start_point, back_point) # calculate the angle of the pocket - angle = angle_vectors_projected(ref_side.xaxis, xxaxis, ref_side.normal) + angle = angle_vectors_projected(ref_side.xaxis, xxaxis, ref_side.normal, deg=True) # x'-axis and y'-axis (see BTLx Documentation p.46) xaxis = ref_side.xaxis.rotated(math.radians(angle), ref_side.normal) yaxis = ref_side.yaxis.rotated(math.radians(angle), ref_side.normal) # calculate the inclination of the pocket - inclination = angle_vectors_projected(xaxis, xxaxis, yaxis) + inclination = angle_vectors_projected(xaxis, xxaxis, yaxis, deg=True) # calculate the slope of the pocket - slope = angle_vectors_projected(yaxis, yyaxis, xxaxis) + slope = angle_vectors_projected(yaxis, yyaxis, xxaxis, deg=True) # calculate internal_angle internal_angle = angle_vectors_signed(xxaxis, yyaxis, ref_side.normal, deg=True) diff --git a/src/compas_timber/utils/__init__.py b/src/compas_timber/utils/__init__.py index b29dfcbf3a..25a53de1a8 100644 --- a/src/compas_timber/utils/__init__.py +++ b/src/compas_timber/utils/__init__.py @@ -18,7 +18,6 @@ from compas.geometry import subtract_vectors from compas.geometry import Frame from compas.geometry import Transformation -from compas.geometry import Projection from compas.geometry import intersection_line_plane from compas.geometry import closest_point_on_segment from compas.geometry import intersection_line_line @@ -296,32 +295,6 @@ def distance_segment_segment(segment_a, segment_b): return distance_point_point(pt_seg_a, pt_seg_b) -def angle_vectors_projected(vector_a, vector_b, normal): - """Computes the angle between two vectors projected onto a plane defined by a normal vector. - - Parameters - ---------- - vector_a : :class:`compas.geometry.Vector` - The first vector. - vector_b : :class:`compas.geometry.Vector` - The second vector. - normal : :class:`compas.geometry.Vector` or :class:`compas.geometry.Plane` or :class:`compas.geometry.Frame` - The normal vector of the plane to project the vectors onto. - - Returns - ------- - float - The angle between the two projected vectors - """ - if isinstance(normal, (Plane, Frame)): - normal = normal.normal - - projection = Projection.from_plane(Plane(Point(0, 0, 0), normal)) - proj_vect_a = vector_a.transformed(projection) - proj_vect_b = vector_b.transformed(projection) - return angle_vectors_signed(proj_vect_a, proj_vect_b, normal, deg=True) - - def is_polyline_clockwise(polyline, normal_vector): """Check if a polyline is clockwise. If the polyline is open, it is closed before the check. @@ -432,7 +405,6 @@ def is_point_in_polyline(point, polyline, in_plane=True, tol=TOL): "intersection_line_beam_param", "classify_polyline_segments", "distance_segment_segment", - "angle_vectors_projected", "is_polyline_clockwise", "correct_polyline_direction", "get_polyline_segment_perpendicular_vector",