Fixed bug in skip objects modal, where the object ID markers are not correctly positioned over their corresponding objects in the build plate preview

This commit is contained in:
maziggy
2026-01-03 10:26:06 +01:00
parent 55ec08d4d1
commit 5dae3d3021
6 changed files with 115 additions and 81 deletions
+3 -1
View File
@@ -1181,9 +1181,10 @@ async def get_printable_objects(
if downloaded and temp_path.exists():
with open(temp_path, "rb") as f:
data = f.read()
objects = extract_printable_objects_from_3mf(data, include_positions=True)
objects, bbox_all = extract_printable_objects_from_3mf(data, include_positions=True)
if objects:
client.state.printable_objects = objects
client.state.printable_objects_bbox_all = bbox_all
logger.info(f"Reloaded {len(objects)} objects for printer {printer_id}")
except Exception as e:
logger.debug(f"Failed to reload objects from printer: {e}")
@@ -1219,6 +1220,7 @@ async def get_printable_objects(
"total": len(objects),
"skipped_count": len(client.state.skipped_objects),
"is_printing": client.state.state in ("RUNNING", "PAUSE"),
"bbox_all": getattr(client.state, "printable_objects_bbox_all", None),
}
+36 -22
View File
@@ -342,7 +342,7 @@ class ThreeMFParser:
def extract_printable_objects_from_3mf(
data: bytes, plate_number: int | None = None, include_positions: bool = False
) -> dict[int, str] | dict[int, dict]:
) -> dict[int, str] | dict[int, dict] | tuple[dict[int, dict], list | None]:
"""Extract printable objects from 3MF file bytes.
This is a lightweight function used during print start to get the list
@@ -351,16 +351,17 @@ def extract_printable_objects_from_3mf(
Args:
data: Raw bytes of the 3MF file
plate_number: Which plate was printed (1-based), or None for first plate
include_positions: If True, return dict with name and position info
include_positions: If True, return tuple of (objects dict, bbox_all)
Returns:
If include_positions=False: Dictionary mapping identify_id (int) to object name (str)
If include_positions=True: Dictionary mapping identify_id to {name, x, y} dict
If include_positions=True: Tuple of (dict mapping identify_id to {name, x, y}, bbox_all list or None)
"""
import json
from io import BytesIO
printable_objects: dict = {}
bbox_all: list | None = None
try:
with zipfile.ZipFile(BytesIO(data), "r") as zf:
@@ -371,7 +372,6 @@ def extract_printable_objects_from_3mf(
root = ET.fromstring(content)
# Find the correct plate
plate_idx = plate_number or 1
if plate_number:
plate = root.find(f".//plate[@plate_idx='{plate_number}']")
if plate is None:
@@ -382,19 +382,35 @@ def extract_printable_objects_from_3mf(
if plate is None:
return printable_objects
# Get actual plate index from metadata (sliced files only have one plate)
plate_idx = plate_number or 1
for meta in plate.findall("metadata"):
if meta.get("key") == "index":
try:
plate_idx = int(meta.get("value", "1"))
except ValueError:
pass
break
# Load position data from plate_N.json if we need positions
bbox_objects = []
# Build a lookup by name since bbox_objects.id != slice_info identify_id
bbox_by_name: dict[str, list] = {}
if include_positions:
plate_json_path = f"Metadata/plate_{plate_idx}.json"
if plate_json_path in zf.namelist():
try:
plate_json = json.loads(zf.read(plate_json_path).decode())
bbox_objects = plate_json.get("bbox_objects", [])
# Get bbox_all - the bounding box of all objects (used for image bounds)
bbox_all = plate_json.get("bbox_all")
for bbox_obj in plate_json.get("bbox_objects", []):
obj_name = bbox_obj.get("name")
bbox = bbox_obj.get("bbox", [])
if obj_name and len(bbox) >= 4:
bbox_by_name[obj_name] = bbox
except (json.JSONDecodeError, KeyError):
pass
# Extract objects from slice_info.config
objects_list = []
for obj in plate.findall("object"):
identify_id = obj.get("identify_id")
name = obj.get("name")
@@ -403,27 +419,25 @@ def extract_printable_objects_from_3mf(
if identify_id and name and skipped.lower() != "true":
try:
obj_id = int(identify_id)
objects_list.append((obj_id, name))
if include_positions:
x, y = None, None
# Match by name to get bbox coordinates
bbox = bbox_by_name.get(name)
if bbox:
# Calculate center from bbox [x_min, y_min, x_max, y_max]
x = (bbox[0] + bbox[2]) / 2
y = (bbox[1] + bbox[3]) / 2
printable_objects[obj_id] = {"name": name, "x": x, "y": y}
else:
printable_objects[obj_id] = name
except ValueError:
pass
# Match objects with positions by index (both lists are in same order)
for idx, (obj_id, name) in enumerate(objects_list):
if include_positions:
x, y = None, None
if idx < len(bbox_objects):
bbox = bbox_objects[idx].get("bbox", [])
if len(bbox) >= 4:
# Calculate center from bbox [x_min, y_min, x_max, y_max]
x = (bbox[0] + bbox[2]) / 2
y = (bbox[1] + bbox[3]) / 2
printable_objects[obj_id] = {"name": name, "x": x, "y": y}
else:
printable_objects[obj_id] = name
except Exception:
pass
if include_positions:
return printable_objects, bbox_all
return printable_objects