Exemple #1
0
    def update(self, state: PhysicsState, origin: Entity):
        if not self._visible:
            self._hyperbola.visible = False
            self._ring.visible = False
            return

        orb_params = calc.orbit_parameters(state.reference_entity(),
                                           state.craft_entity())
        thickness = min(
            self.PROJECTION_THICKNESS * vpython.canvas.get_selected().range,
            abs(0.1 * max(orb_params.major_axis, orb_params.minor_axis)))
        pos = orb_params.centre - origin.pos
        direction = vpython.vector(*orb_params.eccentricity, 0)
        e_mag = calc.fastnorm(orb_params.eccentricity)

        if e_mag < 1:
            # Project an elliptical orbit
            self._hyperbola.visible = False
            self._ring.pos = vpython.vector(*pos, 0)
            # size.x is actually the thickness, confusingly.
            # size.y and size.z are the width and length of the ellipse.
            self._ring.size.y = orb_params.major_axis
            self._ring.size.z = orb_params.minor_axis
            self._ring.up = direction
            self._ring.size.x = thickness
            self._ring.visible = True

        elif e_mag > 1:
            # Project a hyperbolic orbit
            self._ring.visible = False
            self._hyperbola.origin = vpython.vector(*pos, 0)

            # For a vpython.curve object, setting the axis is confusing.
            # To control direction, set the .up attribute (magnitude doesn't
            # matter for .up)
            # To control size, set the .size attribute (magnitude matters).
            # Setting .axis will also change .size, not sure how.

            self._hyperbola.size = vpython.vector(orb_params.minor_axis / 2,
                                                  orb_params.major_axis / 2, 1)
            self._hyperbola.up = -direction
            self._hyperbola.radius = thickness
            self._hyperbola.visible = True
            assert self._hyperbola.axis.z == 0, self._hyperbola.axis
            assert self._hyperbola.up.z == 0, self._hyperbola.up

        else:
            # e == 1 pretty much never happens, and if it does we'll just wait
            # until it goes away
            return
Exemple #2
0
    def __init__(self, draw_state: PhysicsState, *, title: str,
                 running_as_mirror: bool) -> None:
        assert len(draw_state) >= 1

        self._state = draw_state
        # create a vpython canvas, onto which all 3D and HTML drawing happens.
        self._scene = self._init_canvas(title)

        self._show_label: bool = True
        self._show_trails: bool = DEFAULT_TRAILS
        self._paused: bool = False
        self._removed_saveload_hint: bool = False
        self._origin: Entity
        self.texture_path: Path = Path('data', 'textures')
        self._commands: List[Request] = []
        self._paused_label = vpython.label(
            text="Simulation paused; saving and loading enabled.\n"
            "When finished, unpause by setting a time acceleration.",
            xoffset=0,
            yoffset=200,
            line=False,
            height=25,
            border=20,
            opacity=1,
            visible=False)

        self._3dobjs: Dict[str, ThreeDeeObj] = {}
        # remove vpython ambient lighting
        self._scene.lights = []  # This line shouldn't be removed

        self._set_origin(common.DEFAULT_CENTRE)

        for entity in draw_state:
            self._3dobjs[entity.name] = self._build_threedeeobj(entity)

        self._orbit_projection = OrbitProjection()
        self._3dobjs[draw_state.reference].draw_landing_graphic(
            draw_state.reference_entity())
        self._3dobjs[draw_state.target].draw_landing_graphic(
            draw_state.target_entity())

        self._sidebar = Sidebar(self, running_as_mirror)
        self._scene.bind('keydown', self._handle_keydown)

        # Add an animation when launching the program to describe the solar
        # system and the current location
        while self._scene.range > 600000:
            vpython.rate(100)
            self._scene.range = self._scene.range * 0.92
        self.recentre_camera(common.DEFAULT_CENTRE)
Exemple #3
0
def navmode_heading(flight_state: PhysicsState) -> float:
    """
    Returns the heading that the craft should be facing in current navmode.

    Don't call this with Manual navmode.
    If the navmode is relative to the reference, and the reference is set to
    the craft, this will return the craft's current heading as a sane default.
    """
    navmode = flight_state.navmode
    craft = flight_state.craft_entity()
    ref = flight_state.reference_entity()
    targ = flight_state.target_entity()
    requested_vector: np.ndarray

    if navmode == Navmode['Manual']:
        raise ValueError('Autopilot requested for manual navmode')
    elif navmode.name == 'CCW Prograde':
        if flight_state.reference == flight_state.craft:
            return craft.heading
        normal = craft.pos - ref.pos
        requested_vector = np.array([-normal[1], normal[0]])
    elif navmode == Navmode['CW Retrograde']:
        if flight_state.reference == flight_state.craft:
            return craft.heading
        normal = craft.pos - ref.pos
        requested_vector = np.array([normal[1], -normal[0]])
    elif navmode == Navmode['Depart Reference']:
        if flight_state.reference == flight_state.craft:
            return craft.heading
        requested_vector = craft.pos - ref.pos
    elif navmode == Navmode['Approach Target']:
        if flight_state.target == flight_state.craft:
            return craft.heading
        requested_vector = targ.pos - craft.pos
    elif navmode == Navmode['Pro Targ Velocity']:
        if flight_state.target == flight_state.craft:
            return craft.heading
        requested_vector = craft.v - targ.v
    elif navmode == Navmode['Anti Targ Velocity']:
        if flight_state.target == flight_state.craft:
            return craft.heading
        requested_vector = targ.v - craft.v
    else:
        raise ValueError(f'Got an unexpected NAVMODE: {flight_state.navmode}')

    return np.arctan2(requested_vector[1], requested_vector[0])
Exemple #4
0
    def _update_obj(self, entity: Entity, state: PhysicsState, origin: Entity):
        super()._update_obj(entity, state, origin)
        self._obj.boosters.pos = self._obj.pos
        self._obj.boosters.axis = self._obj.axis
        # Attach the parachute to the forward cone of the habitat.
        self._obj.parachute.pos = (
            self._obj.pos + calc.angle_to_vpy(entity.heading) * entity.r * 0.8)

        parachute_is_visible = ((state.craft == entity.name)
                                and state.parachute_deployed)
        if parachute_is_visible:
            drag = calc.drag(state)
            drag_mag = np.inner(drag, drag)
        for parachute in [self._obj.parachute, self._small_habitat.parachute]:
            if not parachute_is_visible or drag_mag < 0.00001:
                # If parachute_is_visible == False, don't show the parachute.
                # If the drag is basically zero, don't show the parachute.
                parachute.visible = False
                continue

            if drag_mag > 0.1:
                parachute.width = self.PARACHUTE_RADIUS * 2
                parachute.height = self.PARACHUTE_RADIUS * 2
            else:
                # Below a certain threshold the parachute deflates.
                parachute.width = self.PARACHUTE_RADIUS
                parachute.height = self.PARACHUTE_RADIUS

            parachute.axis = -vpython.vec(*drag, 0)
            parachute.visible = True

        if not self._broken and entity.broken:
            # We weren't broken before, but looking at new data we realize
            # we're now broken. Change the habitat texture.
            new_texture = self._obj.texture.replace('Habitat.jpg',
                                                    'Habitat-broken.jpg')
            assert new_texture != self._obj.texture, \
                f'{new_texture!r} == {self._obj.texture!r}'
            self._obj.texture = new_texture
            self._small_habitat.texture = new_texture
            self._broken = entity.broken
        elif self._broken and not entity.broken:
            # We were broken before, but we've repaired ourselves somehow.
            new_texture = self._obj.texture.replace('Habitat-broken.jpg',
                                                    'Habitat.jpg')
            assert new_texture != self._obj.texture, \
                f'{new_texture!r} == {self._obj.texture!r}'
            self._obj.texture = new_texture
            self._small_habitat.texture = new_texture
            self._broken = entity.broken

        # Set reference and target arrows of the minimap habitat.
        same = state.reference == entity.name
        default = vpython.vector(0, 0, -1)

        ref_arrow_axis = (entity.screen_pos(state.reference_entity()).norm() *
                          entity.r * -1.2)
        v = entity.v - state.reference_entity().v
        velocity_arrow_axis = \
            vpython.vector(*v, 0).norm() * entity.r

        self._ref_arrow.axis = default if same else ref_arrow_axis
        self._velocity_arrow.axis = default if same else velocity_arrow_axis
        self._small_habitat.axis = self._obj.axis
        self._small_habitat.boosters.axis = self._obj.axis

        # Invisible-ize the SRBs if we ran out.
        if state.srb_time == common.SRB_EMPTY and self._obj.boosters.visible:
            self._obj.boosters.visible = False
            self._small_habitat.boosters.visible = False