| 1 | #!/usr/bin/env python |
|---|
| 2 | # |
|---|
| 3 | # Author: Patrick Hung (patrickh @caltech) |
|---|
| 4 | # Copyright (c) 1997-2016 California Institute of Technology. |
|---|
| 5 | # License: 3-clause BSD. The full license text is available at: |
|---|
| 6 | # - http://mmckerns.github.io/project/mystic/browser/mystic/LICENSE |
|---|
| 7 | # |
|---|
| 8 | # This example is a derivative of vtk's ClipCow |
|---|
| 9 | # It is a visualization of Prince Rupert's problem |
|---|
| 10 | |
|---|
| 11 | import vtk, Tkinter |
|---|
| 12 | from vtk.tk.vtkTkRenderWindowInteractor import \ |
|---|
| 13 | vtkTkRenderWindowInteractor |
|---|
| 14 | from numpy import array, cross |
|---|
| 15 | from vtk.util.colors import peacock, tomato |
|---|
| 16 | |
|---|
| 17 | #cube = vtk.vtkBYUReader() |
|---|
| 18 | #cube.SetGeometryFileName("./cube.g") |
|---|
| 19 | cube = vtk.vtkCubeSource() |
|---|
| 20 | cube.SetCenter(0.5,0.5,0.5) |
|---|
| 21 | |
|---|
| 22 | cubeNormals = vtk.vtkPolyDataNormals() |
|---|
| 23 | cubeNormals.SetInputConnection(cube.GetOutputPort()) |
|---|
| 24 | cube = cubeNormals |
|---|
| 25 | |
|---|
| 26 | def PlaneNormal(a, b, c): |
|---|
| 27 | assert(a >= 0 and a <= 1) |
|---|
| 28 | assert(b >= 0 and b <= 1) |
|---|
| 29 | assert(c >= 0 and c <= 2) |
|---|
| 30 | A = array([a, 0.0, 0.0]) |
|---|
| 31 | B = array([0.0, b, 0.0]) |
|---|
| 32 | if c < 1: |
|---|
| 33 | C = array([c, 0.0, 1.0]) |
|---|
| 34 | else: |
|---|
| 35 | C = array([1.0, c-1.0, 1.0]) |
|---|
| 36 | BA = B-A |
|---|
| 37 | CA = C-A |
|---|
| 38 | return cross(BA, CA) |
|---|
| 39 | |
|---|
| 40 | def CuttingPlane(a, b, c): |
|---|
| 41 | N = PlaneNormal(a,b,c) |
|---|
| 42 | plane = vtk.vtkPlane() |
|---|
| 43 | plane.SetOrigin(a, 0, 0) |
|---|
| 44 | plane.SetNormal(*N) |
|---|
| 45 | return plane |
|---|
| 46 | |
|---|
| 47 | def getAxes(Origin, scale = 1): |
|---|
| 48 | axes = vtk.vtkAxes() |
|---|
| 49 | axes.SetOrigin(*Origin) |
|---|
| 50 | axes.SetScaleFactor(scale) |
|---|
| 51 | |
|---|
| 52 | axesTubes = vtk.vtkTubeFilter() |
|---|
| 53 | |
|---|
| 54 | axesTubes.SetInputConnection(axes.GetOutputPort()) |
|---|
| 55 | axesTubes.SetRadius(0.01) |
|---|
| 56 | axesTubes.SetNumberOfSides(6) |
|---|
| 57 | |
|---|
| 58 | axesMapper = vtk.vtkPolyDataMapper() |
|---|
| 59 | axesMapper.SetInputConnection(axesTubes.GetOutputPort()) |
|---|
| 60 | |
|---|
| 61 | axesActor = vtk.vtkActor() |
|---|
| 62 | axesActor.SetMapper(axesMapper) |
|---|
| 63 | |
|---|
| 64 | XText = vtk.vtkVectorText() |
|---|
| 65 | XText.SetText("x") |
|---|
| 66 | |
|---|
| 67 | XTextMapper = vtk.vtkPolyDataMapper() |
|---|
| 68 | XTextMapper.SetInputConnection(XText.GetOutputPort()) |
|---|
| 69 | |
|---|
| 70 | XActor = vtk.vtkFollower() |
|---|
| 71 | XActor.SetMapper(XTextMapper) |
|---|
| 72 | XActor.SetScale(.1, .1, .1) |
|---|
| 73 | XActor.SetPosition(1, Origin[1], Origin[2]) |
|---|
| 74 | XActor.GetProperty().SetColor(0, 0, 0) |
|---|
| 75 | |
|---|
| 76 | YText = vtk.vtkVectorText() |
|---|
| 77 | YText.SetText("y") |
|---|
| 78 | |
|---|
| 79 | YTextMapper = vtk.vtkPolyDataMapper() |
|---|
| 80 | YTextMapper.SetInputConnection(YText.GetOutputPort()) |
|---|
| 81 | |
|---|
| 82 | YActor = vtk.vtkFollower() |
|---|
| 83 | YActor.SetMapper(YTextMapper) |
|---|
| 84 | YActor.SetScale(.1, .1, .1) |
|---|
| 85 | YActor.SetPosition(Origin[0], 1, Origin[2]) |
|---|
| 86 | YActor.GetProperty().SetColor(0, 0, 0) |
|---|
| 87 | return axesActor, XActor, YActor |
|---|
| 88 | |
|---|
| 89 | axesActor, XActor, YActor = getAxes([-.1, -.1, -.1]) |
|---|
| 90 | |
|---|
| 91 | |
|---|
| 92 | P = (0.75, 0.75, 1.25) |
|---|
| 93 | |
|---|
| 94 | # vtkClipPolyData requires an implicit function to define what it is to |
|---|
| 95 | # clip with. Any implicit function, including complex boolean combinations |
|---|
| 96 | # can be used. Notice that we can specify the value of the implicit function |
|---|
| 97 | # with the SetValue method. |
|---|
| 98 | clipper = vtk.vtkClipPolyData() |
|---|
| 99 | clipper.SetInputConnection(cubeNormals.GetOutputPort()) |
|---|
| 100 | clipper.SetClipFunction(CuttingPlane(*P)) |
|---|
| 101 | |
|---|
| 102 | clipper.GenerateClipScalarsOn() |
|---|
| 103 | clipper.GenerateClippedOutputOn() |
|---|
| 104 | clipper.SetValue(0) |
|---|
| 105 | clipMapper = vtk.vtkPolyDataMapper() |
|---|
| 106 | clipMapper.SetInputConnection(clipper.GetOutputPort()) |
|---|
| 107 | clipMapper.ScalarVisibilityOff() |
|---|
| 108 | backProp = vtk.vtkProperty() |
|---|
| 109 | backProp.SetDiffuseColor(tomato) |
|---|
| 110 | clipActor = vtk.vtkActor() |
|---|
| 111 | clipActor.SetMapper(clipMapper) |
|---|
| 112 | clipActor.GetProperty().SetColor(peacock) |
|---|
| 113 | clipActor.SetBackfaceProperty(backProp) |
|---|
| 114 | |
|---|
| 115 | # Here we are cutting the cube. Cutting creates lines where the cut |
|---|
| 116 | # function intersects the model. (Clipping removes a portion of the |
|---|
| 117 | # model but the dimension of the data does not change.) |
|---|
| 118 | # |
|---|
| 119 | # The reason we are cutting is to generate a closed polygon at the |
|---|
| 120 | # boundary of the clipping process. The cutter generates line |
|---|
| 121 | # segments, the stripper then puts them together into polylines. We |
|---|
| 122 | # then pull a trick and define polygons using the closed line |
|---|
| 123 | # segements that the stripper created. |
|---|
| 124 | cutEdges = vtk.vtkCutter() |
|---|
| 125 | cutEdges.SetInputConnection(cubeNormals.GetOutputPort()) |
|---|
| 126 | cutEdges.SetCutFunction(CuttingPlane(*P)) |
|---|
| 127 | cutEdges.GenerateCutScalarsOn() |
|---|
| 128 | cutEdges.SetValue(0, 0) |
|---|
| 129 | cutStrips = vtk.vtkStripper() |
|---|
| 130 | cutStrips.SetInputConnection(cutEdges.GetOutputPort()) |
|---|
| 131 | cutStrips.Update() |
|---|
| 132 | cutPoly = vtk.vtkPolyData() |
|---|
| 133 | cutPoly.SetPoints(cutStrips.GetOutput().GetPoints()) |
|---|
| 134 | cutPoly.SetPolys(cutStrips.GetOutput().GetLines()) |
|---|
| 135 | |
|---|
| 136 | # Triangle filter is robust enough to ignore the duplicate point at |
|---|
| 137 | # the beginning and end of the polygons and triangulate them. |
|---|
| 138 | cutTriangles = vtk.vtkTriangleFilter() |
|---|
| 139 | cutTriangles.SetInput(cutPoly) |
|---|
| 140 | cutMapper = vtk.vtkPolyDataMapper() |
|---|
| 141 | cutMapper.SetInput(cutPoly) |
|---|
| 142 | cutMapper.SetInputConnection(cutTriangles.GetOutputPort()) |
|---|
| 143 | cutActor = vtk.vtkActor() |
|---|
| 144 | cutActor.SetMapper(cutMapper) |
|---|
| 145 | cutActor.GetProperty().SetColor(peacock) |
|---|
| 146 | |
|---|
| 147 | # The clipped part of the cube is rendered wireframe. |
|---|
| 148 | restMapper = vtk.vtkPolyDataMapper() |
|---|
| 149 | restMapper.SetInput(clipper.GetClippedOutput()) |
|---|
| 150 | restMapper.ScalarVisibilityOff() |
|---|
| 151 | restActor = vtk.vtkActor() |
|---|
| 152 | restActor.SetMapper(restMapper) |
|---|
| 153 | restActor.GetProperty().SetRepresentationToWireframe() |
|---|
| 154 | |
|---|
| 155 | # Create graphics stuff |
|---|
| 156 | renWin = vtk.vtkRenderWindow() |
|---|
| 157 | ren = vtk.vtkRenderer() |
|---|
| 158 | ren.SetBackground(tomato) |
|---|
| 159 | renWin.AddRenderer(ren) |
|---|
| 160 | |
|---|
| 161 | # Add the actors to the renderer, set the background and size |
|---|
| 162 | ren.AddActor(clipActor) |
|---|
| 163 | ren.AddActor(cutActor) |
|---|
| 164 | ren.AddActor(restActor) |
|---|
| 165 | ren.AddActor(axesActor) |
|---|
| 166 | ren.AddActor(XActor) |
|---|
| 167 | ren.AddActor(YActor) |
|---|
| 168 | ren.SetBackground(1, 1, 1) |
|---|
| 169 | ren.ResetCamera() |
|---|
| 170 | camera = ren.GetActiveCamera() |
|---|
| 171 | camera.SetFocalPoint(0.489125, 0.481143, 0.445) |
|---|
| 172 | camera.SetPosition(-0.870854, -1.51779, 3.14336) |
|---|
| 173 | camera.SetParallelScale(1.00818) |
|---|
| 174 | camera.SetParallelProjection(1) |
|---|
| 175 | camera.SetViewUp(-0.239476, 0.833984, 0.497114) |
|---|
| 176 | ren.ResetCameraClippingRange() |
|---|
| 177 | |
|---|
| 178 | renWin.SetSize(400, 400) |
|---|
| 179 | |
|---|
| 180 | def Cut(v): |
|---|
| 181 | Q = (P[0], P[1], v) |
|---|
| 182 | cp = CuttingPlane(*Q) |
|---|
| 183 | pn = PlaneNormal(*Q) |
|---|
| 184 | clipper.SetClipFunction(cp) |
|---|
| 185 | clipper.SetValue(0) |
|---|
| 186 | cutEdges.SetCutFunction(cp) |
|---|
| 187 | cutEdges.SetValue(0, 0) |
|---|
| 188 | cutStrips.Update() |
|---|
| 189 | cutPoly.SetPoints(cutStrips.GetOutput().GetPoints()) |
|---|
| 190 | cutPoly.SetPolys(cutStrips.GetOutput().GetLines()) |
|---|
| 191 | cutMapper.Update() |
|---|
| 192 | renWin.Render() |
|---|
| 193 | |
|---|
| 194 | root = Tkinter.Tk() |
|---|
| 195 | vtkw = vtkTkRenderWindowInteractor(root, rw=renWin, width=800) |
|---|
| 196 | |
|---|
| 197 | def set_cut(sz): |
|---|
| 198 | sz = float(sz) |
|---|
| 199 | # print ren.GetActiveCamera() |
|---|
| 200 | Cut(sz) |
|---|
| 201 | |
|---|
| 202 | # propagate this GUI setting to the corresponding VTK object. |
|---|
| 203 | size_slider = Tkinter.Scale(root, from_=0.0, |
|---|
| 204 | to=2.0, res=0.01, |
|---|
| 205 | orient='horizontal', label="Clipping Center", |
|---|
| 206 | command=set_cut) |
|---|
| 207 | |
|---|
| 208 | size_slider.set(P[2]) |
|---|
| 209 | vtkw.Initialize() |
|---|
| 210 | size_slider.pack(side="top", fill="both") |
|---|
| 211 | vtkw.pack(side="top", fill='both', expand=1) |
|---|
| 212 | |
|---|
| 213 | |
|---|
| 214 | # Define a quit method that exits cleanly. |
|---|
| 215 | def quit(obj=root): |
|---|
| 216 | obj.quit() |
|---|
| 217 | |
|---|
| 218 | root.protocol("WM_DELETE_WINDOW", quit) |
|---|
| 219 | |
|---|
| 220 | renWin.Render() |
|---|
| 221 | vtkw.Start() |
|---|
| 222 | |
|---|
| 223 | # start the Tkinter event loop. |
|---|
| 224 | root.mainloop() |
|---|
| 225 | |
|---|
| 226 | # end of file |
|---|