1+ # Copyright (c) 2025 UltiMaker
2+ # Cura is released under the terms of the LGPLv3 or higher.
13import copy
24import json
5+ import numpy
36
4- from typing import Optional , Dict
7+ from typing import Optional , Dict , List
58
6- from PyQt6 .QtCore import QBuffer
9+ from PyQt6 .QtCore import QBuffer , QTimer
710from PyQt6 .QtGui import QImage , QImageWriter
811
912from UM .Scene .SceneNodeDecorator import SceneNodeDecorator
@@ -18,8 +21,15 @@ def __init__(self) -> None:
1821 self ._paint_texture = None
1922 self ._texture_data_mapping : Dict [str , tuple [int , int ]] = {}
2023
24+ self ._painted_extruders : Optional [List [int ]] = None
25+
2126 self .paintTextureChanged = Signal ()
2227
28+ self ._texture_change_timer = QTimer ()
29+ self ._texture_change_timer .setInterval (500 ) # Long interval to avoid triggering during painting
30+ self ._texture_change_timer .setSingleShot (True )
31+ self ._texture_change_timer .timeout .connect (self ._onTextureChangeTimerFinished )
32+
2333 def isSliceable (self ) -> bool :
2434 return True
2535
@@ -29,6 +39,32 @@ def getPaintTexture(self) -> Optional[Texture]:
2939 def getPaintTextureChangedSignal (self ) -> Signal :
3040 return self .paintTextureChanged
3141
42+ def setPaintedExtrudersCountDirty (self ) -> None :
43+ self ._texture_change_timer .start ()
44+
45+ def _onTextureChangeTimerFinished (self ) -> None :
46+ self ._painted_extruders = None
47+
48+ if (self ._paint_texture is None or self ._paint_texture .getImage () is None or
49+ "extruder" not in self ._texture_data_mapping ):
50+ return
51+
52+ image = self ._paint_texture .getImage ()
53+ image_bits = image .constBits ()
54+ image_bits .setsize (image .sizeInBytes ())
55+ image_array = numpy .frombuffer (image_bits , dtype = numpy .uint32 )
56+
57+ bit_range_start , bit_range_end = self ._texture_data_mapping ["extruder" ]
58+ full_int32 = 0xffffffff
59+ bit_mask = (((full_int32 << (32 - 1 - (bit_range_end - bit_range_start ))) & full_int32 ) >> (
60+ 32 - 1 - bit_range_end ))
61+
62+ texel_counts = numpy .bincount ((image_array & bit_mask ) >> bit_range_start )
63+ self ._painted_extruders = [extruder_nr for extruder_nr , count in enumerate (texel_counts ) if count > 0 ]
64+
65+ from cura .CuraApplication import CuraApplication
66+ CuraApplication .getInstance ().globalContainerStackChanged .emit ()
67+
3268 def setPaintTexture (self , texture : Texture ) -> None :
3369 self ._paint_texture = texture
3470 self .paintTextureChanged .emit ()
@@ -63,6 +99,9 @@ def packTexture(self) -> Optional[bytearray]:
6399
64100 return texture_buffer .data ()
65101
102+ def getPaintedExtruders (self ) -> Optional [List [int ]]:
103+ return self ._painted_extruders
104+
66105 def __deepcopy__ (self , memo ) -> "SliceableObjectDecorator" :
67106 copied_decorator = SliceableObjectDecorator ()
68107 copied_decorator .setPaintTexture (copy .deepcopy (self .getPaintTexture ()))
0 commit comments