# ! /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 copy
import numpy
import openglider
from openglider.vector.spline import BSpline
from openglider.vector.interpolate import Interpolation
[docs]class ArcSinc:
def __init__(self):
self.start = 0.
self.end = numpy.pi
self.arsinc = None
def __call__(self, val):
if self.arsinc is None:
self.interpolate(openglider.config['asinc_interpolation_points'])
return self.arsinc(val)
[docs] def interpolate(self, numpoints):
data = []
for i in range(numpoints + 1):
phi = self.end + (i * 1. / numpoints) * (self.start - self.end) # reverse for interpolation (increasing x_values)
data.append([numpy.sinc(phi / numpy.pi), phi])
self.arsinc = Interpolation(data)
@property
def numpoints(self):
return len(self.arsinc.x)
@numpoints.setter
def numpoints(self, numpoints):
self.interpolate(numpoints)
[docs]class Ballooning(object):
arcsinc = ArcSinc()
def __init__(self, f_upper, f_lower):
self.upper = f_upper
self.lower = f_lower
def __json__(self):
return {'f_upper': self.upper,
'f_lower': self.lower}
def __getitem__(self, xval):
"""Get Ballooning Value (%) for a certain XValue"""
if -1 <= xval < 0:
#return self.upper.xpoint(-xval)[1]
return self.upper(-xval)
elif 0 <= xval <= 1:
#return -self.lower.xpoint(xval)[1]
return self.lower(xval)
else:
raise ValueError("Ballooning only between -1 and 1")
def __call__(self, arg):
"""Get Ballooning Arc (phi) for a certain XValue"""
return self.phi(1. / (self[arg] + 1))
def __add__(self, other):
"""Add another Ballooning to this one, needed for merging purposes"""
upper = []
for point in self.upper.data:
upper.append([point[0], point[1]+other.upper(point[0])])
lower = []
for point in self.lower.data:
lower.append([point[0], point[1]+other.lower(point[0])])
return Ballooning(Interpolation(upper), Interpolation(lower))
def __imul__(self, val):
for point in self.upper.data:
point[1] *= val
for point in self.lower.data:
point[1] *= val
return self
def __mul__(self, value):
"""Multiply Ballooning With a Value"""
new = self.copy()
new *= value
return new
[docs] def copy(self):
return copy.deepcopy(self)
@classmethod
[docs] def phi(cls, *baloon):
"""
Return the angle of the piece of cake.
b/l=R*phi/(R*Sin(phi)) -> Phi=arsinc(l/b)
"""
return cls.arcsinc(baloon)
[docs] def mapx(self, xvals):
return [self[i] for i in xvals]
@property
def amount_maximal(self):
return max(max([p[1] for p in self.upper]), max([p[1] for p in self.lower]))
@property
def amount_integral(self):
# Integration of 2-points always:
amount = 0
for curve in [self.upper, self.lower]:
for p1, p2 in zip(curve[:-1], curve[1:]):
# points: (x1,y1), (x2,y2)
# _ p2
# p1_/ |
# | |
# |___|
amount += (p1[1] + (p2[1]-p1[1])/2) * (p2[0]-p1[0])
return amount / 2
@amount_maximal.setter
def amount_maximal(self, amount):
factor = float(amount) / self.amount_maximal
self.scale(factor)
[docs] def scale(self, factor):
self.upper.scale(1, factor)
self.lower.scale(1, factor)
def _repr_svg_(self):
import svgwrite
import svgwrite.container
height = self.amount_maximal * 2
drawing = svgwrite.Drawing(size=[800, 800*height])
drawing.viewbox(0, -height/2, 1, height)
g = svgwrite.container.Group()
g.scale(1, -1)
upper = drawing.polyline(self.upper.data, style="stroke:black; vector-effect: non-scaling-stroke; fill: none;")
lower = drawing.polyline([(p[0], -p[1]) for p in self.lower.data], style="stroke:black; vector-effect: non-scaling-stroke; fill: none;")
g.add(upper)
g.add(lower)
drawing.add(g)
return drawing.tostring()
[docs]class BallooningBezier(Ballooning):
def __init__(self, upper=None, lower=None, name="ballooning"):
super(BallooningBezier, self).__init__(None, None)
upper = upper or [[0, 0], [0.1, 0], [0.2, 0.14], [0.8, 0.14], [0.9, 0], [1, 0]]
lower = lower or [[0, 0], [0.1, 0], [0.2, 0.14], [0.8, 0.14], [0.9, 0], [1, 0]]
self.upper_spline = BSpline(upper)
self.lower_spline = BSpline(lower)
self.name = name
self.apply_splines()
def __json__(self):
return {"upper": [p.tolist() for p in self.upper_spline.controlpoints],
"lower": [p.tolist() for p in self.lower_spline.controlpoints]}
@property
def points(self):
upper = list(self.upper_spline.get_sequence())
lower = map(lambda x: [x[0], -x[1]], self.lower_spline.get_sequence()[::-1])
return upper + lower
[docs] def apply_splines(self):
self.upper = self.upper_spline.interpolation()
self.lower = self.lower_spline.interpolation()
def __imul__(self, factor): # TODO: Check consistency
"""Multiplication of BezierBallooning"""
# Multiplicate as normal interpolated ballooning, then refit
Ballooning.__imul__(self, factor)
# print("JO")
self.upper_spline.fit(self.upper.data)
self.lower_spline.fit(self.lower.data)
return self
@property
def numpoints(self):
return len(self.upper)
@numpoints.setter
def numpoints(self, numpoints):
Ballooning.__init__(self, self.upper_spline.interpolation(numpoints), self.lower_spline.interpolation(numpoints))
@property
def controlpoints(self):
return self.upper_spline.controlpoints, self.lower_spline.controlpoints
@controlpoints.setter
def controlpoints(self, controlpoints):
upper, lower = controlpoints
if upper is not None:
self.upper_spline.controlpoints = upper
if lower is not None:
self.lower_spline.controlpoints = lower
Ballooning.__init__(self, self.upper_spline.interpolation(), self.lower_spline.interpolation())
[docs] def scale(self, factor):
super(BallooningBezier, self).scale(factor)
self.upper_spline.scale(1, factor)
self.lower_spline.scale(1, factor)