Skip to content

Commit

Permalink
Merge pull request #1415 from compas-dev/frame-inversion
Browse files Browse the repository at this point in the history
Frame inversion
  • Loading branch information
tomvanmele authored Dec 13, 2024
2 parents 347e5ec + cadad46 commit 67f52ea
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 5 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

* Added implementation of `RhinoBrep.fillet()` and `RhinoBrep.filleted()` to `compas_rhino`.
* Added `Frame.invert` and `Frame.inverted`.
* Added `Frame.flip` and `Frame.flipped` as alias for invert and inverted.
* Added `Vector.flip` and `Vector.flipped` as alias for invert and inverted.

### Changed

* Fixed `native_edge` property of `RhinoBrepEdge`.
* Expose the parameters `radius` and `nmax` from `compas.topology._face_adjacency` to `compas.topology.face_adjacency` and further propagate them to `unify_cycles` and `Mesh.unify_cycles`.
* Modify `face_adjacency` to avoid using `compas.topology._face_adjacency` by default when there are more than 100 faces, unless one of the parameters `radius`, `nmax` is passed.
* Changed `unify_cycles` to use the first face in the list as root if no root is provided.

### Removed

Expand All @@ -32,8 +38,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

* Fixed `PluginNotInstalledError` when using `Brep.from_boolean_*` in Rhino.
* Expose the parameters `radius` and `nmax` from `compas.topology._face_adjacency` to `compas.topology.face_adjacency` and further propagate them to `unify_cycles` and `Mesh.unify_cycles`.
* Modify `face_adjacency` to avoid using `compas.topology._face_adjacency` by default when there are more than 100 faces, unless one of the parameters `radius`, `nmax` is passed
* Added support for `Polyline` as input for `compas_rhino.Brep.from_extrusion`.

### Removed
Expand Down
15 changes: 15 additions & 0 deletions src/compas/geometry/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,21 @@ def to_transformation(self):
# Methods
# ==========================================================================

def invert(self):
"""Invert the frame while keeping the X axis fixed."""
self._yaxis = self.yaxis * -1
self._zaxis = None

flip = invert

def inverted(self):
"""Return an inverted copy of the frame."""
frame = self.copy() # type: Frame
frame.invert()
return frame

flipped = inverted

def interpolate_frame(self, other, t):
"""Interpolates between two frames at a given parameter t in the range [0, 1]
Expand Down
4 changes: 4 additions & 0 deletions src/compas/geometry/vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,8 @@ def invert(self):
"""
self.scale(-1.0)

flip = invert

def inverted(self):
"""Returns a inverted copy of this vector
Expand All @@ -697,6 +699,8 @@ def inverted(self):
"""
return self.scaled(-1.0)

flipped = inverted

def scale(self, n):
"""Scale this vector by a factor n.
Expand Down
10 changes: 7 additions & 3 deletions src/compas/topology/orientation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
from __future__ import division
from __future__ import print_function

import random

from compas.geometry import centroid_points
from compas.itertools import pairwise
from compas.topology import breadth_first_traverse
Expand Down Expand Up @@ -193,6 +191,11 @@ def unify_cycles(vertices, faces, root=None, nmax=None, max_distance=None):
Exception
If no all faces are included in the unnification process.
Notes
-----
The cycles of the faces will be aligned with the cycle direction of the root face.
If no root face is specified, the first face in the list will be used.
"""

def unify(node, nbr):
Expand All @@ -210,7 +213,8 @@ def unify(node, nbr):
return

if root is None:
root = random.choice(list(range(len(faces))))
# root = random.choice(list(range(len(faces))))
root = 0

adj = face_adjacency(vertices, faces, nmax=nmax, max_distance=max_distance) # this is the only place where the vertex coordinates are used

Expand Down
32 changes: 32 additions & 0 deletions tests/compas/geometry/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,35 @@ def test_interpolate_frame_start_end():

three_quarter_frame = frame1.interpolate_frame(frame2, 0.75)
assert TOL.is_allclose([math.degrees(three_quarter_frame.axis_angle_vector.y)], [-67.5], atol=TOL.angular)


def test_frame_invert():
frame = Frame([0, 0, 0])

assert TOL.is_close(frame.xaxis.dot([1, 0, 0]), 1.0)
assert TOL.is_close(frame.yaxis.dot([0, 1, 0]), 1.0)
assert TOL.is_close(frame.zaxis.dot([0, 0, 1]), 1.0)

frame.invert()

assert TOL.is_close(frame.xaxis.dot([1, 0, 0]), 1.0)
assert TOL.is_close(frame.yaxis.dot([0, -1, 0]), 1.0)
assert TOL.is_close(frame.zaxis.dot([0, 0, -1]), 1.0)


def test_frame_inverted():
frame = Frame([0, 0, 0])

assert TOL.is_close(frame.xaxis.dot([1, 0, 0]), 1.0)
assert TOL.is_close(frame.yaxis.dot([0, 1, 0]), 1.0)
assert TOL.is_close(frame.zaxis.dot([0, 0, 1]), 1.0)

other = frame.inverted()

assert TOL.is_close(frame.xaxis.dot([1, 0, 0]), 1.0)
assert TOL.is_close(frame.yaxis.dot([0, 1, 0]), 1.0)
assert TOL.is_close(frame.zaxis.dot([0, 0, 1]), 1.0)

assert TOL.is_close(other.xaxis.dot([1, 0, 0]), 1.0)
assert TOL.is_close(other.yaxis.dot([0, -1, 0]), 1.0)
assert TOL.is_close(other.zaxis.dot([0, 0, -1]), 1.0)
4 changes: 4 additions & 0 deletions tests/compas/topology/test_unify_cycles.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
def test_unify_cycles():
if compas.IPY:
return

test_data = compas.json_load(os.path.join(HERE, "..", "fixtures", "topology", "vertices_faces.json"))
vertices = test_data["vertices"]
faces = test_data["faces"]
Expand All @@ -25,12 +26,15 @@ def test_unify_cycles():
# no parameters
unify_cycles(vertices, faces)
assert TOL.is_close(volume, volume_polyhedron((vertices, faces)))

# only max_nbrs
unify_cycles(vertices, faces, nmax=max_nbrs)
assert TOL.is_close(volume, volume_polyhedron((vertices, faces)))

# only max_distance
unify_cycles(vertices, faces, max_distance=max_edge_length)
assert TOL.is_close(volume, volume_polyhedron((vertices, faces)))

# both parameters
unify_cycles(vertices, faces, nmax=max_nbrs, max_distance=max_edge_length)
assert TOL.is_close(volume, volume_polyhedron((vertices, faces)))
Expand Down

0 comments on commit 67f52ea

Please sign in to comment.