Skip to content

Commit fff824b

Browse files
committed
add units to curves
1 parent cb5761a commit fff824b

File tree

13 files changed

+122
-57
lines changed

13 files changed

+122
-57
lines changed

openglider/glider/cell/attachment_point.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from openglider.glider.shape import Shape
88
from openglider.lines.node import Node, NODE_TYPE_ENUM
99
from openglider.utils import table
10-
from openglider.vector.unit import Percentage
10+
from openglider.vector.unit import Length, Percentage
1111

1212
if TYPE_CHECKING:
1313
from openglider.glider.cell.cell import Cell
@@ -21,7 +21,7 @@ class CellAttachmentPoint(Node):
2121
rib_pos: Percentage
2222
node_type: NODE_TYPE_ENUM = Node.NODE_TYPE.UPPER
2323
ballooned: bool=False
24-
offset: float = 0.
24+
offset: Length = 0.
2525

2626
def __repr__(self) -> str:
2727
return f"<Attachment point '{self.name}' ({self.rib_pos})>"

openglider/glider/curve.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,25 @@
55

66
from openglider.glider.shape import Shape
77
from openglider.utils.cache import cached_property
8+
from openglider.vector.unit import Angle, Length, Percentage, Quantity
89

9-
class FreeCurve:
10+
class CurveBase:
11+
unit: str | None
12+
13+
def to_unit(self, value: float):
14+
if self.unit is None:
15+
return value
16+
17+
if self.unit in Angle.unit_variants or self.unit == Angle.unit:
18+
return Angle(value, unit=self.unit)
19+
if self.unit in Length.unit_variants or self.unit == Length.unit:
20+
return Length(value, unit=self.unit)
21+
if self.unit in Percentage.unit_variants:
22+
return Percentage(value, self.unit)
23+
24+
raise ValueError()
25+
26+
class FreeCurve(CurveBase):
1027
def __init__(self, points: list[euklid.vector.Vector2D], shape: Shape):
1128
self.shape = shape
1229
self.interpolation = euklid.vector.Interpolation(points)
@@ -67,11 +84,12 @@ def to_controlpoints(self, points: list[euklid.vector.Vector2D]) -> list[euklid.
6784
def points_2d(self) -> list[euklid.vector.Vector2D]:
6885
return self.to_2d(self.interpolation.nodes)
6986

70-
def get(self, rib_no: int) -> float:
87+
def get(self, rib_no: int) -> float | Quantity:
7188
if rib_no == 0 and self.shape.has_center_cell:
7289
rib_no = 1
7390

74-
return self.interpolation.get_value(rib_no)
91+
value = self.interpolation.get_value(rib_no)
92+
return self.to_unit(value)
7593

7694
def draw(self) -> euklid.vector.PolyLine2D:
7795
x_values = [p[0] for p in self.controlpoints]
@@ -92,8 +110,8 @@ def draw(self) -> euklid.vector.PolyLine2D:
92110
return euklid.vector.PolyLine2D(self.to_2d([euklid.vector.Vector2D([x, self.interpolation.get_value(x)]) for x in x_values_lst]))
93111

94112

95-
class Curve:
96-
upper = False
113+
class Curve(CurveBase):
114+
unit: str
97115
def __init__(self, points: list[euklid.vector.Vector2D], shape: Shape):
98116
self.interpolation = euklid.vector.Interpolation(points)
99117
self.shape = shape
@@ -154,16 +172,13 @@ def points_2d(self) -> euklid.vector.PolyLine2D:
154172
self.shape.get_point(*p) for p in self.interpolation.nodes
155173
])
156174

157-
def get(self, rib_no: int) -> float:
175+
def get(self, rib_no: int) -> float | Quantity:
158176
if rib_no == 0 and self.shape.has_center_cell:
159177
rib_no = 1
160178

161179
y = self.interpolation.get_value(rib_no)
162180

163-
if self.upper:
164-
y = -y
165-
166-
return y
181+
return self.to_unit(y)
167182

168183
def draw(self) -> euklid.vector.PolyLine2D:
169184
x_values = [p[0] for p in self.controlpoints]
@@ -191,8 +206,7 @@ def draw(self) -> euklid.vector.PolyLine2D:
191206

192207

193208
class ShapeCurve(Curve):
194-
195-
def get(self, rib_no: int) -> float:
209+
def get(self, rib_no: int) -> float | Quantity:
196210
if rib_no == 0 and self.shape.has_center_cell:
197211
rib_no = 1
198212

@@ -203,7 +217,7 @@ def get(self, rib_no: int) -> float:
203217
if len(results) != 1:
204218
raise Exception(f"wrong number of cut results: {len(results)}")
205219

206-
return results[0][1]
220+
return self.to_unit(results[0][1])
207221

208222

209223
class ShapeBSplineCurve(ShapeCurve):

openglider/glider/parametric/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22
from typing import Any, ClassVar, Self
33

4+
from packaging.version import Version
45
import euklid
56
import pydantic
67

@@ -121,7 +122,7 @@ class ParametricGliderConfig(ConfigTable):
121122

122123
use_sag: bool = True
123124

124-
version: str = __version__
125+
version: Version = Version(__version__)
125126

126127
@classmethod
127128
def __from_json__(cls, **data: Any) -> Self:

openglider/glider/parametric/export_ods.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,6 @@ def add_curve(name: str, curve: CurveType, column_no: int) -> None:
185185
add_curve("rib_distribution", glider.shape.rib_distribution, 4)
186186
add_curve("arc", glider.arc.curve, 6)
187187
add_curve("aoa", glider.aoa, 8)
188-
add_curve("zrot", glider.zrot, 10)
189188
add_curve("ballooning_merge_curve", glider.ballooning_merge_curve, 12)
190189
add_curve("profile_merge_curve", glider.profile_merge_curve, 14)
191190

openglider/glider/parametric/glider.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from openglider.utils.distribution import Distribution
2929
from openglider.utils.table import Table
3030
from openglider.utils.types import CurveType, SymmetricCurveType
31-
from openglider.vector.unit import Percentage
31+
from openglider.vector.unit import Percentage, Quantity
3232

3333
if TYPE_CHECKING:
3434
from openglider.glider.curve import GliderCurveType
@@ -53,7 +53,6 @@ class ParametricGlider:
5353
speed: float
5454
glide: float
5555
tables: GliderTables = Field(default_factory=lambda: GliderTables())
56-
zrot: SymmetricCurveType = Field(default_factory=lambda: euklid.spline.SymmetricBSplineCurve([[0,0],[1,0]]))
5756

5857
num_interpolate: int=30
5958
num_profile: int | None=None
@@ -278,7 +277,7 @@ def resolvers(self) -> list[Parser]:
278277
parsers = []
279278
curves=self.get_curves()
280279

281-
def resolver_factory(rib_no: int) -> Callable[[str], float]:
280+
def resolver_factory(rib_no: int) -> Callable[[str], float | Quantity]:
282281
def resolve(name: str) -> float:
283282
if name not in curves:
284283
raise ValueError(
@@ -386,7 +385,6 @@ def get_glider_3d(self, glider: Glider=None, num: int=50, num_profile: int | Non
386385
shape_ribs = self.shape.ribs
387386

388387
aoa_values = self.get_aoa()
389-
zrot_int = euklid.vector.Interpolation(self.zrot.get_sequence(num).nodes)
390388

391389
arc_pos = self.arc.get_arc_positions(x_values).tolist()
392390
rib_angles = self.arc.get_rib_angles(x_values)
@@ -421,16 +419,18 @@ def get_glider_3d(self, glider: Glider=None, num: int=50, num_profile: int | Non
421419
}
422420
data.update(self.tables.rib_modifiers.get_rib_args(rib_no, resolvers=resolvers))
423421

422+
rotation = self.tables.rib_modifiers.get_rotation(rib_no, resolvers=resolvers)
423+
424424
rib = Rib(
425425
profile_2d=profile,
426426
pos=startpoint,
427427
chord=chord,
428428
arcang=rib_angles[rib_no],
429-
xrot=self.tables.rib_modifiers.get_xrot(rib_no),
429+
xrot=rotation.x or self.tables.rib_modifiers.get_xrot(rib_no),
430430
offset=self.tables.rib_modifiers.get_offset(rib_no, resolvers=resolvers),
431431
glide=self.glide,
432432
aoa_absolute=aoa_values[rib_no],
433-
zrot=zrot_int.get_value(abs(x_value)),
433+
zrot=rotation.z,
434434
holes=this_rib_holes,
435435
rigidfoils=this_rigid_foils,
436436
name=f"rib{rib_no}",
@@ -568,5 +568,4 @@ def rescale(curve: CurveType) -> None:
568568
rescale(self.ballooning_merge_curve)
569569
rescale(self.profile_merge_curve)
570570
rescale(self.aoa)
571-
rescale(self.zrot)
572571
self.arc.rescale(self.shape.rib_x_values)

openglider/glider/parametric/import_ods.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import math
55
import numbers
66
from typing import TYPE_CHECKING
7+
from packaging.version import Version
78

89
import euklid
910
import pyfoil
@@ -72,7 +73,7 @@ def import_ods_glider(cls: type[ParametricGlider], tables: list[Table]) -> Param
7273
# profiles = [BezierProfile2D(profile) for profile in transpose_columns(sheets[3])]
7374
profiles = [pyfoil.Airfoil(profile, name).normalized() for name, profile in transpose_columns(tables[3])]
7475

75-
if config.version > "0.0.1":
76+
if config.version > Version("0.0.1"):
7677
has_center_cell = not tables[0]["C2"] == 0
7778
cell_no = (tables[0].num_rows - 2) * 2 + has_center_cell
7879
geometry = get_geometry_parametric(table_dct[TableNames.parametric_data], cell_no, config)
@@ -88,7 +89,8 @@ def import_ods_glider(cls: type[ParametricGlider], tables: list[Table]) -> Param
8889
migrate_header = cell_sheet[0, 0] is not None and cell_sheet[0, 0] < "V4"
8990

9091
glider_tables = GliderTables()
91-
glider_tables.curves = CurveTable(table_dct.get("Curves", None))
92+
glider_tables.curves = CurveTable(table_dct.get("Curves", None), config.version)
93+
9294
glider_tables.cuts = CutTable(cell_sheet, migrate_header=migrate_header)
9395
glider_tables.ballooning_modifiers = BallooningModifierTable(cell_sheet, migrate_header=migrate_header)
9496
glider_tables.holes = HolesTable(rib_sheet, migrate_header=migrate_header)
@@ -121,7 +123,6 @@ class Geometry(BaseModel):
121123
shape: ParametricShape
122124
arc: ArcCurve
123125
aoa: SymmetricCurveType
124-
zrot: SymmetricCurveType
125126
profile_merge_curve: SymmetricCurveType
126127
ballooning_merge_curve: SymmetricCurveType
127128

@@ -135,7 +136,6 @@ def get_geometry_explicit(sheet: Table, config: ParametricGliderConfig) -> Geome
135136
arc = []
136137
profile_merge = []
137138
ballooning_merge = []
138-
zrot = []
139139

140140
y = z = span_last = alpha = 0.
141141
for i in range(1, sheet.num_rows):
@@ -162,8 +162,6 @@ def get_geometry_explicit(sheet: Table, config: ParametricGliderConfig) -> Geome
162162
profile_merge.append([span, line[8]])
163163
ballooning_merge.append([span, line[9]])
164164

165-
zrot.append([span, line[7] * math.pi / 180])
166-
167165
span_last = span
168166

169167
def symmetric_fit(data: list[list[float]], bspline: bool=True) -> SymmetricCurveType:
@@ -200,7 +198,6 @@ def symmetric_fit(data: list[list[float]], bspline: bool=True) -> SymmetricCurve
200198
shape=parametric_shape,
201199
arc=arc_curve,
202200
aoa=symmetric_fit(aoa),
203-
zrot=symmetric_fit(zrot),
204201
profile_merge_curve=symmetric_fit(profile_merge, bspline=True),
205202
ballooning_merge_curve=symmetric_fit(ballooning_merge, bspline=True)
206203
)
@@ -214,13 +211,16 @@ def get_geometry_parametric(table: Table, cell_num: int, config: ParametricGlide
214211
"rib_distribution": euklid.spline.BezierCurve,
215212
"arc": euklid.spline.SymmetricBSplineCurve,
216213
"aoa": euklid.spline.SymmetricBSplineCurve,
217-
"zrot": euklid.spline.SymmetricBSplineCurve,
218214
"profile_merge_curve": euklid.spline.SymmetricBSplineCurve,
219215
"ballooning_merge_curve": euklid.spline.SymmetricBSplineCurve
220216
}
221217

222218
for column in range(0, table.num_columns, 2):
223219
key = table[0, column]
220+
if key not in curve_types:
221+
if key == "zrot":
222+
continue
223+
raise ValueError("Invalid curve: {key}")
224224
points = []
225225

226226
if table[0, column+1] is not None:

openglider/glider/parametric/table/base/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def default_resolver(key: str) -> float:
3030

3131
class Parser(BaseModel):
3232
units: list[type[Quantity]] = Field(default_factory=default_units.copy)
33-
variable_resolver: Callable[[str], float] = default_resolver
33+
variable_resolver: Callable[[str], float | Quantity] = default_resolver
3434

3535
_parser: Forward | None = pydantic.PrivateAttr(default=None)
3636
_units: dict[str, type[Quantity]] | None = pydantic.PrivateAttr(default=None)

openglider/glider/parametric/table/base/table.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from openglider.glider.parametric.table.base.dto import DTO
1010
from openglider.glider.parametric.table.base.parser import Parser
1111
from openglider.utils.table import Table
12+
from openglider.vector.unit import Quantity
1213

1314
from .keyword import Keyword
1415

@@ -110,7 +111,7 @@ def get(self, row_no: int, keywords: list[str] | None=None, **kwargs: Any) -> li
110111
return elements
111112

112113
@staticmethod
113-
def get_curve_value(curves: dict[str, GliderCurveType] | None, curve_name: str | float, rib_no: int) -> float:
114+
def get_curve_value(curves: dict[str, GliderCurveType] | None, curve_name: str | float, rib_no: int) -> float | Quantity:
114115
if curves is None:
115116
raise ValueError("No curves specified")
116117

openglider/glider/parametric/table/curve.py

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from __future__ import annotations
22

3-
from typing import Any
3+
from typing import Any, Type
4+
from packaging.version import Version
45
from openglider.glider.parametric.table.base import TableType
56

7+
from openglider.version import version
68
from openglider.utils.table import Table
79
from openglider.glider.shape import Shape
810
import openglider.glider.curve
@@ -12,8 +14,15 @@ class CurveTable:
1214
table_type = TableType.general
1315
table_name = "Curves"
1416

15-
def __init__(self, table: Table=None):
16-
self.table = table or Table()
17+
def __init__(self, table: Table | None=None, openglider_version: Version=version):
18+
if table is None:
19+
self.table = Table()
20+
else:
21+
self.table = table or Table()
22+
23+
if openglider_version < Version("0.1.3"):
24+
self.table = migrate_0_1_2(self.table)
25+
1726
self.table.name = self.table_name
1827

1928
def __json__(self) -> dict[str, Any]:
@@ -27,11 +36,13 @@ def get_curves(self, shape: Shape) -> dict[str, openglider.glider.curve.GliderCu
2736
curve_columns = 2
2837

2938
while column < self.table.num_columns:
30-
name = self.table[0, column]
31-
curve_type = self.table[0, column + 1] or "Curve"
39+
name = self.table[0, column+1]
40+
curve_type = self.table[1, column+1] or "Curve"
41+
curve_unit = self.table[2, column+1]
42+
3243
points = []
3344

34-
for row in range(1, self.table.num_rows):
45+
for row in range(3, self.table.num_rows):
3546
coords = [self.table[row, column+i] for i in range(curve_columns)]
3647

3748
if any([c is None for c in coords]):
@@ -40,10 +51,13 @@ def get_curves(self, shape: Shape) -> dict[str, openglider.glider.curve.GliderCu
4051
points.append(coords)
4152

4253
try:
43-
curve_cls = getattr(openglider.glider.curve, curve_type)
54+
curve_cls: Type[openglider.glider.curve.Curve] = getattr(openglider.glider.curve, curve_type)
4455
except Exception:
4556
raise Exception(f"invalid curve type: {curve_type}")
46-
curves[name] = curve_cls(points, shape)
57+
curve_instance = curve_cls(points, shape)
58+
curve_instance.unit = curve_unit
59+
60+
curves[name] = curve_instance
4761

4862
column += curve_columns
4963

@@ -67,3 +81,20 @@ def apply_curves(self, curves: dict[str, openglider.glider.curve.GliderCurveType
6781

6882

6983

84+
def migrate_0_1_2(table: Table) -> Table:
85+
new_table = Table()
86+
87+
column = 0
88+
while column < table.num_columns:
89+
new_column = Table()
90+
new_column.insert_row(["Name", table[0, column]])
91+
new_column.insert_row(["Type", table[0, column+1]])
92+
new_column.insert_row(["Unit", None])
93+
old_column = table.get_columns(column, column+2)
94+
95+
new_column.append_bottom(old_column.get_rows(1, old_column.num_rows))
96+
97+
new_table.append_right(new_column)
98+
column += 2
99+
100+
return new_table

0 commit comments

Comments
 (0)