Skip to content

Commit

Permalink
*.obj export for mesh
Browse files Browse the repository at this point in the history
  • Loading branch information
hiaselhans committed May 13, 2016
1 parent 275fed9 commit 6d24e26
Show file tree
Hide file tree
Showing 6 changed files with 342 additions and 242 deletions.
2 changes: 1 addition & 1 deletion openglider/glider/cell/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def quad(l_i, r_i):
r_i += 1
connection_info = {cell.rib1: numpy.array(ribs[0], int),
cell.rib2: numpy.array(ribs[-1], int)}
return Mesh(numpy.array(points), triangles, connection_info)
return Mesh(numpy.array(points), triangles, connection_info, self.material_code)

def mirror(self):
left, right = self.cut_front["left"], self.cut_front["right"]
Expand Down
20 changes: 20 additions & 0 deletions openglider/glider/glider.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from openglider.airfoil import Profile2D
from openglider.glider.in_out import IMPORT_GEOMETRY, EXPORT_3D
from openglider.glider.shape import Shape
from openglider.mesh import Mesh, MeshGroup
from openglider.plots.projection import flatten_list
from openglider.utils import consistent_value
from openglider.utils.distribution import Distribution
Expand Down Expand Up @@ -95,6 +96,25 @@ def rename_parts(self):
cell.name = self.cell_naming_scheme.format(cell=cell, cell_no=cell_no)
cell.rename_parts()

def get_panel_groups(self):
panels = {}
for cell in self.cells:
for panel in cell.panels:
panels.setdefault(panel.material_code, [])
panels[panel.material_code] += panel

return panels

def get_mesh(self, midribs=0):
meshed_ribs = [Mesh.from_rib(rib) for rib in self.ribs]
meshed_panels = []
for cell in self.cells:
for panel in cell.panels:
meshed_panels.append(panel.get_mesh(cell, midribs))

return MeshGroup(*meshed_ribs), MeshGroup(*meshed_panels)


def return_ribs(self, num=None, ballooning=True):
"""
Get a list of rib-curves
Expand Down
243 changes: 2 additions & 241 deletions openglider/mesh/__init__.py
Original file line number Diff line number Diff line change
@@ -1,241 +1,2 @@
from __future__ import division
import copy
import numpy as np
import meshpy.triangle as mptriangle
from openglider.mesh.meshpy_triangle import custom_triangulation



class Mesh(object):
"""
Mesh Surface: vertices and polygons
"""
def __init__(self, vertices=None, polygons=None, connection=None):
""" A mesh has vertices, polygons and connection information.
polygons can be:
line (2 indices)
triangle (3 vertices)
quadrangle (4 vertices)
"""
self.vertices = vertices # np array of all vertices
if self.vertices is None:
self.vertices = np.array([], float).reshape(0, 3)
self.polygons = polygons or [] # list of all element indices
self.connection = connection or {} # store all the connection info

@classmethod
def from_rib(cls, rib, hole_num=10, mesh_option="Qzip"):
""" Y... no additional points on boarder
i... algorythm (other algo crash)
p... triangulation
q... quality
a... area constraint
"""
profile = rib.profile_2d
triangle_in = {}
vertices = list(profile.data)[:-1]
connection = {rib: np.array(range(len(vertices)))}
segments = [[i, i+1] for i, _ in enumerate(vertices)]
segments[-1][-1] = 0
triangle_in["vertices"] = vertices
triangle_in["segments"] = segments

# adding the vertices and segments of the holes
# to get TRIANGLE know where to remove triangles
# a list of points which lay inside the holes
# must be passed
if len(rib.holes) > 0 and hole_num > 3:
triangle_in["holes"] = []
for nr, hole in enumerate(rib.holes):
start_index = len(triangle_in["vertices"])
hole_vertices = hole.get_flattened(rib, num=hole_num, scale=False)
segments = [[i + start_index, i + start_index + 1] for i, _ in enumerate(hole_vertices)]
segments[-1][-1] = start_index
triangle_in["vertices"] += hole_vertices
triangle_in["segments"] += segments
triangle_in["holes"].append(hole.get_center(rib, scale=False).tolist())

# _triangle_output = triangle.triangulate(triangle_in, "pziq")
mesh_info = mptriangle.MeshInfo()
mesh_info.set_points(triangle_in["vertices"])
mesh_info.set_facets(triangle_in["segments"])
if "holes" in triangle_in:
mesh_info.set_holes(triangle_in["holes"])
mesh = custom_triangulation(mesh_info, mesh_option) # see triangle options
try:
triangles = list(mesh.elements)
vertices = rib.align_all(mesh.points)
except KeyError:
print("there was a keyerror")
return cls()
return cls(vertices, triangles, connection)

@classmethod
def from_diagonal(cls, diagonal, cell, insert_points=4):
"""
get a mesh from a diagonal (2 poly lines)
"""
left, right = diagonal.get_3d(cell)
if insert_points:
point_array = []
number_array = []
# create array of points
# the outermost points build the segments
n_l = len(left)
n_r = len(right)
count = 0
for y_pos in np.linspace(0., 1., insert_points + 2):
# from left to right
point_line = []
number_line = []
num_points = int(n_l * (1. - y_pos) + n_r * y_pos)
for x_pos in np.linspace(0., 1., num_points):
point_line.append(left[x_pos * (n_l - 1)] * (1. - y_pos) +
right[x_pos * (n_r - 1)] * y_pos)
number_line.append(count)
count += 1
point_array.append(point_line)
number_array.append(number_line)
edge = number_array[0]
edge += [line[-1] for line in number_array[1:]]
edge += number_array[-1][-2::-1] # last line reversed without the last element
edge += [line[0] for line in number_array[1:-1]][::-1]
segment = [[edge[i], edge[i +1]] for i in range(len(edge) - 1)]
segment.append([edge[-1], edge[0]])
point_array = np.array([point for line in point_array for point in line])
points2d = map_to_2d(point_array)

mesh_info = meshpy_triangle.MeshInfo()
mesh_info.set_points(points2d)
mesh = custom_triangulation(mesh_info, "Qz")
return cls(point_array, list(mesh.elements))

else:
vertices = np.array(list(left) + list(right)[::-1])
polygon = [range(len(vertices))]
return cls(vertices, polygon)


def copy(self):
poly_copy = [p[:] for p in self.polygons]
return self.__class__(self.vertices, poly_copy)

def triangularize(self):
"""
Make triangles from quads
"""
faces_new = []
for face in self.polygons:
if len(face) == 3:
faces_new.append(face)
elif len(face) == 4:
faces_new.append(face[:3])
faces_new.append(face[2:] + face[:1])

return self.__class__(self.vertices, faces_new)

def __json__(self):
return {
"vertices": self.vertices.tolist(),
"polygons": self.polygons
}

def __add__(self, other):
"""adding two mesh objects"""
# copy the mesh, otherwise the input mesh get changed
new_mesh = Mesh(copy.copy(self.vertices),
copy.copy(self.polygons),
copy.copy(self.connection))
# translate to a python list for easier manipulation
vertices_list = self.vertices.tolist()
count = len(vertices_list)
# connection information stored in both meshes
intersections = (set(self.connection.keys()) &
set(other.connection.keys()))
# create a map from connection info
connect_rules = dict()
for intersect in intersections:
for j, index in enumerate(other.connection[intersect]):
connect_rules[index] = self.connection[intersect][j]
# mapping other.vertices
replace_rules = dict()
for i, vertex in enumerate(other.vertices):
value = connect_rules.get(i)
if value is not None:
replace_rules[i] = value
count -= 1
else:
replace_rules[i] = i+count
vertices_list.append(vertex)
# apply the replacement rules
new_mesh.polygons += [
[replace_rules[index] for index in pol] for pol in other.polygons]
new_mesh.vertices = np.array(vertices_list)

for key in other.connection.keys():
if key not in intersections:
new_mesh.connection[key] = [
replace_rules[value] for value in other.connection[key]]
return new_mesh

def __iadd__(self, other):
self = self + other
return self

def delete_duplicates(self, min_dist=10**(-10)):
"""delete points that are close to each other"""
replace_dict = {}
for i, point_i in enumerate(self.vertices[:-1]):
if not i in replace_dict:
for j, point_j in enumerate(self.vertices[i+1:]):
_j = j + i + 1
if not _j in replace_dict:
if np.linalg.norm(point_j - point_i) < min_dist:
replace_dict[_j] = i
for j, point_j in enumerate(self.vertices):
if j not in replace_dict:
replace_dict[j] = j
self.apply_rules(replace_dict)
self.delete_vertices_not_used()

def apply_rules(self, rules):
"""apply a dict of replacement rules to the polygons"""
self.polygons = [[rules[index] for index in pol] for pol in self.polygons]
new_connection_info = {}
for key in self.connection:
new_connection_info[key] = [rules[i] for i in self.connection[key]]
self.connection = new_connection_info

def delete_vertices_not_used(self):
"""delete all vertices not used in the polygons"""
sorted_indices = list(sorted(set(index for pol in self.polygons for index in pol)))#
self.apply_rules({value: j for j, value in enumerate(sorted_indices)})
self.vertices = np.array([point for j, point in enumerate(self.vertices) if j in sorted_indices])


def apply_z(vertices):
v = vertices.T
return np.array([v[0], np.zeros(len(v[0]), v[1])]).T



def map_to_2d(points):
""" map points to 2d least square plane
min([x, y, z, 1] * [A, B, C, D].T)"""
mat = np.array(points).T
mat = np.array([mat[0], mat[1], mat[2], np.ones(len(mat[0]))])
u, d, v = np.linalg.svd(mat.T)
n = v[-1][0:3]
l_n = np.linalg.norm(n)
n /= l_n
x = np.cross(n, n[::-1])
y = np.cross(n, x)
to_2d_mat = np.array([x, y]).T
return np.array(points).dot(to_2d_mat)


if __name__ == "__main__":
a = Mesh(np.array([[0,0,0], [0,1,0], [1,0,0]]), [[0,1,2]])
b = Mesh(np.array([[0,0,0], [0,1,0], [-1,0,0]]), [[0,2,1]])
c = a + b
c.delete_duplicates(0.1)
from openglider.mesh.mesh import Mesh
from openglider.mesh.group import MeshGroup
52 changes: 52 additions & 0 deletions openglider/mesh/group.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from openglider.mesh.mesh import Mesh


class MeshGroup(object):
def __init__(self, *objects):
self.objects = list(objects)

def __repr__(self):
text = "MeshGroup: [\n"
for obj in self.objects:
text += "\t{},\n".format(obj)
text += "]"
return text

def __iadd__(self, other):
assert isinstance(other, MeshGroup)
self.objects += other.objects
return self

def append(self, obj):
self.objects.append(obj)

def group_materials(self, join=False):
by_material = {}
for obj in self.objects:
by_material.setdefault(obj.name, self.__class__())
by_material[obj.name].append(obj)

if join:
for key, group in by_material.items():
by_material[key] = group.join()

return by_material

def join(self):
mesh = Mesh()
for obj in self.objects:
mesh += obj
return mesh

def export_obj(self, filepath=None):
offset = 0
out = ""
for obj in self.objects:
out += obj.export_obj(offset=offset)
offset += len(obj.vertices)

if filepath is not None:
with open(filepath, "w") as outfile:
outfile.write(out)

return out
Loading

0 comments on commit 6d24e26

Please sign in to comment.