chiark / gitweb /
2aa1dac8ff8f9d36751a41e93186738d5c6a9757
[cura.git] /
1 /*\r
2 <?xml version='1.0' standalone='yes' ?>\r
3 \r
4 <script>\r
5         <name>Export Gnu Triangulated Surface</name>\r
6         <author>Enrique Perez</author>\r
7         <version>1.0</version>\r
8         <date>29-Apr-2008</date>\r
9         <description>\r
10 This script exports triangle meshes to GNU Triangulated Surface (GTS) format files.  The GTS Library homepage is at: http://gts.sourceforge.net/\r
11     </description>\r
12         <licenseType>gpl</licenseType>\r
13 </script>\r
14 */\r
15 //later once stable remove unused preferences, all the preferences will be kept in the unused Skeinforge, finish comments for this and import\r
16 /**\r
17 Get the info for all the selected curves and meshes.\r
18 */\r
19 \r
20 ObjectInfo[] getCurvesMeshesInfo()\r
21 {\r
22   scene = window.getScene();\r
23   selection = scene.getSelection();\r
24   Vector objectInfoVector = new Vector();\r
25 \r
26   for ( int objectIndex = 0; objectIndex < selection.length; objectIndex++ ) {\r
27     objectInfo = scene.getObject( selection[ objectIndex ] );\r
28 \r
29     if ( objectInfo.object instanceof TriangleMesh ) {\r
30       objectInfoVector.add( objectInfo );\r
31     }\r
32   }\r
33 \r
34   if ( objectInfoVector.size() > 0 ) {\r
35     return objectInfoVector.toArray( new ObjectInfo[ objectInfoVector.size() ] );\r
36   }\r
37 \r
38   for ( int objectIndex = 0; objectIndex < scene.getNumObjects(); objectIndex++ ) {\r
39     objectInfo = scene.getObject( objectIndex );\r
40 \r
41     if ( objectInfo.object instanceof TriangleMesh ) {\r
42       return new ObjectInfo[] { objectInfo };\r
43     }\r
44   }\r
45 \r
46   return new ObjectInfo[ 0 ];\r
47 }\r
48 \r
49 String getEdgeString( edges, edgeTriple, vertexIndexFirst, vertexIndexSecond )\r
50 {\r
51   for ( int edgeTripleIndex = 0; edgeTripleIndex < edgeTriple.length; edgeTripleIndex++ ) {\r
52     edgeIndex = edgeTriple[ edgeTripleIndex ];\r
53     edge = edges[ edgeIndex ];\r
54 \r
55     if ( edge.v1 == vertexIndexFirst && edge.v2 == vertexIndexSecond ) {\r
56       return getPlusOneToString( edgeIndex );\r
57     }\r
58 \r
59     if ( edge.v1 == vertexIndexSecond && edge.v2 == vertexIndexFirst ) {\r
60       return getPlusOneToString( edgeIndex );\r
61     }\r
62   }\r
63 \r
64   print( "Inconsistent triangle mesh." );\r
65   print( edgeTriple );\r
66   print( vertexIndexFirst );\r
67   print( vertexIndexSecond );\r
68 }\r
69 \r
70 String getPlusOneToString( int number )\r
71 {\r
72   return ( number + 1 ).toString() ;\r
73 }\r
74 \r
75 /**\r
76 Get Vec3 rotated around X axis from counterclockwise angle and vector.\r
77 \r
78 @param  angle counterclockwise angle from 1, 0\r
79 @param  vector3 Vec3 whose rotation will be returned\r
80 @return  vector3 rotated around X axis\r
81 */\r
82 \r
83 Vec3 getRoundXAxis( double angle, Vec3 vector3 )\r
84 {\r
85   x = Math.cos( angle );\r
86   y = Math.sin( angle );\r
87 \r
88   return new Vec3( vector3.x, vector3.y * x - vector3.z * y, vector3.y * y + vector3.z * x );\r
89 }\r
90 \r
91 /**\r
92 Get Vec3 rotated around Y axis from counterclockwise angle and vector.\r
93 \r
94 @param  angle counterclockwise angle from 1, 0\r
95 @param  vector3 Vec3 whose rotation will be returned\r
96 @return  vector3 rotated around Y axis\r
97 */\r
98 \r
99 Vec3 getRoundYAxis( double angle, Vec3 vector3 )\r
100 {\r
101   x = Math.cos( angle );\r
102   y = Math.sin( angle );\r
103 \r
104   return new Vec3( vector3.x * x - vector3.z * y, vector3.y, vector3.x * y + vector3.z * x );\r
105 }\r
106 \r
107 /**\r
108 Get Vec3 rotated around Z axis from counterclockwise angle and vector.\r
109 \r
110 @param  angle counterclockwise angle from 1, 0\r
111 @param  vector3 Vec3 whose rotation will be returned\r
112 @return  vector3 rotated around Z axis\r
113 */\r
114 \r
115 Vec3 getRoundZAxis( double angle, Vec3 vector3 )\r
116 {\r
117   x = Math.cos( angle );\r
118   y = Math.sin( angle );\r
119 \r
120   return new Vec3( vector3.x * x - vector3.y * y, vector3.x * y + vector3.y * x, vector3.z );\r
121 }\r
122 \r
123 /**\r
124 Output the curves and/or meshes.\r
125 */\r
126 \r
127 void outputCurvesMeshes( ObjectInfo[] objectInfoArray )\r
128 {\r
129   if ( objectInfoArray.length < 1 ) {\r
130     return;\r
131   }\r
132 \r
133   for ( int objectIndex = 0; objectIndex < objectInfoArray.length; objectIndex++ ) {\r
134     objectInfo = objectInfoArray[ objectIndex ];\r
135 \r
136     if ( objectInfoArray.length > 1 && exportSelectionCheckbox.getState() ) {\r
137 \r
138       if ( bufferedWriter != null ) {\r
139         //Close the output stream\r
140         bufferedWriter.close();\r
141         bufferedWriter = null;\r
142       }\r
143 \r
144       file = null;\r
145       parentFile = new File( gnuSurfaceFilenameTextField.getText() ).getParentFile();\r
146 \r
147       if ( parentFile != null ) {\r
148         file = new File( parentFile, objectInfo.name + ".gts" );\r
149       }\r
150 \r
151       if ( file != null ) {\r
152         bufferedWriter = new BufferedWriter( new FileWriter( file ) );\r
153       }\r
154     }\r
155 \r
156     if ( file != null && bufferedWriter != null ) {\r
157       print( objectInfo.name + " is being saved as the GNU Triangulated Surface file " + file.getAbsolutePath() );\r
158     }\r
159 \r
160     outputCurveMesh( objectInfo );\r
161 \r
162     if ( bufferedWriter != null ) {\r
163       //Close the output stream\r
164       bufferedWriter.close();\r
165       bufferedWriter = null;\r
166     }\r
167   }\r
168 }\r
169 \r
170 /**\r
171 Output the curve and/or mesh.\r
172 Quoted from http://gts.sourceforge.net/reference/gts-surfaces.html#GTS-SURFACE-WRITE\r
173 "All the lines beginning with GTS_COMMENTS (#!) are ignored. The first line contains three unsigned integers separated by spaces. The first integer is the number of vertices, nv, the second is the number of edges, ne and the third is the number of faces, nf.\r
174 \r
175 Follows nv lines containing the x, y and z coordinates of the vertices. Follows ne lines containing the two indices (starting from one) of the vertices of each edge. Follows nf lines containing the three ordered indices (also starting from one) of the edges of each face.\r
176 \r
177 The format described above is the least common denominator to all GTS files. Consistent with an object-oriented approach, the GTS file format is extensible. Each of the lines of the file can be extended with user-specific attributes accessible through the read() and write() virtual methods of each of the objects written (surface, vertices, edges or faces). When read with different object classes, these extra attributes are just ignored."\r
178 */\r
179 \r
180 void outputCurveMesh( ObjectInfo objectInfo )\r
181 {\r
182   triangleMesh = objectInfo.object;\r
183   edges = triangleMesh.getEdges();\r
184   faces = triangleMesh.getFaces();\r
185   vertexPositions = triangleMesh.getVertexPositions();\r
186   origin = objectInfo.coords.getOrigin();\r
187   orientation = objectInfo.coords.getRotationAngles();\r
188   orientationXRadians = orientation[ 0 ] * Math.PI / 180.0;\r
189   orientationYRadians = orientation[ 1 ] * Math.PI / 180.0;\r
190   orientationZRadians = orientation[ 2 ] * Math.PI / 180.0;\r
191   outputString( vertexPositions.length + " " + edges.length + " " + faces.length + " Number of Vertices, Number of Edges, Number of Faces" );\r
192 \r
193   for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) {\r
194     vertexPosition = vertexPositions[ vertexIndex ];\r
195     vertexPosition = getRoundZAxis( - orientationZRadians, vertexPosition );\r
196     vertexPosition = getRoundXAxis( - orientationXRadians, vertexPosition );\r
197     vertexPosition = getRoundYAxis( orientationYRadians, vertexPosition );\r
198     vertexPosition.add( origin );\r
199     vertexLine = vertexPosition.x.toString() + " " + vertexPosition.y.toString() + " " + vertexPosition.z.toString();\r
200 \r
201     if ( vertexIndex == 0 ) {\r
202       vertexLine += " Vertex Coordinates XYZ";\r
203     }\r
204 \r
205     outputString( vertexLine );\r
206   }\r
207 \r
208   for ( int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++ ) {\r
209     edge = edges[ edgeIndex ];\r
210     edgeLine = getPlusOneToString( edge.v1 ) + " " + getPlusOneToString( edge.v2 );\r
211 \r
212     if ( edgeIndex == 0 ) {\r
213       edgeLine += " Edge Vertex Indices Starting from 1";\r
214     }\r
215 \r
216     outputString( edgeLine );\r
217   }\r
218 \r
219   for ( int faceIndex = 0; faceIndex < faces.length; faceIndex++ ) {\r
220     face = faces[ faceIndex ];\r
221     int[] edgeTriple = { face.e1, face.e2, face.e3 };\r
222 \r
223     faceLine = getEdgeString( edges, edgeTriple, face.v1, face.v2 ) + " " + getEdgeString( edges, edgeTriple, face.v2, face.v3 ) + " " + getEdgeString( edges, edgeTriple, face.v3, face.v1 );\r
224 \r
225     if ( faceIndex == 0 ) {\r
226       faceLine += " Face Edge Indices Starting from 1";\r
227     }\r
228 \r
229     outputString( faceLine );\r
230   }\r
231 }\r
232 \r
233 /**\r
234 Output comma separated strings followed by a linefeed.\r
235 */\r
236 \r
237 void outputString( string )\r
238 {\r
239   if ( printSelectionCheckbox.getState() ) {\r
240     print( string );\r
241   }\r
242 \r
243   if ( bufferedWriter != null ) {\r
244     bufferedWriter.write( string.replace( ", ", "," ) + "\n" );\r
245   }\r
246 }\r
247 \r
248 /**\r
249 Add radio button groups to the preference widgets.\r
250 \r
251 @param  radioButtonGroups radio button groups which will be added to the memorable widgets\r
252 @param  widgetVector memorable widgets\r
253 */\r
254 \r
255 void preferencesAddRadioButtonGroups( RadioButtonGroup[] radioButtonGroups, Vector widgetVector )\r
256 {\r
257   for ( int radioIndex = 0; radioIndex < radioButtonGroups.length; radioIndex++ ) {\r
258     radioButtonGroup = radioButtonGroups[ radioIndex ];\r
259     radioButtonGroupIterator = radioButtonGroup.getRadioButtons();\r
260 \r
261     while ( radioButtonGroupIterator.hasNext() ) {\r
262       radioButton = radioButtonGroupIterator.next();\r
263       preferencesAddWidgetWithString( radioButton, radioButton.getText(), widgetVector );\r
264     }\r
265   }\r
266 }\r
267 \r
268 /**\r
269 Add widgets which have titles.\r
270 \r
271 @param  widgets widgets which have titles\r
272 @param  widgetStrings widget titles\r
273 @param  widgetVector memorable widgets\r
274 */\r
275 \r
276 void preferencesAddWidgetsWithStrings( Widget[] widgets, String[] widgetStrings, Vector widgetVector )\r
277 {\r
278   for ( int widgetIndex = 0; widgetIndex < widgets.length; widgetIndex++ ) {\r
279     widget = widgets[ widgetIndex ];\r
280 \r
281     if ( widget instanceof BCheckBox || widget instanceof BOutline || widget instanceof BTextField || widget instanceof ValueField ) {\r
282       preferencesAddWidgetWithString( widget, widgetStrings[ widgetIndex ], widgetVector );\r
283     }\r
284   }\r
285 }\r
286 \r
287 /**\r
288 Give the widget a name and add it to the widget vector.\r
289 \r
290 @param  widget widget which will be given a name\r
291 @param  widgetStrings widget name\r
292 @param  widgetVector memorable widgets\r
293 */\r
294 \r
295 void preferencesAddWidgetWithString( Widget widget, String widgetString, Vector widgetVector )\r
296 {\r
297   widget.setName( widgetString );\r
298   widgetVector.add( widget );\r
299 }\r
300 \r
301 /**\r
302 Read widget settings from preferences file.\r
303 \r
304 @param  preferencesFilename preferences filename\r
305 @param  widgetVector memorable widgets\r
306 */\r
307 \r
308 void preferencesRead( String preferencesFilename, Vector widgetVector )\r
309 {\r
310   preferencesFile = new File( preferencesFilename );\r
311 \r
312   if ( !preferencesFile.canRead() ) {\r
313     return;\r
314   }\r
315 \r
316   BufferedReader preferencesReader = new BufferedReader( new FileReader( preferencesFile ) );\r
317 \r
318   line = preferencesReader.readLine();\r
319 \r
320   while ( line != null ) {\r
321     preferencesReadLine( line, widgetVector );\r
322     line = preferencesReader.readLine();\r
323   }\r
324 }\r
325 \r
326 /**\r
327 Read line of preferences and set widget to that line.\r
328 \r
329 @param  line line of preferences\r
330 @param  widgetVector memorable widgets\r
331 */\r
332 \r
333 void preferencesReadLine( String line, Vector widgetVector )\r
334 {\r
335   splitLine = line.split( "\t" );\r
336 \r
337   if ( splitLine.length < 2 ) {\r
338     return;\r
339   }\r
340 \r
341   name = splitLine[ 0 ];\r
342 \r
343   for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) {\r
344     widget = widgetVector.elementAt( widgetIndex );\r
345 \r
346     if ( widget.getName().equals( name ) ) {\r
347       preferencesReadWidget( splitLine[ 1 ], widget );\r
348 \r
349       return;\r
350     }\r
351   }\r
352 }\r
353 \r
354 /**\r
355 Set widget to preferences value.\r
356 \r
357 @param  value preferences value\r
358 @param  widget widget to be set to value\r
359 */\r
360 \r
361 void preferencesReadWidget( String value, Widget widget )\r
362 {\r
363   if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) {\r
364     widget.setState( Boolean.valueOf( value ) );\r
365 \r
366     return;\r
367   }\r
368 \r
369   if ( widget instanceof BOutline ) { // it would be better to save the value instead of index because the list might change, but I'm lazy\r
370     bList = widget.getContent().getContent();\r
371     selectedIndex = Integer.valueOf( value );\r
372     bList.setSelected( selectedIndex, true );\r
373     bList.scrollToItem( selectedIndex );\r
374 \r
375     return;\r
376   }\r
377 \r
378   if ( widget instanceof BTextField ) {\r
379     widget.setText( value );\r
380 \r
381     return;\r
382   }\r
383 \r
384   if ( widget instanceof ValueField ) {\r
385     widget.setValue( Double.valueOf( value ) );\r
386   }\r
387 }\r
388 \r
389 /**\r
390 Write widget settings to preferences file.\r
391 \r
392 @param  preferencesFilename preferences filename\r
393 @param  widgetVector memorable widgets\r
394 */\r
395 \r
396 void preferencesWrite( String preferencesFilename, Vector widgetVector )\r
397 {\r
398   preferencesFile = new File( preferencesFilename );\r
399 \r
400   if ( preferencesFile == null ) {\r
401     print( "Can not write preferences to " + preferencesFilename );\r
402 \r
403     return;\r
404   }\r
405 \r
406   BufferedWriter preferencesWriter = new BufferedWriter( new FileWriter( preferencesFile ) );\r
407 \r
408   for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) {\r
409     widget = widgetVector.elementAt( widgetIndex );\r
410     preferencesWriteWidget( preferencesWriter, widget );\r
411   }\r
412 \r
413   //Close the output stream\r
414   preferencesWriter.close();\r
415 }\r
416 \r
417 /**\r
418 Write widget settings to line of preferences.\r
419 \r
420 @param  preferencesWriter buffered preferences file writer\r
421 @param  widget widget to be written\r
422 */\r
423 \r
424 void preferencesWriteWidget( BufferedWriter preferencesWriter, Widget widget )\r
425 {\r
426   widgetString = widget.getName() + "\t";\r
427 \r
428   if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) {\r
429     preferencesWriter.write( widgetString + widget.getState().toString() + "\n" );\r
430 \r
431     return;\r
432   }\r
433 \r
434   if ( widget instanceof BOutline ) { // it would be better to save the value because the list might change, but I'm lazy\r
435     BList bList = widget.getContent().getContent();\r
436     bList = widget.getContent().getContent();\r
437     preferencesWriter.write( widgetString + bList.getSelectedIndex().toString() + "\n" );\r
438 \r
439     return;\r
440   }\r
441 \r
442   if ( widget instanceof BTextField ) {\r
443     preferencesWriter.write( widgetString + widget.getText() + "\n" );\r
444 \r
445     return;\r
446   }\r
447 \r
448   if ( widget instanceof ValueField ) {\r
449     preferencesWriter.write( widgetString + widget.getValue().toString() + "\n" );\r
450   }\r
451 }\r
452 \r
453 // Set default parameters.\r
454 bufferedWriter = null;\r
455 exportSelectionCheckbox = new BCheckBox( "", false );\r
456 file = null;\r
457 gnuSurfaceFilenameTextField = new BTextField( "triangle_mesh.gts" );\r
458 String preferencesFilename = "gnu_triangulated_surface_preferences.csv";\r
459 printSelectionCheckbox = new BCheckBox( "", true );\r
460 \r
461 Widget[] widgets = new Widget[] {       exportSelectionCheckbox,\r
462                                                 printSelectionCheckbox };\r
463 \r
464 String[] widgetStrings = new String[] { "Export Selection:",\r
465                                                 "Print Selection:" };\r
466 \r
467 // change the user interface parameters from default to preferences\r
468 Vector widgetVector = new Vector();\r
469 preferencesAddWidgetsWithStrings( widgets, widgetStrings, widgetVector );\r
470 preferencesAddWidgetWithString( gnuSurfaceFilenameTextField, "GNU Triangulated Surface Filename:", widgetVector );\r
471 preferencesRead( preferencesFilename, widgetVector );\r
472 \r
473 curvesMeshesInfo = getCurvesMeshesInfo();\r
474 \r
475 if ( curvesMeshesInfo.length < 1 ) {\r
476   print( "There is no triangle mesh in the scene, so nothing will be done." );\r
477   print( "It may be that there is a solid shape which is not a triangle mesh, in which case convert that shape to a triangle mesh." );\r
478 \r
479   return;\r
480 }\r
481 \r
482 dialog = new ComponentsDialog( window, "Export and/or Print Selection", widgets, widgetStrings );\r
483 \r
484 if ( !dialog.clickedOk() ) return;\r
485 \r
486 if ( exportSelectionCheckbox.getState() ) {\r
487   fileDescriptor = new BFileChooser( BFileChooser.SAVE_FILE, "Select name for the GNU Triangulated Surface file.");\r
488   fileDescriptor.setSelectedFile( new File( gnuSurfaceFilenameTextField.getText() ) );\r
489   fileDescriptor.showDialog( window );\r
490   file = fileDescriptor.getSelectedFile();\r
491 \r
492   if ( file != null ) {\r
493     gnuSurfaceFilenameTextField.setText( file.getAbsolutePath() );\r
494     bufferedWriter = new BufferedWriter( new FileWriter( file ) );\r
495   }\r
496 }\r
497 \r
498 preferencesWrite( preferencesFilename, widgetVector );\r
499 outputCurvesMeshes( curvesMeshesInfo );\r