Branch data Line data Source code
1 : : /* *****************************************************************
2 : : MESQUITE -- The Mesh Quality Improvement Toolkit
3 : :
4 : : Copyright 2005 Lawrence Livermore National Laboratory. Under
5 : : the terms of Contract B545069 with the University of Wisconsin --
6 : : Madison, Lawrence Livermore National Laboratory retains certain
7 : : rights in this software.
8 : :
9 : : This library is free software; you can redistribute it and/or
10 : : modify it under the terms of the GNU Lesser General Public
11 : : License as published by the Free Software Foundation; either
12 : : version 2.1 of the License, or (at your option) any later version.
13 : :
14 : : This library is distributed in the hope that it will be useful,
15 : : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : : Lesser General Public License for more details.
18 : :
19 : : You should have received a copy of the GNU Lesser General Public License
20 : : (lgpl.txt) along with this library; if not, write to the Free Software
21 : : Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 : :
23 : : [email protected]
24 : :
25 : : ***************************************************************** */
26 : :
27 : : #ifndef MSQ_MESH_WRITER_CPP
28 : : #define MSQ_MESH_WRITER_CPP
29 : :
30 : : #include "MeshWriter.hpp"
31 : : #include "Mesquite.hpp"
32 : : #include "MeshImpl.hpp"
33 : : #include "MsqError.hpp"
34 : : #include "PatchData.hpp"
35 : : #include "PlanarDomain.hpp"
36 : : #include "VtkTypeInfo.hpp"
37 : : #include "EdgeIterator.hpp"
38 : :
39 : : #include <memory>
40 : : #include <limits>
41 : : #include <vector>
42 : : #include <algorithm>
43 : : #include <fstream>
44 : : #include <string>
45 : : #include <iomanip>
46 : : using namespace std;
47 : :
48 : : #include <stdio.h>
49 : :
50 : : namespace MBMesquite
51 : : {
52 : :
53 : : namespace MeshWriter
54 : : {
55 : :
56 : : /**\brief Transform from coordinates in the XY-plane
57 : : * to graphics coordinates.
58 : : */
59 : : class Transform2D
60 : : {
61 : : public:
62 : : Transform2D( PatchData* pd, Projection& proj, unsigned width, unsigned height, bool flip_about_horizontal );
63 : :
64 : : Transform2D( const Vector3D* verts, size_t num_vert, Projection& projection, unsigned width, unsigned height );
65 : :
66 : : void transform( const Vector3D& coords, int& horizontal, int& vertical ) const;
67 : :
68 : 0 : int max_horizontal() const
69 : : {
70 : 0 : return horizMax;
71 : : }
72 : 0 : int max_vertical() const
73 : : {
74 : 0 : return vertMax;
75 : : }
76 : :
77 : : private:
78 : : Projection& myProj;
79 : : float myScale;
80 : : int horizOffset, vertOffset;
81 : : int horizMax, vertMax;
82 : : int vertSign;
83 : : };
84 : :
85 : : /* Write VTK file
86 : : *
87 : : * Copied from src/Mesh/MeshSet.cpp and adapted for removal of
88 : : * MeshSet class by J.Kraftcheck on 2005-7-28.
89 : : *
90 : : * This code is provided mainly for debugging. A more efficient
91 : : * and complete writer implementation is provided in the MeshImpl
92 : : * class for saving meshes that were read from a file initially.
93 : : */
94 : 3 : void write_vtk( Mesh* mesh, const char* out_filename, MsqError& err )
95 : : {
96 [ - + ][ - + ]: 3 : if( MeshImpl* msq_mesh = dynamic_cast< MeshImpl* >( mesh ) )
97 : : {
98 [ # # ][ # # ]: 0 : msq_mesh->write_vtk( out_filename, err );MSQ_CHKERR( err );
[ # # ][ # # ]
[ # # ]
99 : 3 : return;
100 : : }
101 : :
102 : : // loads a global patch
103 [ + - ]: 3 : PatchData pd;
104 [ + - ]: 3 : pd.set_mesh( mesh );
105 [ + - ][ + - ]: 3 : pd.fill_global_patch( err );MSQ_ERRRTN( err );
[ - + ][ # # ]
[ # # ][ - + ]
106 : :
107 : : // write mesh
108 [ + - ][ + - ]: 3 : write_vtk( pd, out_filename, err );MSQ_CHKERR( err );
[ - + ][ # # ]
[ # # ][ + - ]
109 : : }
110 : :
111 : 3 : void write_vtk( PatchData& pd, const char* out_filename, MsqError& err, const Vector3D* OF_gradient )
112 : : {
113 : : // Open the file
114 [ + - ][ + - ]: 3 : std::ofstream file( out_filename );
115 [ + - ][ - + ]: 3 : if( !file )
116 : : {
117 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::FILE_ACCESS );
118 : 0 : return;
119 : : }
120 : :
121 : : // Write a header
122 [ + - ]: 3 : file << "# vtk DataFile Version 2.0\n";
123 [ + - ][ + - ]: 3 : file << "Mesquite Mesh " << out_filename << " .\n";
[ + - ]
124 [ + - ]: 3 : file << "ASCII\n";
125 [ + - ]: 3 : file << "DATASET UNSTRUCTURED_GRID\n";
126 : :
127 : : // Write vertex coordinates
128 [ + - ][ + - ]: 3 : file << "POINTS " << pd.num_nodes() << " float\n";
[ + - ][ + - ]
129 : : size_t i;
130 [ + - ][ + + ]: 3996 : for( i = 0; i < pd.num_nodes(); i++ )
131 : : {
132 [ + - ][ + - ]: 3993 : file << pd.vertex_by_index( i )[0] << ' ' << pd.vertex_by_index( i )[1] << ' ' << pd.vertex_by_index( i )[2]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
133 [ + - ]: 3993 : << '\n';
134 : : }
135 : :
136 : : // Write out the connectivity table
137 : 3 : size_t connectivity_size = 0;
138 [ + - ][ + + ]: 3003 : for( i = 0; i < pd.num_elements(); ++i )
139 [ + - ][ + - ]: 3000 : connectivity_size += pd.element_by_index( i ).node_count() + 1;
140 : :
141 [ + - ][ + - ]: 3 : file << "CELLS " << pd.num_elements() << ' ' << connectivity_size << '\n';
[ + - ][ + - ]
[ + - ][ + - ]
142 [ + - ][ + + ]: 3003 : for( i = 0; i < pd.num_elements(); i++ )
143 : : {
144 [ + - ]: 3000 : std::vector< size_t > vtx_indices;
145 [ + - ][ + - ]: 3000 : pd.element_by_index( i ).get_node_indices( vtx_indices );
146 : :
147 : : // Convert native to VTK node order, if not the same
148 : : const VtkTypeInfo* info =
149 [ + - ][ + - ]: 3000 : VtkTypeInfo::find_type( pd.element_by_index( i ).get_element_type(), vtx_indices.size(), err );MSQ_ERRRTN( err );
[ + - ][ + - ]
[ - + ][ # # ]
[ # # ][ - + ]
150 [ + - ]: 3000 : info->mesquiteToVtkOrder( vtx_indices );
151 : :
152 [ + - ]: 3000 : file << vtx_indices.size();
153 [ + + ]: 27000 : for( std::size_t j = 0; j < vtx_indices.size(); ++j )
154 : : {
155 [ + - ][ + - ]: 24000 : file << ' ' << vtx_indices[j];
[ + - ]
156 : : }
157 [ + - ][ + - ]: 3000 : file << '\n';
158 : 3000 : }
159 : :
160 : : // Write out the element types
161 [ + - ][ + - ]: 3 : file << "CELL_TYPES " << pd.num_elements() << '\n';
[ + - ][ + - ]
162 [ + - ][ + + ]: 3003 : for( i = 0; i < pd.num_elements(); i++ )
163 : : {
164 [ + - ]: 3000 : const VtkTypeInfo* info = VtkTypeInfo::find_type( pd.element_by_index( i ).get_element_type(),
165 [ + - ][ + - ]: 6000 : pd.element_by_index( i ).node_count(), err );MSQ_ERRRTN( err );
[ + - ][ + - ]
[ + - ][ - + ]
[ # # ][ # # ]
[ - + ]
166 [ + - ][ + - ]: 3000 : file << info->vtkType << '\n';
167 : : }
168 : :
169 : : // Write out which points are fixed.
170 [ + - ][ + - ]: 3 : file << "POINT_DATA " << pd.num_nodes() << "\nSCALARS fixed int\nLOOKUP_TABLE default\n";
[ + - ][ + - ]
171 [ + - ][ + + ]: 3996 : for( i = 0; i < pd.num_nodes(); ++i )
172 : : {
173 [ + - ][ + - ]: 3993 : if( pd.vertex_by_index( i ).get_flags() & MsqVertex::MSQ_CULLED )
[ - + ]
174 [ # # ]: 0 : file << "1\n";
175 : : else
176 [ + - ]: 3993 : file << "0\n";
177 : : }
178 [ + - ]: 3 : file << "SCALARS culled short\nLOOKUP_TABLE default\n";
179 [ + - ][ + + ]: 3996 : for( i = 0; i < pd.num_nodes(); ++i )
180 : : {
181 [ + - ][ + - ]: 3993 : if( pd.vertex_by_index( i ).is_free_vertex() )
[ + + ]
182 [ + - ]: 2789 : file << "0\n";
183 : : else
184 [ + - ]: 1204 : file << "1\n";
185 : : }
186 : :
187 [ - + ]: 3 : if( OF_gradient )
188 : : {
189 [ # # ]: 0 : file << "VECTORS gradient double\n";
190 [ # # ][ # # ]: 0 : for( i = 0; i < pd.num_free_vertices(); ++i )
191 [ # # ][ # # ]: 0 : file << OF_gradient[i].x() << " " << OF_gradient[i].y() << " " << OF_gradient[i].z() << "\n";
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
192 [ # # ][ # # ]: 0 : for( i = pd.num_free_vertices(); i < pd.num_nodes(); ++i )
[ # # ]
193 [ # # ]: 0 : file << "0.0 0.0 0.0\n";
194 : : }
195 : :
196 : : // Close the file
197 [ + - ][ + - ]: 3 : file.close();
198 : : }
199 : :
200 : 11 : void write_gnuplot( Mesh* mesh, const char* out_filebase, MsqError& err )
201 : : {
202 : : // loads a global patch
203 [ + - ]: 11 : PatchData pd;
204 [ + - ]: 11 : pd.set_mesh( mesh );
205 [ + - ][ + - ]: 22 : pd.fill_global_patch( err );MSQ_ERRRTN( err );
[ - + ][ # # ]
[ # # ][ - + ]
206 [ + - ][ + - ]: 11 : write_gnuplot( pd, out_filebase, err );
207 : : }
208 : :
209 : 0 : void write_gnuplot( Mesh* mesh, std::vector< Mesh::ElementHandle >& elems, const char* out_filebase, MsqError& err )
210 : : {
211 : : // loads a global patch
212 [ # # ]: 0 : PatchData pd;
213 [ # # ]: 0 : pd.set_mesh( mesh );
214 [ # # ][ # # ]: 0 : std::vector< Mesh::VertexHandle > verts;
215 [ # # ][ # # ]: 0 : pd.set_mesh_entities( elems, verts, err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
216 [ # # ][ # # ]: 0 : write_gnuplot( pd, out_filebase, err );
217 : : }
218 : :
219 : : /* Writes a gnuplot file directly from the MeshSet.
220 : : * This means that any mesh imported successfully into Mesquite
221 : : * can be outputed in gnuplot format.
222 : : *
223 : : * Within gnuplot, use \b plot 'file1.gpt' w l, 'file2.gpt' w l
224 : : *
225 : : * This is not geared for performance, since it has to load a global Patch from
226 : : * the mesh to write a mesh file.
227 : : *
228 : : * Copied from src/Mesh/MeshSet.cpp and adapted for removal of
229 : : * MeshSet class by J.Kraftcheck on 2005-7-28.
230 : : *
231 : : * Re-written to use EdgeIterator by J.Kraftcheck on 2009-6-11
232 : : */
233 : 11 : void write_gnuplot( PatchData& pd, const char* out_filebase, MsqError& err )
234 : : {
235 : : // Open the file
236 [ + - ]: 11 : std::string out_filename = out_filebase;
237 [ + - ]: 11 : out_filename += ".gpt";
238 [ + - ][ + - ]: 22 : std::ofstream file( out_filename.c_str() );
[ + - ]
239 [ + - ][ - + ]: 11 : if( !file )
240 : : {
241 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::FILE_ACCESS );
242 : 0 : return;
243 : : }
244 : :
245 [ + - ][ + - ]: 22 : EdgeIterator edges( &pd, err );MSQ_ERRRTN( err );
[ - + ][ # # ]
[ # # ][ - + ]
[ + - ]
246 : :
247 : : // Write a header
248 [ + - ]: 11 : file << "\n";
249 : :
250 [ + - ][ + + ]: 143 : while( !edges.is_at_end() )
251 : : {
252 [ + - ]: 132 : const Vector3D& s = edges.start();
253 [ + - ]: 132 : const Vector3D& e = edges.end();
254 [ + - ]: 132 : const Vector3D* m = edges.mid();
255 : :
256 [ + - ][ + - ]: 132 : file << s[0] << ' ' << s[1] << ' ' << s[2] << std::endl;
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
257 [ - + ][ # # ]: 132 : if( m ) file << ( *m )[0] << ' ' << ( *m )[1] << ' ' << ( *m )[2] << std::endl;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
258 [ + - ][ + - ]: 132 : file << e[0] << ' ' << e[1] << ' ' << e[2] << std::endl;
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
259 [ + - ][ + - ]: 132 : file << std::endl << std::endl;
260 : :
261 [ + - ][ + - ]: 132 : edges.step( err );MSQ_ERRRTN( err );
[ - + ][ # # ]
[ # # ][ - + ]
262 : : }
263 : :
264 : : // Close the file
265 [ + - ][ + - ]: 22 : file.close();
266 : : }
267 : :
268 : : /** Helper function for write_gnuplot_animator and write_gnuplot_overlay
269 : : *
270 : : * Read a set of input files to determine the bounding box
271 : : * of the combined data.
272 : : */
273 : 1 : static void find_gnuplot_agregate_range( int count, const char* basename, Vector3D& min, Vector3D& max,
274 : : MsqError& err )
275 : : {
276 : : // read all input files to determine extents
277 [ + - ]: 1 : min = Vector3D( HUGE_VAL, HUGE_VAL, HUGE_VAL );
278 [ + - ]: 1 : max = Vector3D( -HUGE_VAL, -HUGE_VAL, -HUGE_VAL );
279 [ + + ]: 12 : for( int i = 0; i <= count; ++i )
280 : : {
281 [ + - ][ + - ]: 11 : stringstream s;
282 [ + - ][ + - ]: 11 : s << basename << '.' << i << ".gpt";
[ + - ][ + - ]
283 [ + - ][ + - ]: 22 : ifstream infile( s.str().c_str() );
[ + - ]
284 [ + - ][ - + ]: 11 : if( !infile )
285 : : {
286 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( s.str(), MsqError::FILE_ACCESS );
[ # # ]
287 : 1 : return;
288 : : }
289 : : double c[3];
290 [ + - ][ + - ]: 275 : while( infile >> c[0] >> c[1] >> c[2] )
[ + - ][ + - ]
[ + + ][ + - ]
291 : : {
292 [ + + ]: 1056 : for( int j = 0; j < 3; ++j )
293 : : {
294 [ + - ][ + + ]: 792 : if( c[j] < min[j] ) min[j] = c[j];
[ + - ]
295 [ + - ][ + + ]: 792 : if( c[j] > max[j] ) max[j] = c[j];
[ + - ]
296 : : }
297 : : }
298 : 11 : }
299 : : }
300 : :
301 : : /** Write a GNU Plot script to produce an animation from a
302 : : * sequence of data files
303 : : */
304 : 0 : void write_gnuplot_animator( int count, const char* basename, MsqError& err )
305 : : {
306 [ # # ]: 0 : if( count <= 0 ) return;
307 : :
308 : 0 : const int DELAY = 10;
309 : 0 : const int WIDTH = 640;
310 : 0 : const int HEIGHT = 480;
311 : :
312 : : // read all input files to determine extents
313 [ # # ][ # # ]: 0 : Vector3D min, max;
314 [ # # ][ # # ]: 0 : find_gnuplot_agregate_range( count, basename, min, max, err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
315 : :
316 : : // chose coordinate plane to plot in
317 [ # # ]: 0 : Vector3D range = max - min;
318 : 0 : int haxis = 0, vaxis = 1;
319 [ # # ][ # # ]: 0 : if( range[0] < range[1] && range[1] < range[2] )
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
320 : : {
321 : 0 : haxis = 1;
322 : 0 : vaxis = 2;
323 : : }
324 [ # # ][ # # ]: 0 : else if( range[1] < range[2] )
[ # # ]
325 : : {
326 : 0 : vaxis = 2;
327 : : }
328 : :
329 : : // open output file
330 [ # # ]: 0 : string base( basename );
331 [ # # ][ # # ]: 0 : ofstream file( ( string( basename ) + ".gnuplot" ).c_str() );
[ # # ][ # # ]
[ # # ]
332 [ # # ][ # # ]: 0 : if( !file )
333 : : {
334 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::FILE_ACCESS );
335 : 0 : return;
336 : : }
337 : :
338 : : // write header
339 [ # # ][ # # ]: 0 : file << "#!gnuplot" << endl;
340 [ # # ][ # # ]: 0 : file << "#" << endl;
341 [ # # ][ # # ]: 0 : file << "# Mesquite Animation of " << basename << ".0 to " << basename << '.' << count << endl;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
342 [ # # ][ # # ]: 0 : file << "#" << endl;
343 : :
344 : : // write plot settings
345 [ # # ][ # # ]: 0 : file << "set term gif animate transparent opt delay " << DELAY << " size " << WIDTH << "," << HEIGHT << endl;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
346 [ # # ][ # # ]: 0 : file << "set xrange [" << min[haxis] - 0.05 * range[haxis] << ":" << max[haxis] + 0.05 * range[haxis] << "]"
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
347 [ # # ]: 0 : << endl;
348 [ # # ][ # # ]: 0 : file << "set yrange [" << min[vaxis] - 0.05 * range[vaxis] << ":" << max[vaxis] + 0.05 * range[vaxis] << "]"
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
349 [ # # ]: 0 : << endl;
350 [ # # ][ # # ]: 0 : file << "set title '" << basename << "'" << endl;
[ # # ][ # # ]
351 [ # # ][ # # ]: 0 : file << "set output '" << basename << ".gif'" << endl;
[ # # ][ # # ]
352 : :
353 : : // plot data
354 [ # # ][ # # ]: 0 : for( int i = 0; i <= count; ++i )
355 [ # # ][ # # ]: 0 : file << "plot '" << basename << '.' << i << ".gpt'"
[ # # ][ # # ]
[ # # ]
356 [ # # ][ # # ]: 0 : << " using " << haxis + 1 << ":" << vaxis + 1 << " w l" << endl;
[ # # ][ # # ]
[ # # ][ # # ]
357 : : }
358 : :
359 : 11 : static unsigned red( int i, int c )
360 : : {
361 [ + + ]: 11 : if( i * 4 <= c )
362 : 3 : return 255;
363 [ + + ]: 8 : else if( i * 4 >= 3 * c )
364 : 3 : return 0;
365 : : else
366 : 5 : return 384 - i * 511 / c;
367 : : }
368 : :
369 : 11 : static unsigned green( int i, int c )
370 : : {
371 [ + + ]: 11 : if( i * 4 < c )
372 : 3 : return i * 510 / c;
373 [ + + ]: 8 : else if( i * 4 > 3 * c )
374 : 3 : return 1023 - i * 1023 / c;
375 : : else
376 : 5 : return 255;
377 : : }
378 : :
379 : 11 : static unsigned blue( int i, int c )
380 : : {
381 [ + + ]: 11 : if( i * 4 <= c )
382 : 3 : return 0;
383 [ + + ]: 8 : else if( i * 4 >= 3 * c )
384 : 3 : return 255;
385 : : else
386 : 5 : return i * 511 / c - 127;
387 : : }
388 : :
389 : : /** Write a GNU Plot script to produce a single plot from a
390 : : * sequence of data files
391 : : */
392 : 1 : void write_gnuplot_overlay( int count, const char* basename, MsqError& err )
393 : : {
394 [ - + ]: 2 : if( count <= 0 ) return;
395 : :
396 : 1 : const int WIDTH = 640;
397 : 1 : const int HEIGHT = 480;
398 : :
399 : : // read all input files to determine extents
400 [ + - ][ + - ]: 1 : Vector3D min, max;
401 [ + - ][ + - ]: 1 : find_gnuplot_agregate_range( count, basename, min, max, err );MSQ_ERRRTN( err );
[ - + ][ # # ]
[ # # ][ - + ]
402 : :
403 : : // chose coordinate plane to plot in
404 [ + - ]: 1 : Vector3D range = max - min;
405 : 1 : int haxis = 0, vaxis = 1;
406 [ + - ][ + - ]: 1 : if( range[0] < range[1] && range[1] < range[2] )
[ - + ][ # # ]
[ # # ][ # # ]
[ - + ]
407 : : {
408 : 0 : haxis = 1;
409 : 0 : vaxis = 2;
410 : : }
411 [ + - ][ + - ]: 1 : else if( range[1] < range[2] )
[ - + ]
412 : : {
413 : 0 : vaxis = 2;
414 : : }
415 : :
416 : : // open output file
417 [ + - ]: 1 : string base( basename );
418 [ + - ][ + - ]: 1 : FILE* file = fopen( ( string( basename ) + ".gnuplot" ).c_str(), "w" );
[ + - ]
419 [ - + ]: 1 : if( !file )
420 : : {
421 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::FILE_ACCESS );
422 : 0 : return;
423 : : }
424 : :
425 : : // write header
426 [ + - ]: 1 : fprintf( file, "#!gnuplot\n" );
427 [ + - ]: 1 : fprintf( file, "#\n" );
428 [ + - ]: 1 : fprintf( file, "# Mesquite Overlay of %s.0 to %s.%d\n", basename, basename, count );
429 [ + - ]: 1 : fprintf( file, "#\n" );
430 : :
431 : : // write plot settings
432 [ + - ]: 1 : fprintf( file, "set term gif size %d,%d\n", WIDTH, HEIGHT );
433 [ + - ][ + - ]: 1 : fprintf( file, "set xrange [%f:%f]\n", min[haxis] - 0.05 * range[haxis], max[haxis] + 0.05 * range[haxis] );
[ + - ][ + - ]
[ + - ]
434 [ + - ][ + - ]: 1 : fprintf( file, "set yrange [%f:%f]\n", min[vaxis] - 0.05 * range[vaxis], max[vaxis] + 0.05 * range[vaxis] );
[ + - ][ + - ]
[ + - ]
435 [ + - ]: 1 : fprintf( file, "set title '%s'\n", basename );
436 [ + - ]: 1 : fprintf( file, "set output '%s.gif'\n", basename );
437 : :
438 : : // plot data
439 : : fprintf( file, "plot '%s.0.gpt' using %d:%d w l lc rgb '#%02x%02x%02x' title 't0'", basename, haxis + 1,
440 [ + - ]: 1 : vaxis + 1, red( 0, count ), green( 0, count ), blue( 0, count ) );
441 [ + + ]: 11 : for( int i = 1; i <= count; ++i )
442 : : {
443 : : fprintf( file, ", \\\n '%s.%d.gpt' using %d:%d w l lc rgb '#%02x%02x%02x' title 't%d'", basename, i,
444 [ + - ]: 10 : haxis + 1, vaxis + 1, red( i, count ), green( i, count ), blue( i, count ), i );
445 : : }
446 [ + - ]: 1 : fprintf( file, "\n" );
447 : :
448 [ + - ][ + - ]: 1 : fclose( file );
449 : : }
450 : :
451 : 0 : void write_stl( Mesh* mesh, const char* filename, MsqError& err )
452 : : {
453 : : // Open the file
454 [ # # ][ # # ]: 0 : ofstream file( filename );
455 [ # # ][ # # ]: 0 : if( !file )
456 : : {
457 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::FILE_ACCESS );
458 : 0 : return;
459 : : }
460 : :
461 : : // Write header
462 : : char header[70];
463 : 0 : sprintf( header, "Mesquite%d", rand() );
464 [ # # ][ # # ]: 0 : file << "solid " << header << endl;
[ # # ]
465 : :
466 [ # # ][ # # ]: 0 : MsqVertex coords[3];
467 [ # # ][ # # ]: 0 : std::vector< Mesh::VertexHandle > verts( 3 );
468 [ # # ][ # # ]: 0 : std::vector< size_t > offsets( 2 );
469 : :
470 : : // Iterate over all elements
471 : 0 : size_t count = 0;
472 [ # # ][ # # ]: 0 : std::vector< Mesh::ElementHandle > elems;
473 : 0 : std::vector< Mesh::ElementHandle >::iterator iter;
474 [ # # ][ # # ]: 0 : mesh->get_all_elements( elems, err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
475 [ # # ][ # # ]: 0 : for( iter = elems.begin(); iter != elems.end(); ++iter )
[ # # ]
476 : : {
477 : : // Skip non-triangles
478 [ # # ]: 0 : Mesh::ElementHandle elem = *iter;
479 : : EntityTopology type;
480 [ # # ][ # # ]: 0 : mesh->elements_get_topologies( &elem, &type, 1, err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
481 [ # # ]: 0 : if( type != TRIANGLE ) continue;
482 : 0 : ++count;
483 : :
484 : : // Get vertex coordinates
485 [ # # ][ # # ]: 0 : mesh->elements_get_attached_vertices( &elem, 1, verts, offsets, err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
486 [ # # ][ # # ]: 0 : mesh->vertices_get_coordinates( arrptr( verts ), coords, 3, err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
487 : :
488 : : // Get triagnle normal
489 [ # # ][ # # ]: 0 : Vector3D n = ( coords[0] - coords[1] ) * ( coords[0] - coords[2] );
[ # # ]
490 [ # # ]: 0 : n.normalize();
491 : :
492 : : // Write triangle
493 [ # # ][ # # ]: 0 : file << "facet normal " << n.x() << " " << n.y() << " " << n.z() << endl;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
494 [ # # ][ # # ]: 0 : file << "outer loop" << endl;
495 [ # # ]: 0 : for( unsigned i = 0; i < 3; ++i )
496 [ # # ][ # # ]: 0 : file << "vertex " << coords[i].x() << " " << coords[i].y() << " " << coords[i].z() << endl;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
497 [ # # ][ # # ]: 0 : file << "endloop" << endl;
498 [ # # ][ # # ]: 0 : file << "endfacet" << endl;
499 : : }
500 : :
501 [ # # ][ # # ]: 0 : file << "endsolid " << header << endl;
[ # # ]
502 : :
503 [ # # ]: 0 : file.close();
504 [ # # ]: 0 : if( count == 0 )
505 : : {
506 : 0 : std::remove( filename );
507 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( "Mesh contains no triangles", MsqError::INVALID_STATE );
[ # # ]
508 : 0 : }
509 : : }
510 : :
511 : 0 : Projection::Projection( PlanarDomain* domain )
512 : : {
513 [ # # ]: 0 : Vector3D normal;
514 [ # # ]: 0 : domain->vertex_normal_at( 0, normal );
515 [ # # ]: 0 : init( normal );
516 : 0 : }
517 : :
518 : 0 : Projection::Projection( const Vector3D& n )
519 : : {
520 [ # # ]: 0 : init( n );
521 : 0 : }
522 : :
523 : 0 : Projection::Projection( const Vector3D& n, const Vector3D& up )
524 : : {
525 [ # # ]: 0 : init( n, up );
526 : 0 : }
527 : :
528 : 0 : Projection::Projection( Axis h, Axis v )
529 : : {
530 [ # # ][ # # ]: 0 : Vector3D horiz( 0, 0, 0 ), vert( 0, 0, 0 );
531 [ # # ]: 0 : horiz[h] = 1.0;
532 [ # # ]: 0 : vert[v] = 1.0;
533 [ # # ][ # # ]: 0 : init( horiz * vert, vert );
534 : 0 : }
535 : :
536 : 0 : void Projection::init( const Vector3D& n )
537 : : {
538 : : // Choose an "up" direction
539 : :
540 : 0 : Axis max = X;
541 [ # # ]: 0 : for( Axis i = Y; i <= Z; i = ( Axis )( i + 1 ) )
542 [ # # ][ # # ]: 0 : if( fabs( n[i] ) > fabs( n[max] ) ) max = i;
[ # # ]
543 : :
544 : : Axis up;
545 [ # # ]: 0 : if( max == Y )
546 : 0 : up = Z;
547 : : else
548 : 0 : up = Y;
549 : :
550 : : // Initialize rotation matrix
551 : :
552 [ # # ]: 0 : Vector3D up_vect( 0, 0, 0 );
553 [ # # ]: 0 : up_vect[up] = 1.0;
554 [ # # ]: 0 : init( n, up_vect );
555 : 0 : }
556 : :
557 : 0 : void Projection::init( const Vector3D& n1, const Vector3D& up1 )
558 : : {
559 [ # # ]: 0 : MsqError err;
560 [ # # ][ # # ]: 0 : const Vector3D n = n1 / n1.length();
561 [ # # ][ # # ]: 0 : const Vector3D u = up1 / up1.length();
562 : :
563 : : // Rotate for projection
564 [ # # ]: 0 : const Vector3D z( 0., 0., 1. );
565 [ # # ]: 0 : Vector3D r = n * z;
566 [ # # ]: 0 : double angle = r.interior_angle( n, z, err );
567 [ # # ]: 0 : Matrix3D rot1 = rotation( r, angle );
568 : :
569 : : // In-plane rotation for up vector
570 [ # # ][ # # ]: 0 : Vector3D pu = u - n * ( n % u );
[ # # ]
571 [ # # ]: 0 : Vector3D y( 0., 1., 0. );
572 [ # # ]: 0 : angle = z.interior_angle( pu, y, err );
573 [ # # ]: 0 : Matrix3D rot2 = rotation( z, angle );
574 : :
575 [ # # ][ # # ]: 0 : this->myTransform = rot1 * rot2;
576 : 0 : }
577 : :
578 : 0 : Matrix3D Projection::rotation( const Vector3D& axis, double angle )
579 : : {
580 : 0 : const double c = cos( angle );
581 : 0 : const double s = sin( angle );
582 [ # # ]: 0 : const double x = axis.x();
583 [ # # ]: 0 : const double y = axis.y();
584 [ # # ]: 0 : const double z = axis.z();
585 : :
586 : 0 : const double xform[9] = { c + x * x * ( 1 - c ), -z * s + x * y * ( 1 - c ), y * s + x * z * ( 1 - c ),
587 : 0 : z * s + x * y * ( 1 - c ), c + y * y * ( 1 - c ), -x * s + y * z * ( 1 - c ),
588 : 0 : -y * s + x * z * ( 1 - c ), x * s + y * z * ( 1 - c ), c + z * z * ( 1 - c ) };
589 [ # # ]: 0 : return Matrix3D( xform );
590 : : }
591 : :
592 : 0 : void Projection::project( const Vector3D& p, float& h, float& v )
593 : : {
594 [ # # ]: 0 : Vector3D pr = myTransform * p;
595 [ # # ]: 0 : h = (float)pr.x();
596 [ # # ]: 0 : v = (float)pr.y();
597 : 0 : }
598 : :
599 : 0 : Transform2D::Transform2D( PatchData* pd, Projection& projection, unsigned width, unsigned height, bool flip )
600 [ # # ]: 0 : : myProj( projection ), vertSign( flip ? -1 : 1 )
601 : : {
602 : : // Get the bounding box of the projected points
603 : : float w_max, w_min, h_max, h_min;
604 : 0 : w_max = h_max = -std::numeric_limits< float >::max();
605 : 0 : w_min = h_min = std::numeric_limits< float >::max();
606 [ # # ]: 0 : MsqError err;
607 [ # # ]: 0 : const MsqVertex* verts = pd->get_vertex_array( err );
608 [ # # ]: 0 : const size_t num_vert = pd->num_nodes();
609 [ # # ]: 0 : for( unsigned i = 0; i < num_vert; ++i )
610 : : {
611 : : float w, h;
612 [ # # ]: 0 : myProj.project( verts[i], w, h );
613 [ # # ]: 0 : if( w > w_max ) w_max = w;
614 [ # # ]: 0 : if( w < w_min ) w_min = w;
615 [ # # ]: 0 : if( h > h_max ) h_max = h;
616 [ # # ]: 0 : if( h < h_min ) h_min = h;
617 : : }
618 : :
619 : : // Determine the scale factor
620 : 0 : const float w_scale = (float)width / ( w_max - w_min );
621 : 0 : const float h_scale = (float)height / ( h_max - h_min );
622 [ # # ]: 0 : myScale = w_scale > h_scale ? h_scale : w_scale;
623 : :
624 : : // Determine offset
625 : 0 : horizOffset = -(int)( myScale * w_min );
626 [ # # ]: 0 : vertOffset = -(int)( myScale * ( flip ? -h_max : h_min ) );
627 : :
628 : : // Determine bounding box
629 : 0 : horizMax = (int)( w_max * myScale ) + horizOffset;
630 [ # # ]: 0 : vertMax = (int)( ( flip ? -h_min : h_max ) * myScale ) + vertOffset;
631 : 0 : }
632 : :
633 : 0 : Transform2D::Transform2D( const Vector3D* verts, size_t num_vert, Projection& projection, unsigned width,
634 : : unsigned height )
635 : 0 : : myProj( projection ), vertSign( 1 )
636 : : {
637 : : // Get the bounding box of the projected points
638 : : float w_max, w_min, h_max, h_min;
639 : 0 : w_max = h_max = -std::numeric_limits< float >::max();
640 : 0 : w_min = h_min = std::numeric_limits< float >::max();
641 [ # # ]: 0 : for( unsigned i = 0; i < num_vert; ++i )
642 : : {
643 : : float w, h;
644 [ # # ]: 0 : myProj.project( verts[i], w, h );
645 [ # # ]: 0 : if( w > w_max ) w_max = w;
646 [ # # ]: 0 : if( w < w_min ) w_min = w;
647 [ # # ]: 0 : if( h > h_max ) h_max = h;
648 [ # # ]: 0 : if( h < h_min ) h_min = h;
649 : : }
650 : :
651 : : // Determine the scale factor
652 : 0 : const float w_scale = (float)width / ( w_max - w_min );
653 : 0 : const float h_scale = (float)height / ( h_max - h_min );
654 [ # # ]: 0 : myScale = w_scale > h_scale ? h_scale : w_scale;
655 : :
656 : : // Determine offset
657 : 0 : horizOffset = -(int)( myScale * w_min );
658 : 0 : vertOffset = -(int)( myScale * h_min );
659 : :
660 : : // Determine bounding box
661 : 0 : horizMax = (int)( w_max * myScale ) + horizOffset;
662 : 0 : vertMax = (int)( h_max * myScale ) + vertOffset;
663 : 0 : }
664 : :
665 : 0 : void Transform2D::transform( const Vector3D& coords, int& horizontal, int& vertical ) const
666 : : {
667 : : float horiz, vert;
668 [ # # ]: 0 : myProj.project( coords, horiz, vert );
669 : 0 : horizontal = (int)( myScale * horiz ) + horizOffset;
670 : 0 : vertical = vertSign * (int)( myScale * vert ) + vertOffset;
671 : 0 : }
672 : :
673 : : /** Write quadratic edge shape in PostScript format.
674 : : *
675 : : * Given the three points composing a quadratic mesh edge,
676 : : * write the cubic Bezier curve of the same shape in
677 : : * PostScript format. The formulas for P1 and P2
678 : : * at the start of this function will result in the cubic
679 : : * terms of the Bezier curve dropping out, leaving the
680 : : * quadratic curve matching the edge shape function as
681 : : * described in Section 3.6 of Hughes. (If you're attempting
682 : : * to verify this, don't forget to adjust for the different
683 : : * parameter ranges: \f$ \xi = 2 t - 1 \f$).
684 : : */
685 : 0 : static void write_eps_quadratic_edge( ostream& s, Transform2D& xform, Vector3D start, Vector3D mid, Vector3D end )
686 : : {
687 [ # # ][ # # ]: 0 : Vector3D P1 = 1. / 3 * ( 4 * mid - end );
[ # # ]
688 [ # # ][ # # ]: 0 : Vector3D P2 = 1. / 3 * ( 4 * mid - start );
[ # # ]
689 : :
690 : : int x, y;
691 [ # # ]: 0 : xform.transform( start, x, y );
692 [ # # ][ # # ]: 0 : s << x << ' ' << y << " moveto" << endl;
[ # # ][ # # ]
[ # # ]
693 [ # # ]: 0 : xform.transform( P1, x, y );
694 [ # # ][ # # ]: 0 : s << x << ' ' << y << ' ';
[ # # ][ # # ]
695 [ # # ]: 0 : xform.transform( P2, x, y );
696 [ # # ][ # # ]: 0 : s << x << ' ' << y << ' ';
[ # # ][ # # ]
697 [ # # ]: 0 : xform.transform( end, x, y );
698 [ # # ][ # # ]: 0 : s << x << ' ' << y << " curveto" << endl;
[ # # ][ # # ]
[ # # ]
699 : 0 : }
700 : :
701 : 0 : void write_eps( Mesh* mesh, const char* filename, Projection proj, MsqError& err, int width, int height )
702 : : {
703 : : // Get a global patch
704 [ # # ]: 0 : PatchData pd;
705 [ # # ]: 0 : pd.set_mesh( mesh );
706 [ # # ][ # # ]: 0 : pd.fill_global_patch( err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
707 : :
708 [ # # ]: 0 : Transform2D transf( &pd, proj, width, height, false );
709 : :
710 : : // Open the file
711 [ # # ][ # # ]: 0 : ofstream s( filename );
[ # # ]
712 [ # # ][ # # ]: 0 : if( !s )
713 : : {
714 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::FILE_ACCESS );
715 : 0 : return;
716 : : }
717 : :
718 : : // Write header
719 [ # # ][ # # ]: 0 : s << "%!PS-Adobe-2.0 EPSF-2.0" << endl;
720 [ # # ][ # # ]: 0 : s << "%%Creator: Mesquite" << endl;
721 [ # # ][ # # ]: 0 : s << "%%Title: Mesquite " << endl;
722 [ # # ][ # # ]: 0 : s << "%%DocumentData: Clean7Bit" << endl;
723 [ # # ][ # # ]: 0 : s << "%%Origin: 0 0" << endl;
724 [ # # ][ # # ]: 0 : s << "%%BoundingBox: 0 0 " << transf.max_horizontal() << ' ' << transf.max_vertical() << endl;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
725 [ # # ][ # # ]: 0 : s << "%%Pages: 1" << endl;
726 : :
727 [ # # ][ # # ]: 0 : s << "%%BeginProlog" << endl;
728 [ # # ][ # # ]: 0 : s << "save" << endl;
729 [ # # ][ # # ]: 0 : s << "countdictstack" << endl;
730 [ # # ][ # # ]: 0 : s << "mark" << endl;
731 [ # # ][ # # ]: 0 : s << "newpath" << endl;
732 [ # # ][ # # ]: 0 : s << "/showpage {} def" << endl;
733 [ # # ][ # # ]: 0 : s << "/setpagedevice {pop} def" << endl;
734 [ # # ][ # # ]: 0 : s << "%%EndProlog" << endl;
735 : :
736 [ # # ][ # # ]: 0 : s << "%%Page: 1 1" << endl;
737 [ # # ][ # # ]: 0 : s << "1 setlinewidth" << endl;
738 [ # # ][ # # ]: 0 : s << "0.0 setgray" << endl;
739 : :
740 : : // Write mesh edges
741 [ # # ][ # # ]: 0 : EdgeIterator iter( &pd, err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
742 [ # # ][ # # ]: 0 : while( !iter.is_at_end() )
743 : : {
744 : : int s_w, s_h, e_w, e_h;
745 [ # # ][ # # ]: 0 : transf.transform( iter.start(), s_w, s_h );
746 [ # # ][ # # ]: 0 : transf.transform( iter.end(), e_w, e_h );
747 : :
748 [ # # ][ # # ]: 0 : s << "newpath" << endl;
749 [ # # ][ # # ]: 0 : s << s_w << ' ' << s_h << " moveto" << endl;
[ # # ][ # # ]
[ # # ]
750 : :
751 [ # # ][ # # ]: 0 : if( !iter.mid() ) { s << e_w << ' ' << e_h << " lineto" << endl; }
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
752 : : else
753 : : {
754 [ # # ][ # # ]: 0 : write_eps_quadratic_edge( s, transf, iter.start(), *iter.mid(), iter.end() );
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
755 : : // draw rings at mid-edge node location
756 : : // transf.transform( *(iter.mid()), w1, h1 );
757 : : // s << w1+2 << ' ' << h1 << " moveto" << endl;
758 : : // s << w1 << ' ' << h1 << " 2 0 360 arc" << endl;
759 : : }
760 [ # # ][ # # ]: 0 : s << "stroke" << endl;
761 : :
762 [ # # ][ # # ]: 0 : iter.step( err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
763 : : }
764 : :
765 : : // Write footer
766 [ # # ][ # # ]: 0 : s << "%%Trailer" << endl;
767 [ # # ][ # # ]: 0 : s << "cleartomark" << endl;
768 [ # # ][ # # ]: 0 : s << "countdictstack" << endl;
769 [ # # ][ # # ]: 0 : s << "exch sub { end } repeat" << endl;
770 [ # # ][ # # ]: 0 : s << "restore" << endl;
771 [ # # ][ # # ]: 0 : s << "%%EOF" << endl;
[ # # ]
772 : : }
773 : :
774 : : /** Quadratic triangle shape function for use in write_eps_triangle */
775 : 0 : static double tN0( double r, double s )
776 : : {
777 : 0 : double t = 1 - r - s;
778 : 0 : return t * ( 2 * t - 1 );
779 : : }
780 : 0 : static double tN1( double r, double )
781 : : {
782 : 0 : return r * ( 2 * r - 1 );
783 : : }
784 : 0 : static double tN2( double, double s )
785 : : {
786 : 0 : return s * ( 2 * s - 1 );
787 : : }
788 : 0 : static double tN3( double r, double s )
789 : : {
790 : 0 : double t = 1 - r - s;
791 : 0 : return 4 * r * t;
792 : : }
793 : 0 : static double tN4( double r, double s )
794 : : {
795 : 0 : return 4 * r * s;
796 : : }
797 : 0 : static double tN5( double r, double s )
798 : : {
799 : 0 : double t = 1 - r - s;
800 : 0 : return 4 * s * t;
801 : : }
802 : : /** Quadratic triangle shape function for use in write_eps_triangle */
803 : 0 : static Vector3D quad_tri_pt( double r, double s, const Vector3D* coords )
804 : : {
805 : 0 : Vector3D result = tN0( r, s ) * coords[0];
806 [ # # ]: 0 : result += tN1( r, s ) * coords[1];
807 [ # # ]: 0 : result += tN2( r, s ) * coords[2];
808 [ # # ]: 0 : result += tN3( r, s ) * coords[3];
809 [ # # ]: 0 : result += tN4( r, s ) * coords[4];
810 [ # # ]: 0 : result += tN5( r, s ) * coords[5];
811 : 0 : return result;
812 : : }
813 : :
814 : 0 : void write_eps_triangle( Mesh* mesh, Mesh::ElementHandle elem, const char* filename, bool draw_iso_lines,
815 : : bool draw_nodes, MsqError& err, int width, int height )
816 : : {
817 : : // Get triangle vertices
818 [ # # ][ # # ]: 0 : MsqVertex coords[6];
819 : : EntityTopology type;
820 [ # # ][ # # ]: 0 : mesh->elements_get_topologies( &elem, &type, 1, err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
821 [ # # ]: 0 : if( type != TRIANGLE )
822 : : {
823 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( "Invalid element type", MsqError::UNSUPPORTED_ELEMENT );
824 : 0 : return;
825 : : }
826 [ # # ]: 0 : std::vector< Mesh::VertexHandle > verts;
827 [ # # ][ # # ]: 0 : std::vector< size_t > junk;
828 [ # # ][ # # ]: 0 : mesh->elements_get_attached_vertices( &elem, 1, verts, junk, err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
829 [ # # ][ # # ]: 0 : if( verts.size() != 3 && verts.size() != 6 )
[ # # ]
830 : : {
831 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( "Invalid element type", MsqError::UNSUPPORTED_ELEMENT );
832 : 0 : return;
833 : : }
834 [ # # ][ # # ]: 0 : mesh->vertices_get_coordinates( arrptr( verts ), coords, verts.size(), err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
835 : :
836 [ # # ][ # # ]: 0 : Vector3D coords2[6];
837 [ # # ]: 0 : std::copy( coords, coords + verts.size(), coords2 );
838 : :
839 [ # # ][ # # ]: 0 : std::vector< bool > fixed( verts.size(), false );
840 [ # # ]: 0 : if( draw_nodes )
841 : : {
842 [ # # ][ # # ]: 0 : mesh->vertices_get_fixed_flag( arrptr( verts ), fixed, verts.size(), err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
843 : : }
844 [ # # ][ # # ]: 0 : write_eps_triangle( coords2, verts.size(), filename, draw_iso_lines, draw_nodes, err, fixed, width, height );
845 : : }
846 : :
847 : 0 : void write_eps_triangle( const Vector3D* coords, size_t num_vtx, const char* filename, bool draw_iso_lines,
848 : : bool draw_nodes, MsqError& err, const std::vector< bool >& fixed, int width, int height )
849 : : {
850 : 0 : const int PT_RAD = 3; // radius of circles for drawing nodes, in points
851 : 0 : const int PAD = PT_RAD + 2; // margin in points
852 : 0 : const double EDGE_GRAY = 0.0; // color for triangle edges, 0.0 => black
853 : 0 : const double ISO_GRAY = 0.7; // color for parameter iso-lines
854 : 0 : const double NODE_GRAY = 0.0; // color for node circle
855 : 0 : const double FIXED_GRAY = 1.0; // color to fill fixed nodes with, 1.0 => white
856 : 0 : const double FREE_GRAY = 0.0; // color to fill free nodes with
857 : :
858 [ # # ]: 0 : Projection proj( X, Y );
859 [ # # ]: 0 : Transform2D transf( coords, num_vtx, proj, width, height );
860 : :
861 : : // Open the file
862 [ # # ][ # # ]: 0 : ofstream str( filename );
[ # # ]
863 [ # # ][ # # ]: 0 : if( !str )
864 : : {
865 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::FILE_ACCESS );
866 : 0 : return;
867 : : }
868 : :
869 : : // Write header
870 [ # # ][ # # ]: 0 : str << "%!PS-Adobe-2.0 EPSF-2.0" << endl;
871 [ # # ][ # # ]: 0 : str << "%%Creator: Mesquite" << endl;
872 [ # # ][ # # ]: 0 : str << "%%Title: Mesquite " << endl;
873 [ # # ][ # # ]: 0 : str << "%%DocumentData: Clean7Bit" << endl;
874 [ # # ][ # # ]: 0 : str << "%%Origin: 0 0" << endl;
875 [ # # ][ # # ]: 0 : str << "%%BoundingBox: " << -PAD << ' ' << -PAD << ' ' << transf.max_horizontal() + PAD << ' '
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
876 [ # # ][ # # ]: 0 : << transf.max_vertical() + PAD << endl;
[ # # ]
877 [ # # ][ # # ]: 0 : str << "%%Pages: 1" << endl;
878 : :
879 [ # # ][ # # ]: 0 : str << "%%BeginProlog" << endl;
880 [ # # ][ # # ]: 0 : str << "save" << endl;
881 [ # # ][ # # ]: 0 : str << "countdictstack" << endl;
882 [ # # ][ # # ]: 0 : str << "mark" << endl;
883 [ # # ][ # # ]: 0 : str << "newpath" << endl;
884 [ # # ][ # # ]: 0 : str << "/showpage {} def" << endl;
885 [ # # ][ # # ]: 0 : str << "/setpagedevice {pop} def" << endl;
886 [ # # ][ # # ]: 0 : str << "%%EndProlog" << endl;
887 : :
888 [ # # ][ # # ]: 0 : str << "%%Page: 1 1" << endl;
889 [ # # ][ # # ]: 0 : str << "1 setlinewidth" << endl;
890 [ # # ][ # # ]: 0 : str << EDGE_GRAY << " setgray" << endl;
[ # # ]
891 : :
892 : 0 : const double h = 0.5, t = 1.0 / 3.0, w = 2.0 / 3.0, s = 1. / 6, f = 5. / 6;
893 : 0 : const int NUM_ISO = 15;
894 : : const double iso_params[NUM_ISO][2][2] = {
895 : : { { h, 0 }, { h, h } }, // r = 1/2
896 : : { { t, 0 }, { t, w } }, // r = 1/3
897 : : { { w, 0 }, { w, t } }, // r = 2/3
898 : : { { s, 0 }, { s, f } }, // r = 1/6
899 : : { { f, 0 }, { f, s } }, // r = 5/6
900 : : { { 0, h }, { h, h } }, // s = 1/2
901 : : { { 0, t }, { w, t } }, // s = 1/3
902 : : { { 0, w }, { t, w } }, // s = 2/3
903 : : { { 0, s }, { f, s } }, // s = 1/6
904 : : { { 0, f }, { s, f } }, // s = 5/6
905 : : { { 0, h }, { h, 0 } }, // t = 1 - r - s = 1/2
906 : : { { 0, w }, { w, 0 } }, // t = 1 - r - s = 1/3
907 : : { { 0, t }, { t, 0 } }, // t = 1 - r - s = 2/3
908 : : { { 0, f }, { f, 0 } }, // t = 1 - r - s = 1/6
909 : : { { 0, s }, { s, 0 } } // t = 1 - r - s = 5/6
910 : 0 : };
911 : :
912 [ # # ]: 0 : if( num_vtx == 3 )
913 : : {
914 : : int x[3], y[3];
915 [ # # ]: 0 : for( size_t i = 0; i < 3; ++i )
916 [ # # ]: 0 : transf.transform( coords[i], x[i], y[i] );
917 : :
918 [ # # ][ # # ]: 0 : str << "newpath" << endl;
919 [ # # ][ # # ]: 0 : str << x[0] << ' ' << y[0] << " moveto" << endl;
[ # # ][ # # ]
[ # # ]
920 [ # # ][ # # ]: 0 : str << x[1] << ' ' << y[1] << " lineto" << endl;
[ # # ][ # # ]
[ # # ]
921 [ # # ][ # # ]: 0 : str << x[2] << ' ' << y[2] << " lineto" << endl;
[ # # ][ # # ]
[ # # ]
922 [ # # ][ # # ]: 0 : str << x[0] << ' ' << y[0] << " lineto" << endl;
[ # # ][ # # ]
[ # # ]
923 [ # # ][ # # ]: 0 : str << "stroke" << endl;
924 : :
925 [ # # ]: 0 : if( draw_iso_lines )
926 : : {
927 [ # # ][ # # ]: 0 : str << ISO_GRAY << " setgray" << endl;
[ # # ]
928 [ # # ][ # # ]: 0 : str << "newpath" << endl;
929 [ # # ]: 0 : for( int i = 0; i < NUM_ISO; ++i )
930 : : {
931 : 0 : double R[2] = { iso_params[i][0][0], iso_params[i][1][0] };
932 : 0 : double S[2] = { iso_params[i][0][1], iso_params[i][1][1] };
933 : 0 : double T[2] = { 1 - R[0] - S[0], 1 - R[1] - S[1] };
934 [ # # ][ # # ]: 0 : Vector3D p[2] = { T[0] * coords[0] + R[0] * coords[1] + S[0] * coords[2],
[ # # ][ # # ]
935 [ # # ][ # # ]: 0 : T[1] * coords[0] + R[1] * coords[1] + S[1] * coords[2] };
[ # # ][ # # ]
[ # # ][ # # ]
936 [ # # ]: 0 : transf.transform( p[0], x[0], y[0] );
937 [ # # ]: 0 : transf.transform( p[1], x[1], y[1] );
938 [ # # ][ # # ]: 0 : str << x[0] << ' ' << y[0] << " moveto" << endl;
[ # # ][ # # ]
[ # # ]
939 [ # # ][ # # ]: 0 : str << x[1] << ' ' << y[1] << " lineto" << endl;
[ # # ][ # # ]
[ # # ]
940 : : }
941 [ # # ][ # # ]: 0 : str << " stroke" << endl;
942 : : }
943 : : }
944 [ # # ]: 0 : else if( num_vtx == 6 )
945 : : {
946 [ # # ][ # # ]: 0 : str << "newpath" << endl;
947 [ # # ][ # # ]: 0 : write_eps_quadratic_edge( str, transf, coords[0], coords[3], coords[1] );
[ # # ][ # # ]
948 [ # # ][ # # ]: 0 : write_eps_quadratic_edge( str, transf, coords[1], coords[4], coords[2] );
[ # # ][ # # ]
949 [ # # ][ # # ]: 0 : write_eps_quadratic_edge( str, transf, coords[2], coords[5], coords[0] );
[ # # ][ # # ]
950 [ # # ][ # # ]: 0 : str << "stroke" << endl;
951 : :
952 [ # # ]: 0 : if( draw_iso_lines )
953 : : {
954 [ # # ][ # # ]: 0 : str << ISO_GRAY << " setgray" << endl;
[ # # ]
955 [ # # ][ # # ]: 0 : str << "newpath" << endl;
956 [ # # ]: 0 : for( int i = 0; i < NUM_ISO; ++i )
957 : : {
958 : 0 : double R[3] = { iso_params[i][0][0], 0, iso_params[i][1][0] };
959 : 0 : double S[3] = { iso_params[i][0][1], 0, iso_params[i][1][1] };
960 : 0 : R[1] = 0.5 * ( R[0] + R[2] );
961 : 0 : S[1] = 0.5 * ( S[0] + S[2] );
962 : : Vector3D p[3] = { quad_tri_pt( R[0], S[0], coords ), quad_tri_pt( R[1], S[1], coords ),
963 [ # # ][ # # ]: 0 : quad_tri_pt( R[2], S[2], coords ) };
[ # # ]
964 [ # # ][ # # ]: 0 : write_eps_quadratic_edge( str, transf, p[0], p[1], p[2] );
[ # # ][ # # ]
965 : : }
966 [ # # ][ # # ]: 0 : str << " stroke" << endl;
967 : : }
968 : : }
969 : :
970 [ # # ]: 0 : if( draw_nodes )
971 : : {
972 [ # # ]: 0 : for( size_t i = 0; i < num_vtx; ++i )
973 : : {
974 : : int iw, ih;
975 : : // fill interior with either white or black depending
976 : : // on whether or not the vertex is fixed.
977 [ # # ][ # # ]: 0 : if( fixed[i] )
978 [ # # ][ # # ]: 0 : str << FIXED_GRAY << " setgray" << endl;
[ # # ]
979 : : else
980 [ # # ][ # # ]: 0 : str << FREE_GRAY << " setgray" << endl;
[ # # ]
981 [ # # ]: 0 : transf.transform( coords[i], iw, ih );
982 [ # # ][ # # ]: 0 : str << w + PT_RAD << ' ' << ih << " moveto" << endl;
[ # # ][ # # ]
[ # # ]
983 [ # # ][ # # ]: 0 : str << iw << ' ' << ih << ' ' << PT_RAD << " 0 360 arc" << endl;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
984 [ # # ][ # # ]: 0 : str << "closepath fill" << endl;
985 [ # # ][ # # ]: 0 : str << NODE_GRAY << " setgray" << endl;
[ # # ]
986 [ # # ][ # # ]: 0 : str << "newpath" << endl;
987 [ # # ][ # # ]: 0 : str << iw << ' ' << ih << ' ' << PT_RAD << " 0 360 arc" << endl;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
988 [ # # ][ # # ]: 0 : str << "stroke" << endl;
989 : : }
990 : : }
991 : :
992 : : // Write footer
993 [ # # ][ # # ]: 0 : str << "%%Trailer" << endl;
994 [ # # ][ # # ]: 0 : str << "cleartomark" << endl;
995 [ # # ][ # # ]: 0 : str << "countdictstack" << endl;
996 [ # # ][ # # ]: 0 : str << "exch sub { end } repeat" << endl;
997 [ # # ][ # # ]: 0 : str << "restore" << endl;
998 [ # # ][ # # ]: 0 : str << "%%EOF" << endl;
[ # # ]
999 : : }
1000 : :
1001 : 0 : void write_svg( Mesh* mesh, const char* filename, Projection proj, MsqError& err )
1002 : : {
1003 : : // Get a global patch
1004 [ # # ]: 0 : PatchData pd;
1005 [ # # ]: 0 : pd.set_mesh( mesh );
1006 [ # # ][ # # ]: 0 : pd.fill_global_patch( err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
1007 : :
1008 [ # # ]: 0 : Transform2D transf( &pd, proj, 400, 400, true );
1009 : :
1010 : : // Open the file
1011 [ # # ][ # # ]: 0 : ofstream file( filename );
[ # # ]
1012 [ # # ][ # # ]: 0 : if( !file )
1013 : : {
1014 [ # # ][ # # ]: 0 : MSQ_SETERR( err )( MsqError::FILE_ACCESS );
1015 : 0 : return;
1016 : : }
1017 : :
1018 : : // Write header
1019 [ # # ][ # # ]: 0 : file << "<?xml version=\"1.0\" standalone=\"no\"?>" << endl;
1020 [ # # ]: 0 : file << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
1021 [ # # ][ # # ]: 0 : << "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" << endl;
1022 [ # # ]: 0 : file << endl;
1023 [ # # ][ # # ]: 0 : file << "<svg width=\"" << transf.max_horizontal() << "\" height=\"" << transf.max_vertical()
[ # # ][ # # ]
[ # # ][ # # ]
1024 [ # # ][ # # ]: 0 : << "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">" << endl;
1025 : :
1026 : : // Write mesh edges
1027 [ # # ][ # # ]: 0 : EdgeIterator iter( &pd, err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
1028 [ # # ][ # # ]: 0 : while( !iter.is_at_end() )
1029 : : {
1030 : : int s_w, s_h, e_w, e_h;
1031 [ # # ][ # # ]: 0 : transf.transform( iter.start(), s_w, s_h );
1032 [ # # ][ # # ]: 0 : transf.transform( iter.end(), e_w, e_h );
1033 : :
1034 [ # # ]: 0 : file << "<line "
1035 [ # # ][ # # ]: 0 : << "x1=\"" << s_w << "\" "
[ # # ]
1036 [ # # ][ # # ]: 0 : << "y1=\"" << s_h << "\" "
[ # # ]
1037 [ # # ][ # # ]: 0 : << "x2=\"" << e_w << "\" "
[ # # ]
1038 [ # # ][ # # ]: 0 : << "y2=\"" << e_h << "\" "
[ # # ]
1039 [ # # ]: 0 : << " style=\"stroke:rgb(99,99,99);stroke-width:2\""
1040 [ # # ][ # # ]: 0 : << "/>" << endl;
1041 : :
1042 [ # # ][ # # ]: 0 : iter.step( err );MSQ_ERRRTN( err );
[ # # ][ # # ]
[ # # ][ # # ]
1043 : : }
1044 : :
1045 : : // Write footer
1046 [ # # ][ # # ]: 0 : file << "</svg>" << endl;
[ # # ]
1047 : : }
1048 : :
1049 : : } // namespace MeshWriter
1050 : :
1051 [ + - ][ + - ]: 120 : } // namespace MBMesquite
1052 : :
1053 : : #endif
|