Skip to content

Commit

Permalink
Added triangulation of bow and stern if needed (instead of added a ve…
Browse files Browse the repository at this point in the history
…rtex at the center)

Added some convenience methods for working on frames (x,y, scaled)
  • Loading branch information
RubendeBruin committed Aug 6, 2024
1 parent d365a2b commit ad10ec8
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 25 deletions.
62 changes: 61 additions & 1 deletion src/pymeshup/frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import numpy as np

from pymeshup.helpers.triangulate_non_convex import triangulate_poly


class Frame():
"""Frames are 2d slices
Expand Down Expand Up @@ -38,6 +41,20 @@ def __init__(self, *args):

self.xy = tuple(self.xy) # make immutable

@classmethod
def from_xy(cls,x, y):
"""Construct a Frame using my_frame = Frame.from_xy(x,y)"""
return cls(*[a for pair in zip(x, y) for a in pair])

def scaled(self, x=1, y=1):
"""Returns a scaled copy of the frame"""

new_x = [x * xx for xx in self.x]
new_y = [y * yy for yy in self.y]


return Frame.from_xy(new_x, new_y)

def copy(self):
f = Frame(0,0) # use dummy data
f.xy = copy(self.xy)
Expand Down Expand Up @@ -83,6 +100,33 @@ def center(self):
def n(self):
return len(self.xy)

@property
def x(self):
return [f[0] for f in self.xy]

@x.setter
def x(self, value):
assert len(value) == self.n, "Length of x should be equal to the number of points"

# update self.xy
self._set_xy(value, self.y)


@property
def y(self):
return [f[1] for f in self.xy]

@y.setter
def y(self, value):
assert len(value) == self.n, "Length of y should be equal to the number of points"

# update self.xy
self._set_xy(self.x, value)

def _set_xy(self, x, y):
self.xy = tuple([(x[i], y[i]) for i in range(self.n)])


def as_vertices_at(self, x):
"""Returns 3d points (vertices) if this frame is located in 3d at x=given"""
return [(x, p[0], p[1]) for p in self.xy]
Expand All @@ -96,4 +140,20 @@ def is_identical_to(self, other):
if self.xy[i] != other.xy[i]:
return False

return True
return True

def to_plane_at(self, x, invert_normal=False):
"""Returns the vertices and faces for a plane at x"""

vertices = self.as_vertices_at(x)

if len(vertices) < 3:
return [], []

if invert_normal:
vertices = vertices[::-1]

vertices, faces = triangulate_poly(vertices)

return vertices, faces

6 changes: 3 additions & 3 deletions src/pymeshup/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,11 +385,11 @@ def run(self):
key_before = [v for v in locals().keys()]

try:
f = StringIO()
with redirect_stdout(f):
_output_redirect = StringIO()
with redirect_stdout(_output_redirect):
exec(code)

s = f.getvalue()
s = _output_redirect.getvalue()
self.ui.teFeedback.setPlainText(s)
self.ui.teFeedback.append("..Done!")

Expand Down
69 changes: 69 additions & 0 deletions src/pymeshup/helpers/triangulate_non_convex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from vtkmodules.vtkCommonCore import vtkIdList, vtkPoints
from vtkmodules.vtkCommonDataModel import vtkCellArray
from vtkmodules.vtkFiltersGeneral import vtkContourTriangulator


def triangulate_poly(vertices):
"""Triangulates a polygon defined by vertices. Polygons can be concave
Returns
"""
out_faces = []

out_points = list(vertices)

# make a vtk polydata object from these points
vtkPts = vtkPoints()
for p in out_points:
vtkPts.InsertNextPoint(*p)

# add the polygon to the polydata
cellArray = vtkCellArray()

# use vtkContourTriangulator to triangulate the polygon
idList = vtkIdList()
for i in range(len(vertices)):
idList.InsertNextId(i)

result = vtkContourTriangulator.TriangulatePolygon(idList, vtkPts,cellArray)

if result != 1:
raise ValueError("Triangulation failed")


# extract the vertices from cellArray
cellArray.InitTraversal()

while True:
idList = vtkIdList()
if cellArray.GetNextCell(idList):
corners = []

for i in range(idList.GetNumberOfIds()):
# corners.append(vtkPts.GetPoint(idList.GetId(i)))
corners.append(idList.GetId(i))

out_faces.append(corners)
else:
break

return out_points, out_faces


if __name__ == '__main__':
verts = [(0, 0, 0), (0, 0.1, 0), (0, 0.1, 0), (0, 1, 0), (0, 1, 0), (0, -1, 0), (0, -1, 0), (0, -0.1, 0), (0, -0.1, 0),
(0, 0, 0)]

verts, faces = triangulate_poly(verts)

import matplotlib.pyplot as plt

for tri in faces:
# get vertices
t = [verts[i] for i in tri]
t.append(t[0])

plt.plot([p[0] for p in t], [p[1] for p in t], 'r-')

plt.show()
47 changes: 31 additions & 16 deletions src/pymeshup/hull.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,19 +162,19 @@ def Hull(*args):
frames.append(f)
x.append(xx)

# add one-frames at start and end, if needed
# The frames are always closed
# n is the number of vertices, where the first is identical to the last
# so n=3 means two unique vertices --> a line
if frames[0].n > 3:
stern = Frame(*frames[0].center())
frames.insert(0,stern)
x.insert(0, x[0])

if frames[-1].n > 3:
bow = Frame(*frames[-1].center())
frames.append(bow)
x.append(x[-1])
# # add one-frames at start and end, if needed
# # The frames are always closed
# # n is the number of vertices, where the first is identical to the last
# # so n=3 means two unique vertices --> a line
# if frames[0].n > 3:
# stern = Frame(*frames[0].center())
# frames.insert(0,stern)
# x.insert(0, x[0])
#
# if frames[-1].n > 3:
# bow = Frame(*frames[-1].center())
# frames.append(bow)
# x.append(x[-1])

vertices = []
faces = []
Expand All @@ -192,6 +192,13 @@ def Hull(*args):


# print("---------------")

# mesh (frame0)

verts, face_ids = frames[0].to_plane_at(x[0])
vertices.extend(verts)
faces.extend(face_ids)

for i in range(len(frames)-1):

f1 = frames[i]
Expand All @@ -201,8 +208,6 @@ def Hull(*args):
vertices2 = f2.as_vertices_at(x[i+1])

# print(f"Building triangles between {len(vertices1)} and {len(vertices2)} vertices")


verts, face_ids = build_triangles(vertices1,vertices2)

# correct face_ids for t
Expand All @@ -212,6 +217,16 @@ def Hull(*args):
vertices.extend(verts)
faces.extend(faces_corrected)

# mesh last frame
verts, face_ids = frames[-1].to_plane_at(x[-1], invert_normal=True)

nva = len(vertices)
faces_corrected = [(a[0] + nva, a[1] + nva, a[2] + nva) for a in face_ids]
faces.extend(faces_corrected)

vertices.extend(verts)


v = Volume()
v.set_vertices_and_faces(vertices, faces)
v.ms.meshing_remove_duplicate_vertices()
Expand All @@ -229,7 +244,7 @@ def Hull(*args):
if v.volume < 0:
v = v.invert_normals()

return v.simplify()
return v

def hull_from_file(filename) -> Volume:
"""Reads a hull from a file:
Expand Down
10 changes: 5 additions & 5 deletions src/pymeshup/volumes.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,10 @@ def simplify(self):
from vtk import vtkDecimatePro

# # triangulate first
# from vtk import vtkTriangleFilter
# tri = vtkTriangleFilter()
# tri.SetInputData(pd)
# tri.Update()
from vtk import vtkTriangleFilter
tri = vtkTriangleFilter()
tri.SetInputData(pd)
tri.Update()

decimate = vtkDecimatePro()
decimate.SetInputData(pd)
Expand Down Expand Up @@ -266,7 +266,7 @@ def Plot(v : Volume or list[Volume]):
faces = m.ms.current_mesh().face_matrix()
m2 = vedo.Mesh([vertices, faces])
m2.actor.GetProperty().SetColor(COLORMAP(icol % 20)[:3])
m2.actor.GetProperty().SetOpacity(0.5)
# m2.actor.GetProperty().SetOpacity(0.5)
p.add(m2)

p.show(axes=1, viewup='z')
95 changes: 95 additions & 0 deletions tests/test_frames.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
from numpy.testing import assert_allclose
from vtkmodules.vtkCommonCore import vtkPoints, vtkIdList
from vtkmodules.vtkCommonDataModel import vtkCellArray
from vtkmodules.vtkFiltersGeneral import vtkContourTriangulator

from pymeshup import *
from pytest import raises

from pymeshup.helpers.triangulate_non_convex import triangulate_poly


def test_create():
f = Frame(1,2,3,4)

def test_scaled():
f = Frame(1, 2, 3, 4)
f2 = f.scaled(2, 3)


def test_create_wrong1():
with raises(ValueError):
Expand Down Expand Up @@ -37,3 +47,88 @@ def test_zero_frame():
def test_onepoint_frame():
f = Frame(1,2)
assert_allclose(f.xy, [(1,2)])

def test_mesh_frame():
import numpy as np

points = np.array([
[0, 0], [1, 0], [1, 1], [2, 1],[2,0],[4,0],[4,2],[3,1.7],[0,2],[0,0]
])

f = Frame(*points.flatten())

points, faces = f.to_plane_at(0)

import matplotlib.pyplot as plt

for tri in faces:

# get vertices
t = [points[i] for i in tri]
t.append(t[0])


plt.plot([p[1] for p in t], [p[2] for p in t], 'r-')

plt.show()

def test_frame_to_plane4():
f = Frame(0,0,
1,0,
1,1,
0,1)

vertices, faces = f.to_plane_at(0)

def test_frame_to_plane3():
f = Frame(0, 0,
1, 0,
1, 1)

vertices, faces = f.to_plane_at(0)

#
#
# # make a vtk polydata object from these points
# vtkPts = vtkPoints()
# for p in points:
# vtkPts.InsertNextPoint(p[0], p[1], 0)
#
# # add the polygon to the polydata
# cellArray = vtkCellArray()
#
# # use vtkContourTriangulator to triangulate the polygon
# idList = vtkIdList()
# for i in range(len(points)):
# idList.InsertNextId(i)
#
# triangulator = vtkContourTriangulator.TriangulatePolygon(idList, vtkPts,cellArray)
#
# import matplotlib.pyplot as plt
#
# # extract the vertices from cellArray
# cellArray.InitTraversal()
# while True:
# idList = vtk.vtkIdList()
# if cellArray.GetNextCell(idList):
# print("Triangle")
#
# corners = []
#
# for i in range(idList.GetNumberOfIds()):
#
# print(vtkPts.GetPoint(idList.GetId(i)))
#
# corners.append(vtkPts.GetPoint(idList.GetId(i)))
#
# corners.append(corners[0])
#
# plt.plot([c[0] for c in corners], [c[1] for c in corners],'r-', lw=4)
#
# else:
# break
#
# plt.plot([p[0] for p in points], [p[1] for p in points], 'b-')
#
# plt.show()

0 comments on commit ad10ec8

Please sign in to comment.