class ImportTool(wx.BoxSizer, DefaultBaseToolUI):
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.VERTICAL)
        DefaultBaseToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)

        self._open_file_button = wx.Button(canvas, label="Import File")
        self._open_file_button.Bind(wx.EVT_BUTTON, self._on_open_file)
        self.AddStretchSpacer()
        self.Add(self._open_file_button, flag=wx.ALL, border=10)
        self.AddStretchSpacer()

    @property
    def name(self) -> str:
        return "Import"

    def bind_events(self):
        super().bind_events()
        self._selection.bind_events()

    def enable(self):
        super().enable()
        self._selection.update_selection()

    def disable(self):
        super().disable()

    def _on_open_file(self, evt):
        with wx.FileDialog(
                self.canvas,
                "Open a Minecraft data file",
                wildcard="|".
                join([  # TODO: Automatically load these from the FormatWrapper classes.
                    "All files (*.construction;*.mcstructure;*.schematic)|*.construction;*.mcstructure;*.schematic",
                    "Construction file (*.construction)|*.construction",
                    "Bedrock mcstructure file (*.mcstructure)|*.mcstructure",
                    "Legacy Schematic file (*.schematic)|*.schematic",
                    "Sponge Schematic file (*.schem)|*.schem",
                ]),
                style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST,
        ) as fileDialog:
            if fileDialog.ShowModal() == wx.ID_CANCEL:
                return
            else:
                pathname = fileDialog.GetPath()
        try:
            level = amulet.load_level(pathname)
        except LoaderNoneMatched:
            wx.MessageBox(f"Could not find a matching loader for {pathname}.")
            log.error(f"Could not find a matching loader for {pathname}.")
        except Exception as e:
            log.error(
                f"Could not open {pathname}. Check the console for more details.\n{traceback.format_exc()}"
            )
            wx.MessageBox(
                f"Could not open {pathname}. Check the console for more details.\n{e}"
            )
        else:
            self.canvas.paste(level, level.dimensions[0])
示例#2
0
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.VERTICAL)
        DefaultBaseToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)

        self._open_file_button = wx.Button(canvas, label="Import File")
        self._open_file_button.Bind(wx.EVT_BUTTON, self._on_open_file)
        self.AddStretchSpacer()
        self.Add(self._open_file_button, flag=wx.ALL, border=10)
        self.AddStretchSpacer()
 def __init__(
     self,
     parent: wx.Window,
     canvas: "EditCanvas",
     world: "BaseLevel",
     options_path: str,
 ):
     super().__init__(parent, canvas, world, options_path)
     self._selection = StaticSelectionBehaviour(self.canvas)
     self._camera_behaviour = CameraBehaviour(self.canvas)
     self._pointer = PointerBehaviour(self.canvas)
     self._show_pointer = False
示例#4
0
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.HORIZONTAL)
        CameraToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)

        self._button_panel = wx.Panel(canvas)
        self._button_sizer = wx.BoxSizer(wx.VERTICAL)
        self._button_panel.SetSizer(self._button_sizer)
        paste_button = wx.Button(self._button_panel, label="Paste")
        self._button_sizer.Add(paste_button, 0, wx.ALL | wx.EXPAND, 5)
        paste_button.Bind(wx.EVT_BUTTON,
                          lambda evt: self.canvas.paste_from_cache())
        self.Add(self._button_panel, 0, wx.ALIGN_CENTER_VERTICAL)

        self._paste_panel: Optional[SelectTransformUI] = None
        self.Layout()
示例#5
0
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.HORIZONTAL)
        DefaultBaseToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)
        self._cursor = PointerBehaviour(self.canvas)
        self._moving = False

        self._button_panel = wx.Panel(canvas)
        self._button_sizer = wx.BoxSizer(wx.VERTICAL)
        self._button_panel.SetSizer(self._button_sizer)
        self.Add(self._button_panel, 0, wx.ALIGN_CENTER_VERTICAL)

        self._paste_panel = SelectTransformUI(self._button_panel)
        self._paste_panel.Bind(EVT_TRANSFORM_CHANGE, self._on_transform_change)
        self._button_sizer.Add(self._paste_panel, 0, wx.EXPAND)

        confirm_button = wx.Button(self._button_panel, label="Confirm")
        self._button_sizer.Add(confirm_button, 0, wx.ALL | wx.EXPAND, 5)
        confirm_button.Bind(wx.EVT_BUTTON, self._paste_confirm)

        self._button_panel.Disable()
        self.Layout()
示例#6
0
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.VERTICAL)
        CameraToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)

        self._active_operation: Optional[OperationUIType] = None

        horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)

        self._operation_choice = SimpleChoiceAny(self.canvas)
        self._reload_operation = wx.BitmapButton(
            self.canvas, bitmap=REFRESH_ICON.bitmap(16, 16)
        )
        self._reload_operation.SetToolTip("Reload Operations")

        horizontal_sizer.Add(self._operation_choice)
        horizontal_sizer.Add(self._reload_operation)

        self.Add(horizontal_sizer)

        assert isinstance(
            self.OperationGroupName, str
        ), "OperationGroupName has not been set or is not a string."

        self._operations = UIOperationManager(self.OperationGroupName)

        self._operation_choice.SetItems(
            {op.identifier: op.name for op in self._operations.operations}
        )
        self._operation_choice.Bind(wx.EVT_CHOICE, self._on_operation_change)

        self._reload_operation.Bind(wx.EVT_BUTTON, self._on_reload_operations)

        self._operation_sizer = wx.BoxSizer(wx.VERTICAL)
        self.Add(self._operation_sizer, 1, wx.EXPAND)
class DefaultOperationUI(OperationUI):
    """An extension of the base OperationUI that adds camera, static selection and some other controls."""
    def __init__(
        self,
        parent: wx.Window,
        canvas: "EditCanvas",
        world: "BaseLevel",
        options_path: str,
    ):
        super().__init__(parent, canvas, world, options_path)
        self._selection = StaticSelectionBehaviour(self.canvas)
        self._camera_behaviour = CameraBehaviour(self.canvas)
        self._pointer = PointerBehaviour(self.canvas)
        self._show_pointer = False

    def enable(self):
        self._selection.update_selection()
        self.canvas.camera.projection_mode = Projection.PERSPECTIVE

    def bind_events(self):
        self._selection.bind_events()
        self.canvas.Bind(EVT_DRAW, self._on_draw)
        self._camera_behaviour.bind_events()
        self._pointer.bind_events()
        self.canvas.Bind(EVT_INPUT_PRESS, self._on_input_press)

    def _on_draw(self, evt):
        self.canvas.renderer.start_draw()
        if self.canvas.camera.projection_mode == Projection.PERSPECTIVE:
            self.canvas.renderer.draw_sky_box()
            glClear(GL_DEPTH_BUFFER_BIT)
        self.canvas.renderer.draw_level()
        self._selection.draw()
        if self._show_pointer:
            self._pointer.draw()
        self.canvas.renderer.end_draw()

    def _on_input_press(self, evt: InputPressEvent):
        if evt.action_id == ACT_BOX_CLICK:
            self._on_box_click()
        evt.Skip()

    def _on_box_click(self):
        pass
示例#8
0
class PasteTool(wx.BoxSizer, DefaultBaseToolUI):
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.HORIZONTAL)
        DefaultBaseToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)
        self._cursor = PointerBehaviour(self.canvas)
        self._moving = False

        self._button_panel = wx.Panel(canvas)
        self._button_sizer = wx.BoxSizer(wx.VERTICAL)
        self._button_panel.SetSizer(self._button_sizer)
        self.Add(self._button_panel, 0, wx.ALIGN_CENTER_VERTICAL)

        self._paste_panel = SelectTransformUI(self._button_panel)
        self._paste_panel.Bind(EVT_TRANSFORM_CHANGE, self._on_transform_change)
        self._button_sizer.Add(self._paste_panel, 0, wx.EXPAND)

        confirm_button = wx.Button(self._button_panel, label="Confirm")
        self._button_sizer.Add(confirm_button, 0, wx.ALL | wx.EXPAND, 5)
        confirm_button.Bind(wx.EVT_BUTTON, self._paste_confirm)

        self._button_panel.Disable()
        self.Layout()

    @property
    def name(self) -> str:
        return "Paste"

    def bind_events(self):
        super().bind_events()
        self._selection.bind_events()
        self.canvas.Bind(EVT_PASTE, self._paste)
        self._cursor.bind_events()
        self.canvas.Bind(EVT_POINT_CHANGE, self._on_pointer_change)
        self.canvas.Bind(EVT_INPUT_PRESS, self._on_input_press)

    def enable(self):
        super().enable()
        self._selection.update_selection()
        self._moving = False

    def disable(self):
        super().disable()
        self._button_panel.Disable()
        self.canvas.renderer.fake_levels.clear()

    def _on_pointer_change(self, evt: PointChangeEvent):
        if self._moving:
            self.canvas.renderer.fake_levels.active_transform = (
                evt.point,
                self._paste_panel.scale,
                self._paste_panel.rotation_radians,
            )
            self._paste_panel.location = evt.point
        evt.Skip()

    def _on_transform_change(self, evt: TransformChangeEvent):
        self.canvas.renderer.fake_levels.active_transform = (
            evt.location,
            evt.scale,
            evt.rotation_radians,
        )
        evt.Skip()

    def _on_input_press(self, evt: InputPressEvent):
        if evt.action_id == ACT_BOX_CLICK:
            self._moving = not self._moving
            if self._moving:
                self.canvas.renderer.fake_levels.active_transform = (
                    self._paste_panel.location,
                    self._paste_panel.scale,
                    self._paste_panel.rotation_radians,
                )
        evt.Skip()

    def _paste(self, evt):
        self._button_panel.Enable()
        structure = evt.structure
        dimension = evt.dimension
        self.canvas.renderer.fake_levels.clear()
        self.canvas.renderer.fake_levels.append(structure, dimension,
                                                (0, 0, 0), (1, 1, 1),
                                                (0, 0, 0))
        self._moving = True

    def _paste_confirm(self, evt):
        fake_levels = self.canvas.renderer.fake_levels
        level_index: int = fake_levels.active_level_index
        if level_index is not None:
            render_level: RenderLevel = fake_levels.render_levels[level_index]
            self.canvas.run_operation(lambda: paste_iter(
                self.canvas.world,
                self.canvas.dimension,
                render_level.level,
                render_level.dimension,
                self._paste_panel.location,
                self._paste_panel.scale,
                self._paste_panel.rotation,
                self._paste_panel.copy_air,
                self._paste_panel.copy_water,
                self._paste_panel.copy_lava,
            ))

    def _on_draw(self, evt):
        self.canvas.renderer.start_draw()
        if self.canvas.camera.projection_mode == Projection.PERSPECTIVE:
            self.canvas.renderer.draw_sky_box()
            glClear(GL_DEPTH_BUFFER_BIT)
        self.canvas.renderer.draw_level()
        self.canvas.renderer.draw_fake_levels()
        self._selection.draw()
        self.canvas.renderer.end_draw()
示例#9
0
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.HORIZONTAL)
        DefaultBaseToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)
        self._cursor = PointerBehaviour(self.canvas)
        self._moving = False
        self._is_enabled = False

        self._paste_panel = wx.Panel(canvas)
        self._paste_sizer = wx.BoxSizer(wx.VERTICAL)
        self._paste_panel.SetSizer(self._paste_sizer)
        self.Add(self._paste_panel, 0, wx.ALIGN_CENTER_VERTICAL)

        def add_line():
            """add a line to the UI"""
            line = wx.StaticLine(self._paste_panel)
            self._paste_sizer.Add(line, 0, wx.BOTTOM | wx.EXPAND, 5)

        def add_tick_box(name: str, state: bool = True):
            tick = wx.CheckBox(self._paste_panel, label=name)
            tick.SetValue(state)
            self._paste_sizer.Add(
                tick,
                flag=BottomLeftRight,
                border=5,
            )
            return tick

        def add_label(name: str):
            label = wx.StaticText(self._paste_panel, label=name)
            label.SetFont(
                wx.Font(
                    12,
                    wx.FONTFAMILY_DEFAULT,
                    wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_NORMAL,
                    True,
                ))
            self._paste_sizer.Add(label, 0, BottomLeftRightCentre, 5)

        self._paste_sizer.AddSpacer(5)
        add_label(lang.get("program_3d_edit.paste_tool.location_label"))
        self._location = TupleIntInput(
            self._paste_panel,
            lang.get("program_3d_edit.paste_tool.location_x_label"),
            lang.get("program_3d_edit.paste_tool.location_y_label"),
            lang.get("program_3d_edit.paste_tool.location_z_label"),
        )
        self._location.x.SetToolTip(
            lang.get("program_3d_edit.paste_tool.location_x_tooltip"))
        self._location.y.SetToolTip(
            lang.get("program_3d_edit.paste_tool.location_y_tooltip"))
        self._location.z.SetToolTip(
            lang.get("program_3d_edit.paste_tool.location_z_tooltip"))
        self._paste_sizer.Add(
            self._location,
            flag=BottomLeftRightExpand,
            border=5,
        )

        self._move_button = MoveButton(
            self._paste_panel,
            self.canvas.camera,
            self.canvas.key_binds,
            lang.get("program_3d_edit.paste_tool.move_selection_label"),
            lang.get("program_3d_edit.paste_tool.move_selection_tooltip"),
            self,
        )
        self._paste_sizer.Add(
            self._move_button,
            flag=BottomLeftRightExpand,
            border=5,
        )

        add_line()

        add_label(lang.get("program_3d_edit.paste_tool.rotation_label"))
        self._free_rotation = wx.CheckBox(
            self._paste_panel,
            label=lang.get("program_3d_edit.paste_tool.free_rotation_label"),
        )
        self._free_rotation.SetToolTip(
            lang.get("program_3d_edit.paste_tool.free_rotation_tooltip"))
        self._paste_sizer.Add(
            self._free_rotation,
            flag=BottomLeftRight,
            border=5,
        )

        self._rotation = RotationTupleInput(
            self._paste_panel,
            lang.get("program_3d_edit.paste_tool.rotation_x_label"),
            lang.get("program_3d_edit.paste_tool.rotation_y_label"),
            lang.get("program_3d_edit.paste_tool.rotation_z_label"),
        )
        self._rotation.x.SetToolTip(
            lang.get("program_3d_edit.paste_tool.rotation_x_tooltip"))
        self._rotation.y.SetToolTip(
            lang.get("program_3d_edit.paste_tool.rotation_y_tooltip"))
        self._rotation.z.SetToolTip(
            lang.get("program_3d_edit.paste_tool.rotation_z_tooltip"))
        self._paste_sizer.Add(
            self._rotation,
            flag=BottomLeftRightExpand,
            border=5,
        )
        self._free_rotation.Bind(wx.EVT_CHECKBOX,
                                 self._on_free_rotation_change)

        rotate_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._paste_sizer.Add(
            rotate_sizer,
            flag=BottomLeftRightCentre,
            border=5,
        )

        self._rotate_left_button = wx.BitmapButton(
            self._paste_panel,
            bitmap=image.icon.tablericons.rotate_2.bitmap(22, 22))
        self._rotate_left_button.SetToolTip(
            lang.get(
                "program_3d_edit.paste_tool.rotate_anti_clockwise_tooltip"))
        self._rotate_left_button.Bind(wx.EVT_BUTTON, self._on_rotate_left)
        rotate_sizer.Add(self._rotate_left_button)

        self._rotate_right_button = wx.BitmapButton(
            self._paste_panel,
            bitmap=image.icon.tablericons.rotate_clockwise_2.bitmap(22, 22),
        )
        self._rotate_right_button.SetToolTip(
            lang.get("program_3d_edit.paste_tool.rotate_clockwise_tooltip"))
        self._rotate_right_button.Bind(wx.EVT_BUTTON, self._on_rotate_right)
        rotate_sizer.Add(self._rotate_right_button)

        add_line()

        add_label(lang.get("program_3d_edit.paste_tool.scale_label"))
        self._scale = TupleFloatInput(
            self._paste_panel,
            lang.get("program_3d_edit.paste_tool.scale_x_label"),
            lang.get("program_3d_edit.paste_tool.scale_y_label"),
            lang.get("program_3d_edit.paste_tool.scale_z_label"),
            start_value=1,
        )
        self._scale.x.SetToolTip(
            lang.get("program_3d_edit.paste_tool.scale_x_tooltip"))
        self._scale.y.SetToolTip(
            lang.get("program_3d_edit.paste_tool.scale_y_tooltip"))
        self._scale.z.SetToolTip(
            lang.get("program_3d_edit.paste_tool.scale_z_tooltip"))
        self._scale.x.SetDigits(2)
        self._scale.y.SetDigits(2)
        self._scale.z.SetDigits(2)
        self._paste_sizer.Add(
            self._scale,
            flag=BottomLeftRightExpand,
            border=5,
        )

        self._paste_panel.Bind(wx.EVT_SPINCTRL, self._on_transform_change)
        self._paste_panel.Bind(wx.EVT_SPINCTRLDOUBLE,
                               self._on_transform_change)

        mirror_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._paste_sizer.Add(
            mirror_sizer,
            flag=BottomLeftRightCentre,
            border=5,
        )

        # the tablericons file names are the wrong way around
        self._mirror_horizontal_button = wx.BitmapButton(
            self._paste_panel,
            bitmap=image.icon.tablericons.flip_vertical.bitmap(22, 22),
        )
        self._mirror_horizontal_button.SetToolTip(
            lang.get("program_3d_edit.paste_tool.mirror_horizontal_tooltip"))
        self._mirror_horizontal_button.Bind(wx.EVT_BUTTON,
                                            self._on_mirror_horizontal)
        mirror_sizer.Add(self._mirror_horizontal_button)

        self._mirror_vertical_button = wx.BitmapButton(
            self._paste_panel,
            bitmap=image.icon.tablericons.flip_horizontal.bitmap(22, 22),
        )
        self._mirror_vertical_button.SetToolTip(
            lang.get("program_3d_edit.paste_tool.mirror_vertical_tooltip"))
        self._mirror_vertical_button.Bind(wx.EVT_BUTTON,
                                          self._on_mirror_vertical)
        mirror_sizer.Add(self._mirror_vertical_button)

        add_line()

        self._copy_air = add_tick_box(
            lang.get("program_3d_edit.paste_tool.copy_air_label"))
        self._copy_air.SetToolTip(
            lang.get("program_3d_edit.paste_tool.copy_air_tooltip"))
        self._copy_water = add_tick_box(
            lang.get("program_3d_edit.paste_tool.copy_water_label"))
        self._copy_water.SetToolTip(
            lang.get("program_3d_edit.paste_tool.copy_water_tooltip"))
        self._copy_lava = add_tick_box(
            lang.get("program_3d_edit.paste_tool.copy_lava_label"))
        self._copy_lava.SetToolTip(
            lang.get("program_3d_edit.paste_tool.copy_lava_tooltip"))

        add_line()

        confirm_button = wx.Button(self._paste_panel, label="Confirm")
        self._paste_sizer.Add(confirm_button, 0, BottomLeftRightExpand, 5)
        confirm_button.Bind(wx.EVT_BUTTON, self._paste_confirm)

        self._paste_panel.Disable()
        self.Layout()
示例#10
0
class PasteTool(wx.BoxSizer, DefaultBaseToolUI):
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.HORIZONTAL)
        DefaultBaseToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)
        self._cursor = PointerBehaviour(self.canvas)
        self._moving = False
        self._is_enabled = False

        self._paste_panel = wx.Panel(canvas)
        self._paste_sizer = wx.BoxSizer(wx.VERTICAL)
        self._paste_panel.SetSizer(self._paste_sizer)
        self.Add(self._paste_panel, 0, wx.ALIGN_CENTER_VERTICAL)

        def add_line():
            """add a line to the UI"""
            line = wx.StaticLine(self._paste_panel)
            self._paste_sizer.Add(line, 0, wx.BOTTOM | wx.EXPAND, 5)

        def add_tick_box(name: str, state: bool = True):
            tick = wx.CheckBox(self._paste_panel, label=name)
            tick.SetValue(state)
            self._paste_sizer.Add(
                tick,
                flag=BottomLeftRight,
                border=5,
            )
            return tick

        def add_label(name: str):
            label = wx.StaticText(self._paste_panel, label=name)
            label.SetFont(
                wx.Font(
                    12,
                    wx.FONTFAMILY_DEFAULT,
                    wx.FONTSTYLE_NORMAL,
                    wx.FONTWEIGHT_NORMAL,
                    True,
                ))
            self._paste_sizer.Add(label, 0, BottomLeftRightCentre, 5)

        self._paste_sizer.AddSpacer(5)
        add_label(lang.get("program_3d_edit.paste_tool.location_label"))
        self._location = TupleIntInput(
            self._paste_panel,
            lang.get("program_3d_edit.paste_tool.location_x_label"),
            lang.get("program_3d_edit.paste_tool.location_y_label"),
            lang.get("program_3d_edit.paste_tool.location_z_label"),
        )
        self._location.x.SetToolTip(
            lang.get("program_3d_edit.paste_tool.location_x_tooltip"))
        self._location.y.SetToolTip(
            lang.get("program_3d_edit.paste_tool.location_y_tooltip"))
        self._location.z.SetToolTip(
            lang.get("program_3d_edit.paste_tool.location_z_tooltip"))
        self._paste_sizer.Add(
            self._location,
            flag=BottomLeftRightExpand,
            border=5,
        )

        self._move_button = MoveButton(
            self._paste_panel,
            self.canvas.camera,
            self.canvas.key_binds,
            lang.get("program_3d_edit.paste_tool.move_selection_label"),
            lang.get("program_3d_edit.paste_tool.move_selection_tooltip"),
            self,
        )
        self._paste_sizer.Add(
            self._move_button,
            flag=BottomLeftRightExpand,
            border=5,
        )

        add_line()

        add_label(lang.get("program_3d_edit.paste_tool.rotation_label"))
        self._free_rotation = wx.CheckBox(
            self._paste_panel,
            label=lang.get("program_3d_edit.paste_tool.free_rotation_label"),
        )
        self._free_rotation.SetToolTip(
            lang.get("program_3d_edit.paste_tool.free_rotation_tooltip"))
        self._paste_sizer.Add(
            self._free_rotation,
            flag=BottomLeftRight,
            border=5,
        )

        self._rotation = RotationTupleInput(
            self._paste_panel,
            lang.get("program_3d_edit.paste_tool.rotation_x_label"),
            lang.get("program_3d_edit.paste_tool.rotation_y_label"),
            lang.get("program_3d_edit.paste_tool.rotation_z_label"),
        )
        self._rotation.x.SetToolTip(
            lang.get("program_3d_edit.paste_tool.rotation_x_tooltip"))
        self._rotation.y.SetToolTip(
            lang.get("program_3d_edit.paste_tool.rotation_y_tooltip"))
        self._rotation.z.SetToolTip(
            lang.get("program_3d_edit.paste_tool.rotation_z_tooltip"))
        self._paste_sizer.Add(
            self._rotation,
            flag=BottomLeftRightExpand,
            border=5,
        )
        self._free_rotation.Bind(wx.EVT_CHECKBOX,
                                 self._on_free_rotation_change)

        rotate_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._paste_sizer.Add(
            rotate_sizer,
            flag=BottomLeftRightCentre,
            border=5,
        )

        self._rotate_left_button = wx.BitmapButton(
            self._paste_panel,
            bitmap=image.icon.tablericons.rotate_2.bitmap(22, 22))
        self._rotate_left_button.SetToolTip(
            lang.get(
                "program_3d_edit.paste_tool.rotate_anti_clockwise_tooltip"))
        self._rotate_left_button.Bind(wx.EVT_BUTTON, self._on_rotate_left)
        rotate_sizer.Add(self._rotate_left_button)

        self._rotate_right_button = wx.BitmapButton(
            self._paste_panel,
            bitmap=image.icon.tablericons.rotate_clockwise_2.bitmap(22, 22),
        )
        self._rotate_right_button.SetToolTip(
            lang.get("program_3d_edit.paste_tool.rotate_clockwise_tooltip"))
        self._rotate_right_button.Bind(wx.EVT_BUTTON, self._on_rotate_right)
        rotate_sizer.Add(self._rotate_right_button)

        add_line()

        add_label(lang.get("program_3d_edit.paste_tool.scale_label"))
        self._scale = TupleFloatInput(
            self._paste_panel,
            lang.get("program_3d_edit.paste_tool.scale_x_label"),
            lang.get("program_3d_edit.paste_tool.scale_y_label"),
            lang.get("program_3d_edit.paste_tool.scale_z_label"),
            start_value=1,
        )
        self._scale.x.SetToolTip(
            lang.get("program_3d_edit.paste_tool.scale_x_tooltip"))
        self._scale.y.SetToolTip(
            lang.get("program_3d_edit.paste_tool.scale_y_tooltip"))
        self._scale.z.SetToolTip(
            lang.get("program_3d_edit.paste_tool.scale_z_tooltip"))
        self._scale.x.SetDigits(2)
        self._scale.y.SetDigits(2)
        self._scale.z.SetDigits(2)
        self._paste_sizer.Add(
            self._scale,
            flag=BottomLeftRightExpand,
            border=5,
        )

        self._paste_panel.Bind(wx.EVT_SPINCTRL, self._on_transform_change)
        self._paste_panel.Bind(wx.EVT_SPINCTRLDOUBLE,
                               self._on_transform_change)

        mirror_sizer = wx.BoxSizer(wx.HORIZONTAL)
        self._paste_sizer.Add(
            mirror_sizer,
            flag=BottomLeftRightCentre,
            border=5,
        )

        # the tablericons file names are the wrong way around
        self._mirror_horizontal_button = wx.BitmapButton(
            self._paste_panel,
            bitmap=image.icon.tablericons.flip_vertical.bitmap(22, 22),
        )
        self._mirror_horizontal_button.SetToolTip(
            lang.get("program_3d_edit.paste_tool.mirror_horizontal_tooltip"))
        self._mirror_horizontal_button.Bind(wx.EVT_BUTTON,
                                            self._on_mirror_horizontal)
        mirror_sizer.Add(self._mirror_horizontal_button)

        self._mirror_vertical_button = wx.BitmapButton(
            self._paste_panel,
            bitmap=image.icon.tablericons.flip_horizontal.bitmap(22, 22),
        )
        self._mirror_vertical_button.SetToolTip(
            lang.get("program_3d_edit.paste_tool.mirror_vertical_tooltip"))
        self._mirror_vertical_button.Bind(wx.EVT_BUTTON,
                                          self._on_mirror_vertical)
        mirror_sizer.Add(self._mirror_vertical_button)

        add_line()

        self._copy_air = add_tick_box(
            lang.get("program_3d_edit.paste_tool.copy_air_label"))
        self._copy_air.SetToolTip(
            lang.get("program_3d_edit.paste_tool.copy_air_tooltip"))
        self._copy_water = add_tick_box(
            lang.get("program_3d_edit.paste_tool.copy_water_label"))
        self._copy_water.SetToolTip(
            lang.get("program_3d_edit.paste_tool.copy_water_tooltip"))
        self._copy_lava = add_tick_box(
            lang.get("program_3d_edit.paste_tool.copy_lava_label"))
        self._copy_lava.SetToolTip(
            lang.get("program_3d_edit.paste_tool.copy_lava_tooltip"))

        add_line()

        confirm_button = wx.Button(self._paste_panel, label="Confirm")
        self._paste_sizer.Add(confirm_button, 0, BottomLeftRightExpand, 5)
        confirm_button.Bind(wx.EVT_BUTTON, self._paste_confirm)

        self._paste_panel.Disable()
        self.Layout()

    @property
    def name(self) -> str:
        return "Paste"

    def bind_events(self):
        super().bind_events()
        self._selection.bind_events()
        self.canvas.Bind(EVT_PASTE, self._paste)
        self._cursor.bind_events()
        self.canvas.Bind(EVT_POINT_CHANGE, self._on_pointer_change)
        self.canvas.Bind(EVT_INPUT_PRESS, self._on_input_press)

    def enable(self):
        super().enable()
        self._move_button.enable()
        self._selection.update_selection()
        self._moving = False

    def disable(self):
        super().disable()
        self._move_button.disable()
        self._paste_panel.Disable()
        self._is_enabled = False
        self.canvas.renderer.fake_levels.clear()

    @property
    def location(self) -> PointCoordinates:
        """The location as specified in the UI."""
        return self._location.value

    @location.setter
    def location(self, location: PointCoordinates):
        """Set the location value.
        Will update the UI and the renderer."""
        self._location.value = location
        self._update_transform()

    def _on_free_rotation_change(self, evt):
        if self._free_rotation.GetValue():
            self._rotation.increment = 1
        else:
            self._rotation.increment = 90

    def _on_rotate_left(self, evt):
        self._rotate(-90)

    def _on_rotate_right(self, evt):
        self._rotate(90)

    def _rotate(self, angle: int):
        """Rotate the floating selection by the angle based on the camera rotation."""
        angle = math.radians(angle)
        ry, rx = self.canvas.camera.rotation
        if rx < -45:
            rotation_change = rotation_matrix_xyz(0, angle, 0)
        elif -45 <= rx < 45:
            if -135 <= ry < -45:
                # east
                rotation_change = rotation_matrix_xyz(angle, 0, 0)
            elif -45 <= ry < 45:
                # south
                rotation_change = rotation_matrix_xyz(0, 0, angle)
            elif 45 <= ry < 135:
                # west
                rotation_change = rotation_matrix_xyz(-angle, 0, 0)
            else:
                # north
                rotation_change = rotation_matrix_xyz(0, 0, -angle)
        else:
            rotation_change = rotation_matrix_xyz(0, -angle, 0)

        self._rotation.value = numpy.rad2deg(
            decompose_transformation_matrix(
                numpy.matmul(
                    rotation_change,
                    rotation_matrix_xyz(*self._rotation_radians())))[1])
        self._update_transform()

    def _rotation_radians(self) -> Tuple[float, float, float]:
        return tuple(math.radians(v) for v in self._rotation.value)

    def _on_mirror_vertical(self, evt):
        ry, rx = self.canvas.camera.rotation
        if -45 <= rx < 45:
            # looking north, east, south or west vertical mirror is always in y
            self._mirror(1)
        elif -135 <= ry < -45 or 45 <= ry < 135:
            # looking down or up facing east or west
            self._mirror(0)
        else:
            # looking down or up facing north or south
            self._mirror(2)

    def _on_mirror_horizontal(self, evt):
        ry, rx = self.canvas.camera.rotation
        if -135 <= ry < -45 or 45 <= ry < 135:
            # facing east or west
            self._mirror(2)
        else:
            # facing north or south
            self._mirror(0)

    def _mirror(self, axis: int):
        """Mirror the selection in the given axis.

        :param axis: The axis to scale in 0=x, 1=y, 2=z
        :return:
        """
        scale = [(-1, 1, 1), (1, -1, 1), (1, 1, -1)][axis]
        self._scale.value, rotation, _ = decompose_transformation_matrix(
            numpy.matmul(
                scale_matrix(*scale),
                transform_matrix(self._scale.value, self._rotation_radians(),
                                 (0, 0, 0)),
            ))
        self._rotation.value = numpy.rad2deg(rotation)
        self._update_transform()

    def _on_pointer_change(self, evt: PointChangeEvent):
        if self._is_enabled and self._moving:
            self.canvas.renderer.fake_levels.active_transform = (
                evt.point,
                self._scale.value,
                self._rotation_radians(),
            )
            self._location.value = evt.point
        evt.Skip()

    def _on_transform_change(self, evt):
        self._update_transform()
        evt.Skip()

    def _update_transform(self):
        """Update the renderer with the new values."""
        self.canvas.renderer.fake_levels.active_transform = (
            self._location.value,
            self._scale.value,
            self._rotation_radians(),
        )

    def _on_input_press(self, evt: InputPressEvent):
        if evt.action_id == ACT_BOX_CLICK:
            if self._is_enabled:
                self._moving = not self._moving
                if self._moving:
                    self.canvas.renderer.fake_levels.active_transform = (
                        self._location.value,
                        self._scale.value,
                        self._rotation_radians(),
                    )
        evt.Skip()

    def _paste(self, evt):
        self._paste_panel.Enable()
        self._is_enabled = True
        structure = evt.structure
        dimension = evt.dimension
        self.canvas.renderer.fake_levels.clear()
        self.canvas.renderer.fake_levels.append(structure, dimension,
                                                (0, 0, 0), (1, 1, 1),
                                                (0, 0, 0))
        self._moving = True

    def _paste_operation(self):
        if all(self._scale.value):
            fake_levels = self.canvas.renderer.fake_levels
            level_index: int = fake_levels.active_level_index
            if level_index is not None:
                render_level: RenderLevel = fake_levels.render_levels[
                    level_index]
                yield from paste_iter(
                    self.canvas.world,
                    self.canvas.dimension,
                    render_level.level,
                    render_level.dimension,
                    self._location.value,
                    self._scale.value,
                    self._rotation.value,
                    self._copy_air.GetValue(),
                    self._copy_water.GetValue(),
                    self._copy_lava.GetValue(),
                )
        else:
            raise OperationSuccessful(
                lang.get("program_3d_edit.paste_tool.zero_scale_message"))

    def _paste_confirm(self, evt):
        self.canvas.run_operation(self._paste_operation)

    def _on_draw(self, evt):
        self.canvas.renderer.start_draw()
        if self.canvas.camera.projection_mode == Projection.PERSPECTIVE:
            self.canvas.renderer.draw_sky_box()
            glClear(GL_DEPTH_BUFFER_BIT)
        self.canvas.renderer.draw_level()
        self.canvas.renderer.draw_fake_levels()
        self._selection.draw()
        self.canvas.renderer.end_draw()
示例#11
0
class PasteTool(wx.BoxSizer, CameraToolUI):
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.HORIZONTAL)
        CameraToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)

        self._button_panel = wx.Panel(canvas)
        self._button_sizer = wx.BoxSizer(wx.VERTICAL)
        self._button_panel.SetSizer(self._button_sizer)
        paste_button = wx.Button(self._button_panel, label="Paste")
        self._button_sizer.Add(paste_button, 0, wx.ALL | wx.EXPAND, 5)
        paste_button.Bind(wx.EVT_BUTTON,
                          lambda evt: self.canvas.paste_from_cache())
        self.Add(self._button_panel, 0, wx.ALIGN_CENTER_VERTICAL)

        self._paste_panel: Optional[SelectTransformUI] = None
        self.Layout()

    @property
    def name(self) -> str:
        return "Paste"

    def bind_events(self):
        super().bind_events()
        self._selection.bind_events()
        self.canvas.Bind(EVT_PASTE, self._paste)

    def enable(self):
        super().enable()
        self._selection.update_selection()

    def disable(self):
        super().disable()
        self.canvas.renderer.fake_levels.unload()

    def _remove_paste(self):
        if self._paste_panel is not None:
            self._paste_panel.Destroy()
            self._paste_panel = None

    def _paste(self, evt):
        structure = evt.structure
        dimension = evt.dimension
        self._remove_paste()
        self._paste_panel = SelectTransformUI(self._button_panel, self.canvas,
                                              structure, dimension,
                                              self._paste_confirm)
        self._button_sizer.Add(self._paste_panel, 0, wx.EXPAND)
        self.Layout()

    def _paste_confirm(self):
        self.canvas.run_operation(lambda: paste_iter(
            self.canvas.world,
            self.canvas.dimension,
            self._paste_panel.structure,
            self._paste_panel.dimension,
            self._paste_panel.location,
            (1, 1, 1),
            self._paste_panel.rotation,
            self._paste_panel.copy_air,
            self._paste_panel.copy_water,
            self._paste_panel.copy_lava,
        ))

    def _on_draw(self, evt):
        self.canvas.renderer.start_draw()
        if self.canvas.camera.projection_mode == Projection.PERSPECTIVE:
            self.canvas.renderer.draw_sky_box()
            glClear(GL_DEPTH_BUFFER_BIT)
        self.canvas.renderer.draw_level()
        self.canvas.renderer.draw_fake_levels()
        self._selection.draw()
        self.canvas.renderer.end_draw()
示例#12
0
class BaseSelectOperationUI(wx.BoxSizer, CameraToolUI):
    OperationGroupName = None

    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.VERTICAL)
        CameraToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)

        self._active_operation: Optional[OperationUIType] = None

        horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)

        self._operation_choice = SimpleChoiceAny(self.canvas)
        self._reload_operation = wx.BitmapButton(
            self.canvas, bitmap=REFRESH_ICON.bitmap(16, 16)
        )
        self._reload_operation.SetToolTip("Reload Operations")

        horizontal_sizer.Add(self._operation_choice)
        horizontal_sizer.Add(self._reload_operation)

        self.Add(horizontal_sizer)

        assert isinstance(
            self.OperationGroupName, str
        ), "OperationGroupName has not been set or is not a string."

        self._operations = UIOperationManager(self.OperationGroupName)

        self._operation_choice.SetItems(
            {op.identifier: op.name for op in self._operations.operations}
        )
        self._operation_choice.Bind(wx.EVT_CHOICE, self._on_operation_change)

        self._reload_operation.Bind(wx.EVT_BUTTON, self._on_reload_operations)

        self._operation_sizer = wx.BoxSizer(wx.VERTICAL)
        self.Add(self._operation_sizer, 1, wx.EXPAND)

    @property
    def name(self) -> str:
        raise NotImplementedError

    @property
    def operation(self) -> str:
        return self._operation_choice.GetCurrentObject()

    def _unload_active_operation(self):
        """Unload and destroy the UI for the active operation."""
        if self._active_operation is not None:
            self._active_operation.unload()
            if isinstance(self._active_operation, wx.Window):
                self._active_operation.Destroy()
            elif isinstance(self._active_operation, wx.Sizer):
                self._operation_sizer.GetItem(self._active_operation).DeleteWindows()
            self._active_operation = None

    def _on_operation_change(self, evt):
        """Run when the operation selection changes."""
        self._setup_operation()
        evt.Skip()

    def _setup_operation(self):
        """Create the UI for the new operation."""
        operation_path = self._operation_choice.GetCurrentObject()
        if operation_path:
            operation = self._operations[operation_path]
            self._unload_active_operation()
            self._active_operation = operation(
                self.canvas, self.canvas, self.canvas.world
            )
            self._operation_sizer.Add(
                self._active_operation, *self._active_operation.wx_add_options
            )
            self.Layout()

    def bind_events(self):
        super().bind_events()
        self._selection.bind_events()

    def enable(self):
        super().enable()
        self._selection.update_selection()
        self._setup_operation()

    def disable(self):
        super().disable()
        self._unload_active_operation()

    def _on_reload_operations(self, evt):
        """Run when the button is pressed to reload the operations."""
        self.reload_operations()

    def reload_operations(self):
        """Reload all operations and repopulate the UI."""
        # store the id of the old operation
        operation_id = self.operation

        # reload the operations
        self._operations.reload()

        # repopulate the selection
        self._operation_choice.SetItems(
            {op.identifier: op.name for op in self._operations.operations}
        )

        if operation_id:
            operation_loader = self._operations[operation_id]
            identifiers = self._operation_choice.values

            if identifiers:
                if operation_id in identifiers:
                    self._operation_choice.SetSelection(identifiers.index(operation_id))
                else:
                    log.info(f"Operation {operation_id} was not found.")
                    self._operation_choice.SetSelection(0)
            else:
                log.error("No operations found. Something has gone wrong.")

            self._setup_operation()

    def _on_draw(self, evt):
        self.canvas.renderer.start_draw()
        if self.canvas.camera.projection_mode == Projection.PERSPECTIVE:
            self.canvas.renderer.draw_sky_box()
            glClear(GL_DEPTH_BUFFER_BIT)
        self.canvas.renderer.draw_level()
        self._selection.draw()
        self.canvas.renderer.end_draw()