#! /usr/bin/python2
# -*- coding: utf-8; -*-
#
# (c) 2013 booya (http://booya.at)
#
# This file is part of the OpenGlider project.
#
# OpenGlider is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# OpenGlider is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OpenGlider. If not, see <http://www.gnu.org/licenses/>.
import math
import copy
import numpy
from openglider import Profile2D
from openglider.glider.in_out import IMPORT_GEOMETRY, EXPORT_3D
from openglider.vector import norm
from openglider.plots.projection import flatten_list
[docs]class Glider(object):
def __init__(self):
self.cells = []
self.data = {}
[docs] def import_geometry(self, path, filetype=None):
if not filetype:
filetype = path.split(".")[-1]
IMPORT_GEOMETRY[filetype](path, self)
[docs] def export_geometry(self, path="", filetype=None):
#if not filetype:
# filetype = path.split(".")[-1]
#EXPORT_GEOMETRY[filetype](self, path)
pass
[docs] def export_3d(self, path="", filetype=None, midribs=0, numpoints=None, floatnum=6):
if not filetype:
filetype = path.split(".")[-1]
EXPORT_3D[filetype](self, path, midribs, numpoints, floatnum)
[docs] def return_ribs(self, num=0):
if not self.cells:
return numpy.array([])
num += 1
#will hold all the points
ribs = []
#print(len(self.cells))
for cell in self.cells:
for y in range(num):
ribs.append(cell.midrib(y * 1. / num).data)
ribs.append(self.cells[-1].midrib(1.).data)
return ribs
[docs] def return_polygons(self, num=0):
if not self.cells:
return numpy.array([]), numpy.array([])
ribs = self.return_ribs(num)
num += 1
numpoints = len(ribs[0]) # points per rib
ribs = numpy.concatenate(ribs) # ribs was [[point1[x,y,z],[point2[x,y,z]],[point1[x,y,z],point2[x,y,z]]]
polygons = []
for i in range(len(self.cells) * num): # without +1, because we use i+1 below
for k in range(numpoints - 1): # same reason as above
polygons.append(
[i * numpoints + k, i * numpoints + k + 1, (i + 1) * numpoints + k + 1, (i + 1) * numpoints + k])
return polygons, ribs
[docs] def close_rib(self, rib=-1):
self.ribs[rib].profile_2d *= 0.
self.ribs[rib].recalc()
[docs] def get_midrib(self, y=0):
k = y % 1
i = int(y - k)
if i == len(self.cells) and k == 0: # Stabi-rib
i -= 1
k = 1
return self.cells[i].midrib_basic_cell(k)
[docs] def mirror(self, cutmidrib=True, complete=False):
# lets assume we have at least one cell to mirror :)
if self.cells[0].rib1.pos[1] != 0 and cutmidrib: # Cut midrib
self.cells = self.cells[1:]
for rib in self.ribs:
rib.mirror()
for cell in self.cells:
cell.rib1, cell.rib2 = cell.rib2, cell.rib1
self.cells = self.cells[::-1]
[docs] def recalc(self):
# TODO: make obsolete
for cell in self.cells:
cell.recalc()
[docs] def copy(self):
return copy.deepcopy(self)
[docs] def copy_complete(self):
"""Returns a mirrored and combined copy of the glider, ready for export/view"""
other = self.copy()
other2 = self.copy()
other2.mirror()
other2.cells[-1].rib2 = other.cells[0].rib1
other2.cells = other2.cells + other.cells
return other2
[docs] def scale(self, faktor):
for rib in self.ribs:
rib.pos *= faktor
rib.chord *= faktor
@property
[docs] def shape(self):
return flatten_list(self.get_spanwise(0), self.get_spanwise(1))
@property
[docs] def ribs(self):
if not self.cells:
return []
return [self.cells[0].rib1] + [cell.rib2 for cell in self.cells]
@property
def numpoints(self):
numpoints = self.ribs[0].profile_2d.numpoints
for rib in self.ribs:
if not rib.profile_2d.numpoints == numpoints:
raise ValueError("Numpoints vary for the glider")
return numpoints
@numpoints.setter
[docs] def numpoints(self, numpoints):
xvalues = Profile2D.calculate_x_values(numpoints)
for rib in self.ribs:
rib.profile_2d.x_values = xvalues
# TODO: check consistency
@property
[docs] def x_values(self):
return self.ribs[0].profile_2d.x_values
@property
def span(self):
span = 0.
front = self.get_spanwise()
last = front[0] * [0, 0, 1] # centerrib only halfed
for this in front[1:]:
span += norm((this - last) * [0, 1, 1])
last = this
return 2 * span
@span.setter
[docs] def span(self, span):
faktor = span / self.span
self.scale(faktor)
@property
def area(self):
area = 0.
if len(self.ribs) == 0:
return 0
front = self.get_spanwise(0)
back = self.get_spanwise(1)
front[0][1] = 0 # Get only half a midrib, if there is...
back[0][1] = 0
for i in range(len(front) - 1):
area += norm(numpy.cross(front[i] - front[i + 1], back[i + 1] - front[i + 1]))
area += norm(numpy.cross(back[i] - back[i + 1], back[i] - front[i]))
# By this we get twice the area of half the glider :)
# http://en.wikipedia.org/wiki/Triangle#Using_vectors
return area
@area.setter
[docs] def area(self, area):
faktor = area / self.area
self.scale(math.sqrt(faktor))
@property
def aspect_ratio(self):
return self.span ** 2 / self.area
@aspect_ratio.setter
[docs] def aspect_ratio(self, aspect_ratio):
area_backup = self.area
factor = self.aspect_ratio / aspect_ratio
for rib in self.ribs:
rib.chord *= factor
rib.recalc()
self.area = area_backup
[docs] def get_spanwise(self, x=None):
points = []
if x:
for rib in self.ribs:
points.append(rib.align([x, 0, 0]))
else:
points = [rib.pos for rib in self.ribs] # This is much faster
return points