Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added cylinder primitive #328

Merged
merged 6 commits into from
Oct 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
199 changes: 163 additions & 36 deletions fury/primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

SCIPY_1_4_PLUS = LooseVersion(short_version) >= LooseVersion('1.4.0')


SPHERE_FILES = {
'symmetric362': pjoin(DATA_DIR, 'evenly_distributed_sphere_362.npz'),
'symmetric642': pjoin(DATA_DIR, 'evenly_distributed_sphere_642.npz'),
Expand Down Expand Up @@ -182,7 +181,7 @@ def normalize_input(arr, arr_name=''):
directions = normalize_input(directions, 'directions')
for pts, dirs in enumerate(directions):
w = np.cos(0.5 * np.pi)
denom = np.linalg.norm(dirs/2.)
denom = np.linalg.norm(dirs / 2.)
f = (np.sin(0.5 * np.pi) / denom) if denom else 0
dirs = np.append((dirs / 2.) * f, w)
rot = transform.Rotation.from_quat(dirs)
Expand Down Expand Up @@ -335,6 +334,7 @@ def prim_superquadric(roundness=(1, 1), sphere_name='symmetric362'):
True

"""

def _fexp(x, p):
"""Return a different kind of exponentiation."""
return np.sign(x) * (np.abs(x) ** p)
Expand Down Expand Up @@ -609,22 +609,22 @@ def prim_octagonalprism():
# to 7 decimal places
two = float('{:.7f}'.format(math.sqrt(2)))

vertices = np.array([[-1, -(1+two), -1],
[1, -(1+two), -1],
[1, (1+two), -1],
[-1, (1+two), -1],
[-(1+two), -1, -1],
[(1+two), -1, -1],
[(1+two), 1, -1],
[-(1+two), 1, -1],
[-1, -(1+two), 1],
[1, -(1+two), 1],
[1, (1+two), 1],
[-1, (1+two), 1],
[-(1+two), -1, 1],
[(1+two), -1, 1],
[(1+two), 1, 1],
[-(1+two), 1, 1]])
vertices = np.array([[-1, -(1 + two), -1],
[1, -(1 + two), -1],
[1, (1 + two), -1],
[-1, (1 + two), -1],
[-(1 + two), -1, -1],
[(1 + two), -1, -1],
[(1 + two), 1, -1],
[-(1 + two), 1, -1],
[-1, -(1 + two), 1],
[1, -(1 + two), 1],
[1, (1 + two), 1],
[-1, (1 + two), 1],
[-(1 + two), -1, 1],
[(1 + two), -1, 1],
[(1 + two), 1, 1],
[-(1 + two), 1, 1]])
triangles = np.array([[0, 8, 9],
[9, 1, 0],
[5, 13, 9],
Expand Down Expand Up @@ -670,25 +670,152 @@ def prim_frustum():

"""
vertices = np.array([[-.5, -.5, .5],
[.5, -.5, .5],
[.5, .5, .5],
[-.5, .5, .5],
[-1, -1, -.5],
[1, -1, -.5],
[1, 1, -.5],
[-1, 1, -.5]])
[.5, -.5, .5],
[.5, .5, .5],
[-.5, .5, .5],
[-1, -1, -.5],
[1, -1, -.5],
[1, 1, -.5],
[-1, 1, -.5]])
triangles = np.array([[4, 6, 5],
[6, 4, 7],
[0, 2, 1],
[2, 0, 3],
[4, 3, 0],
[3, 4, 7],
[7, 2, 3],
[2, 7, 6],
[6, 1, 2],
[1, 6, 5],
[5, 0, 1],
[0, 5, 4]], dtype='u8')
[6, 4, 7],
[0, 2, 1],
[2, 0, 3],
[4, 3, 0],
[3, 4, 7],
[7, 2, 3],
[2, 7, 6],
[6, 1, 2],
[1, 6, 5],
[5, 0, 1],
[0, 5, 4]], dtype='u8')
vertices /= 2
triangles = fix_winding_order(vertices, triangles, clockwise=True)
return vertices, triangles


def prim_cylinder(radius=0.5, height=1, sectors=36, capped=True):
"""Return vertices and triangles for a cylinder.

Parameters
----------
radius: float
Radius of the cylinder
height: float
Height of the cylinder
sectors: int
Sectors in the cylinder
capped: bool
Whether the cylinder is capped at both ends or open

Returns
-------
vertices: ndarray
vertices coords that compose our cylinder
triangles: ndarray
triangles that compose our cylinder
"""

if not isinstance(sectors, int):
tushar5526 marked this conversation as resolved.
Show resolved Hide resolved
raise TypeError("Only integers are allowed for sectors parameter")
if not sectors > 7:
raise ValueError("Sectors parameter should be greater than 7")
sector_step = 2 * math.pi / sectors
unit_circle_vertices = []

# generate a unit circle on XY plane
for i in range(sectors + 1):
sector_angle = i * sector_step
unit_circle_vertices.append(math.cos(sector_angle))
unit_circle_vertices.append(0)
unit_circle_vertices.append(math.sin(sector_angle))

vertices = []
# generate vertices for a cylinder
for i in range(2):
h = -height / 2 + i * height
k = 0
for j in range(sectors + 1):
ux = unit_circle_vertices[k]
uz = unit_circle_vertices[k + 2]
# position vector
vertices.append(ux * radius)
vertices.append(h)
vertices.append(uz * radius)
k += 3

# base and top circle vertices
base_center_index = None
top_center_index = None

if capped:
base_center_index = int(len(vertices) / 3)
top_center_index = base_center_index + sectors + 1

for i in range(2):
h = -height / 2 + i * height
vertices.append(0)
vertices.append(h)
vertices.append(0)
k = 0
for j in range(sectors):
ux = unit_circle_vertices[k]
uz = unit_circle_vertices[k + 2]
# position vector
vertices.append(ux * radius)
vertices.append(h)
vertices.append(uz * radius)
k += 3

if capped:
vertices = (np.array(vertices).reshape(2 * (sectors + 1) + 2 * sectors + 2, 3))
else:
vertices = (np.array(vertices).reshape(2 * (sectors + 1), 3))

triangles = []
k1 = 0
k2 = sectors + 1

# triangles for the side surface
for i in range(sectors):
triangles.append(k1)
triangles.append(k2)
triangles.append(k1 + 1)

triangles.append(k2)
triangles.append(k2 + 1)
triangles.append(k1 + 1)
k1 += 1
k2 += 1

if capped:
k = base_center_index + 1
for i in range(sectors):
if i < sectors - 1:
triangles.append(base_center_index)
triangles.append(k)
triangles.append(k + 1)
else:
triangles.append(base_center_index)
triangles.append(k)
triangles.append(base_center_index + 1)
k += 1

k = top_center_index + 1
for i in range(sectors):
if i < sectors - 1:
triangles.append(top_center_index)
triangles.append(k + 1)
triangles.append(k)
else:
triangles.append(top_center_index)
triangles.append(top_center_index + 1)
triangles.append(k)
k += 1

if capped:
triangles = (np.array(triangles).reshape(4 * sectors, 3))
else:
triangles = (np.array(triangles).reshape(2 * sectors, 3))

return vertices, triangles
22 changes: 22 additions & 0 deletions fury/tests/test_primitive.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,28 @@ def test_superquadric_primitives():
# TODO: We need to check some superquadrics shape


def test_cylinder_primitive():
verts, faces = fp.prim_cylinder(radius=.5, height=1, sectors=10)
npt.assert_equal(verts.shape, (44, 3))
npt.assert_almost_equal(np.mean(verts), 0, decimal=1)
npt.assert_equal(verts.min(), -.5)
npt.assert_equal(verts.max(), .5)

# basic tests for triangle
npt.assert_equal(faces.shape, (40, 3))
npt.assert_equal(np.unique(np.concatenate(faces, axis=None)).tolist(),
list(range(len(verts))))

verts, faces = fp.prim_cylinder(radius=.5, height=1, sectors=10, capped=False)
npt.assert_equal(verts.shape, (22, 3))
npt.assert_almost_equal(np.mean(verts), 0, decimal=1)
npt.assert_equal(verts.min(), -.5)
npt.assert_equal(verts.max(), .5)
npt.assert_equal(np.unique(np.concatenate(faces, axis=None)).tolist(),
list(range(len(verts))))



def test_repeat_primitive():
# init variables
verts, faces = fp.prim_square()
Expand Down