Skip to content

Commit

Permalink
Merge branch 'main' into DirectJointRule_BugFix
Browse files Browse the repository at this point in the history
  • Loading branch information
chenkasirer committed Feb 15, 2024
2 parents a9442e1 + fd665fb commit 5be528b
Show file tree
Hide file tree
Showing 37 changed files with 464 additions and 218 deletions.
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Added `debug_geometries` attribute to `BeamJoiningError`.
* (Re)added `BooleanSubtraction` feature.
* Added flag `modify_cross` to `L-Butt` joint.
* Added flag `reject_i` to `L-Butt` joint.

### Changed

* `BeamFromCurve` GH component accepts now referenced Rhino curves, referenced Rhino object IDs and internalized lines.
* `BeamFromCurve` GH component accepts now referenced Rhino curves, referenced Rhino object IDs and internalized lines.
* Fixed `FeatureError` when L-Butt applies the cutting plane.
* Fixed T-Butt doesn't get extended to cross beam's plane.
* `SimpleSequenceGenerator` updated to work with `compas.datastructures.assembly` and generates building plan acording to type.
* Changed GH Categories for joint rules
* Changed GH Categories for joint rules.
* Made `beam_side_incident` a `staticmethod` of `Joint` and reworked it.
* Extended `DecomposeBeam` component to optionally show beam frame and faces.

### Removed

* Removed component `ShowBeamFrame`.
* Changed GH Categories for joint rules
* `BrepGeometryConsumer` continues to apply features even after the first error.
* `DrillHole` component calculates length from input line.
* `DrillHole` has default diameter proportional to beam cross-section.
* Removed input `Length` from `DrillHole` component.
* Fixed broken `TrimmingFeature` component.

## [0.6.1] 2024-02-02

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.7934267.svg)](https://doi.org/10.5281/zenodo.7934267)
[![Twitter Follow](https://img.shields.io/twitter/follow/compas_dev?style=social)](https://twitter.com/compas_dev)

![COMPAS Timber](https://raw.githubusercontent.com/gramaziokohler/compas_timber/main/docs/_images/compas_timber.png)
![COMPAS Timber](https://raw.githubusercontent.com/gramaziokohler/compas_timber/main/docs/_images/compas_timber.jpg)

`compas_timber` is a user-friendly open-source software toolkit to streamline the design of timber frame structures. Despite its advances in digitalization compared to other building techniques, timber construction is often perceived as a challenging field, involving intricate processes in design, planning, coordination, and fabrication. We aim to increase the use of timber in architecture by lowering the threshold of creating versatile and resource-aware designs.

Expand Down
Binary file added docs/_images/compas_timber.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/_images/compas_timber.png
Binary file not shown.
1 change: 0 additions & 1 deletion docs/api/compas_timber.connections.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ Functions
:nosignatures:

find_neighboring_beams
beam_side_incidence

Exceptions
==========
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
COMPAS TIMBER
********************************************************************************

.. figure:: /_images/compas_timber.png
.. figure:: /_images/compas_timber.jpg
:figclass: figure
:class: figure-img img-fluid

Expand Down
5 changes: 2 additions & 3 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@ Installation
Stable
======

Install with pip
Install with conda
------------------

In an new environment:

.. code-block:: bash
conda create -n <myenvname> python=3.10 --yes
conda create -n <myenvname> compas_timber --yes
conda activate <myenvname>
pip install compas_timber
Install to Rhino 7.0

Expand Down
Binary file modified examples/Grasshopper/CT_NEW_UI_Example.3dm
Binary file not shown.
Binary file modified examples/Grasshopper/CT_NEW_UI_Example.gh
Binary file not shown.
2 changes: 0 additions & 2 deletions src/compas_timber/connections/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from .french_ridge_lap import FrenchRidgeLapJoint
from .joint import BeamJoinningError
from .joint import Joint
from .joint import beam_side_incidence
from .lap_joint import LapJoint
from .l_butt import LButtJoint
from .l_miter import LMiterJoint
Expand All @@ -15,7 +14,6 @@

__all__ = [
"Joint",
"beam_side_incidence",
"LapJoint",
"BeamJoinningError",
"TButtJoint",
Expand Down
7 changes: 2 additions & 5 deletions src/compas_timber/connections/french_ridge_lap.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from .joint import BeamJoinningError
from .joint import Joint
from .joint import beam_side_incidence
from .solver import JointTopology


Expand Down Expand Up @@ -77,15 +76,13 @@ def joint_type(self):

@property
def cutting_plane_top(self):
angles_faces = beam_side_incidence(self.beam_a, self.beam_b)
cfr = max(angles_faces, key=lambda x: x[0])[1]
_, cfr = self.get_face_most_towards_beam(self.beam_a, self.beam_b, ignore_ends=True)
cfr = Frame(cfr.point, cfr.xaxis, cfr.yaxis * -1.0) # flip normal
return cfr

@property
def cutting_plane_bottom(self):
angles_faces = beam_side_incidence(self.beam_b, self.beam_a)
cfr = max(angles_faces, key=lambda x: x[0])[1]
_, cfr = self.get_face_most_towards_beam(self.beam_b, self.beam_b, ignore_ends=True)
return cfr

def restore_beams_from_keys(self, assemly):
Expand Down
151 changes: 117 additions & 34 deletions src/compas_timber/connections/joint.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,31 @@
from .solver import JointTopology


def beam_side_incidence(beam1, beam2):
"""Returns a map of faces of beam2 and the angle of their normal with beam1's centerline.
This is used to find a cutting plane when joining the two beams.
Parameters
----------
beam1 : :class:`~compas_timber.parts.Beam`
The beam that attaches with one of its ends to the side of Beam2.
beam2 : :class:`~compas_timber.parts.Beam`
The other beam.
Returns
-------
list(tuple(float, :class:`~compas.geometry.Frame`))
"""

# find the orientation of beam1's centerline so that it's pointing outward of the joint
# find the closest end
p1x, p2x = intersection_line_line(beam1.centerline, beam2.centerline)
which, _ = beam1.endpoint_closest_to_point(Point(*p1x))

if which == "start":
centerline_vec = beam1.centerline.vector
else:
centerline_vec = beam1.centerline.vector * -1

# map faces to their angle with centerline, choose smallest
angle_face = [(angle_vectors(side.normal, centerline_vec), side) for side in beam2.faces[:4]]
return angle_face


class BeamJoinningError(Exception):
"""Indicates that an error has occurred while trying to join two or more beams.
This error should indicate that an error has occurred while calculating the features which
should be applied by this joint.
Attributes
----------
beams : list(:class:`~compas_timber.parts.Beam`)
The beams that were supposed to be joined.
debug_geometries : list(:class:`~compas.geometry.Geometry`)
A list of geometries that can be used to visualize the error.
debug_info : str
A string containing debug information about the error.
joint : :class:`~compas_timber.connections.Joint`
The joint that was supposed to join the beams.
"""

def __init__(self, beams, joint, debug_info=None):
def __init__(self, beams, joint, debug_info=None, debug_geometries=None):
super(BeamJoinningError, self).__init__()
self.beams = beams
self.joint = joint
self.debug_info = debug_info
self.debug_geometries = debug_geometries or []


class Joint(Data):
Expand All @@ -76,6 +55,7 @@ def __init__(self, frame=None, key=None):
super(Joint, self).__init__()
self.frame = frame or Frame.worldXY()
self.key = key
self.attributes = {}

@property
def __data__(self):
Expand Down Expand Up @@ -170,3 +150,106 @@ def ends(self):
self._ends[str(beam.key)] = "end"

return self._ends

@staticmethod
def get_face_most_towards_beam(beam_a, beam_b, ignore_ends=True):
"""Of all the faces of `beam_b`, returns the one whose normal most faces `beam_a`.
This is done by calculating the inner-product of `beam_a`'s centerline which each of the face normals of `beam_b`.
The face with the result closest to 1 is chosen.
Parameters
----------
beam_a : :class:`~compas_timber.parts.Beam`
The beam that attaches with one of its ends to `beam_b`.
beam_b : :class:`~compas_timber.parts.Beam`
The other beam.
ignore_ends : bool, optional
If True, the faces at each end of `beam_b` are ignored.
Returns
-------
tuple(face_index, :class:`~compas.geometry.Frame`)
Tuple containing the index of the chosen face and a frame at the center of if.
"""
face_dict = Joint._beam_side_incidence(beam_a, beam_b, ignore_ends)
face_index = max(face_dict, key=face_dict.get) # type: ignore
return face_index, beam_b.faces[face_index]

@staticmethod
def get_face_most_ortho_to_beam(beam_a, beam_b, ignore_ends=True):
"""Of all the faces of `beam_b`, returns the one whose normal is most orthogonal to `beam_a`.
This is done by calculating the inner-product of `beam_a`'s centerline which each of the face normals of `beam_b`.
The face with the result closest to 0 is chosen.
Parameters
----------
beam_a : :class:`~compas_timber.parts.Beam`
The beam that attaches with one of its ends to `beam_b`.
beam_b : :class:`~compas_timber.parts.Beam`
The other beam.
ignore_ends : bool, optional
If True, the faces at each end of `beam_b` are ignored.
Returns
-------
tuple(face_index, :class:`~compas.geometry.Frame`)
Tuple containing the index of the chosen face and a frame at the center of if.
"""
face_dict = Joint._beam_side_incidence(beam_a, beam_b, ignore_ends)
face_index = min(face_dict, key=face_dict.get) # type: ignore
return face_index, beam_b.faces[face_index]

@staticmethod
def _beam_side_incidence(beam_a, beam_b, ignore_ends=True):
"""Returns a map of face indices of beam_b and the angle of their normal with beam_a's centerline.
This is used to find a cutting plane when joining the two beams.
Parameters
----------
beam_a : :class:`~compas_timber.parts.Beam`
The beam that attaches with one of its ends to the side of beam_b.
beam_b : :class:`~compas_timber.parts.Beam`
The other beam.
ignore_ends : bool, optional
If True, only the first four faces of `beam_b` are considered. Otherwise all faces are considered.
Examples
--------
>>> face_angles = Joint.beam_side_incidence(beam_a, beam_b)
>>> closest_face_index = min(face_angles, key=face_angles.get)
>>> cutting_plane = beam_b.faces[closest_face_index]
Returns
-------
dict(int, float)
A map of face indices of beam_b and their respective angle with beam_a's centerline.
"""
# find the orientation of beam_a's centerline so that it's pointing outward of the joint
# find the closest end
p1x, _ = intersection_line_line(beam_a.centerline, beam_b.centerline)
if p1x is None:
raise AssertionError("No intersection found")

end, _ = beam_a.endpoint_closest_to_point(Point(*p1x))

if end == "start":
centerline_vec = beam_a.centerline.vector
else:
centerline_vec = beam_a.centerline.vector * -1

if ignore_ends:
beam_b_faces = beam_b.faces[:4]
else:
beam_b_faces = beam_b.faces

face_angles = {}
for face_index, face in enumerate(beam_b_faces):
face_angles[face_index] = angle_vectors(face.normal, centerline_vec)

return face_angles
Loading

0 comments on commit 5be528b

Please sign in to comment.