mirror of
https://github.com/Ultimaker/Cura.git
synced 2026-02-16 09:29:34 -07:00
Fix clear painting with optimized painting mechanisms
CURA-12731
This commit is contained in:
parent
c890a553dd
commit
a2db7b3004
5 changed files with 80 additions and 18 deletions
44
plugins/PaintTool/PaintClearCommand.py
Normal file
44
plugins/PaintTool/PaintClearCommand.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from PyQt6.QtGui import QUndoCommand, QImage, QPainter, QBrush
|
||||
|
||||
from UM.View.GL.Texture import Texture
|
||||
|
||||
from .PaintCommand import PaintCommand
|
||||
|
||||
|
||||
class PaintClearCommand(PaintCommand):
|
||||
"""Provides the command that clear all the painting for the current mode"""
|
||||
|
||||
def __init__(self, texture: Texture, bit_range: tuple[int, int]) -> None:
|
||||
super().__init__(texture, bit_range)
|
||||
|
||||
self._original_texture_image: Optional[QImage] = texture.getImage().copy()
|
||||
|
||||
self._cleared_texture = self._original_texture_image.copy()
|
||||
painter = QPainter(self._cleared_texture)
|
||||
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing, False)
|
||||
painter.setCompositionMode(QPainter.CompositionMode.RasterOp_SourceAndDestination)
|
||||
painter.fillRect(self._cleared_texture.rect(), QBrush(self._getClearTextureBitMask()))
|
||||
|
||||
painter.end()
|
||||
|
||||
def id(self) -> int:
|
||||
return 1
|
||||
|
||||
def redo(self) -> None:
|
||||
self._texture.setSubImage(self._cleared_texture, 0, 0)
|
||||
|
||||
def undo(self) -> None:
|
||||
self._texture.setSubImage(self._original_texture_image, 0, 0)
|
||||
|
||||
def mergeWith(self, command: QUndoCommand) -> bool:
|
||||
if not isinstance(command, PaintClearCommand):
|
||||
return False
|
||||
|
||||
# There is actually nothing more to do here, both clear commands already have the same original texture
|
||||
return True
|
||||
22
plugins/PaintTool/PaintCommand.py
Normal file
22
plugins/PaintTool/PaintCommand.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Copyright (c) 2025 UltiMaker
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
from PyQt6.QtGui import QUndoCommand
|
||||
|
||||
from UM.View.GL.Texture import Texture
|
||||
|
||||
|
||||
class PaintCommand(QUndoCommand):
|
||||
"""Provides a command that somehow modifies the actual painting on objects with undo/redo mechanisms"""
|
||||
|
||||
def __init__(self, texture: Texture, bit_range: tuple[int, int]) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._texture: Texture = texture
|
||||
self._bit_range: tuple[int, int] = bit_range
|
||||
|
||||
def _getClearTextureBitMask(self):
|
||||
bit_range_start, bit_range_end = self._bit_range
|
||||
full_int32 = 0xffffffff
|
||||
return full_int32 ^ (((full_int32 << (32 - 1 - (bit_range_end - bit_range_start))) & full_int32) >>
|
||||
(32 - 1 - bit_range_end))
|
||||
|
|
@ -192,11 +192,7 @@ class PaintTool(Tool):
|
|||
self._updateScene()
|
||||
|
||||
def clear(self) -> None:
|
||||
width, height = self._view.getUvTexDimensions()
|
||||
clear_image = QImage(width, height, QImage.Format.Format_RGB32)
|
||||
clear_image.fill(Qt.GlobalColor.white)
|
||||
self._view.addStroke(clear_image, 0, 0, "none" if self.getPaintType() != "extruder" else "0", False)
|
||||
|
||||
self._view.clearPaint()
|
||||
self._updateScene()
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
from typing import cast, Optional
|
||||
import math
|
||||
|
||||
from PyQt6.QtCore import Qt, QRect, QPoint
|
||||
from PyQt6.QtCore import QRect, QPoint
|
||||
from PyQt6.QtGui import QUndoCommand, QImage, QPainter, QPainterPath, QPen, QBrush
|
||||
|
||||
from UM.View.GL.Texture import Texture
|
||||
from UM.Logger import Logger
|
||||
|
||||
from .PaintCommand import PaintCommand
|
||||
|
||||
class PaintUndoCommand(QUndoCommand):
|
||||
class PaintUndoCommand(PaintCommand):
|
||||
"""Provides the command that does the actual painting on objects with undo/redo mechanisms"""
|
||||
|
||||
PEN_OVERLAP_WIDTH = 2.5
|
||||
|
|
@ -22,18 +22,15 @@ class PaintUndoCommand(QUndoCommand):
|
|||
set_value: int,
|
||||
bit_range: tuple[int, int],
|
||||
mergeable: bool) -> None:
|
||||
super().__init__()
|
||||
super().__init__(texture, bit_range)
|
||||
|
||||
self._original_texture_image: Optional[QImage] = texture.getImage().copy() if not mergeable else None
|
||||
self._texture: Texture = texture
|
||||
self._stroke_path: QPainterPath = stroke_path
|
||||
self._calculateBoundingRect()
|
||||
self._set_value: int = set_value
|
||||
self._bit_range: tuple[int, int] = bit_range
|
||||
self._mergeable: bool = mergeable
|
||||
|
||||
def id(self) -> int:
|
||||
# Since the undo stack will contain only commands of this type, we can use a fixed ID
|
||||
return 0
|
||||
|
||||
def redo(self) -> None:
|
||||
|
|
@ -43,17 +40,12 @@ class PaintUndoCommand(QUndoCommand):
|
|||
bounding_rect_rounded = QRect(QPoint(math.floor(bounding_rect.left()), math.floor(bounding_rect.top())),
|
||||
QPoint(math.ceil(bounding_rect.right()), math.ceil(bounding_rect.bottom())))
|
||||
|
||||
bit_range_start, bit_range_end = self._bit_range
|
||||
full_int32 = 0xffffffff
|
||||
clear_texture_bit_mask = full_int32 ^ (((full_int32 << (32 - 1 - (bit_range_end - bit_range_start))) & full_int32) >> (
|
||||
32 - 1 - bit_range_end))
|
||||
|
||||
stroked_image = actual_image.copy(bounding_rect_rounded)
|
||||
painter = QPainter(stroked_image)
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing, False)
|
||||
painter.translate(-bounding_rect.left(), -bounding_rect.top())
|
||||
|
||||
painter.setBrush(QBrush(clear_texture_bit_mask))
|
||||
painter.setBrush(QBrush(self._getClearTextureBitMask()))
|
||||
painter.setPen(QPen(painter.brush(), self.PEN_OVERLAP_WIDTH))
|
||||
painter.setCompositionMode(QPainter.CompositionMode.RasterOp_SourceAndDestination)
|
||||
painter.drawPath(self._stroke_path)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ from UM.i18n import i18nCatalog
|
|||
from UM.Math.Color import Color
|
||||
|
||||
from .PaintUndoCommand import PaintUndoCommand
|
||||
from .PaintClearCommand import PaintClearCommand
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
|
|
@ -178,6 +179,13 @@ class PaintView(CuraView):
|
|||
(bit_range_start, bit_range_end),
|
||||
merge_with_previous))
|
||||
|
||||
def clearPaint(self):
|
||||
if self._current_paint_texture is None or self._current_paint_texture.getImage() is None:
|
||||
return
|
||||
|
||||
self._prepareDataMapping()
|
||||
self._paint_undo_stack.push(PaintClearCommand(self._current_paint_texture, self._current_bits_ranges))
|
||||
|
||||
def undoStroke(self) -> None:
|
||||
self._paint_undo_stack.undo()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue