Silly98 commited on
Commit
99bcebb
·
verified ·
1 Parent(s): d4b1cfc

Upload face_labeler.py

Browse files
Files changed (1) hide show
  1. face_labeler.py +171 -25
face_labeler.py CHANGED
@@ -1,5 +1,7 @@
1
- import sys
2
  import os
 
 
3
  from dataclasses import dataclass
4
  from typing import List, Optional, Tuple
5
 
@@ -22,31 +24,83 @@ from OCC.Core.TopAbs import TopAbs_FACE
22
  from OCC.Core.TopoDS import topods
23
  from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB
24
  from OCC.Display.qtDisplay import qtViewer3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  CLASS_NAMES = [
27
- "ExtrudeSide",
28
- "ExtrudeEnd",
29
- "CutSide",
30
- "CutEnd",
31
- "Fillet",
32
- "Chamfer",
33
- "RevolveSide",
34
- "RevolveEnd",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  ]
36
 
37
  CLASS_COLORS_HEX = [
38
- "#e41a1c", # red
39
- "#377eb8", # blue
40
- "#4daf4a", # green
41
- "#984ea3", # purple
42
- "#0e2579", # teal-blue (fillet)
43
- "#a65628", # brown
44
- "#f781bf", # pink
45
- "#00a7a7", # teal
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  ]
47
 
48
  UNLABELED_COLOR_HEX = "#d0d0d0"
49
  HIGHLIGHT_COLOR_HEX = "#FFD400" # fixed for current selection
 
50
 
51
 
52
  def hex_to_rgb01(color_hex: str) -> Tuple[float, float, float]:
@@ -74,9 +128,9 @@ class FaceItem:
74
 
75
 
76
  class FaceLabeler(QtWidgets.QMainWindow):
77
- def __init__(self, step_path: Optional[str] = None):
78
  super().__init__()
79
- self.setWindowTitle("BRep Face Labeler")
80
  self.resize(1600, 1000)
81
  self.setMinimumSize(1200, 800)
82
 
@@ -89,10 +143,13 @@ class FaceLabeler(QtWidgets.QMainWindow):
89
  self.labels: List[Optional[int]] = []
90
  self.current_index: Optional[int] = None
91
  self.highlight_enabled = True
 
 
92
 
93
  self.highlight_color = rgb01_to_quantity(hex_to_rgb01(HIGHLIGHT_COLOR_HEX))
94
 
95
  self._build_ui()
 
96
 
97
  if step_path:
98
  self.load_step(step_path)
@@ -110,6 +167,7 @@ class FaceLabeler(QtWidgets.QMainWindow):
110
  self.display.Context.SetAutomaticHilight(False)
111
  except Exception:
112
  pass
 
113
  root_layout.addWidget(self.viewer, 1)
114
 
115
  panel = QtWidgets.QWidget(central)
@@ -141,6 +199,10 @@ class FaceLabeler(QtWidgets.QMainWindow):
141
  nav_layout.addWidget(self.btn_next)
142
  panel_layout.addLayout(nav_layout)
143
 
 
 
 
 
144
  self.info_label = QtWidgets.QLabel("No STEP loaded")
145
  self.info_label.setWordWrap(True)
146
  panel_layout.addWidget(self.info_label)
@@ -166,6 +228,15 @@ class FaceLabeler(QtWidgets.QMainWindow):
166
 
167
  self.setCentralWidget(central)
168
 
 
 
 
 
 
 
 
 
 
169
  def keyPressEvent(self, event) -> None: # pragma: no cover - UI only
170
  if event.key() in (QtCore.Qt.Key_Right, QtCore.Qt.Key_D):
171
  self.on_next()
@@ -193,6 +264,17 @@ class FaceLabeler(QtWidgets.QMainWindow):
193
  "Unlabeled faces remain. Label all faces before exporting.",
194
  )
195
  return
 
 
 
 
 
 
 
 
 
 
 
196
  path, _ = QtWidgets.QFileDialog.getSaveFileName(
197
  self, "Export .seg", "", "SEG Files (*.seg)"
198
  )
@@ -267,6 +349,8 @@ class FaceLabeler(QtWidgets.QMainWindow):
267
  return
268
  reader.TransferRoots()
269
  shape = reader.OneShape()
 
 
270
 
271
  self.display.EraseAll()
272
  self.face_items.clear()
@@ -280,10 +364,15 @@ class FaceLabeler(QtWidgets.QMainWindow):
280
  ais = self.display.DisplayShape(face, update=False, color=self.unlabeled_color)
281
  if isinstance(ais, list):
282
  ais = ais[0]
 
283
  try:
284
  self.display.Context.SetDisplayMode(ais, 1, False)
285
  except Exception:
286
  pass
 
 
 
 
287
  self.face_items.append(FaceItem(face=face, ais=ais))
288
  self.labels.append(None)
289
  explorer.Next()
@@ -297,6 +386,7 @@ class FaceLabeler(QtWidgets.QMainWindow):
297
 
298
  self.display.FitAll()
299
  self.set_current_index(0)
 
300
 
301
  def save_seg(self, path: str) -> None:
302
  with open(path, "w", encoding="utf-8") as handle:
@@ -359,17 +449,73 @@ class FaceLabeler(QtWidgets.QMainWindow):
359
  ais.SetColor(color)
360
  except Exception:
361
  self.display.Context.SetColor(ais, color, False)
 
362
  self.display.Context.Redisplay(ais, False)
363
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
 
365
  def main() -> int:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  app = QtWidgets.QApplication(sys.argv)
367
- step_path = None
368
- if len(sys.argv) > 1:
369
- candidate = sys.argv[1]
370
- if os.path.exists(candidate):
371
- step_path = candidate
372
- window = FaceLabeler(step_path=step_path)
373
  window.show()
374
  return app.exec_()
375
 
 
1
+ import sys
2
  import os
3
+ import argparse
4
+ import pathlib
5
  from dataclasses import dataclass
6
  from typing import List, Optional, Tuple
7
 
 
24
  from OCC.Core.TopoDS import topods
25
  from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB
26
  from OCC.Display.qtDisplay import qtViewer3d
27
+ try:
28
+ from OCC.Core.Aspect import Aspect_TOL_SOLID
29
+ from OCC.Core.Prs3d import Prs3d_LineAspect
30
+ except Exception: # pragma: no cover - optional OCC build
31
+ Aspect_TOL_SOLID = None
32
+ Prs3d_LineAspect = None
33
+ try:
34
+ from OCC.Core.Graphic3d import Graphic3d_NOM_MATTE, Graphic3d_NOM_NEON
35
+ except Exception: # pragma: no cover - optional OCC build
36
+ Graphic3d_NOM_MATTE = None
37
+ Graphic3d_NOM_NEON = None
38
+ try:
39
+ from OCC.Core.Graphic3d import Graphic3d_TOSM_UNLIT
40
+ except Exception: # pragma: no cover - optional OCC build
41
+ Graphic3d_TOSM_UNLIT = None
42
 
43
  CLASS_NAMES = [
44
+ "SOL",
45
+ "EOS",
46
+ "rect_slot",
47
+ "tri_slot",
48
+ "cir_slot",
49
+ "rect_psg",
50
+ "tri_psg",
51
+ "hexa_psg",
52
+ "hole",
53
+ "rect_step",
54
+ "tside_step",
55
+ "slant_step",
56
+ "rect_b_step",
57
+ "tri_step",
58
+ "cir_step",
59
+ "rect_b_slot",
60
+ "cir_b_slot",
61
+ "u_b_slot",
62
+ "rect_pkt",
63
+ "key_pkt",
64
+ "tri_pkt",
65
+ "hexa_pkt",
66
+ "o_ring",
67
+ "b_hole",
68
+ "chamfer",
69
+ "fillet",
70
  ]
71
 
72
  CLASS_COLORS_HEX = [
73
+ "#1f77b4", # blue
74
+ "#ff7f0e", # orange
75
+ "#2ca02c", # green
76
+ "#d62728", # red
77
+ "#9467bd", # purple
78
+ "#8c564b", # brown
79
+ "#e377c2", # pink
80
+ "#7f7f7f", # gray
81
+ "#bcbd22", # olive
82
+ "#17becf", # cyan
83
+ "#393b79", # dark blue
84
+ "#637939", # dark green
85
+ "#8c6d31", # dark mustard
86
+ "#843c39", # dark red
87
+ "#7b4173", # dark purple
88
+ "#3182bd", # blue alt
89
+ "#e6550d", # orange alt
90
+ "#31a354", # green alt
91
+ "#756bb1", # purple alt
92
+ "#636363", # dark gray
93
+ "#6baed6", # light blue
94
+ "#fd8d3c", # light orange
95
+ "#74c476", # light green
96
+ "#9e9ac8", # light purple
97
+ "#a1d99b", # pale green
98
+ "#fdd0a2", # pale orange
99
  ]
100
 
101
  UNLABELED_COLOR_HEX = "#d0d0d0"
102
  HIGHLIGHT_COLOR_HEX = "#FFD400" # fixed for current selection
103
+ EDGE_COLOR_HEX = "#2b2b2b"
104
 
105
 
106
  def hex_to_rgb01(color_hex: str) -> Tuple[float, float, float]:
 
128
 
129
 
130
  class FaceLabeler(QtWidgets.QMainWindow):
131
+ def __init__(self, step_path: Optional[str] = None, output_dir: Optional[str] = None):
132
  super().__init__()
133
+ self.setWindowTitle("BRepMFR Face Labeler")
134
  self.resize(1600, 1000)
135
  self.setMinimumSize(1200, 800)
136
 
 
143
  self.labels: List[Optional[int]] = []
144
  self.current_index: Optional[int] = None
145
  self.highlight_enabled = True
146
+ self.step_path: Optional[str] = None
147
+ self.output_dir: Optional[str] = output_dir
148
 
149
  self.highlight_color = rgb01_to_quantity(hex_to_rgb01(HIGHLIGHT_COLOR_HEX))
150
 
151
  self._build_ui()
152
+ self.update_step_label()
153
 
154
  if step_path:
155
  self.load_step(step_path)
 
167
  self.display.Context.SetAutomaticHilight(False)
168
  except Exception:
169
  pass
170
+ self._configure_viewer_visuals()
171
  root_layout.addWidget(self.viewer, 1)
172
 
173
  panel = QtWidgets.QWidget(central)
 
199
  nav_layout.addWidget(self.btn_next)
200
  panel_layout.addLayout(nav_layout)
201
 
202
+ self.step_label = QtWidgets.QLabel("STEP: (none)")
203
+ self.step_label.setWordWrap(True)
204
+ panel_layout.addWidget(self.step_label)
205
+
206
  self.info_label = QtWidgets.QLabel("No STEP loaded")
207
  self.info_label.setWordWrap(True)
208
  panel_layout.addWidget(self.info_label)
 
228
 
229
  self.setCentralWidget(central)
230
 
231
+ def update_step_label(self) -> None:
232
+ if self.step_path:
233
+ name = os.path.basename(self.step_path)
234
+ self.step_label.setText(f"STEP: {name}")
235
+ self.setWindowTitle(f"BRepMFR Face Labeler - {name}")
236
+ else:
237
+ self.step_label.setText("STEP: (none)")
238
+ self.setWindowTitle("BRepMFR Face Labeler")
239
+
240
  def keyPressEvent(self, event) -> None: # pragma: no cover - UI only
241
  if event.key() in (QtCore.Qt.Key_Right, QtCore.Qt.Key_D):
242
  self.on_next()
 
264
  "Unlabeled faces remain. Label all faces before exporting.",
265
  )
266
  return
267
+ if self.output_dir:
268
+ if not self.step_path:
269
+ QtWidgets.QMessageBox.warning(self, "Export", "No STEP loaded.")
270
+ return
271
+ output_dir = pathlib.Path(self.output_dir)
272
+ output_dir.mkdir(parents=True, exist_ok=True)
273
+ filename = pathlib.Path(self.step_path).with_suffix(".seg").name
274
+ path = output_dir / filename
275
+ self.save_seg(str(path))
276
+ return
277
+
278
  path, _ = QtWidgets.QFileDialog.getSaveFileName(
279
  self, "Export .seg", "", "SEG Files (*.seg)"
280
  )
 
349
  return
350
  reader.TransferRoots()
351
  shape = reader.OneShape()
352
+ self.step_path = path
353
+ self.update_step_label()
354
 
355
  self.display.EraseAll()
356
  self.face_items.clear()
 
364
  ais = self.display.DisplayShape(face, update=False, color=self.unlabeled_color)
365
  if isinstance(ais, list):
366
  ais = ais[0]
367
+ self._apply_face_material(ais)
368
  try:
369
  self.display.Context.SetDisplayMode(ais, 1, False)
370
  except Exception:
371
  pass
372
+ try:
373
+ self.display.Context.Redisplay(ais, False)
374
+ except Exception:
375
+ pass
376
  self.face_items.append(FaceItem(face=face, ais=ais))
377
  self.labels.append(None)
378
  explorer.Next()
 
386
 
387
  self.display.FitAll()
388
  self.set_current_index(0)
389
+ self.display.Repaint()
390
 
391
  def save_seg(self, path: str) -> None:
392
  with open(path, "w", encoding="utf-8") as handle:
 
449
  ais.SetColor(color)
450
  except Exception:
451
  self.display.Context.SetColor(ais, color, False)
452
+ self._apply_face_material(ais)
453
  self.display.Context.Redisplay(ais, False)
454
 
455
+ def _apply_face_material(self, ais) -> None:
456
+ # Prefer emissive material to keep colors stable regardless of lighting.
457
+ applied = False
458
+ if Graphic3d_NOM_NEON is not None:
459
+ try:
460
+ ais.SetMaterial(Graphic3d_NOM_NEON)
461
+ applied = True
462
+ except Exception:
463
+ pass
464
+ if not applied and Graphic3d_NOM_MATTE is not None:
465
+ try:
466
+ ais.SetMaterial(Graphic3d_NOM_MATTE)
467
+ except Exception:
468
+ pass
469
+ self._apply_face_edges(ais)
470
+
471
+ def _configure_viewer_visuals(self) -> None:
472
+ # Try unlit shading to avoid view-dependent brightening.
473
+ if Graphic3d_TOSM_UNLIT is None:
474
+ return
475
+ try:
476
+ self.display.View.SetShadingModel(Graphic3d_TOSM_UNLIT)
477
+ except Exception:
478
+ pass
479
+
480
+ def _apply_face_edges(self, ais) -> None:
481
+ if Prs3d_LineAspect is None or Aspect_TOL_SOLID is None:
482
+ return
483
+ try:
484
+ drawer = ais.Attributes()
485
+ drawer.SetFaceBoundaryDraw(True)
486
+ line_aspect = Prs3d_LineAspect(
487
+ rgb01_to_quantity(hex_to_rgb01(EDGE_COLOR_HEX)),
488
+ Aspect_TOL_SOLID,
489
+ 1.0,
490
+ )
491
+ drawer.SetFaceBoundaryAspect(line_aspect)
492
+ except Exception:
493
+ pass
494
+
495
 
496
  def main() -> int:
497
+ parser = argparse.ArgumentParser(description="BRepMFR Face Labeler")
498
+ parser.add_argument("step_path", nargs="?", help="Optional STEP file to open")
499
+ parser.add_argument(
500
+ "--output_dir",
501
+ type=str,
502
+ default=None,
503
+ help="Output directory for .seg exports (auto-save with input filename)",
504
+ )
505
+ parser.add_argument(
506
+ "--output_folder",
507
+ type=str,
508
+ default=None,
509
+ help="Alias of --output_dir",
510
+ )
511
+ args = parser.parse_args()
512
+ if args.output_dir and args.output_folder and args.output_dir != args.output_folder:
513
+ raise SystemExit("--output_dir and --output_folder must match when both are provided")
514
+
515
  app = QtWidgets.QApplication(sys.argv)
516
+ step_path = args.step_path if args.step_path and os.path.exists(args.step_path) else None
517
+ output_dir = args.output_folder or args.output_dir
518
+ window = FaceLabeler(step_path=step_path, output_dir=output_dir)
 
 
 
519
  window.show()
520
  return app.exec_()
521