CyGuy8 Claude Fable 5 commited on
Commit
ff58e99
·
1 Parent(s): b8e790e

Show palette color names in the Shape Settings Color column

Browse files

Colors 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>

Files changed (2) hide show
  1. app.py +32 -3
  2. 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
- if len(row) > color_pos and row[color_pos]:
1621
- copy["color"] = str(row[color_pos])
 
 
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