@@ -244,9 +244,9 @@ class ShotProps:
244244 cant_cosine : float # Cosine of the cant angle
245245 cant_sine : float # Sine of the cant angle
246246 alt0_ft : float # Initial altitude in feet
247- calc_step : float # Calculation step size
248247 muzzle_velocity_fps : float # Muzzle velocity in feet per second
249248 stability_coefficient : float = field (init = False )
249+ calc_step : float = field (init = False ) # Calculation step size
250250 filter_flags : Union [TrajFlag , int ] = field (init = False )
251251
252252 def __post_init__ (self ):
@@ -260,6 +260,28 @@ def winds(self) -> Tuple[Wind, ...]:
260260 def look_angle (self ) -> Angular :
261261 return Angular .Radian (self .look_angle_rad )
262262
263+ @classmethod
264+ def from_shot (cls , shot : Shot ) -> 'ShotProps' :
265+ """Initializes a ShotProps instance from a Shot instance."""
266+ return cls (
267+ shot = shot ,
268+ bc = shot .ammo .dm .BC ,
269+ curve = calculate_curve (shot .ammo .dm .drag_table ),
270+ mach_list = _get_only_mach_data (shot .ammo .dm .drag_table ),
271+ look_angle_rad = shot .look_angle >> Angular .Radian ,
272+ twist_inch = shot .weapon .twist >> Distance .Inch ,
273+ length_inch = shot .ammo .dm .length >> Distance .Inch ,
274+ diameter_inch = shot .ammo .dm .diameter >> Distance .Inch ,
275+ weight_grains = shot .ammo .dm .weight >> Weight .Grain ,
276+ barrel_elevation_rad = shot .barrel_elevation >> Angular .Radian ,
277+ barrel_azimuth_rad = shot .barrel_azimuth >> Angular .Radian ,
278+ sight_height_ft = shot .weapon .sight_height >> Distance .Foot ,
279+ cant_cosine = math .cos (shot .cant_angle >> Angular .Radian ),
280+ cant_sine = math .sin (shot .cant_angle >> Angular .Radian ),
281+ alt0_ft = shot .atmo .altitude >> Distance .Foot ,
282+ muzzle_velocity_fps = shot .ammo .get_velocity_for_temp (shot .atmo .powder_temp ) >> Velocity .FPS ,
283+ )
284+
263285 def drag_by_mach (self , mach : float ) -> float :
264286 """
265287 Calculates a standard drag factor (SDF) for the given Mach number. Where:
@@ -535,6 +557,18 @@ def calculate_curve(data_points: List[DragDataPoint]) -> List[CurvePoint]:
535557 return curve
536558
537559
560+ def _get_only_mach_data (data : List [DragDataPoint ]) -> List [float ]:
561+ """
562+ Extracts Mach values from a list of DragDataPoint objects.
563+
564+ Args:
565+ data (List[DragDataPoint]): A list of DragDataPoint objects.
566+
567+ Returns:
568+ List[float]: A list containing only the Mach values from the input data.
569+ """
570+ return [dp .Mach for dp in data ]
571+
538572# # use ._get_only_mach_data with ._calculate_by_curve_and_mach_list because it's faster
539573# def calculate_by_curve(data: List[DragDataPoint], curve: List[CurvePoint], mach: float) -> float:
540574# """
@@ -660,7 +694,7 @@ class HitResult:
660694 extra (bool): Whether special points (TrajFlag > 0) were requested.
661695 error (Optional[RangeError]): RangeError, if any.
662696 """
663- shot : ShotProps
697+ props : ShotProps
664698 trajectory : list [TrajectoryData ] = field (repr = False )
665699 extra : bool = False
666700 error : Optional [RangeError ] = None
@@ -681,6 +715,11 @@ def __check_extra__(self):
681715 f"Use Calculator.fire(..., extra_data=True)"
682716 )
683717
718+ def __check_flag__ (self , flag : Union [TrajFlag , int ]):
719+ if not self .props .filter_flags & flag :
720+ raise AttributeError (f"{ TrajFlag .name (flag )} was not requested in trajectory. "
721+ f"Use Calculator.fire(..., extra_data=True)" )
722+
684723 def zeros (self ) -> list [TrajectoryData ]:
685724 """
686725 Returns:
@@ -690,7 +729,7 @@ def zeros(self) -> list[TrajectoryData]:
690729 AttributeError: If extra_data was not requested.
691730 ArithmeticError: If zero crossing points are not found.
692731 """
693- self .__check_extra__ ( )
732+ self .__check_flag__ ( TrajFlag . ZERO )
694733 data = [row for row in self .trajectory if row .flag & TrajFlag .ZERO ]
695734 if len (data ) < 1 :
696735 raise ArithmeticError ("Can't find zero crossing points" )
@@ -702,9 +741,9 @@ def flag(self, flag: Union[TrajFlag, int]) -> Optional[TrajectoryData]:
702741 TrajectoryData: First TrajectoryData row with the specified flag.
703742
704743 Raises:
705- AttributeError: If extra_data was not requested.
744+ AttributeError: If flag was not requested.
706745 """
707- self .__check_extra__ ( )
746+ self .__check_flag__ ( flag )
708747 for row in self .trajectory :
709748 if row .flag & flag :
710749 return row
@@ -934,7 +973,7 @@ def danger_space(self,
934973 target_height_half = target_height .raw_value / 2.0
935974
936975 target_row = self .get_at ('slant_distance' , target_at_range )
937- is_climbing = target_row .angle .raw_value - self .shot .look_angle .raw_value > 0
976+ is_climbing = target_row .angle .raw_value - self .props .look_angle .raw_value > 0
938977 slant_height_begin = target_row .slant_height .raw_value + (- 1 if is_climbing else 1 ) * target_height_half
939978 slant_height_end = target_row .slant_height .raw_value - (- 1 if is_climbing else 1 ) * target_height_half
940979 try :
@@ -950,7 +989,7 @@ def danger_space(self,
950989 target_height ,
951990 begin_row ,
952991 end_row ,
953- self .shot .look_angle )
992+ self .props .look_angle )
954993
955994 def dataframe (self , formatted : bool = False ) -> 'DataFrame' : # type: ignore
956995 """
0 commit comments