Python. Как получить координаты x, y смещенного сплайна из списка точек x и y и смещения

Мне нужно создать параллельный корпус с коррекцией профиля профиля аэродинамического профиля, но я не могу понять, как сделать все точки равноудаленными точками на кривой основного профиля на нужном расстоянии.

это мой пример профиля аэродинамического профиля

это мой лучший и нехороший подход

EDIT @Patrick Solution для расстояния 0,2

2 ответа

У вас будут специальные склоны бесконечности/ноль, но основной подход - использовать интерполяцию для вычисления наклона в точке, а затем найти перпендикулярный наклон, а затем вычислить точку на этом расстоянии.

Я изменил пример из здесь, чтобы добавить второй график. Он работает с файлом данных, который вы предоставили, но вам может потребоваться изменить расчет знака для другого конверта.

РЕДАКТИРОВАТЬ. В соответствии с вашими комментариями о том, что конверт будет непрерывным, я добавил в конце дрянной полукруг, который очень близок к тому, чтобы делать это за вас. По сути, при создании конверта, круглее и более выпуклый, вы можете это сделать, тем лучше он будет работать. Кроме того, вам нужно пересечь начало и конец, иначе у вас будет пробел.

Кроме того, это почти наверняка можно было бы сделать более эффективным - я не эксперт по количеству, каким-то образом, так что это просто чистый Python.

def offset(coordinates, distance):
 coordinates = iter(coordinates)
 x1, y1 = coordinates.next()
 z = distance
 points = []
 for x2, y2 in coordinates:
 # tangential slope approximation
 try:
 slope = (y2 - y1) / (x2 - x1)
 # perpendicular slope
 pslope = -1/slope # (might be 1/slope depending on direction of travel)
 except ZeroDivisionError:
 continue
 mid_x = (x1 + x2) / 2
 mid_y = (y1 + y2) / 2
 sign = ((pslope > 0) == (x1 > x2)) * 2 - 1
 # if z is the distance to your parallel curve,
 # then your delta-x and delta-y calculations are:
 # z**2 = x**2 + y**2
 # y = pslope * x
 # z**2 = x**2 + (pslope * x)**2
 # z**2 = x**2 + pslope**2 * x**2
 # z**2 = (1 + pslope**2) * x**2
 # z**2 / (1 + pslope**2) = x**2
 # z / (1 + pslope**2)**0.5 = x
 delta_x = sign * z / ((1 + pslope**2)**0.5)
 delta_y = pslope * delta_x
 points.append((mid_x + delta_x, mid_y + delta_y))
 x1, y1 = x2, y2
 return points
def add_semicircle(x_origin, y_origin, radius, num_x = 50):
 points = []
 for index in range(num_x):
 x = radius * index / num_x
 y = (radius ** 2 - x ** 2) ** 0.5
 points.append((x, -y))
 points += [(x, -y) for x, y in reversed(points)]
 return [(x + x_origin, y + y_origin) for x, y in points]
def round_data(data):
 # Add infinitesimal rounding of the envelope
 assert data[-1] == data[0]
 x0, y0 = data[0]
 x1, y1 = data[1]
 xe, ye = data[-2]
 x = x0 - (x0 - x1) * .01
 y = y0 - (y0 - y1) * .01
 yn = (x - xe) / (x0 - xe) * (y0 - ye) + ye
 data[0] = x, y
 data[-1] = x, yn
 data.extend(add_semicircle(x, (y + yn) / 2, abs((y - yn) / 2)))
 del data[-18:]
from pylab import *
with open('ah79100c.dat', 'rb') as f:
 f.next()
 data = [[float(x) for x in line.split()] for line in f if line.strip()]
t = [x[0] for x in data]
s = [x[1] for x in data]
round_data(data)
parallel = offset(data, 0.1)
t2 = [x[0] for x in parallel]
s2 = [x[1] for x in parallel]
plot(t, s, 'g', t2, s2, 'b', lw=1)
title('Wing with envelope')
grid(True)
axes().set_aspect('equal', 'datalim')
savefig("test.png")
show()


Если вы захотите (и сможете) установить сторонний инструмент, я настоятельно рекомендую модуль Shapely. Здесь небольшой образец, который смещает как внутрь, так и наружу:

from StringIO import StringIO
import matplotlib.pyplot as plt
import numpy as np
import requests
import shapely.geometry as shp
# Read the points 
AFURL = 'http://m-selig.ae.illinois.edu/ads/coord_seligFmt/ah79100c.dat'
afpts = np.loadtxt(StringIO(requests.get(AFURL).content), skiprows=1)
# Create a Polygon from the nx2 array in `afpts`
afpoly = shp.Polygon(afpts)
# Create offset airfoils, both inward and outward
poffafpoly = afpoly.buffer(0.03) # Outward offset
noffafpoly = afpoly.buffer(-0.03) # Inward offset
# Turn polygon points into numpy arrays for plotting
afpolypts = np.array(afpoly.exterior)
poffafpolypts = np.array(poffafpoly.exterior)
noffafpolypts = np.array(noffafpoly.exterior)
# Plot points
plt.plot(*afpolypts.T, color='black')
plt.plot(*poffafpolypts.T, color='red')
plt.plot(*noffafpolypts.T, color='green')
plt.axis('equal')
plt.show()

А вот выход; обратите внимание, как автоматически удаляются "лупы" (самопересечения) на внутреннем смещении:

licensed under cc by-sa 3.0 with attribution.