Skip to content

Commit

Permalink
Merge pull request #17 from compas-dev/maintenance-v0.1.x
Browse files Browse the repository at this point in the history
Maintenance v0.1.x
  • Loading branch information
tomvanmele authored Dec 1, 2017
2 parents d93376d + f89a603 commit 89b1350
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 41 deletions.
80 changes: 64 additions & 16 deletions src/compas/geometry/algorithms/hull_numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,56 @@ def convex_hull_numpy(points):
Indices of the points on the hull.
Faces of the hull.
Warning
-------
This function requires Numpy ands Scipy.
Notes
-----
The faces of the hull returned by this function do not necessarily have consistent
cycle directions. To obtain a mesh with consistent cycle directions, construct
a mesh from the returned vertices, this function should be used in combination
with :func:`compas.topology.unify_cycles`.
Examples
--------
.. code-block:: python
#
import random
from compas.datastructures import Mesh
from compas.geometry import distance_point_point
from compas.geometry import convex_hull_numpy
from compas.topology import unify_cycles
from compas.viewers import MeshViewer
radius = 5
origin = (0., 0., 0.)
count = 0
points = []
while count < 10:
x = (random.random() - 0.5) * radius * 2
y = (random.random() - 0.5) * radius * 2
z = (random.random() - 0.5) * radius * 2
pt = x, y, z
if distance_point_point(origin, pt) <= radius:
points.append(pt)
count += 1
vertices, faces = convex_hull_numpy(points)
i_index = {i: index for index, i in enumerate(vertices)}
vertices = [points[index] for index in vertices]
faces = [[i_index[i] for i in face] for face in faces]
faces = unify_cycles(vertices, faces)
mesh = Mesh.from_vertices_and_faces(vertices, faces)
viewer = MeshViewer(mesh)
viewer.setup()
viewer.show()
"""
points = asarray(points)
Expand Down Expand Up @@ -73,10 +114,18 @@ def convex_hull_xy_numpy(points):
Returns
-------
tuple
list
Indices of the points on the hull.
list
Faces of the hull.
Notes
-----
The faces of the hull returned by this function do not necessarily have consistent
cycle directions. To obtain a mesh with consistent cycle directions, construct
a mesh from the returned vertices, this function should be used in combination
with :func:`compas.topology.unify_cycles`.
Examples
--------
.. code-block:: python
Expand All @@ -91,9 +140,6 @@ def convex_hull_xy_numpy(points):

points = points[:, :2]
hull = ConvexHull(points)
# temp = zeros((hull.vertices.shape[0], 1))
# temp[:, :-1] = points[hull.vertices]
# return temp
return hull.vertices, hull.simplices


Expand All @@ -108,19 +154,19 @@ def convex_hull_xy_numpy(points):

import random

from compas.geometry.distance import distance_point_point
from compas.geometry import distance_point_point

from compas.datastructures import Mesh
from compas.viewers import MeshViewer

from compas.topology import mesh_unify_cycles
from compas.topology import unify_cycles

radius = 5
origin = (0., 0., 0.)
count = 0
points = []

while count < 10:
while count < 1000:
x = (random.random() - 0.5) * radius * 2
y = (random.random() - 0.5) * radius * 2
z = (random.random() - 0.5) * radius * 2
Expand All @@ -134,14 +180,16 @@ def convex_hull_xy_numpy(points):

i_index = {i: index for index, i in enumerate(vertices)}

mesh = Mesh.from_vertices_and_faces(
[points[index] for index in vertices],
[[i_index[i] for i in face] for face in faces[1:]]
)
vertices = [points[index] for index in vertices]
faces = [[i_index[i] for i in face] for face in faces]
faces = unify_cycles(vertices, faces)

mesh_unify_cycles(mesh)
mesh = Mesh.from_vertices_and_faces(vertices, faces)

viewer = MeshViewer(mesh)

viewer.axes_on = False
viewer.grid_on = False

viewer.setup()
viewer.show()
8 changes: 0 additions & 8 deletions src/compas/plotters/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@
__all__ = ['Plotter', ]


# https://matplotlib.org/faq/usage_faq.html#what-is-interactive-mode
# https://matplotlib.org/api/pyplot_summary.html
# https://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure
# https://matplotlib.org/api/axes_api.html
# https://matplotlib.org/api/index.html


class Plotter(object):
"""Definition of a plotter object based on matplotlib.
Expand Down Expand Up @@ -76,7 +69,6 @@ class Plotter(object):
Computing In Science & Engineering (9) 3, p.90-95.
Available at: http://ieeexplore.ieee.org/document/4160265/citations.
"""
def __init__(self, figsize=(16.0, 12.0), dpi=100.0, interactive=False, tight=False, **kwargs):
"""Initialises a plotter object"""
Expand Down
111 changes: 107 additions & 4 deletions src/compas/topology/orientation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from __future__ import absolute_import
from __future__ import division

from compas.utilities import pairwise
from compas.geometry import centroid_points
from compas.topology import breadth_first_traverse


Expand All @@ -12,12 +14,89 @@


__all__ = [
'face_adjacency',
'mesh_face_adjacency',
'unify_cycles',
'mesh_unify_cycles',
'mesh_flip_cycles',
]


def face_adjacency(mesh):
def face_adjacency(xyz, faces):
""""""
points = [centroid_points([xyz[index] for index in face]) for face in faces]

try:
from scipy.spatial import cKDTree

tree = cKDTree(points)
_, closest = tree.query(points, k=10, n_jobs=-1)

except Exception:
try:
import Rhino

except Exception:
from compas.geometry import KDTree

tree = KDTree(points)
closest = [tree.nearest_neighbours(point, 10) for point in points]
closest = [[index for _, index, _ in nnbrs] for nnbrs in closest]

else:
from Rhino.Geometry import RTree
from Rhino.Geometry import Sphere
from Rhino.Geometry import Point3d

tree = RTree()
for i, point in enumerate(points):
tree.Insert(Point3d(* point), i)

def callback(sender, e):
data = e.Tag
data.append(e.Id)

closest = []
for i, point in enumerate(points):
sphere = Sphere(Point3d(* point), 2.0)
data = []
tree.Search(sphere, callback, data)
closest.append(data)

adjacency = {}

for face, vertices in enumerate(faces):
nbrs = []
found = set()

nnbrs = set(closest[face])

for u, v in pairwise(vertices + vertices[0:1]):
for nbr in nnbrs:

if nbr == face:
continue
if nbr in found:
continue

for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
if v == a and u == b:
nbrs.append(nbr)
found.add(nbr)
break

for a, b in pairwise(faces[nbr] + faces[nbr][0:1]):
if u == a and v == b:
nbrs.append(nbr)
found.add(nbr)
break

adjacency[face] = nbrs

return adjacency


def mesh_face_adjacency(mesh):
"""Build a face adjacency dict.
Parameters
Expand Down Expand Up @@ -86,7 +165,7 @@ def callback(sender, e):
index = fkey_index[fkey]
found = set()

nnbrs = closest[index]
nnbrs = set(closest[index])

for u, v in mesh.face_halfedges(fkey):
for index in nnbrs:
Expand Down Expand Up @@ -114,6 +193,30 @@ def callback(sender, e):
return adjacency


def unify_cycles(vertices, faces, root=0):
""""""
def unify(node, nbr):
# find the common edge
for u, v in pairwise(faces[nbr] + faces[nbr][0:1]):
if u in faces[node] and v in faces[node]:
# node and nbr have edge u-v in common
i = faces[node].index(u)
j = faces[node].index(v)
if i == j - 1 or (j == 0 and u == faces[node][-1]):
# if the traversal of a neighbouring halfedge
# is in the same direction
# flip the neighbour
faces[nbr][:] = faces[nbr][::-1]
return

adj = face_adjacency(vertices, faces)

visited = breadth_first_traverse(adj, root, unify)

assert len(list(visited)) == len(faces), 'Not all faces were visited'
return faces


def mesh_unify_cycles(mesh, root=None):
"""Unify the cycle directions of all faces.
Expand All @@ -135,7 +238,7 @@ def unify(node, nbr):
# node and nbr have edge u-v in common
i = mesh.face[node].index(u)
j = mesh.face[node].index(v)
if i == j - 1:
if i == j - 1 or (j == 0 and u == mesh.face[node][-1]):
# if the traversal of a neighbouring halfedge
# is in the same direction
# flip the neighbour
Expand All @@ -145,7 +248,7 @@ def unify(node, nbr):
if root is None:
root = mesh.get_any_face()

adj = face_adjacency(mesh)
adj = mesh_face_adjacency(mesh)

visited = breadth_first_traverse(adj, root, unify)

Expand Down
1 change: 1 addition & 0 deletions src/compas/topology/traversal.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ def breadth_first_ordering(adjacency, root):


def breadth_first_traverse(adjacency, root, callback=None):
""""""
tovisit = deque([root])
visited = set([root])

Expand Down
24 changes: 12 additions & 12 deletions src/compas/viewers/meshviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,22 @@ def display(self):
'color' : (0.4, 0.4, 0.4),
'size' : 5.0})

normals = []
for fkey in self.mesh.faces():
n = self.mesh.face_normal(fkey, unitized=True)
sp = self.mesh.face_centroid(fkey)
ep = [sp[axis] + n[axis] for axis in (0, 1, 2)]
normals.append({
'start' : sp,
'end' : ep,
'color' : (0.0, 1.0, 0.0),
'width' : 2.0
})
# normals = []
# for fkey in self.mesh.faces():
# n = self.mesh.face_normal(fkey, unitized=True)
# sp = self.mesh.face_centroid(fkey)
# ep = [sp[axis] + n[axis] for axis in (0, 1, 2)]
# normals.append({
# 'start' : sp,
# 'end' : ep,
# 'color' : (0.0, 1.0, 0.0),
# 'width' : 2.0
# })

xdraw_polygons(polygons)
xdraw_lines(lines)
xdraw_points(points)
xdraw_lines(normals)
# xdraw_lines(normals)

def keypress(self, key, x, y):
"""
Expand Down
13 changes: 12 additions & 1 deletion src/compas/viewers/viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,27 @@ class Viewer(object):
The width of the viewer window.
height : int
The height of the viewer window.
clear_color : sequence(4)
near : float
Distance of the near clipping plane. Default is `0.1`.
far : float
Distance of the far clipping plane. Default is `1000.0`.
fov : float
Field of view. Default is `50.0`.
clear_color : 4-tuple of float
A sequence of 4 floats defining the background color of the scene.
Default is `(0.9, 0.9, 0.9, 1.0)`.
grid_on : bool
Grid on or off.
axes_on : bool
Grid on or off.
mouse : Mouse
A ``Mouse`` object.
camera : Camera
A ``Camera`` object.
grid : Grid
A ``Grid`` object.
displayfuncs : list of callable
A list of functions called by the display callback to render the scene.
Notes
-----
Expand Down

0 comments on commit 89b1350

Please sign in to comment.