Skip to content

Commit e55569b

Browse files
committed
Add clipping plane support for X/Y/Z dimensions.
* New widget for viewing the clipping plane settings. * Update the viewer to add clipping planes.
1 parent 88d2f7e commit e55569b

File tree

3 files changed

+122
-5
lines changed

3 files changed

+122
-5
lines changed

cq_editor/main_window.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .widgets.viewer import OCCViewer
99
from .widgets.console import ConsoleWidget
1010
from .widgets.object_tree import ObjectTree
11+
from .widgets.clipping_planes import ClippingPlanes
1112
from .widgets.traceback_viewer import TracebackPane
1213
from .widgets.debugger import Debugger, LocalsView
1314
from .widgets.cq_object_inspector import CQObjectInspector
@@ -128,6 +129,13 @@ def prepare_panes(self):
128129
self,
129130
defaultArea='bottom'))
130131

132+
self.registerComponent('clipping_planes',
133+
ClippingPlanes(self),
134+
lambda c: dock(c,
135+
'Clipping Planes',
136+
self,
137+
defaultArea='right'))
138+
131139
for d in self.docks.values():
132140
d.show()
133141

@@ -259,6 +267,9 @@ def prepare_actions(self):
259267
self.components['debugger'].sigTraceback\
260268
.connect(self.components['traceback_viewer'].addTraceback)
261269

270+
self.components['clipping_planes'].sigClippingPlaneChanged\
271+
.connect(self.components['viewer'].update_clipping_plane)
272+
262273
# trigger re-render when file is modified externally or saved
263274
self.components['editor'].triggerRerender \
264275
.connect(self.components['debugger'].render)

cq_editor/widgets/clipping_planes.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from PyQt5.QtWidgets import QApplication, QCheckBox, QHBoxLayout, QLabel, QSpinBox, QWidget
2+
from PyQt5.QtCore import Qt, pyqtSlot, pyqtSignal
3+
4+
from qtconsole.rich_jupyter_widget import RichJupyterWidget
5+
6+
from ..mixins import ComponentMixin
7+
from ..utils import layout
8+
9+
class ClippingPlane(QWidget):
10+
11+
sigClippingPlaneChanged = pyqtSignal(bool,int,bool)
12+
13+
def __init__(self, parent, dimension):
14+
super(ClippingPlane, self).__init__(parent)
15+
16+
self.check = QCheckBox(f"{dimension} Axis", self)
17+
self.spinbox = QSpinBox(self)
18+
self.spinbox.setValue(50)
19+
self.spinbox.setRange(1, 256)
20+
self.invert = QCheckBox("Invert", self)
21+
22+
self.group = layout(self,(self.spinbox,self.invert),layout_type=QHBoxLayout, spacing=5)
23+
24+
layout(self,(self.check,self.group),layout_type=QHBoxLayout, top_widget=self)
25+
26+
self.group.setDisabled(True)
27+
self.check.stateChanged.connect(self.handleChecked)
28+
self.invert.stateChanged.connect(self.handleInvertChecked)
29+
self.spinbox.valueChanged.connect(self.handlePositionChanged)
30+
31+
def handleChecked(self, arg):
32+
self.group.setDisabled(not self.check.checkState())
33+
self.emitState()
34+
35+
def handleInvertChecked(self, arg):
36+
self.emitState()
37+
38+
def handlePositionChanged(self, arg):
39+
self.emitState()
40+
41+
def emitState(self):
42+
self.sigClippingPlaneChanged.emit(self.check.checkState(), self.spinbox.value(), self.invert.checkState())
43+
44+
class ClippingPlanes(QWidget,ComponentMixin):
45+
46+
name = 'Clipping Planes'
47+
48+
sigClippingPlaneChanged = pyqtSignal(str,bool,int,bool)
49+
50+
def __init__(self, parent):
51+
super(ClippingPlanes, self).__init__(parent)
52+
53+
self.x = ClippingPlane(self, "X")
54+
self.x.sigClippingPlaneChanged.connect(self.onXPlaneChanged)
55+
self.y = ClippingPlane(self, "Y")
56+
self.y.sigClippingPlaneChanged.connect(self.onYPlaneChanged)
57+
self.z = ClippingPlane(self, "Z")
58+
self.z.sigClippingPlaneChanged.connect(self.onZPlaneChanged)
59+
60+
layout(self,(self.x,self.y,self.z),top_widget=self)
61+
62+
@pyqtSlot(bool,int,bool)
63+
def onXPlaneChanged(self,enabled,val,inverted):
64+
self.sigClippingPlaneChanged.emit("X",enabled,val,inverted)
65+
66+
@pyqtSlot(bool,int,bool)
67+
def onYPlaneChanged(self,enabled,val,inverted):
68+
self.sigClippingPlaneChanged.emit("Y",enabled,val,inverted)
69+
70+
@pyqtSlot(bool,int,bool)
71+
def onZPlaneChanged(self,enabled,val,inverted):
72+
self.sigClippingPlaneChanged.emit("Z",enabled,val,inverted)
73+
74+

cq_editor/widgets/viewer.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
from PyQt5.QtCore import pyqtSlot, pyqtSignal
44
from PyQt5.QtGui import QIcon
55

6-
from OCP.Graphic3d import Graphic3d_Camera, Graphic3d_StereoMode, Graphic3d_NOM_JADE,\
7-
Graphic3d_MaterialAspect
6+
from OCP.Graphic3d import Graphic3d_Camera, Graphic3d_ClipPlane, Graphic3d_StereoMode, Graphic3d_NOM_JADE,\
7+
Graphic3d_MaterialAspect, Graphic3d_Texture2Dplane
88
from OCP.AIS import AIS_Shaded,AIS_WireFrame, AIS_ColoredShape, AIS_Axis
9-
from OCP.Aspect import Aspect_GDM_Lines, Aspect_GT_Rectangular
10-
from OCP.Quantity import Quantity_NOC_BLACK as BLACK, Quantity_TOC_RGB as TOC_RGB,\
9+
from OCP.Aspect import Aspect_GDM_Lines, Aspect_GT_Rectangular, Aspect_HS_GRID
10+
from OCP.TCollection import TCollection_AsciiString
11+
from OCP.Quantity import Quantity_NOC_BLACK as BLACK, Quantity_NOC_WHITE as WHITE, Quantity_TOC_RGB as TOC_RGB,\
1112
Quantity_Color
1213
from OCP.Geom import Geom_Axis1Placement
13-
from OCP.gp import gp_Ax3, gp_Dir, gp_Pnt, gp_Ax1
14+
from OCP.gp import gp_Ax3, gp_Dir, gp_Pnt, gp_Ax1, gp_Pln
1415

1516
from ..utils import layout, get_save_filename
1617
from ..mixins import ComponentMixin
@@ -56,6 +57,14 @@ def __init__(self,parent=None):
5657
self.canvas = OCCTWidget()
5758
self.canvas.sigObjectSelected.connect(self.handle_selection)
5859

60+
61+
self.x_clipping_plane = self.setup_clipping_plane(gp_Pln(-1, 0.0, 0.0, 100))
62+
self.y_clipping_plane = self.setup_clipping_plane(gp_Pln(0.0, -1, 0.0, 100))
63+
self.z_clipping_plane = self.setup_clipping_plane(gp_Pln(0.0, 0.0, -1, 100))
64+
self.canvas.view.AddClipPlane(self.x_clipping_plane)
65+
self.canvas.view.AddClipPlane(self.y_clipping_plane)
66+
self.canvas.view.AddClipPlane(self.z_clipping_plane)
67+
5968
self.create_actions(self)
6069

6170
self.layout_ = layout(self,
@@ -66,6 +75,16 @@ def __init__(self,parent=None):
6675
self.setup_default_drawer()
6776
self.updatePreferences()
6877

78+
def setup_clipping_plane(self, eq):
79+
cp = Graphic3d_ClipPlane()
80+
cp.SetEquation(eq)
81+
cp.SetCapping(True)
82+
cp.SetUseObjectMaterial(True)
83+
# cp.SetCappingTexture(Graphic3d_Texture2Dplane(TCollection_AsciiString("./clip-texture.png")))
84+
cp.SetOn(False)
85+
86+
return cp
87+
6988
def setup_default_drawer(self):
7089

7190
# set the default color and material
@@ -369,6 +388,19 @@ def set_selected(self,ais):
369388

370389
self.redraw()
371390

391+
@pyqtSlot(str,bool,int,bool)
392+
def update_clipping_plane(self,plane,enabled,val,inverted):
393+
if plane == "X":
394+
self.x_clipping_plane.SetEquation(gp_Pln(1 if inverted else -1, 0.0, 0.0, val))
395+
self.x_clipping_plane.SetOn(enabled)
396+
elif plane == "Y":
397+
self.y_clipping_plane.SetEquation(gp_Pln(0.0, 1 if inverted else -1, 0.0, val))
398+
self.y_clipping_plane.SetOn(enabled)
399+
elif plane == "Z":
400+
self.z_clipping_plane.SetEquation(gp_Pln(0.0, 0.0, 1 if inverted else -1, val))
401+
self.z_clipping_plane.SetOn(enabled)
402+
403+
self.redraw()
372404

373405
if __name__ == "__main__":
374406

0 commit comments

Comments
 (0)