Skip to content

Commit 220d77e

Browse files
Refactor mask inversion logic with improved object type conversion
Co-authored-by: kent <kent@invoke.ai>
1 parent a309d04 commit 220d77e

File tree

1 file changed

+51
-8
lines changed

1 file changed

+51
-8
lines changed

invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,12 @@ export const canvasSlice = createSlice({
10001000
return;
10011001
}
10021002

1003-
// Create a rectangle covering the current bounding box
1003+
// If there are no objects to invert, do nothing
1004+
if (entity.objects.length === 0) {
1005+
return;
1006+
}
1007+
1008+
// Create a rectangle covering the current bounding box relative to the entity position
10041009
const bboxRect = state.bbox.rect;
10051010
const fillRectObject = {
10061011
id: getPrefixedId('rect'),
@@ -1014,14 +1019,52 @@ export const canvasSlice = createSlice({
10141019
color: { r: 255, g: 255, b: 255, a: 1 },
10151020
};
10161021

1017-
// Convert existing objects to eraser effect by creating a composite inverted mask
1018-
// The strategy is to replace all existing objects with:
1019-
// 1. A full rectangle covering the bbox
1020-
// 2. The original objects as "erasers" to punch holes through the rectangle
1021-
const originalObjects = [...entity.objects];
1022+
// To invert a mask, we need to:
1023+
// 1. Start with a full rectangle covering the bbox (this becomes the "base mask")
1024+
// 2. Convert existing brush/rect objects to eraser lines to "punch holes" in the base mask
1025+
const convertedObjects = entity.objects.map((obj) => {
1026+
if (obj.type === 'brush_line') {
1027+
// Convert brush lines to eraser lines
1028+
return {
1029+
...obj,
1030+
id: getPrefixedId('eraser_line'),
1031+
type: 'eraser_line' as const,
1032+
};
1033+
} else if (obj.type === 'brush_line_with_pressure') {
1034+
// Convert brush lines with pressure to eraser lines with pressure
1035+
return {
1036+
...obj,
1037+
id: getPrefixedId('eraser_line'),
1038+
type: 'eraser_line_with_pressure' as const,
1039+
};
1040+
} else if (obj.type === 'rect') {
1041+
// Convert rectangles to eraser "rectangles" by making them transparent
1042+
return {
1043+
...obj,
1044+
id: getPrefixedId('rect'),
1045+
color: { ...obj.color, a: 0 }, // Make transparent to act as eraser
1046+
};
1047+
} else if (obj.type === 'eraser_line') {
1048+
// Convert eraser lines to brush lines
1049+
return {
1050+
...obj,
1051+
id: getPrefixedId('brush_line'),
1052+
type: 'brush_line' as const,
1053+
};
1054+
} else if (obj.type === 'eraser_line_with_pressure') {
1055+
// Convert eraser lines with pressure to brush lines with pressure
1056+
return {
1057+
...obj,
1058+
id: getPrefixedId('brush_line'),
1059+
type: 'brush_line_with_pressure' as const,
1060+
};
1061+
}
1062+
// Keep images and other objects as is
1063+
return obj;
1064+
});
10221065

1023-
// Start with the full rectangle, then "erase" the original painted areas
1024-
entity.objects = [fillRectObject, ...originalObjects];
1066+
// Replace all objects with the base rectangle followed by converted objects
1067+
entity.objects = [fillRectObject, ...convertedObjects];
10251068
},
10261069
//#region BBox
10271070
bboxScaledWidthChanged: (state, action: PayloadAction<number>) => {

0 commit comments

Comments
 (0)