Esempio n. 1
0
def render_variable_cell(
    id: str,
    value: T,
    formatter: VariableFormatter,
    cell_size: Tuple[int, int],
    is_selected: bool,
    frame: Optional[int] = None,
    highlight_range: Optional[Tuple[range, ig.Color4f]] = None,
) -> Tuple[Maybe[T], bool, bool, bool]:
    ig.push_id(id)

    window_pos = ig.get_window_position()
    item_spacing = ig.get_style().item_spacing

    cell_cursor_pos = ig.get_cursor_pos()
    cell_cursor_pos = (
        cell_cursor_pos.x + window_pos.x - item_spacing.x,
        cell_cursor_pos.y + window_pos.y - ig.get_scroll_y() - item_spacing.y,
    )

    if highlight_range is not None:
        assert frame is not None
        frames, color = highlight_range
        margin = 5
        offset_top = margin if frame == frames.start else 0
        offset_bottom = margin if frame == frames.stop - 1 else 0
        dl = ig.get_window_draw_list()
        dl.add_rect_filled(
            cell_cursor_pos[0] + margin,
            cell_cursor_pos[1] + offset_top,
            cell_cursor_pos[0] + cell_size[0] - margin,
            cell_cursor_pos[1] + cell_size[1] - offset_bottom,
            ig.get_color_u32_rgba(*color),
        )

    changed_data, selected, pressed = render_variable_value(
        'value',
        value,
        formatter,
        (
            cell_size[0] - 2 * item_spacing.x,
            cell_size[1] - 2 * item_spacing.y,
        ),
        highlight=is_selected,
    )

    clear_edit = ig.is_item_hovered() and ig.is_mouse_down(2)

    ig.pop_id()
    return changed_data, clear_edit, selected, pressed
Esempio n. 2
0
def input_down_gradual(name: str, until_full: float) -> float:
    ig.push_id('ctrl-down-gradual-' + name)
    down_start: Ref[Optional[float]] = use_state('down-start', None)

    down = _input_down_uncond(name)
    if not down:
        down_start.value = None
        result = 0.0
    elif down_start.value is None:
        down_start.value = time.time()
        result = 0.0
    else:
        result = min((time.time() - down_start.value) / until_full, 1)

    ig.pop_id()
    return result if ig.global_keyboard_capture() else 0.0
Esempio n. 3
0
def render_variable_value(
    id: str,
    value: T,
    formatter: VariableFormatter,
    size: Tuple[int, int],
    highlight: bool = False,
) -> Tuple[Maybe[T], bool, bool]:
    ig.push_id(id)

    if isinstance(formatter, TextFormatter):
        result = _render_text(value, formatter, size, highlight)
    elif isinstance(formatter, CheckboxFormatter):
        result = _render_checkbox(value, formatter, size, highlight)
    else:
        raise NotImplementedError(formatter)

    ig.pop_id()
    return result
Esempio n. 4
0
def render_key_binding_settings(id: str) -> None:
    ig.push_id(id)
    begin_binding_form()

    ig.text('Frame advance:')
    button_width = 0
    binding_button('frame-prev-fast', '-10', button_width)
    binding_button('frame-prev', '-1', button_width)
    binding_button('frame-prev-alt', '-1', button_width)
    binding_button('frame-next', '+1', button_width)
    binding_button('frame-next-alt', '+1', button_width)
    binding_button('frame-next-fast', '+10', button_width)

    ig.dummy(1, 5)

    ig.text('3D movement:')
    button_width = 70
    binding_button('3d-camera-move-f', 'Forward', button_width)
    binding_button('3d-camera-move-b', 'Back', button_width)
    binding_button('3d-camera-move-l', 'Left', button_width)
    binding_button('3d-camera-move-r', 'Right', button_width)
    binding_button('3d-camera-move-u', 'Up', button_width)
    binding_button('3d-camera-move-d', 'Down', button_width)

    ig.dummy(1, 5)

    ig.text('Playback:')
    button_width = 90
    binding_button('playback-play', 'Play', button_width)
    binding_button('playback-rewind', 'Rewind', button_width)
    binding_button('playback-speed-up', 'Speed up', button_width)
    binding_button('playback-slow-down', 'Slow down', button_width)
    ig.text_colored(
        'Tip: Use the frame advance keys\nduring playback to change speed\ntemporarily.',
        1.0,
        1.0,
        1.0,
        0.5,
    )

    ig.dummy(200, 1)
    ig.pop_id()
Esempio n. 5
0
def render_frame_slider(
    id: str,
    current_frame: int,
    num_frames: int,
    loaded_frames: List[int] = [],
) -> Maybe[int]:
    ig.push_id(id)

    pos = ig.get_cursor_pos()
    pos = (
        pos[0] + ig.get_window_position()[0],
        pos[1] + ig.get_window_position()[1] - ig.get_scroll_y(),
    )
    width = ig.get_content_region_available_width()

    ig.push_item_width(width)
    changed, new_frame = ig.slider_int(
        '##slider',
        current_frame,
        0,
        num_frames - 1,
    )
    ig.pop_item_width()

    dl = ig.get_window_draw_list()
    for frame in loaded_frames:
        line_pos = pos[0] + frame / num_frames * width
        dl.add_line(
            line_pos,
            pos[1] + 13,
            line_pos,
            pos[1] + 18,
            ig.get_color_u32_rgba(1, 0, 0, 1),
        )

    ig.pop_id()

    if changed:
        return Just(new_frame)
    else:
        return None
Esempio n. 6
0
def input_pressed_repeat(name: str, delay: float, per_second: float) -> int:
    ig.push_id('ctrl-pressed-repeat-' + name)
    down_start: Ref[Optional[float]] = use_state('down-start', None)
    counted = use_state('counted', 0)

    down = _input_down_uncond(name)
    if not down:
        down_start.value = None
        count = 0
    elif down_start.value is None:
        down_start.value = time.time()
        counted.value = 0
        count = 1
    else:
        held = time.time() - down_start.value
        total_count = int(per_second * max(held - delay, 0))
        count = total_count - counted.value
        counted.value = total_count

    ig.pop_id()
    return count if ig.global_keyboard_capture() else 0
Esempio n. 7
0
def render_input_text_with_error(
    id: str,
    value: str,
    buffer_size: int,
    width: int,
    validate: Callable[[str], T],
) -> Maybe[T]:
    ig.push_id(id)

    cursor_pos = ig.get_cursor_pos()
    cursor_pos = (
        ig.get_window_position()[0] + cursor_pos[0],
        ig.get_window_position()[1] + cursor_pos[1] - ig.get_scroll_y(),
    )

    ig.push_item_width(width)
    changed, new_value = ig.input_text('##input', value, buffer_size)
    ig.pop_item_width()

    result_value = None
    if changed:
        try:
            result_value = Just(validate(new_value))
        except:
            # TODO: Show error message
            dl = ig.get_window_draw_list()
            dl.add_rect(
                cursor_pos[0],
                cursor_pos[1],
                cursor_pos[0] + width,
                cursor_pos[1] + ig.get_text_line_height() +
                2 * ig.get_style().frame_padding[1],
                ig.get_color_u32_rgba(1, 0, 0, 1),
            )
            new_value = value

    ig.pop_id()
    return result_value
Esempio n. 8
0
  def render(self) -> None:
    ig.push_id(str(epoch))

    # if ig.is_key_pressed(ord('`')):
    #   self.show_debug_pane = not self.show_debug_pane

    self.handle_controller()

    prev_frame_time = use_state_with('prev-frame-time', time.time)
    accum_time = use_state('accum-time', 0.0)
    now = time.time()
    accum_time.value += now - prev_frame_time.value
    prev_frame_time.value = now

    play_speed = self.model.play_speed
    if play_speed == 0.0:
      accum_time.value = 0
    else:
      target_fps = 30 * abs(play_speed)
      target_dt = 1 / target_fps
      updates = 0
      while accum_time.value >= target_dt and updates < 20:
        accum_time.value -= target_dt
        self.model.selected_frame += 1 if play_speed > 0 else -1
        self.handle_controller()
        updates += 1

    ig_window_size = ig.get_window_size()
    window_size = (int(ig_window_size.x), int(ig_window_size.y))

    ig.columns(2)
    self.render_left_column(window_size)
    ig.next_column()
    self.render_right_column()
    ig.columns(1)

    ig.pop_id()
Esempio n. 9
0
def render_controller_settings(id: str) -> None:
    ig.push_id(id)
    begin_binding_form()

    def cardinal(prefix: str) -> None:
        ig.dummy(45, 1)
        ig.same_line()
        binding_button('n64-' + prefix + '^', prefix + '^')
        binding_button('n64-' + prefix + '<', prefix + '<')
        ig.same_line()
        ig.set_cursor_pos((110, ig.get_cursor_pos().y))
        binding_button('n64-' + prefix + '>', prefix + '>')
        ig.dummy(45, 1)
        ig.same_line()
        binding_button('n64-' + prefix + 'v', prefix + 'v')

    binding_button('n64-A', 'A')
    binding_button('n64-B', 'B')
    binding_button('n64-Z', 'Z')

    ig.dummy(1, 5)
    binding_button('n64-L', 'L')
    binding_button('n64-R', 'R')
    binding_button('n64-S', 'S')

    ig.dummy(1, 5)
    cardinal('')

    ig.dummy(1, 15)
    cardinal('C')

    ig.dummy(1, 15)
    cardinal('D')

    ig.dummy(200, 1)
    ig.pop_id()
Esempio n. 10
0
  def handle_controller(self) -> None:
    ig.push_id('controller-inputs')

    buttons_enabled = use_state('buttons-enabled', False)
    stick_enabled = use_state('stick-enabled', False)

    def add_callbacks() -> Ref[bool]:
      input_edit = Ref(False)
      def disable_controller(*args, **kwargs) -> None:
        if not input_edit.value:
          buttons_enabled.value = False
          stick_enabled.value = False
      self.model.on_edit(disable_controller)

      def frame_change(*args, **kwargs) -> None:
        if self.model.play_speed == 0.0:
          disable_controller()
      self.model.on_selected_frame_change(frame_change)
      return input_edit
    input_edit = use_state_with('initialize', add_callbacks).value

    prev_play_speed = use_state('prev-play-speed', 0.0)
    if self.model.play_speed != prev_play_speed.value:
      buttons_enabled.value = False
      stick_enabled.value = False
    prev_play_speed.value = self.model.play_speed

    controller_button_values = {
      'input-button-a': input_down('n64-A'),
      'input-button-b': input_down('n64-B'),
      'input-button-z': input_down('n64-Z'),
      'input-button-s': input_down('n64-S'),
      'input-button-l': input_down('n64-L'),
      'input-button-r': input_down('n64-R'),
      'input-button-cu': input_down('n64-C^'),
      'input-button-cl': input_down('n64-C<'),
      'input-button-cr': input_down('n64-C>'),
      'input-button-cd': input_down('n64-Cv'),
      'input-button-du': input_down('n64-D^'),
      'input-button-dl': input_down('n64-D<'),
      'input-button-dr': input_down('n64-D>'),
      'input-button-dd': input_down('n64-Dv'),
    }
    if any(controller_button_values.values()):
      buttons_enabled.value = True
      stick_enabled.value = True
    for variable_name, new_button_value in controller_button_values.items():
      variable = Variable(variable_name).with_frame(self.model.selected_frame)
      button_value = self.model.get(variable)
      if buttons_enabled.value and button_value != new_button_value:
        input_edit.value = True
        self.model.set(variable, new_button_value)
        input_edit.value = False

    controller_stick_values = (
      input_float('n64->') - input_float('n64-<'),
      input_float('n64-^') - input_float('n64-v'),
    )
    # Require a larger magnitude for enabling controller since dead zone may be too small
    if any(abs(v) > 0.1 for v in controller_stick_values):
      stick_enabled.value = True
      buttons_enabled.value = True
    if stick_enabled.value:
      stick_x_var = Variable('input-stick-x').with_frame(self.model.selected_frame)
      stick_y_var = Variable('input-stick-y').with_frame(self.model.selected_frame)
      new_stick = self.compute_stick_from_controller(*controller_stick_values)
      stick = (self.model.get(stick_x_var), self.model.get(stick_y_var))
      if stick != new_stick:
        input_edit.value = True
        self.model.set(stick_x_var, new_stick[0])
        self.model.set(stick_y_var, new_stick[1])
        input_edit.value = False

    ig.pop_id()
Esempio n. 11
0
def render_game_view_birds_eye(
    id: str,
    framebuffer_size: Tuple[int, int],
    model: Model,
    wall_hitbox_radius: float,
    hovered_surface: Optional[int],
    hidden_surfaces: Set[int],
) -> Optional[int]:
    ig.push_id(id)

    # TODO: Should zoom in on mouse when uncentered
    mouse_state = use_state('mouse-state', MouseTracker()).value
    zoom = use_state('zoom', -4.5)
    target: Ref[Optional[Tuple[float, float]]] = use_state('target', None)
    pos_y: Ref[Optional[float]] = use_state('pos-y', None)

    drag_amount = mouse_state.get_drag_amount()
    zoom.value += mouse_state.get_wheel_amount() / 5
    world_span_x = 200 / math.pow(2, zoom.value)

    viewport = get_viewport(framebuffer_size)

    mario_pos = get_mario_pos(model)

    # Camera xz

    camera_xz = (mario_pos[0], mario_pos[2])
    if target.value is not None:
        camera_xz = target.value

    if drag_amount != (0.0, 0.0):
        world_span_z = world_span_x * viewport.width / viewport.height
        if target.value is None:
            target.value = (mario_pos[0], mario_pos[2])
        target.value = (
            camera_xz[0] + drag_amount[1] * world_span_x / viewport.height,
            camera_xz[1] - drag_amount[0] * world_span_z / viewport.width,
        )
        camera_xz = target.value

    if ig.disableable_button('Lock to Mario', enabled=target.value
                             is not None):
        target.value = None

    # Camera y

    camera_y = mario_pos[1] + 500 if pos_y.value is None else pos_y.value

    ig.set_cursor_pos((viewport.width - 100, 10))
    ig.begin_child('##y-slider')
    new_y, reset = render_pos_y_slider('y-slider', camera_y, mario_pos[1])
    if reset:
        pos_y.value = None
    elif new_y is not None:
        pos_y.value = new_y
        camera_y = pos_y.value
    ig.end_child()

    camera = core.BirdsEyeCamera()
    camera.pos = (camera_xz[0], camera_y, camera_xz[1])
    camera.span_y = world_span_x

    # Mouse xz
    mouse_world_pos = get_mouse_world_pos_birds_eye(camera)
    mouse_ray: Optional[Tuple[Vec3f, Vec3f]]
    if mouse_world_pos is not None:
        ig.set_cursor_pos((10, viewport.height - 25))
        ig.text('(x, z) = (%.3f, %.3f)' % mouse_world_pos)
        mouse_ray = ((mouse_world_pos[0], camera.pos[1], mouse_world_pos[1]),
                     (0, -1, 0))
    else:
        mouse_ray = None

    if mouse_ray is None:
        new_hovered_surface = None
    else:
        new_hovered_surface = trace_ray(model, mouse_ray)

    render_game(
        model,
        viewport,
        camera,
        False,
        wall_hitbox_radius,
        hovered_surface=hovered_surface,
        hidden_surfaces=hidden_surfaces,
    )

    ig.pop_id()
    return new_hovered_surface
Esempio n. 12
0
def render_joystick_control(
    id: str,
    stick_x: float,
    stick_y: float,
    shape='square',
) -> Optional[Tuple[float, float]]:
    ig.push_id(id)
    state = use_state('', JoystickControlState()).value

    dl = ig.get_window_draw_list()

    padding = 10
    content_region = ig.get_content_region_available()
    size = min(
        content_region.x - ig.get_style().scrollbar_size - 2 * padding,
        content_region.y - 2 * padding,
        200,
    )
    size = max(size, 100)

    initial_cursor_pos = ig.get_cursor_pos()
    top_left = (
        initial_cursor_pos[0] + ig.get_window_position()[0] -
        ig.get_scroll_x() + padding,
        initial_cursor_pos[1] + ig.get_window_position()[1] -
        ig.get_scroll_y() + padding,
    )

    background_color = ig.get_color_u32_rgba(0, 0, 0, 0.3)
    if shape == 'square':
        dl.add_rect_filled(
            top_left[0],
            top_left[1],
            top_left[0] + size,
            top_left[1] + size,
            background_color,
        )
    elif shape == 'circle':
        dl.add_circle_filled(
            top_left[0] + size / 2,
            top_left[1] + size / 2,
            size / 2,
            background_color,
            num_segments=32,
        )

    result = None

    if state.active and ig.is_mouse_down():
        new_offset = state.get_value(ig.get_mouse_drag_delta(lock_threshold=0))

        new_stick_x = new_offset[0] / size * 2 - 1
        new_stick_y = (1 - new_offset[1] / size) * 2 - 1
        if shape == 'square':
            new_stick_x = min(max(new_stick_x, -1), 1)
            new_stick_y = min(max(new_stick_y, -1), 1)
        elif shape == 'circle':
            mag = math.sqrt(new_stick_x**2 + new_stick_y**2)
            if mag > 1:
                new_stick_x /= mag
                new_stick_y /= mag

        if (new_stick_x, new_stick_y) != (stick_x, stick_y):
            stick_x, stick_y = new_stick_x, new_stick_y
            result = (stick_x, stick_y)

    offset = (
        (stick_x + 1) / 2 * size,
        (1 - (stick_y + 1) / 2) * size,
    )

    dl.add_line(
        top_left[0] + size / 2,
        top_left[1] + size / 2,
        top_left[0] + offset[0],
        top_left[1] + offset[1],
        ig.get_color_u32_rgba(1, 1, 1, 0.5),
    )

    button_size = 20
    button_pos = (
        padding + initial_cursor_pos[0] + offset[0] - button_size / 2,
        padding + initial_cursor_pos[1] + offset[1] - button_size / 2,
    )
    ig.set_cursor_pos(button_pos)
    ig.button('##joystick-button', button_size, button_size)

    ig.set_cursor_pos((
        initial_cursor_pos[0],
        initial_cursor_pos[1] + size + 2 * padding,
    ))

    if ig.is_item_active():
        state.set_active(offset)
    else:
        state.reset()

    ig.pop_id()
    return result
Esempio n. 13
0
def render_tabs(
    id: str,
    tabs: List[TabInfo],
    open_tab_index: Optional[int] = None,
    allow_windowing=False,
) -> Tuple[Optional[int], Optional[int]]:
    ig.push_id(id)
    root_id = get_local_state_id_stack()
    ig.columns(2)

    closed_tab = None

    rendered = use_state('rendered', False)
    if not rendered.value:
        rendered.value = True
        ig.set_column_width(-1, 120)

    if len(tabs) == 0:
        ig.pop_id()
        return None, closed_tab

    selected_tab_index = use_state_with('selected-tab-index',
                                        lambda: open_tab_index or 0)
    selected_tab_id = use_state_with('selected-tab',
                                     lambda: tabs[selected_tab_index.value].id)

    if open_tab_index is not None:
        selected_tab_index.value = open_tab_index
        selected_tab_id.value = tabs[open_tab_index].id

    windowed_tabs = use_state('windowed-tabs', cast(Set[str], set())).value

    # TODO: Change selected tab if windowed

    # Handle deletion/insertion
    if selected_tab_index.value >= len(tabs):
        selected_tab_index.value = len(tabs) - 1
    if tabs[selected_tab_index.value].id != selected_tab_id.value:
        matching_indices = [
            i for i in range(len(tabs)) if tabs[i].id == selected_tab_id.value
        ]
        if len(matching_indices) > 0:
            selected_tab_index.value = matching_indices[0]
        else:
            selected_tab_id.value = tabs[selected_tab_index.value].id

    ig.begin_child('tabs')
    for i, tab in enumerate(tabs):
        if tab.id in windowed_tabs:
            continue

        _, selected = ig.selectable(
            tab.label + '##tab-' + tab.id,
            selected_tab_id.value == tab.id,
        )
        if selected:
            selected_tab_index.value = i
            selected_tab_id.value = tab.id

        if tab.closable and ig.is_item_hovered() and ig.is_mouse_clicked(2):
            closed_tab = i

        if allow_windowing or tab.closable:
            if ig.begin_popup_context_item(f'##ctx-{tab.id}'):
                if allow_windowing and ig.selectable('Pop out')[0]:
                    windowed_tabs.add(tab.id)
                if tab.closable and ig.selectable('Close')[0]:
                    closed_tab = i
                ig.end_popup_context_item()

    ig.end_child()

    ig.next_column()

    ig.begin_child('content', flags=ig.WINDOW_HORIZONTAL_SCROLLING_BAR)
    tab = tabs[selected_tab_index.value]
    if tab.id not in windowed_tabs:
        push_local_state_rebase(('rebase-tabs', ) + root_id)
        tab.render(tab.id)  # type: ignore
        pop_local_state_rebase()
    ig.end_child()

    ig.columns(1)

    for tab_id in set(windowed_tabs):
        matching = [tab for tab in tabs if tab.id == tab_id]
        if len(matching) == 0:
            windowed_tabs.remove(tab.id)
            continue
        tab = matching[0]

        ig.set_next_window_size(*ig.get_window_size(), ig.ONCE)
        ig.set_next_window_position(*ig.get_window_position(), ig.ONCE)

        ig.push_style_color(ig.COLOR_WINDOW_BACKGROUND, 0.06, 0.06, 0.06, 0.94)
        _, opened = ig.begin(
            tab.label + '##window-' + tab.id,
            closable=True,
            flags=ig.WINDOW_HORIZONTAL_SCROLLING_BAR,
        )
        push_local_state_rebase(('rebase-tabs', ) + root_id)
        tab.render(tab.id)  # type: ignore
        pop_local_state_rebase()
        ig.end()
        ig.pop_style_color()

        if not opened:
            windowed_tabs.remove(tab.id)

    ig.pop_id()
    return (
        None if open_tab_index == selected_tab_index.value else
        selected_tab_index.value,
        closed_tab,
    )