Spaces:
Sleeping
Sleeping
Show palette color names in the Shape Settings Color column
Browse filesColors display and parse as friendly names (Orange, Blue, Green, Red,
Purple, Pink, Teal, Black) instead of hex codes; matching is
case-insensitive, raw hex still works, and unrecognized text keeps the
previous color. Gradio dataframes cannot host in-cell dropdowns, so a
fixed name palette is the closest equivalent.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- app.py +32 -3
- tests/test_nozzle_spacing.py +34 -0
app.py
CHANGED
|
@@ -1157,6 +1157,32 @@ PARALLEL_COLOR_CHOICES = [
|
|
| 1157 |
("Teal", "#17becf"), ("Black", "#000000"),
|
| 1158 |
]
|
| 1159 |
DEFAULT_PARALLEL_COLORS = ("#ff7f0e", "#1f77b4", "#2ca02c")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1160 |
|
| 1161 |
|
| 1162 |
def _group_parts_by_nozzle(parts: list[dict]) -> dict[int, list[dict]]:
|
|
@@ -1558,7 +1584,7 @@ def _shape_settings_rows(records: list[dict]) -> list[list[Any]]:
|
|
| 1558 |
record.get("valve", 4),
|
| 1559 |
_record_nozzle_number(record, int(record["idx"])),
|
| 1560 |
record.get("port", 1),
|
| 1561 |
-
record.get("color", _default_color(record["idx"])),
|
| 1562 |
_coerce_float(record.get("infill", 100.0), 100.0),
|
| 1563 |
bool(record.get("contour_tracing", False)),
|
| 1564 |
bool(record.get("lead_in", False)),
|
|
@@ -1617,8 +1643,10 @@ def _apply_shape_settings(records: list[dict], settings_table: Any) -> list[dict
|
|
| 1617 |
)
|
| 1618 |
if copy["nozzle"] <= 0:
|
| 1619 |
copy["nozzle"] = _record_nozzle_number(copy)
|
| 1620 |
-
|
| 1621 |
-
|
|
|
|
|
|
|
| 1622 |
try:
|
| 1623 |
copy["infill"] = max(
|
| 1624 |
0.0,
|
|
@@ -2806,6 +2834,7 @@ def build_dynamic_demo() -> gr.Blocks:
|
|
| 2806 |
"""
|
| 2807 |
# Shapes & Slicing
|
| 2808 |
Upload any number of STL files, edit per-shape dimensions and print settings in the table, then slice each shape into per-layer outlines.
|
|
|
|
| 2809 |
"""
|
| 2810 |
)
|
| 2811 |
with gr.Row():
|
|
|
|
| 1157 |
("Teal", "#17becf"), ("Black", "#000000"),
|
| 1158 |
]
|
| 1159 |
DEFAULT_PARALLEL_COLORS = ("#ff7f0e", "#1f77b4", "#2ca02c")
|
| 1160 |
+
SHAPE_COLOR_NAMES = [name for name, _hex in PARALLEL_COLOR_CHOICES]
|
| 1161 |
+
_COLOR_NAME_BY_HEX = {hex_value.lower(): name for name, hex_value in PARALLEL_COLOR_CHOICES}
|
| 1162 |
+
_COLOR_HEX_BY_NAME = {name.lower(): hex_value for name, hex_value in PARALLEL_COLOR_CHOICES}
|
| 1163 |
+
|
| 1164 |
+
|
| 1165 |
+
def _color_display(value: str | None) -> str:
|
| 1166 |
+
"""Palette name for a stored color; unknown values show as-is."""
|
| 1167 |
+
text = str(value or "").strip()
|
| 1168 |
+
return _COLOR_NAME_BY_HEX.get(text.lower(), text)
|
| 1169 |
+
|
| 1170 |
+
|
| 1171 |
+
def _color_from_cell(cell, fallback: str) -> str:
|
| 1172 |
+
"""Parse a Color cell: palette name (case-insensitive) or a hex value.
|
| 1173 |
+
|
| 1174 |
+
Anything unrecognized keeps the previous color, so a typo never breaks
|
| 1175 |
+
the visualization.
|
| 1176 |
+
"""
|
| 1177 |
+
text = str(cell or "").strip()
|
| 1178 |
+
if not text:
|
| 1179 |
+
return fallback
|
| 1180 |
+
hex_value = _COLOR_HEX_BY_NAME.get(text.lower())
|
| 1181 |
+
if hex_value:
|
| 1182 |
+
return hex_value
|
| 1183 |
+
if text.startswith("#") and len(text) in (4, 7):
|
| 1184 |
+
return text
|
| 1185 |
+
return fallback
|
| 1186 |
|
| 1187 |
|
| 1188 |
def _group_parts_by_nozzle(parts: list[dict]) -> dict[int, list[dict]]:
|
|
|
|
| 1584 |
record.get("valve", 4),
|
| 1585 |
_record_nozzle_number(record, int(record["idx"])),
|
| 1586 |
record.get("port", 1),
|
| 1587 |
+
_color_display(record.get("color", _default_color(record["idx"]))),
|
| 1588 |
_coerce_float(record.get("infill", 100.0), 100.0),
|
| 1589 |
bool(record.get("contour_tracing", False)),
|
| 1590 |
bool(record.get("lead_in", False)),
|
|
|
|
| 1643 |
)
|
| 1644 |
if copy["nozzle"] <= 0:
|
| 1645 |
copy["nozzle"] = _record_nozzle_number(copy)
|
| 1646 |
+
copy["color"] = _color_from_cell(
|
| 1647 |
+
row[color_pos] if len(row) > color_pos else None,
|
| 1648 |
+
str(copy.get("color") or _default_color(int(copy.get("idx", 1) or 1))),
|
| 1649 |
+
)
|
| 1650 |
try:
|
| 1651 |
copy["infill"] = max(
|
| 1652 |
0.0,
|
|
|
|
| 2834 |
"""
|
| 2835 |
# Shapes & Slicing
|
| 2836 |
Upload any number of STL files, edit per-shape dimensions and print settings in the table, then slice each shape into per-layer outlines.
|
| 2837 |
+
Color accepts: Orange, Blue, Green, Red, Purple, Pink, Teal, Black.
|
| 2838 |
"""
|
| 2839 |
)
|
| 2840 |
with gr.Row():
|
tests/test_nozzle_spacing.py
CHANGED
|
@@ -164,6 +164,40 @@ def test_shape_settings_round_trip_infill_column() -> None:
|
|
| 164 |
assert _apply_shape_settings(records, rows)[0]["infill"] == 0.0
|
| 165 |
|
| 166 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
def test_lead_in_assembly_extension_covers_the_split_extent() -> None:
|
| 168 |
from shapely.geometry import MultiPolygon, box
|
| 169 |
|
|
|
|
| 164 |
assert _apply_shape_settings(records, rows)[0]["infill"] == 0.0
|
| 165 |
|
| 166 |
|
| 167 |
+
def test_shape_settings_color_column_uses_palette_names() -> None:
|
| 168 |
+
records = [
|
| 169 |
+
{
|
| 170 |
+
"idx": 1,
|
| 171 |
+
"name": "circle",
|
| 172 |
+
"stl_path": "circle.stl",
|
| 173 |
+
"target_x": 10.0,
|
| 174 |
+
"target_y": 11.0,
|
| 175 |
+
"target_z": 12.0,
|
| 176 |
+
"pressure": 25.0,
|
| 177 |
+
"valve": 4,
|
| 178 |
+
"nozzle": 1,
|
| 179 |
+
"port": 1,
|
| 180 |
+
"color": "#ff7f0e",
|
| 181 |
+
"contour_tracing": False,
|
| 182 |
+
}
|
| 183 |
+
]
|
| 184 |
+
|
| 185 |
+
rows = _shape_settings_rows(records)
|
| 186 |
+
color_pos = SHAPE_SETTINGS_HEADERS.index("Color")
|
| 187 |
+
# Stored hex displays as its friendly name.
|
| 188 |
+
assert rows[0][color_pos] == "Orange"
|
| 189 |
+
|
| 190 |
+
# Typing a palette name (any case) stores the matching hex.
|
| 191 |
+
rows[0][color_pos] = "red"
|
| 192 |
+
assert _apply_shape_settings(records, rows)[0]["color"] == "#d62728"
|
| 193 |
+
|
| 194 |
+
# Raw hex still works; unknown text keeps the previous color.
|
| 195 |
+
rows[0][color_pos] = "#123456"
|
| 196 |
+
assert _apply_shape_settings(records, rows)[0]["color"] == "#123456"
|
| 197 |
+
rows[0][color_pos] = "not-a-color"
|
| 198 |
+
assert _apply_shape_settings(records, rows)[0]["color"] == "#ff7f0e"
|
| 199 |
+
|
| 200 |
+
|
| 201 |
def test_lead_in_assembly_extension_covers_the_split_extent() -> None:
|
| 202 |
from shapely.geometry import MultiPolygon, box
|
| 203 |
|