In visuallization GUI, fix the surface saving issues with .ply and .stl
[mipav.git] / mipav / src / gov / nih / mipav / view / renderer / WildMagic / Interface / JPanelSurface_WM.java
1 package gov.nih.mipav.view.renderer.WildMagic.Interface;\r
2 \r
3 \r
4 import gov.nih.mipav.model.structures.ModelImage;\r
5 import gov.nih.mipav.model.structures.ModelLUT;\r
6 import gov.nih.mipav.model.structures.ModelRGB;\r
7 import gov.nih.mipav.model.structures.ModelStorageBase;\r
8 import gov.nih.mipav.model.structures.TransMatrix;\r
9 import gov.nih.mipav.util.MipavCoordinateSystems;\r
10 import gov.nih.mipav.view.MipavUtil;\r
11 import gov.nih.mipav.view.ViewJColorChooser;\r
12 import gov.nih.mipav.view.ViewToolBarBuilder;\r
13 import gov.nih.mipav.view.ViewUserInterface;\r
14 import gov.nih.mipav.view.dialogs.JDialogSmoothMesh;\r
15 import gov.nih.mipav.view.renderer.WildMagic.VolumeTriPlanarInterface;\r
16 \r
17 import gov.nih.mipav.view.renderer.WildMagic.Decimate.TriangleMesh;\r
18 \r
19 import java.awt.BorderLayout;\r
20 import java.awt.Color;\r
21 import java.awt.Component;\r
22 import java.awt.Dimension;\r
23 import java.awt.FlowLayout;\r
24 import java.awt.Frame;\r
25 import java.awt.event.ActionEvent;\r
26 import java.awt.event.ActionListener;\r
27 import java.io.File;\r
28 import java.io.IOException;\r
29 import java.util.BitSet;\r
30 import java.util.HashMap;\r
31 import java.util.Hashtable;\r
32 import java.util.Random;\r
33 import java.util.Vector;\r
34 \r
35 import javax.swing.BorderFactory;\r
36 import javax.swing.Box;\r
37 import javax.swing.BoxLayout;\r
38 import javax.swing.DefaultListModel;\r
39 import javax.swing.JButton;\r
40 import javax.swing.JCheckBox;\r
41 import javax.swing.JComboBox;\r
42 import javax.swing.JLabel;\r
43 import javax.swing.JList;\r
44 import javax.swing.JPanel;\r
45 import javax.swing.JScrollPane;\r
46 import javax.swing.JSlider;\r
47 import javax.swing.JTabbedPane;\r
48 import javax.swing.JTextField;\r
49 import javax.swing.JToolBar;\r
50 import javax.swing.border.EmptyBorder;\r
51 import javax.swing.event.ChangeEvent;\r
52 import javax.swing.event.ChangeListener;\r
53 import javax.swing.event.ListSelectionEvent;\r
54 import javax.swing.event.ListSelectionListener;\r
55 \r
56 \r
57 import WildMagic.LibFoundation.Mathematics.ColorRGB;\r
58 import WildMagic.LibFoundation.Mathematics.ColorRGBA;\r
59 import WildMagic.LibFoundation.Mathematics.Vector3f;\r
60 import WildMagic.LibFoundation.Meshes.ConvexHull3f;\r
61 import WildMagic.LibFoundation.Meshes.VETMesh;\r
62 import WildMagic.LibGraphics.Detail.ClodMesh;\r
63 import WildMagic.LibGraphics.Rendering.MaterialState;\r
64 import WildMagic.LibGraphics.Rendering.WireframeState;\r
65 import WildMagic.LibGraphics.SceneGraph.IndexBuffer;\r
66 import WildMagic.LibGraphics.SceneGraph.Polyline;\r
67 import WildMagic.LibGraphics.SceneGraph.StandardMesh;\r
68 import WildMagic.LibGraphics.SceneGraph.TriMesh;\r
69 import WildMagic.LibGraphics.SceneGraph.VertexBuffer;\r
70 \r
71 \r
72 public class JPanelSurface_WM extends JInterfaceBase\r
73         implements ListSelectionListener, ChangeListener {\r
74 \r
75     /** Use serialVersionUID for interoperability. */\r
76     private static final long serialVersionUID = -4600563188022683359L;\r
77 \r
78     /** The colors for the surfaces. */\r
79         private static ColorRGB[] fixedColor;\r
80 \r
81     /** The area label. */\r
82     private JLabel areaLabel;\r
83 \r
84     /** Displays the area of triangle. */\r
85     private JTextField areaText;\r
86 \r
87     /** The color button, which calls a color chooser. */\r
88     private JButton colorButton;\r
89 \r
90     /** The color button label. */\r
91     private JLabel colorLabel;\r
92 \r
93     /** The polygon mode combo box label. */\r
94     private JLabel comboLabel;\r
95 \r
96     /** Decimate button. */\r
97     private JButton decimateButton;\r
98 \r
99     /** The level of detail slider label. */\r
100     private JLabel detailLabel;\r
101 \r
102     /** Level of detail slider. */\r
103     private JSlider detailSlider;\r
104 \r
105     /** The labels below the detail slider. */\r
106     private JLabel[] detailSliderLabels;\r
107 \r
108     /** Save surface button. */\r
109     private JButton saveSurfaceButton;\r
110     \r
111     /** Save .PLY surface button. */\r
112     private JButton savePLYSurfaceButton;\r
113  \r
114     /** Save .STL surface button. */\r
115     private JButton saveSTLSurfaceButton;\r
116     \r
117     /** Paint tool-bar (contained in the SurfacePaint class) */\r
118     private SurfacePaint_WM m_kSurfacePaint = null;\r
119 \r
120     /** The material options button, which launches the material editor window. */\r
121     private JButton m_kAdvancedMaterialOptionsButton;\r
122 \r
123     /** Opens SurfaceTexture dialog:. */\r
124     private JButton m_kSurfaceTextureButton;\r
125 \r
126     /** The opacity slider label. */\r
127     private JLabel opacityLabel;\r
128 \r
129     /** Opacity slider, not enabled yet. */\r
130     private JSlider opacitySlider;\r
131 \r
132     /** The labels below the opacity slider. */\r
133     private JLabel[] opacitySliderLabels;\r
134 \r
135     /** The combo box for the polygon mode to display. */\r
136     private JComboBox polygonModeCB;\r
137 \r
138     /** The scroll pane holding the panel content. Useful when the screen is small. */\r
139     private JScrollPane scroller;\r
140 \r
141     /** Smooth button. */\r
142     private JButton invertNormals, smooth1Button, smooth2Button, smooth3Button, convexHull, subdivideTriangles, extractConnected;\r
143 \r
144     /** Polyline list box in the dialog for surfaces. */\r
145     private JList polylineList;\r
146     \r
147     /** The list box in the dialog for surfaces. */\r
148     private JList surfaceList;\r
149 \r
150     /** Check Box for surface picking. */\r
151     private JCheckBox surfacePickableCB;\r
152 \r
153     /** Check Box for surface back face culling. */\r
154     private JCheckBox surfaceBackFaceCB;\r
155 \r
156     /** Check Box for surface clpping of the volume render. */\r
157     private JCheckBox surfaceClipCB;\r
158 \r
159     /** Check Box for surface transparency. */\r
160     private JCheckBox surfaceTransparencyCB;\r
161 \r
162     /** The number of triangles label. */\r
163     private JLabel triangleLabel;\r
164 \r
165     /** Displays the number of triangles. */\r
166     private JTextField triangleText;\r
167 \r
168     /** The volume label. */\r
169     private JLabel volumeLabel;\r
170 \r
171     /** Displays the volume of triangle. */\r
172     private JTextField volumeText;\r
173 \r
174     /** Polyline counter list <index, groupID> */\r
175     private DefaultListModel polylineCounterList = new DefaultListModel();\r
176     \r
177     /** constant polyline counter */\r
178     private int polylineCounter = 0;\r
179     \r
180     /** Decimation Percentage */\r
181     private double decimationPercentage = 95.0;\r
182     \r
183     /** triangle mesh for decimation. */\r
184     private TriangleMesh[] tmesh;\r
185     \r
186     private Vector<SurfaceState> m_akSurfaceStates = new Vector<SurfaceState>();\r
187     \r
188     \r
189     /**\r
190      * Constructor.\r
191      * @param kVolumeViewer parent frame.\r
192      */\r
193     public JPanelSurface_WM( VolumeTriPlanarInterface kVolumeViewer )\r
194     {\r
195         super(kVolumeViewer);\r
196         m_kSurfacePaint = new SurfacePaint_WM(this, m_kVolumeViewer);\r
197         \r
198                 fixedColor = new ColorRGB[255];\r
199                 ModelLUT lut = new ModelLUT(ModelLUT.STRIPED, 256, new int[] {4, 256}); \r
200                 Color lutColor;\r
201                 for (int n=0;n<255;n++) \r
202                 {\r
203                         lutColor = lut.getColor(n+1);\r
204                         fixedColor[n] = new ColorRGB( lutColor.getRed()/255f,\r
205                                         lutColor.getGreen()/255f, lutColor.getBlue()/255f );\r
206                 }\r
207         init();\r
208     }\r
209     \r
210     /**\r
211      * static function returns the next default surface color, based on the current number of surfaces displayed. If the\r
212      * number of surfaces is less than the fixedColor.length then fixedColor is the source of the surface color,\r
213      * otherwise a random color is generated.\r
214      *\r
215      * @param   index  the number of the new surface\r
216      *\r
217      * @return  Color4f, the default surface color for the new surface.\r
218      */\r
219     public static ColorRGB getNewSurfaceColor(int index) {\r
220         ColorRGB surfaceColor = new ColorRGB();\r
221 \r
222         if (index < fixedColor.length) {\r
223             // Use the fixed colors for the first six surfaces.\r
224             surfaceColor.Copy( fixedColor[index] );\r
225         }\r
226         else\r
227         {\r
228             Random randomGen = new Random();\r
229 \r
230             // Use randomly generated colors for the seventh and\r
231             // later surfaces.\r
232             surfaceColor.Set( 0.5f * (1.0f + randomGen.nextFloat()),\r
233                                   0.5f * (1.0f + randomGen.nextFloat()),\r
234                                   0.5f * (1.0f + randomGen.nextFloat()) );\r
235         }\r
236 \r
237         return surfaceColor;\r
238     }\r
239 \r
240     /**\r
241      * The override necessary to be an ActionListener. This callback is executed whenever the Add or Remove buttons are\r
242      * clicked, or when the color button or light button is clicked, or when the combo box changes. If the Add button is\r
243      * clicked, a file dialog is launched to allow the user to select new surface meshes to load from disk. If the\r
244      * Remove button is clicked, the currently selected surfaces in the list box are removed from the scene graph.\r
245      *\r
246      * @param  event  The action event.\r
247      */\r
248     public void actionPerformed(ActionEvent event) {\r
249         String command = event.getActionCommand();\r
250 \r
251         if (command.equals("Add")) {\r
252                 try {\r
253             addSurface(); } catch ( NullPointerException e ) {}\r
254         } \r
255         else if (command.equals("Remove")) {\r
256             removeSurface();\r
257         }\r
258         else if (command.equals("AddPolyline")) {\r
259             addPolyline();\r
260         } \r
261         else if (command.equals("RemovePolyline")) {\r
262             removePolyline();\r
263         }\r
264         else if (command.equals("ChangeColor")) {\r
265             colorChooser = new ViewJColorChooser(new Frame(), "Pick surface color", new OkColorListener(colorButton),\r
266                                                  new CancelListener());\r
267         } /* \r
268          else if (command.equals("ImageAsTexture")) {\r
269             displayImageAsTexture(getSelectedSurfaces(surfaceList.getSelectedIndices()));\r
270         } */ \r
271         else if (command.equals("AdvancedMaterialOptions")) {\r
272             displayAdvancedMaterialOptions(surfaceList.getSelectedIndices());\r
273         } \r
274         else if (command.equals("SurfaceTexture")) {\r
275             m_kVolumeViewer.actionPerformed(new ActionEvent(m_kSurfaceTextureButton, 0, "SurfaceTexture"));\r
276         }\r
277 \r
278         else if (command.equals("SurfacePickable")) {\r
279             setPickable(surfaceList.getSelectedIndices());\r
280         }  \r
281         else if (command.equals("backface")) {\r
282             setBackface(surfaceList.getSelectedIndices());\r
283         }\r
284         else if (command.equals("transparency")) {\r
285             setTransparency(surfaceList.getSelectedIndices());\r
286         }\r
287         else if (command.equals("Clipping") )\r
288         {\r
289             setClipping(surfaceList.getSelectedIndices());\r
290         }\r
291         else if (command.equals("ChangePolyMode")) {\r
292             changePolyMode(polygonIndexToMode(polygonModeCB.getSelectedIndex()));\r
293         } \r
294         else if (command.equals("saveSurface")) {\r
295             saveSurfaces( surfaceList.getSelectedIndices(), command );          \r
296         }\r
297         else if (command.equals("savePLYSurface")) {\r
298             saveProstateSurfaces( surfaceList.getSelectedIndices(), command );          \r
299         }\r
300         else if (command.equals("saveSTLSurface")) {\r
301             saveProstateSurfaces( surfaceList.getSelectedIndices(), command );          \r
302         }\r
303         else if (command.equals("Smooth")) {\r
304             smoothSurface(surfaceList.getSelectedIndices(), JDialogSmoothMesh.SMOOTH1);\r
305         } else if (command.equals("Smooth2")) {\r
306             smoothSurface(surfaceList.getSelectedIndices(), JDialogSmoothMesh.SMOOTH2);\r
307         } else if (command.equals("Smooth3")) {\r
308             smoothSurface(surfaceList.getSelectedIndices(), JDialogSmoothMesh.SMOOTH3);\r
309         } \r
310         else if (command.equals("Decimate")) {\r
311             decimate(surfaceList.getSelectedIndices());\r
312         }\r
313         else if ( command.equals("InvertNormals") )\r
314         {\r
315                 invertNormals(surfaceList.getSelectedIndices());\r
316         }\r
317         else if ( command.equals("ExtractConnected") )\r
318         {\r
319                 extractConnectedComponents(surfaceList.getSelectedIndices());\r
320         }\r
321         else if ( command.equals("ConvexHull") )\r
322         {\r
323                 convexHull(surfaceList.getSelectedIndices());\r
324         }\r
325         else if ( command.equals( "SubdivideTriangles" ) )\r
326         {\r
327                 subDivideTriangles( surfaceList.getSelectedIndices() );\r
328         }\r
329     }\r
330 \r
331     /**\r
332      * Add surface to the volume image. Calls the FileSurface.openSurfaces function to open a file dialog so the user\r
333      * can choose the surfaces to add.\r
334      */\r
335     public void addSurface() {\r
336         TriMesh[] akSurfaces = FileSurface_WM.openSurfaces(m_kVolumeViewer.getImageA());\r
337         \r
338         if ( akSurfaces != null )\r
339         {\r
340             addSurfaces(akSurfaces);\r
341         }\r
342         \r
343     }\r
344 \r
345     public Vector<SurfaceState> getSurfaceStates()\r
346     {\r
347         return m_akSurfaceStates;\r
348     }\r
349 \r
350     public void setSurfaceStates(Vector<SurfaceState> kSurfaces)\r
351     {\r
352         DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
353         int iSize = kList.getSize();\r
354         m_akSurfaceStates = kSurfaces;\r
355         for ( int i = 0; i < kSurfaces.size(); i++ )\r
356         {            \r
357             kList.add( iSize + i, m_akSurfaceStates.get(i).Name );\r
358             m_kVolumeViewer.addSurface( m_akSurfaceStates.get(i) );\r
359             updateSelected(i);\r
360         }\r
361         surfaceList.setSelectedIndex(kSurfaces.size());\r
362         setElementsEnabled(true);\r
363     }\r
364     \r
365     \r
366     /**\r
367      * Add surfaces to the Volume Tri-Planar renderer.\r
368      * @param akSurfaces new surfaces.\r
369      */\r
370     public void addSurfaces( TriMesh[] akSurfaces )\r
371     {\r
372         DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
373         int iSize = kList.getSize();\r
374         for ( int i = 0; i < akSurfaces.length; i++ )\r
375         {\r
376                 kList.add( iSize + i, akSurfaces[i].GetName() );\r
377                 SurfaceState kSurface = new SurfaceState( akSurfaces[i], akSurfaces[i].GetName() );\r
378                 m_akSurfaceStates.add( kSurface );        \r
379                 m_kVolumeViewer.addSurface( kSurface );\r
380         }\r
381         surfaceList.setSelectedIndex(iSize);\r
382         setElementsEnabled(true);\r
383     }\r
384 \r
385     /**\r
386      * Changes the polygon mode of the selected surface by detaching it, calling the appropriate method, and reattaching\r
387      * it.\r
388      *\r
389      * @param  mode  The new polygon mode to set.\r
390      */\r
391     public void changePolyMode(WireframeState.FillMode mode) {\r
392         int[] aiSelected = surfaceList.getSelectedIndices();\r
393 \r
394         if ( m_kVolumeViewer != null )\r
395         {\r
396             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
397             for (int i = 0; i < aiSelected.length; i++) {\r
398                 m_akSurfaceStates.get( aiSelected[i] ).Fill = mode;\r
399                 m_kVolumeViewer.setPolygonMode( (String)kList.elementAt(aiSelected[i]), mode);\r
400             }\r
401         }\r
402     }\r
403 \r
404     /**\r
405      * Dispose the local memory.\r
406      */\r
407     public void disposeLocal() {\r
408         int i;\r
409         \r
410         if ( tmesh != null ) {\r
411                 for ( i = 0; i < tmesh.length; i++ ) {\r
412                         tmesh[i].dispose();\r
413                         tmesh[i] = null;\r
414                 }\r
415                 tmesh = null;\r
416         }\r
417         /*\r
418         if (  m_kMeshes != null ) {\r
419                 for ( i = 0; i < m_kMeshes.size(); i++ ) {\r
420                         m_kMeshes.set(i, null);\r
421                 }\r
422                 m_kMeshes = null;\r
423         } */\r
424         \r
425         if ( m_kSurfacePaint != null ) {\r
426                 m_kSurfacePaint.dispose();\r
427                 m_kSurfacePaint = null;\r
428         }\r
429         \r
430     }\r
431 \r
432     /**\r
433      * Enables/Disables the SurfacePaint per-vertex functions.\r
434      * @param  bEnable  when true the SurfacePaint per-vertex functions (PaintBrush, Dropper, Eraser, BrushSize) are\r
435      *                  enabled, when false they are disabled.\r
436      */\r
437     public void enableSurfacePaint(boolean bEnable) {\r
438         m_kSurfacePaint.enableSurfacePaint(bEnable);\r
439     }\r
440 \r
441 \r
442     /**\r
443      * Enables/Disables the SurfacePaint Paint Can function.\r
444      * @param  bEnable  when true the Paint Can function is enabled, when false it is disabled.\r
445      */\r
446     public void enableSurfacePaintCan(boolean bEnable) {\r
447         m_kSurfacePaint.enableSurfacePaintCan(bEnable);\r
448     }\r
449 \r
450     /**\r
451      * Return the name of the selected surface.\r
452      * @return name of the selected surface.\r
453      */\r
454     public String getSelectedSurface()\r
455     {\r
456         int[] aiSelected = surfaceList.getSelectedIndices();\r
457         if ( aiSelected.length == 0 )\r
458         {\r
459             return null;\r
460         }               \r
461         DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
462         return (String)kList.elementAt(aiSelected[aiSelected.length - 1]);\r
463     }\r
464 \r
465     /**\r
466      * Return the names of the selected surfaces.\r
467      * @return names of the selected surfaces.\r
468      */\r
469     public String[] getSelectedSurfaces() {\r
470         int[] aiSelected = surfaceList.getSelectedIndices();\r
471         String[] akNames = new String[aiSelected.length];\r
472         DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
473         for (int i = 0; i < aiSelected.length; i++) {\r
474             akNames[i] = new String((String)kList.elementAt(aiSelected[i]));\r
475         }\r
476         return akNames;\r
477     }\r
478 \r
479 \r
480     /**\r
481      * Turn surface texture on/off.\r
482      * @param bTextureOn texture on/off.\r
483      * @param bUseNewImage when true use the user-specified ModelImage, when false use default ModelImage.\r
484      * @param bUseNewLUT when true use the user-specified LUT, when false use the defaulet LUT.\r
485      */\r
486     public void ImageAsTexture( boolean bTextureOn, boolean bUseNewImage, boolean bUseNewLUT )\r
487     {\r
488         int[] aiSelected = surfaceList.getSelectedIndices();\r
489         if ( m_kVolumeViewer != null )\r
490         {\r
491             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
492             for (int i = 0; i < aiSelected.length; i++) {\r
493                 m_kVolumeViewer.setSurfaceTexture( (String)kList.elementAt(aiSelected[i]),\r
494                         bTextureOn, bUseNewImage, bUseNewLUT);\r
495             }\r
496         }\r
497         enableSurfacePaintCan(bTextureOn);\r
498     }\r
499 \r
500     /**\r
501      * Check if the surface pickable checkbox be selected or not.\r
502      * @return  isSelected Surface pickable check box selected or not.\r
503      */\r
504     public boolean isSurfacePickableSelected() {\r
505         return surfacePickableCB.isSelected();\r
506     }\r
507 \r
508     /**\r
509      * Resizing the control panel with ViewJFrameVolumeView's frame width and height.\r
510      *\r
511      * @param  panelWidth   int width\r
512      * @param  frameHeight  int height\r
513      */\r
514     public void resizePanel(int panelWidth, int frameHeight) {\r
515         frameHeight = frameHeight - (40 * 2);\r
516         scroller.setPreferredSize(new Dimension(panelWidth, frameHeight));\r
517         scroller.setSize(new Dimension(panelWidth, frameHeight));\r
518         scroller.revalidate();\r
519     }\r
520     \r
521     /* (non-Javadoc)\r
522      * @see gov.nih.mipav.view.renderer.WildMagic.Interface.JInterfaceBase#setButtonColor(javax.swing.JButton, java.awt.Color)\r
523      */\r
524     public void setButtonColor(JButton _button, Color _color) {\r
525 \r
526         super.setButtonColor(_button, _color);\r
527 \r
528         if ( m_kVolumeViewer != null )\r
529         {\r
530             int[] aiSelected = surfaceList.getSelectedIndices();\r
531             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
532             for (int i = 0; i < aiSelected.length; i++) {\r
533                 ColorRGB kColor = new ColorRGB( _color.getRed()/255.0f, \r
534                         _color.getGreen()/255.0f,\r
535                         _color.getBlue()/255.0f );\r
536                 m_akSurfaceStates.get(aiSelected[i]).SurfaceColor = \r
537                     new Color( _color.getRed(), _color.getGreen(), _color.getBlue() );\r
538                 m_kVolumeViewer.setColor( (String)kList.elementAt(aiSelected[i]),\r
539                         kColor, true );\r
540             }\r
541         }\r
542     }\r
543 \r
544     /**\r
545      * Set the paint can color.\r
546      * @param kDropperColor color.\r
547      * @param kPickPoint picked point on the surface.\r
548      */\r
549     public void setDropperColor( ColorRGBA kDropperColor, Vector3f kPickPoint )\r
550     {\r
551         if ( m_kSurfacePaint != null )\r
552         {\r
553             m_kSurfacePaint.setDropperColor(kDropperColor, kPickPoint);\r
554         }\r
555     }\r
556 \r
557     /**\r
558      * Set the user-specified ModelImage to use as the surface texture.\r
559      * @param kImage ModelImage to use as the surface texture.\r
560      */\r
561     public void SetImageNew(  ModelImage kImage )\r
562     {\r
563         int[] aiSelected = surfaceList.getSelectedIndices();\r
564         if ( m_kVolumeViewer != null )\r
565         {\r
566             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
567             for (int i = 0; i < aiSelected.length; i++) {\r
568                 m_kVolumeViewer.SetImageNew( (String)kList.elementAt(aiSelected[i]), kImage);\r
569             }\r
570         }\r
571     }\r
572 \r
573     /**\r
574      * Set the user-specified LUT for surface texture.\r
575      * @param kLUT ModelLUT\r
576      * @param kRGBT ModelRGB for color images.\r
577      */\r
578     public void SetLUTNew(  ModelStorageBase kLUT )\r
579     {\r
580         int[] aiSelected = surfaceList.getSelectedIndices();\r
581         if ( m_kVolumeViewer != null )\r
582         {\r
583             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
584             for (int i = 0; i < aiSelected.length; i++) {\r
585                 m_kVolumeViewer.SetLUTNew( (String)kList.elementAt(aiSelected[i]), kLUT);\r
586             }\r
587         }\r
588     }\r
589 \r
590     /**\r
591      * Called from the JPanelSurfaceMAterialProperties.java dialog when the dialog is used to change the material\r
592      * properties of a surface. The surface is determined by the index iIndex. The color button is set to the Material\r
593      * diffuse color.\r
594      *\r
595      * @param  kMaterial  Material reference\r
596      * @param  iIndex     int material index\r
597      */\r
598     public void setMaterial(MaterialState kMaterial, int iIndex) {\r
599         colorButton.setBackground( new Color(kMaterial.Diffuse.R, kMaterial.Diffuse.G, kMaterial.Diffuse.B) );\r
600         if ( m_kVolumeViewer != null )\r
601         {\r
602             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
603             m_akSurfaceStates.get(iIndex).Material = kMaterial;\r
604             m_kVolumeViewer.setMaterial( (String)kList.elementAt(iIndex), kMaterial, true);\r
605         }\r
606     }\r
607 \r
608     /* (non-Javadoc)\r
609      * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)\r
610      */\r
611     public void stateChanged(ChangeEvent event)\r
612     {\r
613         if (event.getSource() == opacitySlider)\r
614         {\r
615             setTransparency(surfaceList.getSelectedIndices());\r
616         }\r
617 \r
618         if (event.getSource() == detailSlider) {\r
619 \r
620             if (detailSlider.getValueIsAdjusting()) {\r
621                 // Too many LOD changes occur if you always get the slider\r
622                 // value.  Wait until the user stops dragging the slider.\r
623 \r
624                 // Maybe not. Comment out the next line to have the surface update quickly.\r
625                 // If the CLOD mesh holds a surface over time one could view the surface evolution !!!!\r
626                 return;\r
627             }\r
628 \r
629             \r
630             \r
631             // value in [0,100], corresponds to percent of maximum LOD\r
632             float fValue = 1.0f - (float)detailSlider.getValue() / (float)detailSlider.getMaximum();\r
633             decimationPercentage = fValue * 100.0;\r
634             \r
635             int numTriangles = 0;\r
636             // construct the lists of items whose LODs need to be changed\r
637             int[] aiSelected = surfaceList.getSelectedIndices();\r
638             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
639             boolean found = false;\r
640             if ( aiSelected.length == 0) return;\r
641             \r
642             for (int i = 0; i < aiSelected.length; i++) {\r
643 \r
644                 if ( m_akSurfaceStates.get(aiSelected[i]).Surface instanceof ClodMesh )\r
645                 {\r
646                     ClodMesh kCMesh = (ClodMesh)m_akSurfaceStates.get(aiSelected[i]).Surface;\r
647                     int iValue = (int)(fValue * kCMesh.GetMaximumLOD());\r
648                     kCMesh.TargetRecord( iValue );\r
649                     kCMesh.SelectLevelOfDetail();\r
650                     numTriangles += kCMesh.GetTriangleQuantity();\r
651                     triangleText.setText("" + numTriangles);\r
652                 }\r
653                 else \r
654                 {\r
655                     TriMesh kMesh = m_akSurfaceStates.get(aiSelected[i]).Surface;\r
656                     \r
657                     try {\r
658                          for ( int j = 0; j < tmesh.length; j++ ) {\r
659                                  \r
660                                      if ( tmesh[j].GetName().equals(kMesh.GetName())) {\r
661                                          found = true;\r
662                                                  tmesh[j].doDecimation(decimationPercentage);\r
663                                                  TriMesh mesh = new TriMesh(tmesh[j].getDecimatedVBuffer(), tmesh[j].getDecimatedIBuffer());\r
664                                                  mesh.SetName(new String(kMesh.GetName()));\r
665                                                  SurfaceState kState = new SurfaceState( mesh, mesh.GetName() );\r
666                                                  \r
667                                              m_kVolumeViewer.removeSurface( (String)kList.elementAt(aiSelected[i]) );\r
668                                              m_akSurfaceStates.remove(aiSelected[i]);\r
669                                              \r
670                                              m_akSurfaceStates.add( kState );        \r
671                                              m_kVolumeViewer.addSurface(kState);\r
672                         \r
673                                              numTriangles = tmesh[j].getDecimatedIBuffer().GetIndexQuantity();\r
674                                      } \r
675                          }\r
676                         } catch ( Exception e ) {\r
677                                 e.printStackTrace();\r
678                         }       \r
679                     \r
680                     \r
681                    \r
682                 }\r
683             }\r
684             if ( found ) {\r
685                     numTriangles /= 3;\r
686                     triangleText.setText("" + numTriangles);\r
687                     // keep the current selected mesh type: fill, points, or lines. \r
688                     changePolyMode(polygonIndexToMode(polygonModeCB.getSelectedIndex()));\r
689             }\r
690             \r
691         }\r
692     }\r
693     \r
694     /**\r
695      * Returns true if a surface exists in the Renderer.\r
696      * @return true if a surface exists in the Renderer.\r
697      */\r
698     public boolean surfaceAdded()\r
699     {\r
700         DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
701         if ( kList.size() > 0 )\r
702         {\r
703             return true;\r
704         }\r
705         return false;\r
706     }\r
707     \r
708     /**\r
709      * Toggle which type of Geodesic is displayed on the surface (Euclidian, Dijkstra, Geodesic).\r
710      * @param iWhich type of Geodesic is displayed on the surface (Euclidian, Dijkstra, Geodesic).\r
711      */\r
712     public void toggleGeodesicPathDisplay(int iWhich) {\r
713         int[] aiSelected = surfaceList.getSelectedIndices();\r
714         if ( m_kVolumeViewer != null )\r
715         {\r
716             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
717             for (int i = 0; i < aiSelected.length; i++) {\r
718                 m_kVolumeViewer.toggleGeodesicPathDisplay( (String)kList.elementAt(aiSelected[i]),\r
719                         iWhich);\r
720             }\r
721         }\r
722     }    \r
723     /* (non-Javadoc)\r
724      * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)\r
725      */\r
726     public void valueChanged(ListSelectionEvent kEvent)\r
727     {\r
728         if ( m_kVolumeViewer != null )\r
729         {\r
730             int[] aiSelected = surfaceList.getSelectedIndices();\r
731             if ( aiSelected.length == 1 )\r
732             {\r
733                 setSelected( aiSelected[0] );\r
734             }\r
735         }\r
736     }\r
737     \r
738     public int getSelected()\r
739     {\r
740         int[] aiSelected = surfaceList.getSelectedIndices();\r
741         if ( aiSelected.length > 0 )\r
742         {\r
743             return aiSelected[0];\r
744         }\r
745         return 0;\r
746     }\r
747     \r
748     public void setSelected( int i )\r
749     {\r
750         if ( m_akSurfaceStates.size() <= i )\r
751         {\r
752             return;\r
753         }\r
754         surfaceList.setSelectedIndex(i);\r
755         SurfaceState kState = m_akSurfaceStates.get(i);\r
756         triangleText.setText(String.valueOf( kState.Surface.GetTriangleQuantity() ) );\r
757         volumeText.setText(String.valueOf( m_kVolumeViewer.getVolumeString( kState.Name ) ) );\r
758         areaText.setText(String.valueOf( m_kVolumeViewer.getSurfaceAreaString( kState.Name ) ) );  \r
759 \r
760         colorButton.setBackground( kState.SurfaceColor );\r
761         \r
762         opacitySlider.setValue((int)(kState.Opacity * 100));\r
763         surfaceTransparencyCB.setSelected( kState.TransparencyOn );\r
764         \r
765         polygonModeCB.setSelectedIndex( fillModeToPolygonIndex( kState.Fill ) );\r
766         \r
767         surfacePickableCB.setSelected( kState.Pickable );\r
768         \r
769         surfaceClipCB.setSelected( kState.Clip );\r
770         \r
771         surfaceBackFaceCB.setSelected( kState.BackfaceCull );\r
772 \r
773         if ( kState.Surface instanceof ClodMesh )\r
774         {\r
775             decimateButton.setEnabled(false);\r
776             detailSlider.setEnabled(true);\r
777             detailLabel.setEnabled(true);\r
778         }\r
779         else\r
780         {\r
781             decimateButton.setEnabled(true);\r
782             detailSlider.setEnabled(false);\r
783             detailLabel.setEnabled(false);                    \r
784         }\r
785     }\r
786     \r
787     private void updateSelected( int i )\r
788     {\r
789         SurfaceState kState = m_akSurfaceStates.get(i); \r
790 \r
791         colorButton.setBackground( kState.SurfaceColor );\r
792         m_kVolumeViewer.setColor( kState.Name, \r
793                 new ColorRGB( kState.SurfaceColor.getRed()/255.0f,\r
794                         kState.SurfaceColor.getGreen()/255.0f,\r
795                         kState.SurfaceColor.getBlue()/255.0f ), false );\r
796         m_kVolumeViewer.setMaterial( kState.Name, kState.Material, false );\r
797         \r
798         m_kVolumeViewer.setTransparency( kState.Name, kState.Opacity );\r
799         \r
800         m_kVolumeViewer.setPolygonMode( kState.Name, kState.Fill );\r
801         \r
802         m_kVolumeViewer.setPickable( kState.Name, kState.Pickable );\r
803         \r
804         m_kVolumeViewer.setClipping( kState.Name, kState.Clip );\r
805         \r
806         m_kVolumeViewer.setBackface( kState.Name, kState.BackfaceCull );\r
807     }\r
808 \r
809     /** \r
810      * Add polyline to the render.\r
811      */\r
812     public void addPolyline() {\r
813         Polyline[] akPolylines = FilePolyline_WM.openPolylines(m_kVolumeViewer.getImageA());\r
814         Vector3f m_kTranslate = m_kVolumeViewer.getVolumeGPU().getTranslate();\r
815         \r
816          float m_fMax, m_fX, m_fY, m_fZ;\r
817          float fMaxX = (m_kVolumeViewer.getImageA().getExtents()[0] - 1) * m_kVolumeViewer.getImageA().getFileInfo(0).getResolutions()[0];\r
818          float fMaxY = (m_kVolumeViewer.getImageA().getExtents()[1] - 1) * m_kVolumeViewer.getImageA().getFileInfo(0).getResolutions()[1];\r
819          float fMaxZ = (m_kVolumeViewer.getImageA().getExtents()[2] - 1) * m_kVolumeViewer.getImageA().getFileInfo(0).getResolutions()[2];\r
820 \r
821          m_fMax = fMaxX;\r
822          if (fMaxY > m_fMax) {\r
823              m_fMax = fMaxY;\r
824          }\r
825          if (fMaxZ > m_fMax) {\r
826              m_fMax = fMaxZ;\r
827          }\r
828          m_fX = fMaxX/m_fMax;\r
829          m_fY = fMaxY/m_fMax;\r
830          m_fZ = fMaxZ/m_fMax;\r
831         \r
832         if ( akPolylines != null )\r
833         {\r
834                 DefaultListModel kList = (DefaultListModel)polylineList.getModel();\r
835             int iSize = kList.getSize();\r
836             \r
837             for ( int i = 0; i < akPolylines.length ; i++ ) {\r
838                 polylineCounterList.add(iSize + i, polylineCounter);\r
839                 \r
840                  \r
841                  for ( int j = 0; j < akPolylines[i].VBuffer.GetVertexQuantity(); j++ )\r
842                  {\r
843 \r
844                           akPolylines[i].VBuffer.SetPosition3(j, akPolylines[i].VBuffer.GetPosition3fX(j) - m_kTranslate.X,\r
845                                           akPolylines[i].VBuffer.GetPosition3fY(j) - m_kTranslate.Y, \r
846                                           akPolylines[i].VBuffer.GetPosition3fZ(j) - m_kTranslate.Z );\r
847                            akPolylines[i].VBuffer.SetPosition3(j, \r
848                                         akPolylines[i].VBuffer.GetPosition3fX(j) * 1.0f/m_fX,\r
849                                         akPolylines[i].VBuffer.GetPosition3fY(j) * 1.0f/m_fY,\r
850                                         akPolylines[i].VBuffer.GetPosition3fZ(j) * 1.0f/m_fZ);\r
851                  } \r
852                 \r
853                 akPolylines[i].Local.SetTranslate(new Vector3f(m_kTranslate.X, m_kTranslate.Y, m_kTranslate.Z));\r
854                 m_kVolumeViewer.addPolyline(null, akPolylines[i], polylineCounter);\r
855                 \r
856                 polylineCounter++;\r
857             }\r
858             \r
859             for ( int i = 0; i < akPolylines.length; i++ )\r
860             {\r
861                 kList.add( iSize + i, akPolylines[i].GetName() );\r
862                \r
863             }\r
864             polylineList.setSelectedIndex(iSize);\r
865             \r
866         }\r
867     }\r
868     \r
869     /**\r
870      * Build the toolbar.\r
871      */\r
872     private void buildToolBar() {\r
873 \r
874         ViewToolBarBuilder toolbarBuilder = new ViewToolBarBuilder(this);\r
875 \r
876         JToolBar toolBar = new JToolBar();\r
877         toolBar.putClientProperty("JToolBar.isRollover", Boolean.TRUE);\r
878         toolBar.setFloatable(false);\r
879         \r
880         subdivideTriangles = toolbarBuilder.buildButton("SubdivideTriangles", "divide triangles", "decimate");\r
881         convexHull = toolbarBuilder.buildButton("ConvexHull", "compute convex hull", "decimate");\r
882         extractConnected = toolbarBuilder.buildButton("ExtractConnected", "Extract connected components", "decimate");\r
883         invertNormals = toolbarBuilder.buildButton("InvertNormals", "Reverses triangle order", "decimate");\r
884         smooth1Button = toolbarBuilder.buildButton("Smooth", "Smooth level 1", "sm1");\r
885         smooth2Button = toolbarBuilder.buildButton("Smooth2", "Smooth level 2", "sm2");\r
886         smooth3Button = toolbarBuilder.buildButton("Smooth3", "Smooth level 3", "sm3");\r
887         decimateButton = toolbarBuilder.buildButton("Decimate", "Decimate the surface", "decimate");\r
888         saveSurfaceButton = toolbarBuilder.buildButton("saveSurface", "Save surface to a file", "save");\r
889         savePLYSurfaceButton = toolbarBuilder.buildButton("savePLYSurface", "Save prostate surface to a PLY file", "saveply");\r
890         saveSTLSurfaceButton = toolbarBuilder.buildButton("saveSTLSurface", "Save prostate surface to a STL file", "savestl");\r
891         \r
892         toolBar.add(invertNormals);\r
893         toolBar.add(subdivideTriangles);\r
894         toolBar.add(convexHull);\r
895         toolBar.add(extractConnected);\r
896         toolBar.add(smooth1Button);\r
897         toolBar.add(smooth2Button);\r
898         toolBar.add(smooth3Button);\r
899         toolBar.add(decimateButton);\r
900         toolBar.add(saveSurfaceButton);\r
901         toolBar.add(savePLYSurfaceButton);\r
902         toolBar.add(saveSTLSurfaceButton);\r
903 \r
904         JPanel toolBarPanel = new JPanel();\r
905         toolBarPanel.setLayout(new BorderLayout());\r
906         toolBarPanel.add(toolBar, BorderLayout.WEST);\r
907         toolBarPanel.add(m_kSurfacePaint.getToolBar(), BorderLayout.SOUTH);\r
908 \r
909         mainPanel.add(toolBarPanel, BorderLayout.NORTH);\r
910     }\r
911     \r
912     /**\r
913      * Creates a label in the proper font and color.\r
914      *\r
915      * @param   title  The title of the label.\r
916      *\r
917      * @return  The new label.\r
918      */\r
919     private JLabel createLabel(String title) {\r
920         JLabel label = new JLabel(title);\r
921 \r
922         label.setFont(MipavUtil.font12);\r
923         label.setForeground(Color.black);\r
924 \r
925         return label;\r
926     }\r
927 \r
928     /**\r
929      * Decimate the selected surfaces.\r
930      * @param  aiSelected   selected surfaces.\r
931      */\r
932     private void decimate(int[] aiSelected) {\r
933         \r
934         if ( m_kVolumeViewer != null )\r
935         {\r
936             //TriMesh[] akSurfaces = new TriMesh[ aiSelected.length ];\r
937            \r
938             //DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
939             tmesh = new TriangleMesh[ aiSelected.length ];\r
940             for (int i = 0; i < aiSelected.length; i++) {\r
941                 \r
942                 TriMesh kMesh = m_akSurfaceStates.get(aiSelected[i]).Surface;\r
943                 \r
944                 VertexBuffer kVBuffer = new VertexBuffer(kMesh.VBuffer);\r
945                 IndexBuffer kIBuffer = new IndexBuffer( kMesh.IBuffer);\r
946                 tmesh[i] = new TriangleMesh(kVBuffer, kIBuffer);\r
947                 tmesh[i].SetName(kMesh.GetName());\r
948                 \r
949                 //TriMesh mesh = new TriMesh(kVBuffer, kIBuffer);\r
950                 //mesh.SetName( kMesh.GetName() );\r
951                 //akSurfaces[i] = mesh;\r
952 \r
953                 //m_kVolumeViewer.removeSurface( (String)kList.elementAt(aiSelected[i]) );\r
954                 //m_akSurfaceStates.get(aiSelected[0]).Surface = mesh;\r
955                 //m_kVolumeViewer.addSurface(m_akSurfaceStates.get(aiSelected[0]));\r
956             }\r
957 \r
958 \r
959 \r
960 \r
961             decimateButton.setEnabled(false);\r
962             detailSlider.setEnabled(true);\r
963             detailLabel.setEnabled(true);\r
964 \r
965             for (int j = 0; j < detailSliderLabels.length; j++) {\r
966                 detailSliderLabels[j].setEnabled(true);\r
967             }\r
968         }\r
969     }\r
970 \r
971     /**\r
972      * Decimate the selected surfaces.\r
973      * @param  aiSelected   selected surfaces.\r
974      */\r
975     private void invertNormals(int[] aiSelected) {\r
976         \r
977         if ( m_kVolumeViewer != null )\r
978         {           \r
979             for (int i = 0; i < aiSelected.length; i++) {\r
980                 \r
981                 TriMesh kMesh = m_akSurfaceStates.get(aiSelected[i]).Surface;\r
982                 IndexBuffer kIBuffer = kMesh.IBuffer;\r
983                 int[] aiData = kIBuffer.GetData();\r
984                 for ( int j = 0; j < (aiData.length - 2); j += 3 )\r
985                 {\r
986                         int temp = aiData[j+1];\r
987                         aiData[j+1] = aiData[j+2];\r
988                         aiData[j+2] = temp;\r
989                 }\r
990                 kMesh.UpdateMS(true);\r
991                 kMesh.Reload(true);\r
992             }\r
993         }\r
994     }\r
995 \r
996     /**\r
997      * Decimate the selected surfaces.\r
998      * @param  aiSelected   selected surfaces.\r
999      */\r
1000     private void extractConnectedComponents(int[] aiSelected) {\r
1001         \r
1002         if ( m_kVolumeViewer != null )\r
1003         {           \r
1004             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
1005             int iSize = kList.getSize();\r
1006             for (int i = 0; i < aiSelected.length; i++) {\r
1007                 \r
1008                 TriMesh kMesh = m_akSurfaceStates.get(aiSelected[i]).Surface;\r
1009                 VETMesh kVETMesh = new VETMesh(2 * kMesh.VBuffer.GetVertexQuantity(), .9f, 2 * kMesh.IBuffer\r
1010                         .GetIndexQuantity(), .9f, 2 * kMesh.GetTriangleQuantity(), .9f, kMesh.IBuffer.GetData());\r
1011 \r
1012                 final Vector<VETMesh> kComponents = new Vector<VETMesh>();\r
1013                 kVETMesh.GetComponents(kComponents);\r
1014                 \r
1015                 if ( kComponents.size() > 1 )\r
1016                 {\r
1017                         //System.err.println( kComponents.size() );\r
1018                         int largest = 0;\r
1019                         int max = 0;\r
1020                         for ( int j = 0; j < kComponents.size(); j++ )\r
1021                         {\r
1022                                 if ( kComponents.elementAt(j).GetTriangleQuantity() > max )\r
1023                                 {\r
1024                                         max = kComponents.elementAt(j).GetTriangleQuantity();\r
1025                                         largest = j;\r
1026                                 }\r
1027                         }\r
1028                         VertexBuffer kVBuffer = new VertexBuffer(kMesh.VBuffer);\r
1029                         IndexBuffer kIBuffer = new IndexBuffer( kComponents.elementAt(largest).GetTriangles() );\r
1030                         TriMesh mesh = new TriMesh(kVBuffer, kIBuffer);\r
1031                         mesh = removeUnusedVertices( mesh );\r
1032                         mesh.SetName(kMesh.GetName() + "_" + largest );\r
1033 \r
1034                         SurfaceState kSurface = new SurfaceState( mesh, mesh.GetName() );\r
1035 \r
1036                         //System.err.println( mesh.GetName() );\r
1037 \r
1038                         kList.add( iSize++, mesh.GetName() );\r
1039 \r
1040                         m_akSurfaceStates.add( kSurface );        \r
1041                         m_kVolumeViewer.addSurface( kSurface );\r
1042                 }\r
1043             }\r
1044         }\r
1045     }\r
1046     \r
1047     private void convexHull(int[] aiSelected) {\r
1048         \r
1049         if ( m_kVolumeViewer != null )\r
1050         {           \r
1051             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
1052             int iSize = kList.getSize();\r
1053             for (int i = 0; i < aiSelected.length; i++) {\r
1054                 \r
1055                 TriMesh mesh = m_akSurfaceStates.get(aiSelected[i]).Surface;\r
1056                 TriMesh chMesh = convexHull(mesh);\r
1057                 \r
1058                 chMesh.SetName(mesh.GetName() + "_convex_hull" );\r
1059 \r
1060                 SurfaceState kSurface = new SurfaceState( chMesh, chMesh.GetName() );\r
1061 \r
1062                 //System.err.println( mesh.GetName() );\r
1063 \r
1064                 kList.add( iSize++, chMesh.GetName() );\r
1065 \r
1066                 m_akSurfaceStates.add( kSurface );        \r
1067                 m_kVolumeViewer.addSurface( kSurface );\r
1068             }\r
1069         }\r
1070     }\r
1071 \r
1072     \r
1073     private float[] CalcMinMaxEdgeLength( TriMesh mesh )\r
1074     {\r
1075         float[] distance = new float[]{Float.MAX_VALUE, -Float.MAX_VALUE};\r
1076     \r
1077         int[] tris = new int[3];\r
1078         for ( int i = 0; i < mesh.GetTriangleQuantity(); i++ )\r
1079         {\r
1080                 if ( mesh.GetTriangle(i, tris) )\r
1081                 {\r
1082                         Vector3f p0 = mesh.VBuffer.GetPosition3(tris[0]);\r
1083                         Vector3f p1 = mesh.VBuffer.GetPosition3(tris[1]);\r
1084                         Vector3f p2 = mesh.VBuffer.GetPosition3(tris[2]);\r
1085 \r
1086                         float lengthP0P1 = p0.distance(p1);\r
1087                         float lengthP1P2 = p1.distance(p2);\r
1088                         float lengthP2P0 = p2.distance(p0);\r
1089 \r
1090                         distance[0] = Math.min( distance[0], lengthP0P1 );\r
1091                         distance[0] = Math.min( distance[0], lengthP1P2 );\r
1092                         distance[0] = Math.min( distance[0], lengthP2P0 );\r
1093 \r
1094                         distance[1] = Math.max( distance[1], lengthP0P1 );\r
1095                         distance[1] = Math.max( distance[1], lengthP1P2 );\r
1096                         distance[1] = Math.max( distance[1], lengthP2P0 );\r
1097                 }\r
1098         }\r
1099         \r
1100         return distance;\r
1101     }\r
1102     \r
1103     private void subDivideTriangles( int[] aiSelected )\r
1104     {\r
1105         if ( m_kVolumeViewer != null )\r
1106         {           \r
1107             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
1108             int iSize = kList.getSize();\r
1109             for (int i = 0; i < aiSelected.length; i++) {\r
1110                 \r
1111                 TriMesh mesh = m_akSurfaceStates.get(aiSelected[i]).Surface;\r
1112                 \r
1113                 float[] distance = CalcMinMaxEdgeLength(mesh);\r
1114                 System.err.println( distance[0] + " " + distance[1] );\r
1115                 \r
1116                 TriMesh chMesh = StandardMesh.SubDivide(mesh, distance[1]/4f );\r
1117                 \r
1118                 chMesh.SetName(mesh.GetName() + "_div" );\r
1119 \r
1120                 SurfaceState kSurface = new SurfaceState( chMesh, chMesh.GetName() );\r
1121 \r
1122                 //System.err.println( mesh.GetName() );\r
1123 \r
1124                 kList.add( iSize++, chMesh.GetName() );\r
1125 \r
1126                 m_akSurfaceStates.add( kSurface );        \r
1127                 m_kVolumeViewer.addSurface( kSurface );\r
1128             }\r
1129         }\r
1130     }\r
1131     \r
1132     \r
1133 \r
1134     private TriMesh convexHull( TriMesh mesh )\r
1135     {\r
1136                 ConvexHull3f convexHull = new ConvexHull3f( mesh.VBuffer.GetVertexQuantity(), mesh.VBuffer.GetPositionArray(), 0.00001f, true );\r
1137                 IndexBuffer iBuffer = new IndexBuffer(convexHull.GetIndices());\r
1138                 VertexBuffer vBuffer = new VertexBuffer( mesh.VBuffer );\r
1139                 TriMesh convexHullMesh = new TriMesh( vBuffer, iBuffer );\r
1140 \r
1141                 System.err.println( mesh.GetTriangleQuantity() + " " + convexHullMesh.GetTriangleQuantity() );\r
1142 //              System.err.println( convexHullMesh.VBuffer.GetVertexQuantity() );\r
1143                 convexHullMesh = removeUnusedVertices(convexHullMesh);\r
1144                 return convexHullMesh;\r
1145     }\r
1146 \r
1147     \r
1148 \r
1149     /**\r
1150      * Display the Surface Material dialog for the selected surfaces.\r
1151      * @param aiSelected the selected surfaces.\r
1152      */\r
1153     private void displayAdvancedMaterialOptions(int[] aiSelected) {\r
1154         if ( m_kVolumeViewer != null )\r
1155         {\r
1156             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
1157             for (int i = 0; i < aiSelected.length; i++) {\r
1158                 new JFrameSurfaceMaterialProperties_WM(this, aiSelected[i], m_kVolumeViewer.GetLights(),\r
1159                         m_kVolumeViewer.getMaterial( (String)kList.elementAt(aiSelected[i]) ) );\r
1160             }\r
1161         }\r
1162     }\r
1163    \r
1164     /**\r
1165      * Initializes the GUI components.\r
1166      */\r
1167     private void init() {\r
1168         // setSize(400, 256);\r
1169 \r
1170         // Scroll panel that hold the control panel layout in order to use JScrollPane\r
1171         JPanel mainScrollPanel = new JPanel();\r
1172         mainScrollPanel.setLayout(new BorderLayout());\r
1173 \r
1174         scroller = new JScrollPane(mainScrollPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,\r
1175                                    JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);\r
1176 \r
1177         mainPanel = new JPanel(new BorderLayout());\r
1178         buildToolBar();\r
1179 \r
1180         // Layout\r
1181         //\r
1182         // +-----------------------------------+\r
1183         // |                       color button|\r
1184         // |   surfaceList         opacity     |\r
1185         // |                       shininess   |\r
1186         // |                       triangles   |\r
1187         // |                       LOD slider  |\r
1188         // |   add  remove         light button|\r
1189         // +-----------------------------------+\r
1190 \r
1191         JPanel buttonPanel = new JPanel();\r
1192 \r
1193         // buttons for add/remove of surfaces from list\r
1194         JButton addButton = new JButton("Add");\r
1195         addButton.addActionListener(this);\r
1196         addButton.setActionCommand("Add");\r
1197         addButton.setFont(MipavUtil.font12B);\r
1198         addButton.setPreferredSize(MipavUtil.defaultButtonSize);\r
1199 \r
1200         JButton removeButton = new JButton("Remove");\r
1201         removeButton.addActionListener(this);\r
1202         removeButton.setActionCommand("Remove");\r
1203         removeButton.setFont(MipavUtil.font12B);\r
1204         removeButton.setPreferredSize(MipavUtil.defaultButtonSize);\r
1205         \r
1206         buttonPanel.add(addButton);\r
1207         buttonPanel.add(removeButton);\r
1208         \r
1209         // list panel for surface filenames\r
1210         surfaceList = new JList(new DefaultListModel());\r
1211         surfaceList.addListSelectionListener(this);\r
1212         surfaceList.setPrototypeCellValue("aaaaaaaaaaaaaaaa.aaa    ");\r
1213 \r
1214         JScrollPane kScrollPane = new JScrollPane(surfaceList);\r
1215         JPanel scrollPanel = new JPanel();\r
1216 \r
1217         scrollPanel.setLayout(new BorderLayout());\r
1218         scrollPanel.add(kScrollPane);\r
1219         scrollPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\r
1220 \r
1221         JPanel listPanel = new JPanel();\r
1222         listPanel.setLayout(new BorderLayout());\r
1223         listPanel.add(scrollPanel, BorderLayout.CENTER);\r
1224         listPanel.add(buttonPanel, BorderLayout.SOUTH);\r
1225         listPanel.setBorder(buildTitledBorder("Surface list"));\r
1226 \r
1227         // Polyline start\r
1228         JPanel buttonPanelPolyline = new JPanel();\r
1229 \r
1230         // buttons for add/remove of surfaces from list\r
1231         JButton addButtonPolyline = new JButton("Add");\r
1232 \r
1233         addButtonPolyline.addActionListener(this);\r
1234         addButtonPolyline.setActionCommand("AddPolyline");\r
1235         addButtonPolyline.setFont(MipavUtil.font12B);\r
1236         addButtonPolyline.setPreferredSize(MipavUtil.defaultButtonSize);\r
1237 \r
1238         JButton removeButtonPolyline = new JButton("Remove");\r
1239 \r
1240         removeButtonPolyline.addActionListener(this);\r
1241         removeButtonPolyline.setActionCommand("RemovePolyline");\r
1242         removeButtonPolyline.setFont(MipavUtil.font12B);\r
1243         removeButtonPolyline.setPreferredSize(MipavUtil.defaultButtonSize);\r
1244 \r
1245         buttonPanelPolyline.add(addButtonPolyline);\r
1246         buttonPanelPolyline.add(removeButtonPolyline);\r
1247 \r
1248         // list panel for surface filenames\r
1249         polylineList = new JList(new DefaultListModel());\r
1250         polylineList.addListSelectionListener(this);\r
1251         polylineList.setPrototypeCellValue("aaaaaaaaaaaaaaaa.aaa    ");\r
1252 \r
1253         JScrollPane kScrollPanePolyline = new JScrollPane(polylineList);\r
1254         JPanel scrollPanelPolyline = new JPanel();\r
1255 \r
1256         scrollPanelPolyline.setLayout(new BorderLayout());\r
1257         scrollPanelPolyline.add(kScrollPanePolyline);\r
1258         scrollPanelPolyline.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\r
1259 \r
1260         JPanel listPanelPolyline = new JPanel();\r
1261         listPanelPolyline.setLayout(new BorderLayout());\r
1262         listPanelPolyline.add(scrollPanelPolyline, BorderLayout.CENTER);\r
1263         listPanelPolyline.add(buttonPanelPolyline, BorderLayout.SOUTH);\r
1264         listPanelPolyline.setBorder(buildTitledBorder("Polyline list"));\r
1265         // polyline end\r
1266         \r
1267         JPanel paintTexturePanel = new JPanel();\r
1268         paintTexturePanel.setLayout(new FlowLayout(FlowLayout.LEFT));\r
1269 \r
1270 \r
1271         /* Creates the surface paint button, which launches the surface\r
1272          * dialog: */\r
1273         m_kSurfaceTextureButton = new JButton("Surface Texture");\r
1274         m_kSurfaceTextureButton.addActionListener(this);\r
1275         m_kSurfaceTextureButton.setActionCommand("SurfaceTexture");\r
1276         m_kSurfaceTextureButton.setSelected(false);\r
1277         m_kSurfaceTextureButton.setEnabled(false);\r
1278         paintTexturePanel.add(m_kSurfaceTextureButton);\r
1279 \r
1280         colorButton = new JButton("   ");\r
1281         colorButton.setToolTipText("Change surface color");\r
1282         colorButton.addActionListener(this);\r
1283         colorButton.setActionCommand("ChangeColor");\r
1284 \r
1285         colorLabel = new JLabel("Surface color");\r
1286         colorLabel.setFont(MipavUtil.font12B);\r
1287         colorLabel.setForeground(Color.black);\r
1288 \r
1289         JPanel colorPanel = new JPanel();\r
1290         colorPanel.setLayout(new FlowLayout(FlowLayout.LEFT));\r
1291         colorPanel.add(colorButton);\r
1292         colorPanel.add(colorLabel);\r
1293 \r
1294         /* Creates the advanced material options button, which launches the\r
1295          * material editor dialog: */\r
1296         m_kAdvancedMaterialOptionsButton = new JButton("Advanced Options");\r
1297         m_kAdvancedMaterialOptionsButton.setToolTipText("Change surface material properties");\r
1298         m_kAdvancedMaterialOptionsButton.addActionListener(this);\r
1299         m_kAdvancedMaterialOptionsButton.setActionCommand("AdvancedMaterialOptions");\r
1300         m_kAdvancedMaterialOptionsButton.setEnabled(false);\r
1301         colorPanel.add(m_kAdvancedMaterialOptionsButton);\r
1302 \r
1303         // Slider for changing opacity; not currently enabled.\r
1304         opacityLabel = new JLabel("Opacity");\r
1305         opacityLabel.setFont(MipavUtil.font12B);\r
1306         opacityLabel.setForeground(Color.black);\r
1307 \r
1308         opacitySliderLabels = new JLabel[3];\r
1309         detailSliderLabels = new JLabel[3];\r
1310         opacitySliderLabels[0] = createLabel("0");\r
1311         opacitySliderLabels[1] = createLabel("50");\r
1312         opacitySliderLabels[2] = createLabel("100");\r
1313         detailSliderLabels[0] = createLabel("0");\r
1314         detailSliderLabels[1] = createLabel("50");\r
1315         detailSliderLabels[2] = createLabel("100");\r
1316 \r
1317         Hashtable<Integer,JLabel> labels = new Hashtable<Integer,JLabel>();\r
1318 \r
1319         labels.put(new Integer(0), opacitySliderLabels[0]);\r
1320         labels.put(new Integer(50), opacitySliderLabels[1]);\r
1321         labels.put(new Integer(100), opacitySliderLabels[2]);\r
1322 \r
1323         opacitySlider = new JSlider(0, 100, 100);\r
1324         opacitySlider.setFont(MipavUtil.font12);\r
1325         opacitySlider.setMinorTickSpacing(10);\r
1326         opacitySlider.setPaintTicks(true);\r
1327         opacitySlider.addChangeListener(this);\r
1328         opacitySlider.setLabelTable(labels);\r
1329         opacitySlider.setPaintLabels(true);\r
1330         opacitySlider.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1331         opacityLabel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1332         opacitySlider.setEnabled(true);\r
1333         opacityLabel.setEnabled(true);\r
1334         opacitySliderLabels[0].setEnabled(true);\r
1335         opacitySliderLabels[1].setEnabled(true);\r
1336         opacitySliderLabels[2].setEnabled(true);\r
1337 \r
1338         triangleLabel = new JLabel("Number of triangles");\r
1339         triangleLabel.setFont(MipavUtil.font12B);\r
1340         triangleLabel.setForeground(Color.black);\r
1341         triangleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1342 \r
1343         triangleText = new JTextField(10);\r
1344         triangleText.setEditable(false);\r
1345         triangleText.setBorder(new EmptyBorder(triangleText.getInsets()));\r
1346         triangleText.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1347 \r
1348         JPanel trianglePanel = new JPanel();\r
1349 \r
1350         trianglePanel.setLayout(new FlowLayout(FlowLayout.LEFT));\r
1351         trianglePanel.add(triangleLabel);\r
1352         trianglePanel.add(triangleText);\r
1353         trianglePanel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1354 \r
1355         volumeLabel = new JLabel("Volume of mesh");\r
1356         volumeLabel.setFont(MipavUtil.font12B);\r
1357         volumeLabel.setForeground(Color.black);\r
1358         volumeLabel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1359 \r
1360         volumeText = new JTextField(20);\r
1361         volumeText.setEditable(false);\r
1362         volumeText.setBorder(new EmptyBorder(volumeText.getInsets()));\r
1363         volumeText.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1364 \r
1365         JPanel volumePanel = new JPanel();\r
1366 \r
1367         volumePanel.setLayout(new FlowLayout(FlowLayout.LEFT));\r
1368         volumePanel.add(volumeLabel);\r
1369         volumePanel.add(volumeText);\r
1370         volumePanel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1371 \r
1372         areaLabel = new JLabel("Surface area");\r
1373         areaLabel.setFont(MipavUtil.font12B);\r
1374         areaLabel.setForeground(Color.black);\r
1375         areaLabel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1376 \r
1377         areaText = new JTextField(20);\r
1378         areaText.setEditable(false);\r
1379         areaText.setBorder(new EmptyBorder(areaText.getInsets()));\r
1380         areaText.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1381 \r
1382         JPanel areaPanel = new JPanel();\r
1383 \r
1384         areaPanel.setLayout(new FlowLayout(FlowLayout.LEFT));\r
1385         areaPanel.add(areaLabel);\r
1386         areaPanel.add(areaText);\r
1387         areaPanel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1388 \r
1389         // Slider for changing level of detail.  Range is [0,100] with initial\r
1390         // value 100.\r
1391         detailLabel = new JLabel("Level of Detail");\r
1392         detailLabel.setFont(MipavUtil.font12B);\r
1393         detailLabel.setForeground(Color.black);\r
1394 \r
1395         Hashtable<Integer,JLabel> labels2 = new Hashtable<Integer,JLabel>();\r
1396 \r
1397         labels2.put(new Integer(0), detailSliderLabels[0]);\r
1398         labels2.put(new Integer(50), detailSliderLabels[1]);\r
1399         labels2.put(new Integer(100), detailSliderLabels[2]);\r
1400 \r
1401         detailSlider = new JSlider(0, 100, 100);\r
1402         detailSlider.setFont(MipavUtil.font12);\r
1403         detailSlider.setMinorTickSpacing(10);\r
1404         detailSlider.setPaintTicks(true);\r
1405         detailSlider.addChangeListener(this);\r
1406         detailSlider.setLabelTable(labels2);\r
1407         detailSlider.setPaintLabels(true);\r
1408         detailSlider.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1409         detailLabel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1410         detailSlider.setEnabled(false);\r
1411         detailLabel.setEnabled(false);\r
1412         detailSliderLabels[0].setEnabled(false);\r
1413         detailSliderLabels[1].setEnabled(false);\r
1414         detailSliderLabels[2].setEnabled(false);\r
1415 \r
1416         JPanel sliderPanel = new JPanel();\r
1417 \r
1418         sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.Y_AXIS));\r
1419         sliderPanel.add(opacityLabel);\r
1420         sliderPanel.add(opacitySlider);\r
1421         sliderPanel.add(trianglePanel);\r
1422         sliderPanel.add(volumePanel);\r
1423         sliderPanel.add(areaPanel);\r
1424         sliderPanel.add(detailLabel);\r
1425         sliderPanel.add(detailSlider);\r
1426 \r
1427         comboLabel = new JLabel("Polygon mode:");\r
1428         comboLabel.setFont(MipavUtil.font12B);\r
1429         comboLabel.setForeground(Color.black);\r
1430 \r
1431         polygonModeCB = new JComboBox(new String[] { "Fill", "Line", "Point" });\r
1432         polygonModeCB.addActionListener(this);\r
1433         polygonModeCB.setActionCommand("ChangePolyMode");\r
1434         polygonModeCB.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1435         polygonModeCB.setFont(MipavUtil.font12);\r
1436         polygonModeCB.setBackground(Color.white);\r
1437 \r
1438         surfacePickableCB = new JCheckBox("Surface Pickable", true);\r
1439         surfacePickableCB.addActionListener(this);\r
1440         surfacePickableCB.setActionCommand("SurfacePickable");\r
1441         surfacePickableCB.setFont(MipavUtil.font12B);\r
1442         surfacePickableCB.setSelected(false);\r
1443 \r
1444         surfaceClipCB = new JCheckBox("Surface Clipping", true);\r
1445         surfaceClipCB.addActionListener(this);\r
1446         surfaceClipCB.setActionCommand("Clipping");\r
1447         surfaceClipCB.setFont(MipavUtil.font12B);\r
1448         surfaceClipCB.setSelected(false);\r
1449         surfaceClipCB.setEnabled(false);\r
1450 \r
1451         surfaceBackFaceCB = new JCheckBox("Backface Culling", true);\r
1452         surfaceBackFaceCB.addActionListener(this);\r
1453         surfaceBackFaceCB.setActionCommand("backface");\r
1454         surfaceBackFaceCB.setFont(MipavUtil.font12B);\r
1455         surfaceBackFaceCB.setSelected(true);\r
1456 \r
1457         surfaceTransparencyCB = new JCheckBox("Transparency", true);\r
1458         surfaceTransparencyCB.addActionListener(this);\r
1459         surfaceTransparencyCB.setActionCommand("transparency");\r
1460         surfaceTransparencyCB.setFont(MipavUtil.font12B);\r
1461         surfaceTransparencyCB.setSelected(true);\r
1462         \r
1463         JPanel cbSurfacePanel = new JPanel();\r
1464         cbSurfacePanel.setLayout(new BoxLayout(cbSurfacePanel, BoxLayout.Y_AXIS));\r
1465         cbSurfacePanel.add(surfacePickableCB);\r
1466         cbSurfacePanel.add(surfaceClipCB);\r
1467         cbSurfacePanel.add(surfaceBackFaceCB);\r
1468         cbSurfacePanel.add(surfaceTransparencyCB);\r
1469 \r
1470         JPanel cbPanel = new JPanel();\r
1471         cbPanel.setLayout(new FlowLayout(FlowLayout.LEFT));\r
1472         cbPanel.add(comboLabel);\r
1473         cbPanel.add(polygonModeCB);\r
1474         cbPanel.add(cbSurfacePanel);\r
1475 \r
1476         paintTexturePanel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1477         colorPanel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1478         sliderPanel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1479         cbPanel.setAlignmentX(Component.LEFT_ALIGNMENT);\r
1480         colorPanel.setAlignmentY(Component.TOP_ALIGNMENT);\r
1481         sliderPanel.setAlignmentY(Component.TOP_ALIGNMENT);\r
1482         cbPanel.setAlignmentY(Component.TOP_ALIGNMENT);\r
1483 \r
1484         JPanel optionsPanel = new JPanel();\r
1485         optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.Y_AXIS));\r
1486         optionsPanel.add(colorPanel);\r
1487         optionsPanel.add(paintTexturePanel);\r
1488         optionsPanel.add(sliderPanel);\r
1489         optionsPanel.add(cbPanel);\r
1490         optionsPanel.setBorder(buildTitledBorder("Surface options"));\r
1491 \r
1492         JPanel rightPanel = new JPanel();\r
1493         rightPanel.setLayout(new BorderLayout());\r
1494         rightPanel.add(optionsPanel, BorderLayout.NORTH);\r
1495 \r
1496         \r
1497         JTabbedPane tabbedPane = new JTabbedPane();\r
1498         tabbedPane.setFont(MipavUtil.font12B);\r
1499         tabbedPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT);\r
1500         tabbedPane.addChangeListener(this);\r
1501         \r
1502         tabbedPane.addTab("Surface", null, listPanel);\r
1503         // tabbedPane.addTab("Polyline", null, listPanelPolyline);\r
1504         tabbedPane.setSelectedIndex(0);\r
1505      \r
1506         Box contentBox = new Box(BoxLayout.Y_AXIS);\r
1507 \r
1508         contentBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\r
1509         contentBox.add(tabbedPane);\r
1510         contentBox.add(rightPanel);\r
1511  \r
1512         mainScrollPanel.add(contentBox, BorderLayout.NORTH);\r
1513 \r
1514         mainPanel.add(scroller, BorderLayout.CENTER);\r
1515 \r
1516         // no surfaces yet, so the elements shouldn't be enabled\r
1517         setElementsEnabled(false);\r
1518  \r
1519         \r
1520     }\r
1521 \r
1522     /**\r
1523      * Convert from the polygon mode combo-box list index to the PolygonAttributes.POLYGON_LINE,\r
1524      * PolygonAttributes.POLYGON_POINT, and PolygonAttributes.POLYGON_FILL values:\r
1525      *\r
1526      * @param   index  the index of the selected polygon mode in the polygonModeCB combo box.\r
1527      *\r
1528      * @return  the corresponding PolygonAttributes defined value.\r
1529      */\r
1530     private WireframeState.FillMode polygonIndexToMode(int index) {\r
1531         switch (index) {\r
1532 \r
1533             case 1:\r
1534                 return WireframeState.FillMode.FM_LINE;\r
1535 \r
1536             case 2:\r
1537                 return WireframeState.FillMode.FM_POINT;\r
1538 \r
1539             case 0:\r
1540             default:\r
1541                 return WireframeState.FillMode.FM_FILL;\r
1542         }\r
1543     }\r
1544     \r
1545     private int fillModeToPolygonIndex(WireframeState.FillMode mode) {\r
1546         switch (mode) {\r
1547             case FM_LINE:\r
1548                 return 1;\r
1549             case FM_POINT:\r
1550                 return 2;\r
1551             case FM_FILL:\r
1552             default:\r
1553                 return 0;\r
1554         }\r
1555     }\r
1556 \r
1557     /**\r
1558      * Remove polyline from the render\r
1559      */\r
1560     private void removePolyline() {\r
1561         int[] aiSelected = polylineList.getSelectedIndices();\r
1562         int index;\r
1563         DefaultListModel kList = (DefaultListModel)polylineList.getModel();\r
1564         if ( m_kVolumeViewer != null )\r
1565         {\r
1566             for (int i = 0; i < aiSelected.length; i++) {\r
1567                 index = (Integer)(polylineCounterList.elementAt(aiSelected[i]));\r
1568                 m_kVolumeViewer.removePolyline( index );\r
1569                 polylineCounterList.remove(aiSelected[i]);\r
1570             }\r
1571         }\r
1572         for (int i = 0; i < aiSelected.length; i++) {\r
1573             kList.remove(aiSelected[i]);\r
1574         }\r
1575     }\r
1576 \r
1577     /**\r
1578      * Remove the selected surfaces.\r
1579      */\r
1580     private void removeSurface()\r
1581     {\r
1582         int[] aiSelected = surfaceList.getSelectedIndices();\r
1583 \r
1584         DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
1585         if ( m_kVolumeViewer != null )\r
1586         {\r
1587             for (int i = 0; i < aiSelected.length; i++) {\r
1588                 m_kVolumeViewer.removeSurface( (String)kList.elementAt(aiSelected[i]) );\r
1589             }\r
1590         }\r
1591         for (int i = 0; i < aiSelected.length; i++) {\r
1592             kList.remove(aiSelected[i]);\r
1593             m_akSurfaceStates.remove(aiSelected[i]);\r
1594         }\r
1595     }\r
1596     \r
1597     \r
1598     private TriMesh removeUnusedVertices( TriMesh kMesh )\r
1599     {\r
1600         int[] aiIndex = kMesh.IBuffer.GetData();\r
1601         Vector<Vector3f> vertexList = new Vector<Vector3f>();\r
1602         Vector<Integer> triList = new Vector<Integer>();\r
1603         int indexCount = 0;\r
1604         for ( int i = 0; i < aiIndex.length; i++ )\r
1605         {\r
1606                 Vector3f kPos = kMesh.VBuffer.GetPosition3(aiIndex[i]);\r
1607                 if ( !vertexList.contains( kPos ) )\r
1608                 {\r
1609                         vertexList.add( kPos );\r
1610                         triList.add( indexCount++ );\r
1611                 }\r
1612                 else\r
1613                 {\r
1614                         triList.add( vertexList.indexOf( kPos ) );\r
1615                 }\r
1616         }\r
1617         if ( vertexList.size() == kMesh.VBuffer.GetVertexQuantity() )\r
1618         {\r
1619                 System.err.println( "All vertices used" );\r
1620                 return kMesh;\r
1621         }\r
1622         int[] aiNewIndex  = new int[ triList.size() ];\r
1623         for ( int i = 0; i < triList.size(); i++ )\r
1624         {\r
1625                 aiNewIndex[i] = triList.elementAt(i);\r
1626         }\r
1627         IndexBuffer kIBuffer = new IndexBuffer( aiNewIndex );\r
1628         VertexBuffer kVBuffer = new VertexBuffer( vertexList );\r
1629         return new TriMesh( kVBuffer, kIBuffer );\r
1630     }\r
1631     \r
1632     /**\r
1633      * Save the selected surfaces. The kCommand parameter determines the file type.\r
1634      * @param aiSelected selected surfaces.\r
1635      * @param kCommand save command, specifies the file type.\r
1636      */\r
1637     private void saveSurfaces( int[] aiSelected, String kCommand )\r
1638     {\r
1639 \r
1640         if (aiSelected == null) {\r
1641             MipavUtil.displayError("Select a surface to save.");\r
1642             return;\r
1643         }  \r
1644         if (aiSelected.length != 1) {\r
1645             MipavUtil.displayError("Select one surface to save.");\r
1646             return;\r
1647         }\r
1648 \r
1649         if ( m_kVolumeViewer != null )\r
1650         {\r
1651             TriMesh[] akSurfaces = new TriMesh[ aiSelected.length ];\r
1652             for (int i = 0; i < aiSelected.length; i++) {\r
1653                 akSurfaces[i] = m_akSurfaceStates.get(aiSelected[i]).Surface;\r
1654             }\r
1655 \r
1656             File[] akFiles = FileSurface_WM.openFiles(false);\r
1657 \r
1658             if (akFiles == null) {\r
1659                 return;\r
1660             }\r
1661             String kName = akFiles[0].getAbsolutePath();\r
1662                                              \r
1663 \r
1664             try {\r
1665                 float[] startLocation = m_kVolumeViewer.getImageA().getFileInfo(0).getOrigin();\r
1666                 int[] extents = m_kVolumeViewer.getImageA().getExtents();\r
1667                 int xDim = extents[0];\r
1668                 int yDim = extents[1];\r
1669                 int zDim = extents[2];\r
1670                 \r
1671                 float[] resols = m_kVolumeViewer.getImageA().getFileInfo()[0].getResolutions();\r
1672                 float xBox = (xDim - 1) * resols[0];\r
1673                 float yBox = (yDim - 1) * resols[1];\r
1674                 float zBox = (zDim - 1) * resols[2];\r
1675                 float maxBox = Math.max(xBox, Math.max(yBox, zBox));\r
1676 \r
1677                 int[] direction = MipavCoordinateSystems.getModelDirections(m_kVolumeViewer.getImageA());\r
1678                 float[] box = new float[]{ xBox, yBox, zBox };\r
1679 \r
1680                 //                 for (int i = 0; i < meshes.length; i++) {\r
1681                 VertexBuffer kVBuffer = new VertexBuffer( akSurfaces[0].VBuffer );\r
1682 \r
1683                 // The loaded vertices go from -(xDim-1)*resX/maxBox to (xDim-1)*resX/maxBox\r
1684                 // The loaded vertex is at 2.0f*pt.x*resX - (xDim-1)*resX\r
1685                 // The mesh files must save the verticies as\r
1686                 // pt.x*resX*direction[0] + startLocation\r
1687                 for (int j = 0; j < kVBuffer.GetVertexQuantity(); j++) {\r
1688                         kVBuffer.SetPosition3( j,\r
1689                                         ((((akSurfaces[0].VBuffer.GetPosition3fX(j) * 2.0f * maxBox) + xBox) / 2.0f) * direction[0]) +\r
1690                                         startLocation[0],\r
1691                                         ((((akSurfaces[0].VBuffer.GetPosition3fY(j) * 2.0f * maxBox) + yBox) / 2.0f) * direction[1]) +\r
1692                                         startLocation[1],\r
1693                                         ((((akSurfaces[0].VBuffer.GetPosition3fZ(j) * 2.0f * maxBox) + zBox) / 2.0f) * direction[2]) +\r
1694                                         startLocation[2] );\r
1695 \r
1696                 }\r
1697 \r
1698                 //double[][] inverseDicomArray = null;\r
1699                 TransMatrix inverse_DicomMatrix = null;\r
1700                 if (m_kVolumeViewer.getImageA().getMatrixHolder().containsType(TransMatrix.TRANSFORM_SCANNER_ANATOMICAL)) {\r
1701                         inverse_DicomMatrix = m_kVolumeViewer.getImageA().getMatrix().clone();\r
1702                         inverse_DicomMatrix.Inverse();\r
1703                         //inverseDicomArray = inverseDicomMatrix.getMatrix();\r
1704                 }\r
1705 \r
1706                 TriMesh surfaceSave = new TriMesh( kVBuffer, akSurfaces[0].IBuffer );\r
1707                 //int iType = akSurfaces[0] instanceof ClodMesh ? 1 : 0;\r
1708                 FileSurface_WM.save(kName, surfaceSave, 0, surfaceSave.VBuffer, true, direction, startLocation, box, inverse_DicomMatrix);\r
1709             } catch (IOException error) {\r
1710                 MipavUtil.displayError("Error while trying to save single mesh");\r
1711             }\r
1712         }\r
1713     }\r
1714     \r
1715     /**\r
1716      * Save the selected surfaces. The kCommand parameter determines the file type.\r
1717      * @param aiSelected selected surfaces.\r
1718      * @param kCommand save command, specifies the file type.\r
1719      */\r
1720     private void saveProstateSurfaces( int[] aiSelected, String kCommand )\r
1721     {\r
1722 \r
1723         if (aiSelected == null) {\r
1724             MipavUtil.displayError("Select a surface to save.");\r
1725             return;\r
1726         }  \r
1727         if (aiSelected.length != 1) {\r
1728             MipavUtil.displayError("Select one surface to save.");\r
1729             return;\r
1730         }\r
1731 \r
1732         if ( m_kVolumeViewer != null )\r
1733         {\r
1734             TriMesh[] akSurfaces = new TriMesh[ aiSelected.length ];\r
1735             for (int i = 0; i < aiSelected.length; i++) {\r
1736                 akSurfaces[i] = m_akSurfaceStates.get(aiSelected[i]).Surface;\r
1737             }\r
1738 \r
1739             File[] akFiles = FileSurface_WM.openFiles(false);\r
1740 \r
1741             if (akFiles == null) {\r
1742                 return;\r
1743             }\r
1744             String kName = akFiles[0].getAbsolutePath();\r
1745             try {\r
1746                 VertexBuffer kVBuffer = new VertexBuffer( akSurfaces[0].VBuffer );\r
1747                 TriMesh surfaceSave = new TriMesh( kVBuffer, akSurfaces[0].IBuffer );\r
1748                 FileSurface_WM.saveProstateSurface(kName, surfaceSave, m_kVolumeViewer.getActiveImage());\r
1749             } catch (IOException error) {\r
1750                 MipavUtil.displayError("Error while trying to save single mesh");\r
1751             }\r
1752         }\r
1753     }\r
1754     \r
1755     \r
1756     /**\r
1757      * Turn backface culling on/off for the selected surfaces.\r
1758      * @param aiSelected selected surfaces.\r
1759      */\r
1760     private void setBackface(int[] aiSelected) {\r
1761         if ( m_kVolumeViewer != null )\r
1762         {\r
1763             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
1764             for (int i = 0; i < aiSelected.length; i++) {\r
1765                 m_akSurfaceStates.get( aiSelected[i] ).BackfaceCull = surfaceBackFaceCB.isSelected();\r
1766                 m_kVolumeViewer.setBackface( (String)kList.elementAt(aiSelected[i]),\r
1767                         surfaceBackFaceCB.isSelected());\r
1768             }\r
1769 \r
1770         }\r
1771     }\r
1772     \r
1773     /**\r
1774      * Turns Clipping on/off for the selected surfaces.\r
1775      * @param  aiSelected  the list of selected surfaces (SurfaceAttributes)\r
1776      */\r
1777     private void setClipping(int[] aiSelected) {\r
1778         if ( m_kVolumeViewer != null )\r
1779         {\r
1780             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
1781             for (int i = 0; i < aiSelected.length; i++) {\r
1782                 m_akSurfaceStates.get( aiSelected[i] ).Clip = surfaceClipCB.isSelected();\r
1783                 m_kVolumeViewer.setClipping( (String)kList.elementAt(aiSelected[i]),\r
1784                                              surfaceClipCB.isSelected());\r
1785             }\r
1786 \r
1787         }\r
1788     }\r
1789     \r
1790     /**\r
1791      * Sets the surface options GUI panel to enabled or disabled. If there are 0 or multiple surfaces selected, all the\r
1792      * options should be disabled.\r
1793      *\r
1794      * @param  flag  Enable or disable.\r
1795      */\r
1796     private void setElementsEnabled(boolean flag) {\r
1797         saveSurfaceButton.setEnabled(flag);\r
1798         savePLYSurfaceButton.setEnabled(flag);\r
1799         saveSTLSurfaceButton.setEnabled(flag);\r
1800         \r
1801         decimateButton.setEnabled(flag);\r
1802         colorButton.setEnabled(flag);\r
1803         subdivideTriangles.setEnabled(flag);\r
1804         convexHull.setEnabled(flag);\r
1805         extractConnected.setEnabled(flag);\r
1806         invertNormals.setEnabled(flag);\r
1807         smooth1Button.setEnabled(flag);\r
1808         smooth2Button.setEnabled(flag);\r
1809         smooth3Button.setEnabled(flag);\r
1810 \r
1811         colorLabel.setEnabled(flag);\r
1812         m_kAdvancedMaterialOptionsButton.setEnabled(flag);\r
1813         m_kSurfaceTextureButton.setEnabled(flag);\r
1814         opacityLabel.setEnabled(flag);\r
1815         opacitySlider.setEnabled(flag);\r
1816         triangleLabel.setEnabled(flag);\r
1817         triangleText.setEnabled(flag);\r
1818         volumeLabel.setEnabled(flag);\r
1819         volumeText.setEnabled(flag);\r
1820         areaLabel.setEnabled(flag);\r
1821         areaText.setEnabled(flag);\r
1822 \r
1823         for (int i = 0; i < opacitySliderLabels.length; i++) {\r
1824             opacitySliderLabels[i].setEnabled(flag);\r
1825         }\r
1826 \r
1827         polygonModeCB.setEnabled(flag);\r
1828         comboLabel.setEnabled(flag);\r
1829         surfacePickableCB.setEnabled(flag);\r
1830         surfaceClipCB.setEnabled(flag);\r
1831         surfaceBackFaceCB.setEnabled(flag);\r
1832         surfaceTransparencyCB.setEnabled(flag);\r
1833         m_kSurfacePaint.setEnabled(flag);\r
1834     }\r
1835     \r
1836     /**\r
1837      * Turn picking culling on/off for the selected surfaces.\r
1838      * @param aiSelected selected surfaces.\r
1839      */\r
1840     private void setPickable(int[] aiSelected) {\r
1841         if ( m_kVolumeViewer != null )\r
1842         {\r
1843             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
1844             for (int i = 0; i < aiSelected.length; i++) {\r
1845                 m_akSurfaceStates.get( aiSelected[i] ).Pickable = surfacePickableCB.isSelected();\r
1846                 m_kVolumeViewer.setPickable( (String)kList.elementAt(aiSelected[i]),\r
1847                         surfacePickableCB.isSelected());\r
1848             }\r
1849         }\r
1850     }\r
1851     \r
1852     /**\r
1853      * Turns Transparency on/off for the selected surfaces.\r
1854      * @param  aiSelected  the list of selected surfaces (SurfaceAttributes)\r
1855      */\r
1856     private void setTransparency(int[] aiSelected) {\r
1857 \r
1858         if ( m_kVolumeViewer != null )\r
1859         {\r
1860             float opacity = opacitySlider.getValue() / 100.0f;\r
1861             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
1862             for (int i = 0; i < aiSelected.length; i++) {\r
1863                 m_akSurfaceStates.get( aiSelected[i] ).Opacity = opacity;\r
1864 \r
1865                 m_akSurfaceStates.get( aiSelected[i] ).TransparencyOn = surfaceTransparencyCB.isSelected();\r
1866                 if ( surfaceTransparencyCB.isSelected() )\r
1867                 {\r
1868                     m_kVolumeViewer.setTransparency( (String)kList.elementAt(aiSelected[i]), opacity);\r
1869                 }\r
1870             }\r
1871         }\r
1872     }\r
1873     \r
1874     /**\r
1875      * Smoothes the selected surfaces. One dialog per group of selected surfaces is displayed (not a different dialog\r
1876      * per-surface).\r
1877      *\r
1878      * @param  aiSelected    the list of selected surfaces (SurfaceAttributes)\r
1879      * @param  iSmoothType  the level of smoothing JDialogSmoothMesh.SMOOTH1, JDialogSmoothMesh.SMOOTH2, or\r
1880      *                     JDialogSmoothMesh.SMOOTH3\r
1881      */\r
1882     private void smoothSurface(int[] aiSelected, int iSmoothType )\r
1883     {\r
1884 \r
1885         if (aiSelected == null) {\r
1886             MipavUtil.displayError("Select a surface to smooth.");\r
1887             return;\r
1888         }\r
1889 \r
1890         JDialogSmoothMesh dialog = new JDialogSmoothMesh(null, true, iSmoothType);\r
1891 \r
1892         if (dialog.isCancelled()) {\r
1893             return;\r
1894         }\r
1895 \r
1896         if ( m_kVolumeViewer != null )\r
1897         {\r
1898             DefaultListModel kList = (DefaultListModel)surfaceList.getModel();\r
1899             \r
1900             int numTriangles = 0;\r
1901             float volume = 0;\r
1902             float area = 0;\r
1903             \r
1904             for (int i = 0; i < aiSelected.length; i++) {\r
1905 \r
1906                 TriMesh kMesh = m_akSurfaceStates.get(aiSelected[i]).Surface;\r
1907                 int iTarget = 0;\r
1908                 if (kMesh instanceof ClodMesh) {\r
1909                     iTarget = ((ClodMesh)kMesh).TargetRecord();\r
1910                     ((ClodMesh)kMesh).TargetRecord(0);\r
1911                     ((ClodMesh)kMesh).SelectLevelOfDetail();\r
1912                 }\r
1913                 \r
1914                 if (iSmoothType == JDialogSmoothMesh.SMOOTH1) {\r
1915                     m_kVolumeViewer.smoothMesh((String)kList.elementAt(aiSelected[i]),\r
1916                             dialog.getIterations(),\r
1917                             dialog.getAlpha(),\r
1918                             dialog.getVolumeLimit(),\r
1919                             dialog.getVolumePercent());\r
1920                 }\r
1921                 else if (iSmoothType == JDialogSmoothMesh.SMOOTH2) {\r
1922                     m_kVolumeViewer.smoothTwo((String)kList.elementAt(aiSelected[i]),\r
1923                             dialog.getIterations(), dialog.getStiffness(), dialog.getVolumeLimit(),\r
1924                                         dialog.getVolumePercent());\r
1925                 }\r
1926 \r
1927                 else {\r
1928                     m_kVolumeViewer.smoothThree((String)kList.elementAt(aiSelected[i]),\r
1929                             dialog.getIterations(), dialog.getLambda(), dialog.getMu());\r
1930                 }\r
1931 \r
1932 /*\r
1933                 numTriangles += meshes[j].getIndexCount();\r
1934                 volume += meshes[j].volume();\r
1935                 area += meshes[j].area();\r
1936                 */\r
1937                 \r
1938                 if ((kMesh instanceof ClodMesh) && (iTarget != 0 )) {\r
1939                     ((ClodMesh)kMesh).TargetRecord(iTarget);\r
1940                     ((ClodMesh)kMesh).SelectLevelOfDetail();\r
1941                 }\r
1942             }\r
1943 \r
1944             triangleText.setText(String.valueOf(numTriangles / 3));\r
1945 \r
1946             // One length across the extracted surface changes from -1 to 1\r
1947             // while one length across the actual volume changes by ((dim-1)*res)max\r
1948             volumeText.setText("" + volume);\r
1949             areaText.setText("" + area);\r
1950         }\r
1951     }\r
1952     \r
1953     /**\r
1954      * Pick up the selected color and call method to change the color.\r
1955      */\r
1956     class OkColorListener implements ActionListener {\r
1957 \r
1958         /** Color Button */\r
1959         JButton button;\r
1960 \r
1961         /**\r
1962          * Creates a new OkColorListener object.\r
1963          *\r
1964          * @param  _button  DOCUMENT ME!\r
1965          */\r
1966         OkColorListener(JButton _button) {\r
1967             super();\r
1968             button = _button;\r
1969         }\r
1970 \r
1971         /**\r
1972          * Get color from chooser and set button and color.\r
1973          *\r
1974          * @param  e  Event that triggered function.\r
1975          */\r
1976         public void actionPerformed(ActionEvent e) {\r
1977             Color color = colorChooser.getColor();\r
1978 \r
1979             button.setBackground(color);\r
1980             setButtonColor(button, color);\r
1981             m_kVolumeViewer.updatePlanes();\r
1982         }\r
1983     }\r
1984     \r
1985     \r
1986 }\r