@@ -189,7 +189,6 @@ def draw(self, vis_args=None):
189
189
A full list of color names can be found at:
190
190
https://matplotlib.org/stable/gallery/color/named_colors
191
191
"""
192
- # TODO: we need to modularize this function, it is too big
193
192
if vis_args is None :
194
193
vis_args = {
195
194
"background" : "#EEEEEE" ,
@@ -212,10 +211,27 @@ def draw(self, vis_args=None):
212
211
reverse = csys == 1
213
212
self .rocket .aerodynamic_surfaces .sort_by_position (reverse = reverse )
214
213
214
+ drawn_surfaces = self ._draw_aerodynamic_surfaces (ax , vis_args )
215
+ last_radius , last_x = self ._draw_tubes (ax , drawn_surfaces , vis_args )
216
+ self ._draw_motor (last_radius , last_x , ax , vis_args )
217
+ self ._draw_rail_buttons (ax , vis_args )
218
+ self ._draw_center_of_mass_and_pressure (ax )
219
+
220
+ plt .title ("Rocket Representation" )
221
+ plt .xlim ()
222
+ plt .ylim ([- self .rocket .radius * 4 , self .rocket .radius * 6 ])
223
+ plt .xlabel ("Position (m)" )
224
+ plt .ylabel ("Radius (m)" )
225
+ plt .legend (bbox_to_anchor = (1.05 , 1 ), loc = "upper left" )
226
+ plt .tight_layout ()
227
+ plt .show ()
228
+
229
+ def _draw_aerodynamic_surfaces (self , ax , vis_args ):
230
+ """Draws the aerodynamic surfaces and saves the position of the points
231
+ of interest for the tubes."""
215
232
# List of drawn surfaces with the position of points of interest
216
233
# and the radius of the rocket at that point
217
234
drawn_surfaces = []
218
-
219
235
# Idea is to get the shape of each aerodynamic surface in their own
220
236
# coordinate system and then plot them in the rocket coordinate system
221
237
# using the position of each surface
@@ -225,113 +241,105 @@ def draw(self, vis_args=None):
225
241
226
242
for surface , position in self .rocket .aerodynamic_surfaces :
227
243
if isinstance (surface , NoseCone ):
228
- x_nosecone = - csys * surface .shape_vec [0 ] + position
229
- y_nosecone = surface .shape_vec [1 ]
230
-
231
- ax .plot (
232
- x_nosecone ,
233
- y_nosecone ,
234
- color = vis_args ["nose" ],
235
- linewidth = vis_args ["line_width" ],
236
- )
237
- ax .plot (
238
- x_nosecone ,
239
- - y_nosecone ,
240
- color = vis_args ["nose" ],
241
- linewidth = vis_args ["line_width" ],
242
- )
243
- # close the nosecone
244
- ax .plot (
245
- [x_nosecone [- 1 ], x_nosecone [- 1 ]],
246
- [y_nosecone [- 1 ], - y_nosecone [- 1 ]],
247
- color = vis_args ["nose" ],
248
- linewidth = vis_args ["line_width" ],
249
- )
250
-
251
- # Add the nosecone to the list of drawn surfaces
252
- drawn_surfaces .append (
253
- (surface , x_nosecone [- 1 ], surface .rocket_radius , x_nosecone [- 1 ])
254
- )
255
-
244
+ self ._draw_nose_cone (ax , surface , position , drawn_surfaces , vis_args )
256
245
elif isinstance (surface , Tail ):
257
- x_tail = - csys * surface .shape_vec [0 ] + position
258
- y_tail = surface .shape_vec [1 ]
259
-
260
- ax .plot (
261
- x_tail ,
262
- y_tail ,
263
- color = vis_args ["tail" ],
264
- linewidth = vis_args ["line_width" ],
265
- )
266
- ax .plot (
267
- x_tail ,
268
- - y_tail ,
269
- color = vis_args ["tail" ],
270
- linewidth = vis_args ["line_width" ],
271
- )
272
- # close above and below the tail
273
- ax .plot (
274
- [x_tail [- 1 ], x_tail [- 1 ]],
275
- [y_tail [- 1 ], - y_tail [- 1 ]],
276
- color = vis_args ["tail" ],
277
- linewidth = vis_args ["line_width" ],
278
- )
279
- ax .plot (
280
- [x_tail [0 ], x_tail [0 ]],
281
- [y_tail [0 ], - y_tail [0 ]],
282
- color = vis_args ["tail" ],
283
- linewidth = vis_args ["line_width" ],
284
- )
246
+ self ._draw_tail (ax , surface , position , drawn_surfaces , vis_args )
247
+ elif isinstance (surface , Fins ):
248
+ self ._draw_fins (ax , surface , position , drawn_surfaces , vis_args )
249
+ return drawn_surfaces
250
+
251
+ def _draw_nose_cone (self , ax , surface , position , drawn_surfaces , vis_args ):
252
+ """Draws the nosecone and saves the position of the points of interest
253
+ for the tubes."""
254
+ x_nosecone = - self .rocket ._csys * surface .shape_vec [0 ] + position
255
+ y_nosecone = surface .shape_vec [1 ]
256
+ ax .plot (
257
+ x_nosecone ,
258
+ y_nosecone ,
259
+ color = vis_args ["nose" ],
260
+ linewidth = vis_args ["line_width" ],
261
+ )
262
+ ax .plot (
263
+ x_nosecone ,
264
+ - y_nosecone ,
265
+ color = vis_args ["nose" ],
266
+ linewidth = vis_args ["line_width" ],
267
+ )
268
+ # close the nosecone
269
+ ax .plot (
270
+ [x_nosecone [- 1 ], x_nosecone [- 1 ]],
271
+ [y_nosecone [- 1 ], - y_nosecone [- 1 ]],
272
+ color = vis_args ["nose" ],
273
+ linewidth = vis_args ["line_width" ],
274
+ )
275
+ # Add the nosecone to the list of drawn surfaces
276
+ drawn_surfaces .append (
277
+ (surface , x_nosecone [- 1 ], surface .rocket_radius , x_nosecone [- 1 ])
278
+ )
285
279
286
- # Add the tail to the list of drawn surfaces
287
- drawn_surfaces .append (
288
- (surface , position , surface .bottom_radius , x_tail [- 1 ])
289
- )
280
+ def _draw_tail (self , ax , surface , position , drawn_surfaces , vis_args ):
281
+ """Draws the tail and saves the position of the points of interest
282
+ for the tubes."""
283
+ x_tail = - self .rocket ._csys * surface .shape_vec [0 ] + position
284
+ y_tail = surface .shape_vec [1 ]
285
+ ax .plot (
286
+ x_tail , y_tail , color = vis_args ["tail" ], linewidth = vis_args ["line_width" ]
287
+ )
288
+ ax .plot (
289
+ x_tail , - y_tail , color = vis_args ["tail" ], linewidth = vis_args ["line_width" ]
290
+ )
291
+ # close above and below the tail
292
+ ax .plot (
293
+ [x_tail [- 1 ], x_tail [- 1 ]],
294
+ [y_tail [- 1 ], - y_tail [- 1 ]],
295
+ color = vis_args ["tail" ],
296
+ linewidth = vis_args ["line_width" ],
297
+ )
298
+ ax .plot (
299
+ [x_tail [0 ], x_tail [0 ]],
300
+ [y_tail [0 ], - y_tail [0 ]],
301
+ color = vis_args ["tail" ],
302
+ linewidth = vis_args ["line_width" ],
303
+ )
304
+ # Add the tail to the list of drawn surfaces
305
+ drawn_surfaces .append ((surface , position , surface .bottom_radius , x_tail [- 1 ]))
306
+
307
+ def _draw_fins (self , ax , surface , position , drawn_surfaces , vis_args ):
308
+ """Draws the fins and saves the position of the points of interest
309
+ for the tubes."""
310
+ num_fins = surface .n
311
+ x_fin = - self .rocket ._csys * surface .shape_vec [0 ] + position
312
+ y_fin = surface .shape_vec [1 ] + surface .rocket_radius
313
+ rotation_angles = [2 * np .pi * i / num_fins for i in range (num_fins )]
314
+
315
+ for angle in rotation_angles :
316
+ # Create a rotation matrix for the current angle around the x-axis
317
+ rotation_matrix = np .array ([[1 , 0 ], [0 , np .cos (angle )]])
318
+
319
+ # Apply the rotation to the original fin points
320
+ rotated_points_2d = np .dot (rotation_matrix , np .vstack ((x_fin , y_fin )))
321
+
322
+ # Extract x and y coordinates of the rotated points
323
+ x_rotated , y_rotated = rotated_points_2d
324
+
325
+ # Project points above the XY plane back into the XY plane (set z-coordinate to 0)
326
+ x_rotated = np .where (
327
+ rotated_points_2d [1 ] > 0 , rotated_points_2d [0 ], x_rotated
328
+ )
329
+ y_rotated = np .where (
330
+ rotated_points_2d [1 ] > 0 , rotated_points_2d [1 ], y_rotated
331
+ )
332
+ ax .plot (
333
+ x_rotated ,
334
+ y_rotated ,
335
+ color = vis_args ["fins" ],
336
+ linewidth = vis_args ["line_width" ],
337
+ )
290
338
291
- # Draw fins
292
- elif isinstance (surface , Fins ):
293
- num_fins = surface .n
294
- x_fin = - csys * surface .shape_vec [0 ] + position
295
- y_fin = surface .shape_vec [1 ] + surface .rocket_radius
296
-
297
- # Calculate the rotation angles for the other two fins (symmetrically)
298
- rotation_angles = [2 * np .pi * i / num_fins for i in range (num_fins )]
299
-
300
- # Apply rotation transformations to get points for the other fins in 2D space
301
- for angle in rotation_angles :
302
- # Create a rotation matrix for the current angle around the x-axis
303
- rotation_matrix = np .array ([[1 , 0 ], [0 , np .cos (angle )]])
304
-
305
- # Apply the rotation to the original fin points
306
- rotated_points_2d = np .dot (
307
- rotation_matrix , np .vstack ((x_fin , y_fin ))
308
- )
309
-
310
- # Extract x and y coordinates of the rotated points
311
- x_rotated , y_rotated = rotated_points_2d
312
-
313
- # Project points above the XY plane back into the XY plane (set z-coordinate to 0)
314
- x_rotated = np .where (
315
- rotated_points_2d [1 ] > 0 , rotated_points_2d [0 ], x_rotated
316
- )
317
- y_rotated = np .where (
318
- rotated_points_2d [1 ] > 0 , rotated_points_2d [1 ], y_rotated
319
- )
320
-
321
- # Plot the fins
322
- ax .plot (
323
- x_rotated ,
324
- y_rotated ,
325
- color = vis_args ["fins" ],
326
- linewidth = vis_args ["line_width" ],
327
- )
328
-
329
- # Add the fin to the list of drawn surfaces
330
- drawn_surfaces .append (
331
- (surface , position , surface .rocket_radius , x_rotated [- 1 ])
332
- )
339
+ drawn_surfaces .append ((surface , position , surface .rocket_radius , x_rotated [- 1 ]))
333
340
334
- # Draw tubes
341
+ def _draw_tubes (self , ax , drawn_surfaces , vis_args ):
342
+ """Draws the tubes between the aerodynamic surfaces."""
335
343
for i , d_surface in enumerate (drawn_surfaces ):
336
344
# Draw the tubes, from the end of the first surface to the beginning
337
345
# of the next surface, with the radius of the rocket at that point
@@ -368,18 +376,41 @@ def draw(self, vis_args=None):
368
376
color = vis_args ["body" ],
369
377
linewidth = vis_args ["line_width" ],
370
378
)
379
+ return radius , last_x
371
380
372
- # Draw motor
381
+ def _draw_motor (self , last_radius , last_x , ax , vis_args ):
382
+ """Draws the motor from motor patches"""
373
383
total_csys = self .rocket ._csys * self .rocket .motor ._csys
374
384
nozzle_position = (
375
385
self .rocket .motor_position + self .rocket .motor .nozzle_position * total_csys
376
386
)
377
387
378
- # List of motor patches
388
+ # Get motor patches translated to the correct position
389
+ motor_patches = self ._generate_motor_patches (total_csys , ax , vis_args )
390
+
391
+ # Draw patches
392
+ if not isinstance (self .rocket .motor , EmptyMotor ):
393
+ # Add nozzle last so it is in front of the other patches
394
+ nozzle = self .rocket .motor .plots ._generate_nozzle (
395
+ translate = (nozzle_position , 0 ), csys = self .rocket ._csys
396
+ )
397
+ motor_patches += [nozzle ]
398
+
399
+ outline = self .rocket .motor .plots ._generate_motor_region (
400
+ list_of_patches = motor_patches
401
+ )
402
+ # add outline first so it is behind the other patches
403
+ ax .add_patch (outline )
404
+ for patch in motor_patches :
405
+ ax .add_patch (patch )
406
+
407
+ self ._draw_nozzle_tube (last_radius , last_x , nozzle_position , ax , vis_args )
408
+
409
+ def _generate_motor_patches (self , total_csys , ax , vis_args ):
410
+ """Generates motor patches for drawing"""
379
411
motor_patches = []
380
412
381
- # Get motor patches translated to the correct position
382
- if isinstance (self .rocket .motor , (SolidMotor )):
413
+ if isinstance (self .rocket .motor , SolidMotor ):
383
414
grains_cm_position = (
384
415
self .rocket .motor_position
385
416
+ self .rocket .motor .grains_center_of_mass_position * total_csys
@@ -452,27 +483,17 @@ def draw(self, vis_args=None):
452
483
)
453
484
motor_patches += [tank ]
454
485
455
- # add nozzle last so it is in front of the other patches
456
- if not isinstance (self .rocket .motor , EmptyMotor ):
457
- nozzle = self .rocket .motor .plots ._generate_nozzle (
458
- translate = (nozzle_position , 0 ), csys = self .rocket ._csys
459
- )
460
- motor_patches += [nozzle ]
461
- outline = self .rocket .motor .plots ._generate_motor_region (
462
- list_of_patches = motor_patches
463
- )
464
- # add outline first so it is behind the other patches
465
- ax .add_patch (outline )
466
- for patch in motor_patches :
467
- ax .add_patch (patch )
486
+ return motor_patches
468
487
488
+ def _draw_nozzle_tube (self , last_radius , last_x , nozzle_position , ax , vis_args ):
489
+ """Draws the tube from the last surface to the nozzle position."""
469
490
# Check if nozzle is beyond the last surface, if so draw a tube
470
491
# to it, with the radius of the last surface
471
492
if self .rocket ._csys == 1 :
472
493
if nozzle_position < last_x :
473
494
x_tube = [last_x , nozzle_position ]
474
- y_tube = [radius , radius ]
475
- y_tube_negated = [- radius , - radius ]
495
+ y_tube = [last_radius , last_radius ]
496
+ y_tube_negated = [- last_radius , - last_radius ]
476
497
477
498
ax .plot (
478
499
x_tube ,
@@ -489,8 +510,8 @@ def draw(self, vis_args=None):
489
510
else : # if self.rocket._csys == -1:
490
511
if nozzle_position > last_x :
491
512
x_tube = [last_x , nozzle_position ]
492
- y_tube = [radius , radius ]
493
- y_tube_negated = [- radius , - radius ]
513
+ y_tube = [last_radius , last_radius ]
514
+ y_tube_negated = [- last_radius , - last_radius ]
494
515
495
516
ax .plot (
496
517
x_tube ,
@@ -505,11 +526,12 @@ def draw(self, vis_args=None):
505
526
linewidth = vis_args ["line_width" ],
506
527
)
507
528
508
- # Draw rail buttons
529
+ def _draw_rail_buttons (self , ax , vis_args ):
530
+ """Draws the rail buttons of the rocket."""
509
531
try :
510
532
buttons , pos = self .rocket .rail_buttons [0 ]
511
533
lower = pos
512
- upper = pos + buttons .buttons_distance * csys
534
+ upper = pos + buttons .buttons_distance * self . rocket . _csys
513
535
ax .scatter (
514
536
lower , - self .rocket .radius , marker = "s" , color = vis_args ["buttons" ], s = 15
515
537
)
@@ -519,6 +541,8 @@ def draw(self, vis_args=None):
519
541
except IndexError :
520
542
pass
521
543
544
+ def _draw_center_of_mass_and_pressure (self , ax ):
545
+ """Draws the center of mass and center of pressure of the rocket."""
522
546
# Draw center of mass and center of pressure
523
547
cm = self .rocket .center_of_mass (0 )
524
548
ax .scatter (cm , 0 , color = "#1565c0" , label = "Center of Mass" , s = 10 )
@@ -528,18 +552,6 @@ def draw(self, vis_args=None):
528
552
cp , 0 , label = "Static Center of Pressure" , color = "red" , s = 10 , zorder = 10
529
553
)
530
554
531
- # Set plot attributes
532
- plt .title ("Rocket Representation" )
533
- plt .xlim ()
534
- plt .ylim ([- self .rocket .radius * 4 , self .rocket .radius * 6 ])
535
- plt .xlabel ("Position (m)" )
536
- plt .ylabel ("Radius (m)" )
537
- plt .legend (bbox_to_anchor = (1.05 , 1 ), loc = "upper left" )
538
- plt .tight_layout ()
539
- plt .show ()
540
-
541
- return None
542
-
543
555
def all (self ):
544
556
"""Prints out all graphs available about the Rocket. It simply calls
545
557
all the other plotter methods in this class.
0 commit comments