Skip to content

Commit e6d9fff

Browse files
committed
updating visualization with dark mode and main gif
1 parent 7350d0f commit e6d9fff

File tree

3 files changed

+322
-0
lines changed

3 files changed

+322
-0
lines changed
Binary file not shown.

aerobench/visualize/anim3d.py

+322
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,328 @@ def anim_func(global_frame):
364364
else:
365365
plt.show()
366366

367+
def make_custom_anim(res, filename, viewsize=1000, viewsize_z=1000, f16_scale=30, trail_pts=60,
368+
elev=30, azim=45, skip_frames=None, chase=False, fixed_floor=False,
369+
init_extra=None, update_extra=None):
370+
'''
371+
make a 3d plot of the GCAS maneuver.
372+
373+
see examples/anim3d folder for examples on usage
374+
'''
375+
376+
plot.init_plot()
377+
start = time.time()
378+
379+
if not isinstance(res, list):
380+
res = [res]
381+
382+
if not isinstance(viewsize, list):
383+
viewsize = [viewsize]
384+
385+
if not isinstance(viewsize_z, list):
386+
viewsize_z = [viewsize_z]
387+
388+
if not isinstance(f16_scale, list):
389+
f16_scale = [f16_scale]
390+
391+
if not isinstance(trail_pts, list):
392+
trail_pts = [trail_pts]
393+
394+
if not isinstance(elev, list):
395+
elev = [elev]
396+
397+
if not isinstance(azim, list):
398+
azim = [azim]
399+
400+
if not isinstance(skip_frames, list):
401+
skip_frames = [skip_frames]
402+
403+
if not isinstance(chase, list):
404+
chase = [chase]
405+
406+
if not isinstance(fixed_floor, list):
407+
fixed_floor = [fixed_floor]
408+
409+
if not isinstance(init_extra, list):
410+
init_extra = [init_extra]
411+
412+
if not isinstance(update_extra, list):
413+
update_extra = [update_extra]
414+
415+
#####
416+
# fill in defaults
417+
if filename == '':
418+
full_plot = False
419+
else:
420+
full_plot = True
421+
422+
for i, skip in enumerate(skip_frames):
423+
if skip is not None:
424+
continue
425+
426+
if filename == '': # plot to the screen
427+
skip_frames[i] = 5
428+
elif filename.endswith('.gif'):
429+
skip_frames[i] = 2
430+
else:
431+
skip_frames[i] = 1 # plot every frame
432+
433+
if filename == '':
434+
filename = None
435+
436+
##
437+
all_times = []
438+
all_states = []
439+
all_modes = []
440+
all_ps_list = []
441+
all_Nz_list = []
442+
443+
for r, skip in zip(res, skip_frames):
444+
t = r['times']
445+
s = r['states']
446+
m = r['modes']
447+
ps = r['ps_list']
448+
Nz = r['Nz_list']
449+
450+
t = t[0::skip]
451+
s = s[0::skip]
452+
m = m[0::skip]
453+
ps = ps[0::skip]
454+
Nz = Nz[0::skip]
455+
456+
all_times.append(t)
457+
all_states.append(s)
458+
all_modes.append(m)
459+
all_ps_list.append(ps)
460+
all_Nz_list.append(Nz)
461+
462+
##
463+
464+
fig = plt.figure(figsize=(8, 7))
465+
ax = fig.add_subplot(111, projection='3d')
466+
467+
# Set dark mode
468+
ax.set_facecolor('black')
469+
fig.patch.set_facecolor('black')
470+
ax.xaxis.set_pane_color((0, 0, 0, 1))
471+
ax.yaxis.set_pane_color((0, 0, 0, 1))
472+
ax.zaxis.set_pane_color((0, 0, 0, 1))
473+
474+
ax.xaxis.label.set_color('white')
475+
ax.yaxis.label.set_color('white')
476+
ax.zaxis.label.set_color('white')
477+
ax.tick_params(axis='x', colors='white')
478+
ax.tick_params(axis='y', colors='white')
479+
ax.tick_params(axis='z', colors='white')
480+
481+
##
482+
483+
parent = get_script_path()
484+
plane_point_data = os.path.join(parent, 'f-16.mat')
485+
486+
data = loadmat(plane_point_data)
487+
f16_pts = data['V']
488+
f16_faces = data['F']
489+
490+
plane_polys = Poly3DCollection([], color='white' if full_plot else 'k')
491+
ax.add_collection3d(plane_polys)
492+
493+
ax.set_xlabel('X [ft]', fontsize=14, color='white')
494+
ax.set_ylabel('Y [ft]', fontsize=14, color='white')
495+
ax.set_zlabel('Altitude [ft]', fontsize=14, color='white')
496+
497+
trail_line, = ax.plot([], [], [], color='white', lw=2, zorder=50)
498+
499+
extra_lines = []
500+
501+
for func in init_extra:
502+
if func is not None:
503+
extra_lines.append(func(ax))
504+
else:
505+
extra_lines.append([])
506+
507+
first_frames = []
508+
frames = 0
509+
510+
for t in all_times:
511+
first_frames.append(frames)
512+
frames += len(t)
513+
514+
def anim_func(global_frame):
515+
'updates for the animation frame'
516+
517+
index = 0
518+
first_frame = False
519+
520+
for i, f in enumerate(first_frames):
521+
if global_frame >= f:
522+
index = i
523+
524+
if global_frame == f:
525+
first_frame = True
526+
break
527+
528+
frame = global_frame - first_frames[index]
529+
states = all_states[index]
530+
times = all_times[index]
531+
modes = all_modes[index]
532+
Nz_list = all_Nz_list[index]
533+
ps_list = all_ps_list[index]
534+
535+
print(f"Frame: {global_frame}/{frames} - Index {index} frame {frame}/{len(times)}")
536+
537+
speed = states[frame][0]
538+
alpha = states[frame][1]
539+
beta = states[frame][2]
540+
alt = states[frame][11]
541+
542+
phi = states[frame][StateIndex.PHI]
543+
theta = states[frame][StateIndex.THETA]
544+
psi = states[frame][StateIndex.PSI]
545+
546+
dx = states[frame][StateIndex.POS_E]
547+
dy = states[frame][StateIndex.POS_N]
548+
dz = states[frame][StateIndex.ALT]
549+
550+
if first_frame:
551+
ax.view_init(elev[index], azim[index])
552+
553+
for i, lines in enumerate(extra_lines):
554+
for line in lines:
555+
line.set_visible(i == index)
556+
557+
if chase[index]:
558+
ax.view_init(elev[index], rad2deg(-psi) - 90.0)
559+
560+
colors = ['red', 'blue', 'green', 'magenta']
561+
562+
mode_names = []
563+
564+
for mode in modes:
565+
if not mode in mode_names:
566+
mode_names.append(mode)
567+
568+
mode = modes[frame]
569+
mode_index = modes.index(mode)
570+
col = colors[mode_index % len(colors)]
571+
572+
s = f16_scale[index]
573+
s = 25 if s is None else s
574+
pts = scale3d(f16_pts, [-s, s, s])
575+
576+
pts = rotate3d(pts, theta, psi - math.pi/2, -phi)
577+
578+
size = viewsize[index]
579+
size = 1000 if size is None else size
580+
minx = dx - size
581+
maxx = dx + size
582+
miny = dy - size
583+
maxy = dy + size
584+
585+
vz = viewsize_z[index]
586+
vz = 1000 if vz is None else vz
587+
588+
if fixed_floor[index]:
589+
minz = 0
590+
maxz = vz
591+
else:
592+
minz = dz - vz
593+
maxz = dz + vz
594+
595+
ax.set_xlim([minx, maxx])
596+
ax.set_ylim([miny, maxy])
597+
ax.set_zlim([minz, maxz])
598+
599+
verts = []
600+
fc = []
601+
ec = []
602+
count = 0
603+
604+
# draw ground
605+
if minz <= 0 <= maxz:
606+
z = 0
607+
verts.append([(minx, miny, z), (maxx, miny, z), (maxx, maxy, z), (minx, maxy, z)])
608+
fc.append('0.8')
609+
ec.append('0.8')
610+
611+
# draw f16
612+
for face in f16_faces:
613+
face_pts = []
614+
615+
count = count + 1
616+
617+
if not full_plot and count % 10 != 0:
618+
continue
619+
620+
for findex in face:
621+
face_pts.append((pts[findex-1][0] + dx, \
622+
pts[findex-1][1] + dy, \
623+
pts[findex-1][2] + dz))
624+
625+
verts.append(face_pts)
626+
fc.append('white')
627+
ec.append('white')
628+
629+
plane_polys.set_verts(verts)
630+
plane_polys.set_facecolor(fc)
631+
plane_polys.set_edgecolor(ec)
632+
633+
# do trail
634+
t = trail_pts[index]
635+
t = 200 if t is None else t
636+
trail_len = t // skip_frames[index]
637+
start_index = max(0, frame-trail_len)
638+
639+
pos_xs = [pt[StateIndex.POS_E] for pt in states]
640+
pos_ys = [pt[StateIndex.POS_N] for pt in states]
641+
pos_zs = [pt[StateIndex.ALT] for pt in states]
642+
643+
trail_line.set_data(np.asarray(pos_xs[start_index:frame]), np.asarray(pos_ys[start_index:frame]))
644+
trail_line.set_3d_properties(np.asarray(pos_zs[start_index:frame]))
645+
646+
if update_extra[index] is not None:
647+
update_extra[index](frame)
648+
649+
plt.tight_layout()
650+
651+
interval = 30
652+
653+
if filename.endswith('.gif'):
654+
interval = 60
655+
656+
anim_obj = animation.FuncAnimation(fig, anim_func, frames, interval=interval, \
657+
blit=False, repeat=True)
658+
659+
if filename is not None:
660+
661+
if filename.endswith('.gif'):
662+
print("\nSaving animation to '{}' using 'imagemagick'...".format(filename))
663+
anim_obj.save(filename, dpi=180, writer='imagemagick') # dpi was 80
664+
print("Finished saving to {} in {:.1f} sec".format(filename, time.time() - start))
665+
else:
666+
fps = 40
667+
codec = 'libx264'
668+
669+
print("\nSaving '{}' at {:.2f} fps using ffmpeg with codec '{}'.".format(filename, fps, codec))
670+
671+
# if this fails do: 'sudo apt-get install ffmpeg'
672+
try:
673+
extra_args = []
674+
675+
if codec is not None:
676+
extra_args += ['-vcodec', str(codec)]
677+
678+
anim_obj.save(filename, fps=fps, extra_args=extra_args)
679+
print("Finished saving to {} in {:.1f} sec".format(filename, time.time() - start))
680+
except AttributeError:
681+
traceback.print_exc()
682+
print("\nSaving video file failed! Is ffmpeg installed? Can you run 'ffmpeg' in the terminal?")
683+
else:
684+
plt.show()
685+
686+
687+
688+
367689
def scale3d(pts, scale_list):
368690
'scale a 3d ndarray of points, and return the new ndarray'
369691

img/main.gif

1.27 MB
Loading

0 commit comments

Comments
 (0)