flightplotting 0.2.7__tar.gz → 0.2.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flightplotting-0.2.7 → flightplotting-0.2.9}/PKG-INFO +1 -1
- flightplotting-0.2.9/flightplotting/plots.py +371 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting/traces.py +12 -13
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting.egg-info/PKG-INFO +1 -1
- flightplotting-0.2.7/flightplotting/plots.py +0 -285
- {flightplotting-0.2.7 → flightplotting-0.2.9}/.github/workflows/publish_pypi.yml +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/.gitignore +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/.vscode/settings.json +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/COPYING +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/MANIFEST.in +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/README.md +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting/__init__.py +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting/data/ColdDraftF3APlane.obj +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting/data/__init__.py +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting/model.py +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting/templates.py +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting/titlerenderer.py +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting.egg-info/SOURCES.txt +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting.egg-info/dependency_links.txt +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting.egg-info/requires.txt +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/flightplotting.egg-info/top_level.txt +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/pyproject.toml +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/requirements.txt +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/setup.cfg +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/tests/__init__.py +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/tests/data/__init__.py +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/tests/data/p23_flight.json +0 -0
- {flightplotting-0.2.7 → flightplotting-0.2.9}/tests/test_plots.py +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import plotly.graph_objects as go
|
|
2
|
+
from plotly.subplots import make_subplots
|
|
3
|
+
import plotly.express as px
|
|
4
|
+
import flightplotting.templates
|
|
5
|
+
from flightplotting.traces import (
|
|
6
|
+
tiptrace,
|
|
7
|
+
meshes,
|
|
8
|
+
control_input_trace,
|
|
9
|
+
axis_rate_trace,
|
|
10
|
+
aoa_trace,
|
|
11
|
+
cgtrace,
|
|
12
|
+
ribbon,
|
|
13
|
+
vectors,
|
|
14
|
+
axestrace,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from flightdata import State
|
|
18
|
+
from flightdata.base.labeling import get_appended_id
|
|
19
|
+
from geometry import Coord
|
|
20
|
+
from flightplotting.model import obj
|
|
21
|
+
import numpy.typing as npt
|
|
22
|
+
import numpy as np
|
|
23
|
+
import pandas as pd
|
|
24
|
+
from typing import List, Union
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def plotsec(
|
|
28
|
+
secs: State | list[State] | dict[str, State],
|
|
29
|
+
scale=5,
|
|
30
|
+
nmodels=0,
|
|
31
|
+
fig=None,
|
|
32
|
+
color: Union[str, list[str]] = None,
|
|
33
|
+
cg=False,
|
|
34
|
+
width=None,
|
|
35
|
+
height=None,
|
|
36
|
+
show_axes=False,
|
|
37
|
+
ribb: bool = False,
|
|
38
|
+
tips: bool = True,
|
|
39
|
+
origin=False,
|
|
40
|
+
):
|
|
41
|
+
traces = []
|
|
42
|
+
keys = None
|
|
43
|
+
if isinstance(secs, State):
|
|
44
|
+
secs = [secs]
|
|
45
|
+
|
|
46
|
+
if isinstance(secs, dict):
|
|
47
|
+
keys = list(secs.keys())
|
|
48
|
+
secs = list(secs.values())
|
|
49
|
+
showkeys = True
|
|
50
|
+
else:
|
|
51
|
+
keys = list(range(len(secs)))
|
|
52
|
+
showkeys = False
|
|
53
|
+
|
|
54
|
+
for i, sec in enumerate(secs):
|
|
55
|
+
text = sec.data.t #- sec.data.t.iloc[0]
|
|
56
|
+
_color = color if color is not None else px.colors.qualitative.Plotly[i]
|
|
57
|
+
if ribb:
|
|
58
|
+
traces += ribbon(sec, scale * 1.85, _color, name=keys[i])
|
|
59
|
+
if tips:
|
|
60
|
+
traces += tiptrace(sec, scale * 1.85, text=text, name=keys[i])
|
|
61
|
+
if nmodels > 0:
|
|
62
|
+
traces += meshes(nmodels, sec, _color, scale)
|
|
63
|
+
if cg:
|
|
64
|
+
traces.append(cgtrace(sec, line=dict(color=_color, width=2), name=keys[i], text=text))
|
|
65
|
+
|
|
66
|
+
if origin:
|
|
67
|
+
traces += axestrace(Coord.zero(), 50)
|
|
68
|
+
|
|
69
|
+
if showkeys:
|
|
70
|
+
for i, key in enumerate(keys):
|
|
71
|
+
traces.append(
|
|
72
|
+
go.Scatter3d(
|
|
73
|
+
x=[],
|
|
74
|
+
y=[],
|
|
75
|
+
z=[],
|
|
76
|
+
mode="markers",
|
|
77
|
+
marker=dict(size=5, color=px.colors.qualitative.Plotly[i]),
|
|
78
|
+
name=key,
|
|
79
|
+
showlegend=True,
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if fig is None:
|
|
84
|
+
fig = go.Figure(
|
|
85
|
+
data=traces,
|
|
86
|
+
layout=go.Layout(template="flight3d+judge_view", uirevision="foo"),
|
|
87
|
+
)
|
|
88
|
+
if show_axes:
|
|
89
|
+
fig.update_layout(
|
|
90
|
+
scene=dict(
|
|
91
|
+
aspectmode="data",
|
|
92
|
+
xaxis=dict(visible=True, showticklabels=True),
|
|
93
|
+
yaxis=dict(visible=True, showticklabels=True),
|
|
94
|
+
zaxis=dict(visible=True, showticklabels=True),
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
if width is not None:
|
|
98
|
+
fig.update_layout(width=width)
|
|
99
|
+
if height is not None:
|
|
100
|
+
fig.update_layout(height=height)
|
|
101
|
+
else:
|
|
102
|
+
fig.add_traces(traces)
|
|
103
|
+
return fig
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def plotdtw(sec: State, manoeuvres: List[str], span=3, fig=None):
|
|
107
|
+
if fig is None:
|
|
108
|
+
fig = go.Figure(layout=go.Layout(template="flight3d+judge_view"))
|
|
109
|
+
|
|
110
|
+
traces = [] # tiptrace(sec, span)
|
|
111
|
+
|
|
112
|
+
for i, name in enumerate(manoeuvres):
|
|
113
|
+
try:
|
|
114
|
+
seg = sec.get_man_or_el(name)
|
|
115
|
+
|
|
116
|
+
traces += ribbon(seg, span, px.colors.qualitative.Alphabet[i], name)
|
|
117
|
+
|
|
118
|
+
traces.append(
|
|
119
|
+
go.Scatter3d(
|
|
120
|
+
x=seg.pos.x,
|
|
121
|
+
y=seg.pos.y,
|
|
122
|
+
z=seg.pos.z,
|
|
123
|
+
mode="lines",
|
|
124
|
+
line=dict(width=6, color=px.colors.qualitative.Alphabet[i]),
|
|
125
|
+
name=name,
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
except Exception as ex:
|
|
129
|
+
pass
|
|
130
|
+
print("no data for manoeuvre {}, {}".format(name, ex))
|
|
131
|
+
|
|
132
|
+
fig.add_traces(traces)
|
|
133
|
+
|
|
134
|
+
return fig
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def plot_regions(
|
|
138
|
+
st: State, lab_cols: list[str], span=3, colours=None, fig=None, **kwargs
|
|
139
|
+
):
|
|
140
|
+
colours = px.colors.qualitative.Plotly if colours is None else colours
|
|
141
|
+
lab_cols = [lab_cols] if isinstance(lab_cols, str) else lab_cols
|
|
142
|
+
|
|
143
|
+
st = st.label(clabs=st.cumulative_labels(*lab_cols))
|
|
144
|
+
|
|
145
|
+
colmap = {}
|
|
146
|
+
|
|
147
|
+
traces = []
|
|
148
|
+
for i, (k, seg) in enumerate(st.split_labels("clabs").items()):
|
|
149
|
+
if len(seg) < 3:
|
|
150
|
+
continue
|
|
151
|
+
blab, id = get_appended_id(k)
|
|
152
|
+
if blab not in colmap:
|
|
153
|
+
colmap[blab] = colours[len(colmap) % len(colours)]
|
|
154
|
+
traces += ribbon(
|
|
155
|
+
seg,
|
|
156
|
+
span,
|
|
157
|
+
colmap[blab],
|
|
158
|
+
name=blab,
|
|
159
|
+
showlegend=int(id) == 0,
|
|
160
|
+
**kwargs[blab] if blab in kwargs else {},
|
|
161
|
+
)
|
|
162
|
+
traces.append(
|
|
163
|
+
go.Scatter3d(
|
|
164
|
+
x=seg.pos.x,
|
|
165
|
+
y=seg.pos.y,
|
|
166
|
+
z=seg.pos.z,
|
|
167
|
+
mode="lines",
|
|
168
|
+
line=dict(width=0, color=colmap[blab]),
|
|
169
|
+
name=k,
|
|
170
|
+
showlegend=False,
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if fig is None:
|
|
175
|
+
fig = go.Figure(layout=go.Layout(template="flight3d+judge_view"))
|
|
176
|
+
fig.add_traces(traces)
|
|
177
|
+
return fig
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def create_3d_plot(traces):
|
|
181
|
+
return go.Figure(traces, layout=go.Layout(template="flight3d+judge_view"))
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
nb_layout = dict(
|
|
185
|
+
margin=dict(l=5, r=5, t=5, b=1),
|
|
186
|
+
legend=dict(yanchor="top", xanchor="left", x=0.8, y=0.99),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def control_brv_plot(sec, control_inputs=["aileron", "elevator", "rudder", "throttle"]):
|
|
191
|
+
"""create a nice 2d plot showing control inputs and rotational velocities for a section"""
|
|
192
|
+
fig = make_subplots(specs=[[{"secondary_y": True}]])
|
|
193
|
+
|
|
194
|
+
fig.add_traces(axis_rate_trace(sec, dash="dash"), secondary_ys=np.full(3, False))
|
|
195
|
+
|
|
196
|
+
fig.add_traces(control_input_trace(sec), secondary_ys=[True for i in range(4)])
|
|
197
|
+
|
|
198
|
+
rvrng = np.ceil(np.degrees(sec.brvel.abs().max().max()) / 180) * 180
|
|
199
|
+
cirng = np.ceil(sec.data.loc[:, control_inputs].abs().max().max() / 50) * 50
|
|
200
|
+
|
|
201
|
+
fig.update_layout(
|
|
202
|
+
xaxis=dict(title="time, s"),
|
|
203
|
+
yaxis=dict(title="axis rate deg/s", range=(-rvrng, rvrng)),
|
|
204
|
+
yaxis2=dict(title="control pwm offset, ms", range=(-cirng, cirng)),
|
|
205
|
+
**nb_layout,
|
|
206
|
+
)
|
|
207
|
+
return fig
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def aoa_brv_plot(sec):
|
|
211
|
+
"""create a nice 2d plot showing rotational velocities and angle of attack for a section"""
|
|
212
|
+
fig = make_subplots(specs=[[{"secondary_y": True}]])
|
|
213
|
+
fig.add_traces(axis_rate_trace(sec), secondary_ys=np.full(3, False))
|
|
214
|
+
fig.add_traces(
|
|
215
|
+
aoa_trace(sec, colours=px.colors.qualitative.Plotly[4:]),
|
|
216
|
+
secondary_ys=np.full(2, True),
|
|
217
|
+
)
|
|
218
|
+
fig.update_layout(
|
|
219
|
+
xaxis=dict(title="Time (s)"),
|
|
220
|
+
yaxis=dict(title="Axis Rate (deg/s)"),
|
|
221
|
+
yaxis2=dict(title="Angle of Attack (deg)"),
|
|
222
|
+
**nb_layout,
|
|
223
|
+
)
|
|
224
|
+
return fig
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def compare_3d(sec1, sec2):
|
|
228
|
+
fig = make_subplots(1, 2, specs=[[{"type": "scene"}, {"type": "scene"}]])
|
|
229
|
+
flowntr = plotsec(sec1, scale=2, nmodels=4).data
|
|
230
|
+
templtr = plotsec(sec2, scale=2, nmodels=4).data
|
|
231
|
+
|
|
232
|
+
fig.add_traces(
|
|
233
|
+
flowntr,
|
|
234
|
+
cols=[1 for i in range(len(flowntr))],
|
|
235
|
+
rows=[1 for i in range(len(flowntr))],
|
|
236
|
+
)
|
|
237
|
+
fig.add_traces(
|
|
238
|
+
templtr,
|
|
239
|
+
cols=[2 for i in range(len(templtr))],
|
|
240
|
+
rows=[1 for i in range(len(templtr))],
|
|
241
|
+
)
|
|
242
|
+
fig.update_layout(template="flight3d", showlegend=False)
|
|
243
|
+
return fig
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def grid3dplot(plots):
|
|
247
|
+
"""takes an n*m list of lists of 3d figures, puts them into a n*m subplot grid"""
|
|
248
|
+
|
|
249
|
+
nrows = len(plots)
|
|
250
|
+
ncols = len(plots[0])
|
|
251
|
+
|
|
252
|
+
fig = make_subplots(
|
|
253
|
+
cols=len(plots[0]),
|
|
254
|
+
rows=len(plots),
|
|
255
|
+
specs=[[{"type": "scene"} for i in range(ncols)] for j in range(nrows)],
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
sceneids = ["scene{}".format(i + 1) for i in range(ncols * nrows)]
|
|
259
|
+
sceneids[0] = "scene"
|
|
260
|
+
fig.update_layout(
|
|
261
|
+
**{
|
|
262
|
+
"scene{}".format(i + 1 if i > 0 else ""): dict(aspectmode="data")
|
|
263
|
+
for i in range(ncols * nrows)
|
|
264
|
+
}
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
for ir, plotrow in enumerate(plots):
|
|
268
|
+
for ic, plot in enumerate(plotrow):
|
|
269
|
+
fig.add_traces(
|
|
270
|
+
plot.data,
|
|
271
|
+
cols=np.full(len(plot.data), ic + 1).tolist(),
|
|
272
|
+
rows=np.full(len(plot.data), ir + 1).tolist(),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return fig
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def plot_analysis(
|
|
279
|
+
analysis, obj=obj, nmodels=20, scale=4, cg=False, tip=True, fig=None, **kwargs
|
|
280
|
+
):
|
|
281
|
+
obj = obj.scale(scale)
|
|
282
|
+
|
|
283
|
+
fig = go.Figure() if not fig else fig
|
|
284
|
+
|
|
285
|
+
if cg:
|
|
286
|
+
fig.add_traces(cgtrace(analysis.body, **kwargs))
|
|
287
|
+
if tip:
|
|
288
|
+
fig.add_traces(tiptrace(analysis.body, scale * 1.85))
|
|
289
|
+
|
|
290
|
+
fig.add_traces(
|
|
291
|
+
vectors(nmodels, analysis.body, analysis.environment.wind * scale / 3)
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
fig.add_traces(meshes(nmodels, analysis.judge, "blue", obj))
|
|
295
|
+
fig.add_traces(meshes(nmodels, analysis.wind, "red", obj))
|
|
296
|
+
fig.add_traces(meshes(nmodels, analysis.body, "green", obj))
|
|
297
|
+
|
|
298
|
+
fig.update_layout(
|
|
299
|
+
scene=dict(
|
|
300
|
+
aspectmode="data",
|
|
301
|
+
xaxis=dict(visible=True, showticklabels=True),
|
|
302
|
+
yaxis=dict(visible=True, showticklabels=True),
|
|
303
|
+
zaxis=dict(visible=True, showticklabels=True),
|
|
304
|
+
),
|
|
305
|
+
height=800,
|
|
306
|
+
)
|
|
307
|
+
return fig
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def multi_y_subplots(data: dict[str, pd.DataFrame], x: npt.NDArray = None):
|
|
311
|
+
fig = make_subplots(
|
|
312
|
+
rows=len(data),
|
|
313
|
+
cols=1,
|
|
314
|
+
shared_xaxes=True,
|
|
315
|
+
vertical_spacing=0.01,
|
|
316
|
+
#subplot_titles=list(data.keys()),
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
for row, (k, v) in enumerate(data.items(), 1):
|
|
320
|
+
for tr, col in enumerate(v.columns):
|
|
321
|
+
fig.add_trace(
|
|
322
|
+
go.Scatter(
|
|
323
|
+
x=v.index if x is None else np.abs(x),
|
|
324
|
+
y=v[col],
|
|
325
|
+
name=f"{k}_{col}",
|
|
326
|
+
line=dict(
|
|
327
|
+
color=px.colors.qualitative.Plotly[tr],
|
|
328
|
+
dash=[
|
|
329
|
+
"solid",
|
|
330
|
+
"dot",
|
|
331
|
+
"dash",
|
|
332
|
+
"longdash",
|
|
333
|
+
"dashdot",
|
|
334
|
+
"longdashdot",
|
|
335
|
+
][row % 5],
|
|
336
|
+
),
|
|
337
|
+
legend=f"legend{row}",
|
|
338
|
+
),
|
|
339
|
+
row=row,
|
|
340
|
+
col=1,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
for i, yaxis in enumerate(fig.select_yaxes(), 1):
|
|
344
|
+
fig.update_layout(
|
|
345
|
+
{
|
|
346
|
+
f"legend{i}": dict(
|
|
347
|
+
# name = list(data.keys())[i],
|
|
348
|
+
y=yaxis.domain[1],
|
|
349
|
+
yanchor="top",
|
|
350
|
+
),
|
|
351
|
+
f"yaxis{i}": dict(
|
|
352
|
+
title=list(data.keys())[i-1],
|
|
353
|
+
showline=True,
|
|
354
|
+
),
|
|
355
|
+
}
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
return fig.update_layout(
|
|
359
|
+
hovermode="x unified",
|
|
360
|
+
hoversubplots="axis",
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
axis = dict(
|
|
365
|
+
gridcolor="lightgrey",
|
|
366
|
+
linewidth=2,
|
|
367
|
+
linecolor="lightgrey",
|
|
368
|
+
zerolinewidth=2,
|
|
369
|
+
zerolinecolor="lightgrey",
|
|
370
|
+
showline=True,
|
|
371
|
+
)
|
|
@@ -8,7 +8,6 @@ from flightplotting.model import obj, OBJ
|
|
|
8
8
|
import plotly.express as px
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
def boxtrace():
|
|
13
12
|
xlim=170*np.tan(np.radians(60))
|
|
14
13
|
ylim=170
|
|
@@ -28,14 +27,19 @@ def meshes(npoints, seq: State, colour, scale=1, _obj: OBJ=None):
|
|
|
28
27
|
_obj = obj if _obj is None else _obj
|
|
29
28
|
if scale != 1:
|
|
30
29
|
_obj = _obj.scale(scale)
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
locs = []
|
|
31
|
+
if npoints >= 1:
|
|
32
|
+
locs.append(0)
|
|
33
|
+
if npoints >= 2:
|
|
34
|
+
locs.append(-1)
|
|
35
|
+
if npoints >= 3:
|
|
36
|
+
locs = locs + list(np.cumsum(np.full(npoints-2, len(seq) / (npoints-1))).astype(int))
|
|
37
|
+
|
|
33
38
|
ms = []
|
|
34
|
-
|
|
35
|
-
for st in seq[::step]:
|
|
39
|
+
for loc in locs:
|
|
36
40
|
ms.append(_obj.transform(
|
|
37
|
-
Transformation(
|
|
38
|
-
).create_mesh(colour,f"{
|
|
41
|
+
Transformation(seq.pos[loc], seq.att[loc])
|
|
42
|
+
).create_mesh(colour,f"{seq.time.t[loc]:.1f}"))
|
|
39
43
|
return ms
|
|
40
44
|
|
|
41
45
|
def vector(origin, direction, **kwargs):
|
|
@@ -68,12 +72,7 @@ def trace3d(datax, datay, dataz, **kwargs):
|
|
|
68
72
|
def cgtrace(seq, **kwargs):
|
|
69
73
|
return trace3d(
|
|
70
74
|
*seq.pos.data.T,
|
|
71
|
-
**
|
|
72
|
-
dict(
|
|
73
|
-
text=["{:.1f}".format(val) for val in seq.data.index]
|
|
74
|
-
),
|
|
75
|
-
**kwargs
|
|
76
|
-
)
|
|
75
|
+
**kwargs
|
|
77
76
|
)
|
|
78
77
|
|
|
79
78
|
|
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
import plotly.graph_objects as go
|
|
2
|
-
from plotly.subplots import make_subplots
|
|
3
|
-
import plotly.express as px
|
|
4
|
-
import flightplotting.templates
|
|
5
|
-
from flightplotting.traces import (
|
|
6
|
-
tiptrace,
|
|
7
|
-
meshes,
|
|
8
|
-
control_input_trace,
|
|
9
|
-
axis_rate_trace,
|
|
10
|
-
aoa_trace,
|
|
11
|
-
cgtrace,
|
|
12
|
-
ribbon,
|
|
13
|
-
vectors,
|
|
14
|
-
axestrace
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
from flightdata import State
|
|
18
|
-
from flightdata.base.labeling import get_appended_id
|
|
19
|
-
from geometry import Coord
|
|
20
|
-
from flightplotting.model import obj
|
|
21
|
-
import numpy as np
|
|
22
|
-
from typing import List, Union
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def plotsec(secs: State | list[State] | dict[str, State], scale=5, nmodels=0, fig=None,
|
|
26
|
-
color: Union[str, list[str]]=None, cg=False, width=None,
|
|
27
|
-
height=None, show_axes=False, ribb: bool=False, tips: bool=True, origin=False):
|
|
28
|
-
|
|
29
|
-
traces = []
|
|
30
|
-
keys = None
|
|
31
|
-
if isinstance(secs, State):
|
|
32
|
-
secs = [secs]
|
|
33
|
-
|
|
34
|
-
if isinstance(secs, dict):
|
|
35
|
-
keys = list(secs.keys())
|
|
36
|
-
secs = list(secs.values())
|
|
37
|
-
showkeys=True
|
|
38
|
-
else:
|
|
39
|
-
keys = list(range(len(secs)))
|
|
40
|
-
showkeys=False
|
|
41
|
-
|
|
42
|
-
for i, sec in enumerate(secs):
|
|
43
|
-
text = ["{:.1f}".format(val) for val in sec.data.index]
|
|
44
|
-
_color = color if color is not None else px.colors.qualitative.Plotly[i]
|
|
45
|
-
if ribb:
|
|
46
|
-
traces += ribbon(sec, scale * 1.85, _color, name=keys[i])
|
|
47
|
-
if tips:
|
|
48
|
-
traces += tiptrace(sec, scale * 1.85, text=text, name=keys[i])
|
|
49
|
-
if nmodels > 0:
|
|
50
|
-
traces += meshes(nmodels, sec, _color, scale)
|
|
51
|
-
if cg:
|
|
52
|
-
traces.append(cgtrace(sec, line=dict(color=_color, width=2), name=keys[i]))
|
|
53
|
-
|
|
54
|
-
if origin:
|
|
55
|
-
traces += axestrace(Coord.zero(), 50)
|
|
56
|
-
|
|
57
|
-
if showkeys:
|
|
58
|
-
for i, key in enumerate(keys):
|
|
59
|
-
traces.append(go.Scatter3d(
|
|
60
|
-
x=[],
|
|
61
|
-
y=[],
|
|
62
|
-
z=[],
|
|
63
|
-
mode='markers',
|
|
64
|
-
marker=dict(size=5, color=px.colors.qualitative.Plotly[i]),
|
|
65
|
-
name=key,
|
|
66
|
-
showlegend=True
|
|
67
|
-
))
|
|
68
|
-
|
|
69
|
-
if fig is None:
|
|
70
|
-
|
|
71
|
-
fig = go.Figure(
|
|
72
|
-
data=traces,
|
|
73
|
-
layout=go.Layout(template="flight3d+judge_view", uirevision='foo')
|
|
74
|
-
)
|
|
75
|
-
if show_axes:
|
|
76
|
-
fig.update_layout(
|
|
77
|
-
scene=dict(
|
|
78
|
-
aspectmode='data',
|
|
79
|
-
xaxis=dict(visible=True, showticklabels=True),
|
|
80
|
-
yaxis=dict(visible=True, showticklabels=True),
|
|
81
|
-
zaxis=dict(visible=True, showticklabels=True)
|
|
82
|
-
)
|
|
83
|
-
)
|
|
84
|
-
if width is not None:
|
|
85
|
-
fig.update_layout(width=width)
|
|
86
|
-
if height is not None:
|
|
87
|
-
fig.update_layout(height=height)
|
|
88
|
-
else:
|
|
89
|
-
fig.add_traces(traces)
|
|
90
|
-
return fig
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def plotdtw(sec: State, manoeuvres: List[str], span=3, fig=None):
|
|
95
|
-
if fig is None:
|
|
96
|
-
fig = go.Figure(layout=go.Layout(template="flight3d+judge_view"))
|
|
97
|
-
|
|
98
|
-
traces = []#tiptrace(sec, span)
|
|
99
|
-
|
|
100
|
-
for i, name in enumerate(manoeuvres):
|
|
101
|
-
try:
|
|
102
|
-
seg = sec.get_man_or_el(name)
|
|
103
|
-
|
|
104
|
-
traces += ribbon(seg, span, px.colors.qualitative.Alphabet[i], name)
|
|
105
|
-
|
|
106
|
-
traces.append(go.Scatter3d(
|
|
107
|
-
x=seg.pos.x,
|
|
108
|
-
y=seg.pos.y,
|
|
109
|
-
z=seg.pos.z,
|
|
110
|
-
mode='lines',
|
|
111
|
-
line=dict(width=6, color=px.colors.qualitative.Alphabet[i]),
|
|
112
|
-
name=name
|
|
113
|
-
))
|
|
114
|
-
except Exception as ex:
|
|
115
|
-
pass
|
|
116
|
-
print("no data for manoeuvre {}, {}".format(name, ex))
|
|
117
|
-
|
|
118
|
-
fig.add_traces(traces)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return fig
|
|
122
|
-
|
|
123
|
-
def plot_regions(st: State, lab_cols: list[str], span=3, colours=None, fig=None, **kwargs):
|
|
124
|
-
colours = px.colors.qualitative.Plotly if colours is None else colours
|
|
125
|
-
lab_cols = [lab_cols] if isinstance(lab_cols, str) else lab_cols
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
st = st.label(clabs=st.cumulative_labels(*lab_cols))
|
|
129
|
-
|
|
130
|
-
def get_base_label(clab):
|
|
131
|
-
base = clab
|
|
132
|
-
try:
|
|
133
|
-
id = int(clab.split('_')[-1])
|
|
134
|
-
base = clab[:-len(f'_{id}')]
|
|
135
|
-
except Exception:
|
|
136
|
-
pass
|
|
137
|
-
return base
|
|
138
|
-
|
|
139
|
-
colmap = {}
|
|
140
|
-
|
|
141
|
-
traces = []
|
|
142
|
-
for i, (k, seg) in enumerate(st.split_labels('clabs').items()):
|
|
143
|
-
if len(seg) < 3:
|
|
144
|
-
continue
|
|
145
|
-
blab, id = get_appended_id(k)
|
|
146
|
-
if blab not in colmap:
|
|
147
|
-
colmap[blab] = colours[len(colmap) % len(colours)]
|
|
148
|
-
traces += ribbon(
|
|
149
|
-
seg, span, colmap[blab], name=blab,
|
|
150
|
-
showlegend=int(id)==0,
|
|
151
|
-
**kwargs[blab] if blab in kwargs else {}
|
|
152
|
-
)
|
|
153
|
-
traces.append(go.Scatter3d(
|
|
154
|
-
x=seg.pos.x,
|
|
155
|
-
y=seg.pos.y,
|
|
156
|
-
z=seg.pos.z,
|
|
157
|
-
mode='lines',
|
|
158
|
-
line=dict(width=0, color=colmap[blab]),
|
|
159
|
-
name=k,
|
|
160
|
-
showlegend=False
|
|
161
|
-
))
|
|
162
|
-
|
|
163
|
-
if fig is None:
|
|
164
|
-
fig = go.Figure(layout=go.Layout(template="flight3d+judge_view"))
|
|
165
|
-
fig.add_traces(traces)
|
|
166
|
-
return fig
|
|
167
|
-
|
|
168
|
-
def create_3d_plot(traces):
|
|
169
|
-
return go.Figure(
|
|
170
|
-
traces,
|
|
171
|
-
layout=go.Layout(template="flight3d+judge_view"))
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
nb_layout = dict(
|
|
176
|
-
margin=dict(l=5, r=5, t=5, b=1),
|
|
177
|
-
legend=dict(yanchor="top", xanchor="left", x=0.8, y=0.99)
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def control_brv_plot(sec, control_inputs = ["aileron", "elevator", "rudder", "throttle"]):
|
|
182
|
-
"""create a nice 2d plot showing control inputs and rotational velocities for a section"""
|
|
183
|
-
fig = make_subplots(specs=[[{"secondary_y": True}]])
|
|
184
|
-
|
|
185
|
-
fig.add_traces(axis_rate_trace(sec, dash="dash"), secondary_ys=np.full(3, False))
|
|
186
|
-
|
|
187
|
-
fig.add_traces(control_input_trace(sec), secondary_ys=[True for i in range(4)])
|
|
188
|
-
|
|
189
|
-
rvrng = np.ceil(np.degrees(sec.brvel.abs().max().max()) / 180) * 180
|
|
190
|
-
cirng = np.ceil(sec.data.loc[:,control_inputs].abs().max().max() / 50) * 50
|
|
191
|
-
|
|
192
|
-
fig.update_layout(
|
|
193
|
-
xaxis=dict(title="time, s"),
|
|
194
|
-
yaxis=dict(title="axis rate deg/s",range=(-rvrng, rvrng)),
|
|
195
|
-
yaxis2=dict(title="control pwm offset, ms",range=(-cirng, cirng)),
|
|
196
|
-
**nb_layout
|
|
197
|
-
)
|
|
198
|
-
return fig
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def aoa_brv_plot(sec):
|
|
202
|
-
"""create a nice 2d plot showing rotational velocities and angle of attack for a section"""
|
|
203
|
-
fig = make_subplots(specs=[[{"secondary_y": True}]])
|
|
204
|
-
fig.add_traces(axis_rate_trace(sec), secondary_ys=np.full(3, False))
|
|
205
|
-
fig.add_traces(aoa_trace(sec, colours=px.colors.qualitative.Plotly[4:]), secondary_ys=np.full(2, True))
|
|
206
|
-
fig.update_layout(
|
|
207
|
-
xaxis=dict(title="Time (s)"),
|
|
208
|
-
yaxis=dict(title="Axis Rate (deg/s)"),
|
|
209
|
-
yaxis2=dict(title="Angle of Attack (deg)"),
|
|
210
|
-
**nb_layout
|
|
211
|
-
)
|
|
212
|
-
return fig
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def compare_3d(sec1, sec2):
|
|
216
|
-
fig = make_subplots(1, 2, specs=[[{'type': 'scene'}, {'type': 'scene'}]])
|
|
217
|
-
flowntr = plotsec(sec1, scale=2, nmodels=4).data
|
|
218
|
-
templtr = plotsec(sec2, scale=2, nmodels=4).data
|
|
219
|
-
|
|
220
|
-
fig.add_traces(flowntr, cols = [1 for i in range(len(flowntr))], rows=[1 for i in range(len(flowntr))] )
|
|
221
|
-
fig.add_traces(templtr, cols = [2 for i in range(len(templtr))], rows=[1 for i in range(len(templtr))] )
|
|
222
|
-
fig.update_layout(template="flight3d", showlegend=False)
|
|
223
|
-
return fig
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
def grid3dplot(plots):
|
|
227
|
-
"""takes an n*m list of lists of 3d figures, puts them into a n*m subplot grid"""
|
|
228
|
-
|
|
229
|
-
nrows = len(plots)
|
|
230
|
-
ncols = len(plots[0])
|
|
231
|
-
|
|
232
|
-
fig = make_subplots(
|
|
233
|
-
cols=len(plots[0]),
|
|
234
|
-
rows=len(plots),
|
|
235
|
-
specs=[[{"type": "scene"} for i in range(ncols)] for j in range(nrows)]
|
|
236
|
-
)
|
|
237
|
-
|
|
238
|
-
sceneids = ["scene{}".format(i+1) for i in range(ncols*nrows)]
|
|
239
|
-
sceneids[0] = "scene"
|
|
240
|
-
fig.update_layout(**{"scene{}".format(i+1 if i>0 else ""):dict(aspectmode='data') for i in range(ncols*nrows)})
|
|
241
|
-
|
|
242
|
-
for ir, plotrow in enumerate(plots):
|
|
243
|
-
for ic, plot in enumerate(plotrow):
|
|
244
|
-
fig.add_traces(plot.data, cols=np.full(len(plot.data), ic+1).tolist(), rows=np.full(len(plot.data), ir+1).tolist())
|
|
245
|
-
|
|
246
|
-
return fig
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
def plot_analysis(analysis, obj=obj, nmodels=20, scale=4, cg=False, tip=True, fig=None, **kwargs):
|
|
251
|
-
|
|
252
|
-
obj = obj.scale(scale)
|
|
253
|
-
|
|
254
|
-
fig = go.Figure() if not fig else fig
|
|
255
|
-
|
|
256
|
-
if cg:
|
|
257
|
-
fig.add_traces(cgtrace(analysis.body, **kwargs))
|
|
258
|
-
if tip:
|
|
259
|
-
fig.add_traces(tiptrace(analysis.body, scale*1.85))
|
|
260
|
-
|
|
261
|
-
fig.add_traces(vectors(nmodels, analysis.body, analysis.environment.wind * scale / 3))
|
|
262
|
-
|
|
263
|
-
fig.add_traces(meshes(nmodels,analysis.judge, "blue", obj))
|
|
264
|
-
fig.add_traces(meshes(nmodels,analysis.wind, "red", obj))
|
|
265
|
-
fig.add_traces(meshes(nmodels,analysis.body, "green", obj))
|
|
266
|
-
|
|
267
|
-
fig.update_layout(
|
|
268
|
-
scene=dict(
|
|
269
|
-
aspectmode='data',
|
|
270
|
-
xaxis=dict(visible=True, showticklabels=True),
|
|
271
|
-
yaxis=dict(visible=True, showticklabels=True),
|
|
272
|
-
zaxis=dict(visible=True, showticklabels=True)
|
|
273
|
-
), height=800)
|
|
274
|
-
return fig
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
axis = dict(
|
|
278
|
-
gridcolor="lightgrey",
|
|
279
|
-
linewidth=2,
|
|
280
|
-
linecolor='lightgrey',
|
|
281
|
-
zerolinewidth=2,
|
|
282
|
-
zerolinecolor='lightgrey',
|
|
283
|
-
showline=True
|
|
284
|
-
)
|
|
285
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|