MOAB: Mesh Oriented datABase
(version 5.4.1)
|
00001 /* ***************************************************************** 00002 MESQUITE -- The Mesh Quality Improvement Toolkit 00003 00004 Copyright 2005 Lawrence Livermore National Laboratory. Under 00005 the terms of Contract B545069 with the University of Wisconsin -- 00006 Madison, Lawrence Livermore National Laboratory retains certain 00007 rights in this software. 00008 00009 This library is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU Lesser General Public 00011 License as published by the Free Software Foundation; either 00012 version 2.1 of the License, or (at your option) any later version. 00013 00014 This library is distributed in the hope that it will be useful, 00015 but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 Lesser General Public License for more details. 00018 00019 You should have received a copy of the GNU Lesser General Public License 00020 (lgpl.txt) along with this library; if not, write to the Free Software 00021 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00022 00023 [email protected] 00024 00025 ***************************************************************** */ 00026 00027 #ifndef MSQ_MESH_WRITER_CPP 00028 #define MSQ_MESH_WRITER_CPP 00029 00030 #include "MeshWriter.hpp" 00031 #include "Mesquite.hpp" 00032 #include "MeshImpl.hpp" 00033 #include "MsqError.hpp" 00034 #include "PatchData.hpp" 00035 #include "PlanarDomain.hpp" 00036 #include "VtkTypeInfo.hpp" 00037 #include "EdgeIterator.hpp" 00038 00039 #include <memory> 00040 #include <limits> 00041 #include <vector> 00042 #include <algorithm> 00043 #include <fstream> 00044 #include <string> 00045 #include <iomanip> 00046 using namespace std; 00047 00048 #include <cstdio> 00049 00050 namespace MBMesquite 00051 { 00052 00053 namespace MeshWriter 00054 { 00055 00056 /**\brief Transform from coordinates in the XY-plane 00057 * to graphics coordinates. 00058 */ 00059 class Transform2D 00060 { 00061 public: 00062 Transform2D( PatchData* pd, Projection& proj, unsigned width, unsigned height, bool flip_about_horizontal ); 00063 00064 Transform2D( const Vector3D* verts, size_t num_vert, Projection& projection, unsigned width, unsigned height ); 00065 00066 void transform( const Vector3D& coords, int& horizontal, int& vertical ) const; 00067 00068 int max_horizontal() const 00069 { 00070 return horizMax; 00071 } 00072 int max_vertical() const 00073 { 00074 return vertMax; 00075 } 00076 00077 private: 00078 Projection& myProj; 00079 float myScale; 00080 int horizOffset, vertOffset; 00081 int horizMax, vertMax; 00082 int vertSign; 00083 }; 00084 00085 /* Write VTK file 00086 * 00087 * Copied from src/Mesh/MeshSet.cpp and adapted for removal of 00088 * MeshSet class by J.Kraftcheck on 2005-7-28. 00089 * 00090 * This code is provided mainly for debugging. A more efficient 00091 * and complete writer implementation is provided in the MeshImpl 00092 * class for saving meshes that were read from a file initially. 00093 */ 00094 void write_vtk( Mesh* mesh, const char* out_filename, MsqError& err ) 00095 { 00096 if( MeshImpl* msq_mesh = dynamic_cast< MeshImpl* >( mesh ) ) 00097 { 00098 msq_mesh->write_vtk( out_filename, err );MSQ_CHKERR( err ); 00099 return; 00100 } 00101 00102 // loads a global patch 00103 PatchData pd; 00104 pd.set_mesh( mesh ); 00105 pd.fill_global_patch( err );MSQ_ERRRTN( err ); 00106 00107 // write mesh 00108 write_vtk( pd, out_filename, err );MSQ_CHKERR( err ); 00109 } 00110 00111 void write_vtk( PatchData& pd, const char* out_filename, MsqError& err, const Vector3D* OF_gradient ) 00112 { 00113 // Open the file 00114 std::ofstream file( out_filename ); 00115 if( !file ) 00116 { 00117 MSQ_SETERR( err )( MsqError::FILE_ACCESS ); 00118 return; 00119 } 00120 00121 // Write a header 00122 file << "# vtk DataFile Version 2.0\n"; 00123 file << "Mesquite Mesh " << out_filename << " .\n"; 00124 file << "ASCII\n"; 00125 file << "DATASET UNSTRUCTURED_GRID\n"; 00126 00127 // Write vertex coordinates 00128 file << "POINTS " << pd.num_nodes() << " float\n"; 00129 size_t i; 00130 for( i = 0; i < pd.num_nodes(); i++ ) 00131 { 00132 file << pd.vertex_by_index( i )[0] << ' ' << pd.vertex_by_index( i )[1] << ' ' << pd.vertex_by_index( i )[2] 00133 << '\n'; 00134 } 00135 00136 // Write out the connectivity table 00137 size_t connectivity_size = 0; 00138 for( i = 0; i < pd.num_elements(); ++i ) 00139 connectivity_size += pd.element_by_index( i ).node_count() + 1; 00140 00141 file << "CELLS " << pd.num_elements() << ' ' << connectivity_size << '\n'; 00142 for( i = 0; i < pd.num_elements(); i++ ) 00143 { 00144 std::vector< size_t > vtx_indices; 00145 pd.element_by_index( i ).get_node_indices( vtx_indices ); 00146 00147 // Convert native to VTK node order, if not the same 00148 const VtkTypeInfo* info = 00149 VtkTypeInfo::find_type( pd.element_by_index( i ).get_element_type(), vtx_indices.size(), err );MSQ_ERRRTN( err ); 00150 info->mesquiteToVtkOrder( vtx_indices ); 00151 00152 file << vtx_indices.size(); 00153 for( std::size_t j = 0; j < vtx_indices.size(); ++j ) 00154 { 00155 file << ' ' << vtx_indices[j]; 00156 } 00157 file << '\n'; 00158 } 00159 00160 // Write out the element types 00161 file << "CELL_TYPES " << pd.num_elements() << '\n'; 00162 for( i = 0; i < pd.num_elements(); i++ ) 00163 { 00164 const VtkTypeInfo* info = VtkTypeInfo::find_type( pd.element_by_index( i ).get_element_type(), 00165 pd.element_by_index( i ).node_count(), err );MSQ_ERRRTN( err ); 00166 file << info->vtkType << '\n'; 00167 } 00168 00169 // Write out which points are fixed. 00170 file << "POINT_DATA " << pd.num_nodes() << "\nSCALARS fixed int\nLOOKUP_TABLE default\n"; 00171 for( i = 0; i < pd.num_nodes(); ++i ) 00172 { 00173 if( pd.vertex_by_index( i ).get_flags() & MsqVertex::MSQ_CULLED ) 00174 file << "1\n"; 00175 else 00176 file << "0\n"; 00177 } 00178 file << "SCALARS culled short\nLOOKUP_TABLE default\n"; 00179 for( i = 0; i < pd.num_nodes(); ++i ) 00180 { 00181 if( pd.vertex_by_index( i ).is_free_vertex() ) 00182 file << "0\n"; 00183 else 00184 file << "1\n"; 00185 } 00186 00187 if( OF_gradient ) 00188 { 00189 file << "VECTORS gradient double\n"; 00190 for( i = 0; i < pd.num_free_vertices(); ++i ) 00191 file << OF_gradient[i].x() << " " << OF_gradient[i].y() << " " << OF_gradient[i].z() << "\n"; 00192 for( i = pd.num_free_vertices(); i < pd.num_nodes(); ++i ) 00193 file << "0.0 0.0 0.0\n"; 00194 } 00195 00196 // Close the file 00197 file.close(); 00198 } 00199 00200 void write_gnuplot( Mesh* mesh, const char* out_filebase, MsqError& err ) 00201 { 00202 // loads a global patch 00203 PatchData pd; 00204 pd.set_mesh( mesh ); 00205 pd.fill_global_patch( err );MSQ_ERRRTN( err ); 00206 write_gnuplot( pd, out_filebase, err ); 00207 } 00208 00209 void write_gnuplot( Mesh* mesh, std::vector< Mesh::ElementHandle >& elems, const char* out_filebase, MsqError& err ) 00210 { 00211 // loads a global patch 00212 PatchData pd; 00213 pd.set_mesh( mesh ); 00214 std::vector< Mesh::VertexHandle > verts; 00215 pd.set_mesh_entities( elems, verts, err );MSQ_ERRRTN( err ); 00216 write_gnuplot( pd, out_filebase, err ); 00217 } 00218 00219 /* Writes a gnuplot file directly from the MeshSet. 00220 * This means that any mesh imported successfully into Mesquite 00221 * can be outputed in gnuplot format. 00222 * 00223 * Within gnuplot, use \b plot 'file1.gpt' w l, 'file2.gpt' w l 00224 * 00225 * This is not geared for performance, since it has to load a global Patch from 00226 * the mesh to write a mesh file. 00227 * 00228 * Copied from src/Mesh/MeshSet.cpp and adapted for removal of 00229 * MeshSet class by J.Kraftcheck on 2005-7-28. 00230 * 00231 * Re-written to use EdgeIterator by J.Kraftcheck on 2009-6-11 00232 */ 00233 void write_gnuplot( PatchData& pd, const char* out_filebase, MsqError& err ) 00234 { 00235 // Open the file 00236 std::string out_filename = out_filebase; 00237 out_filename += ".gpt"; 00238 std::ofstream file( out_filename.c_str() ); 00239 if( !file ) 00240 { 00241 MSQ_SETERR( err )( MsqError::FILE_ACCESS ); 00242 return; 00243 } 00244 00245 EdgeIterator edges( &pd, err );MSQ_ERRRTN( err ); 00246 00247 // Write a header 00248 file << "\n"; 00249 00250 while( !edges.is_at_end() ) 00251 { 00252 const Vector3D& s = edges.start(); 00253 const Vector3D& e = edges.end(); 00254 const Vector3D* m = edges.mid(); 00255 00256 file << s[0] << ' ' << s[1] << ' ' << s[2] << std::endl; 00257 if( m ) file << ( *m )[0] << ' ' << ( *m )[1] << ' ' << ( *m )[2] << std::endl; 00258 file << e[0] << ' ' << e[1] << ' ' << e[2] << std::endl; 00259 file << std::endl << std::endl; 00260 00261 edges.step( err );MSQ_ERRRTN( err ); 00262 } 00263 00264 // Close the file 00265 file.close(); 00266 } 00267 00268 /** Helper function for write_gnuplot_animator and write_gnuplot_overlay 00269 * 00270 * Read a set of input files to determine the bounding box 00271 * of the combined data. 00272 */ 00273 static void find_gnuplot_agregate_range( int count, 00274 const char* basename, 00275 Vector3D& min, 00276 Vector3D& max, 00277 MsqError& err ) 00278 { 00279 // read all input files to determine extents 00280 min = Vector3D( HUGE_VAL, HUGE_VAL, HUGE_VAL ); 00281 max = Vector3D( -HUGE_VAL, -HUGE_VAL, -HUGE_VAL ); 00282 for( int i = 0; i <= count; ++i ) 00283 { 00284 stringstream s; 00285 s << basename << '.' << i << ".gpt"; 00286 ifstream infile( s.str().c_str() ); 00287 if( !infile ) 00288 { 00289 MSQ_SETERR( err )( s.str(), MsqError::FILE_ACCESS ); 00290 return; 00291 } 00292 double c[3]; 00293 while( infile >> c[0] >> c[1] >> c[2] ) 00294 { 00295 for( int j = 0; j < 3; ++j ) 00296 { 00297 if( c[j] < min[j] ) min[j] = c[j]; 00298 if( c[j] > max[j] ) max[j] = c[j]; 00299 } 00300 } 00301 } 00302 } 00303 00304 /** Write a GNU Plot script to produce an animation from a 00305 * sequence of data files 00306 */ 00307 void write_gnuplot_animator( int count, const char* basename, MsqError& err ) 00308 { 00309 if( count <= 0 ) return; 00310 00311 const int DELAY = 10; 00312 const int WIDTH = 640; 00313 const int HEIGHT = 480; 00314 00315 // read all input files to determine extents 00316 Vector3D min, max; 00317 find_gnuplot_agregate_range( count, basename, min, max, err );MSQ_ERRRTN( err ); 00318 00319 // chose coordinate plane to plot in 00320 Vector3D range = max - min; 00321 int haxis = 0, vaxis = 1; 00322 if( range[0] < range[1] && range[1] < range[2] ) 00323 { 00324 haxis = 1; 00325 vaxis = 2; 00326 } 00327 else if( range[1] < range[2] ) 00328 { 00329 vaxis = 2; 00330 } 00331 00332 // open output file 00333 string base( basename ); 00334 ofstream file( ( string( basename ) + ".gnuplot" ).c_str() ); 00335 if( !file ) 00336 { 00337 MSQ_SETERR( err )( MsqError::FILE_ACCESS ); 00338 return; 00339 } 00340 00341 // write header 00342 file << "#!gnuplot" << endl; 00343 file << "#" << endl; 00344 file << "# Mesquite Animation of " << basename << ".0 to " << basename << '.' << count << endl; 00345 file << "#" << endl; 00346 00347 // write plot settings 00348 file << "set term gif animate transparent opt delay " << DELAY << " size " << WIDTH << "," << HEIGHT << endl; 00349 file << "set xrange [" << min[haxis] - 0.05 * range[haxis] << ":" << max[haxis] + 0.05 * range[haxis] << "]" 00350 << endl; 00351 file << "set yrange [" << min[vaxis] - 0.05 * range[vaxis] << ":" << max[vaxis] + 0.05 * range[vaxis] << "]" 00352 << endl; 00353 file << "set title '" << basename << "'" << endl; 00354 file << "set output '" << basename << ".gif'" << endl; 00355 00356 // plot data 00357 for( int i = 0; i <= count; ++i ) 00358 file << "plot '" << basename << '.' << i << ".gpt'" 00359 << " using " << haxis + 1 << ":" << vaxis + 1 << " w l" << endl; 00360 } 00361 00362 static unsigned red( int i, int c ) 00363 { 00364 if( i * 4 <= c ) 00365 return 255; 00366 else if( i * 4 >= 3 * c ) 00367 return 0; 00368 else 00369 return 384 - i * 511 / c; 00370 } 00371 00372 static unsigned green( int i, int c ) 00373 { 00374 if( i * 4 < c ) 00375 return i * 510 / c; 00376 else if( i * 4 > 3 * c ) 00377 return 1023 - i * 1023 / c; 00378 else 00379 return 255; 00380 } 00381 00382 static unsigned blue( int i, int c ) 00383 { 00384 if( i * 4 <= c ) 00385 return 0; 00386 else if( i * 4 >= 3 * c ) 00387 return 255; 00388 else 00389 return i * 511 / c - 127; 00390 } 00391 00392 /** Write a GNU Plot script to produce a single plot from a 00393 * sequence of data files 00394 */ 00395 void write_gnuplot_overlay( int count, const char* basename, MsqError& err ) 00396 { 00397 if( count <= 0 ) return; 00398 00399 const int WIDTH = 640; 00400 const int HEIGHT = 480; 00401 00402 // read all input files to determine extents 00403 Vector3D min, max; 00404 find_gnuplot_agregate_range( count, basename, min, max, err );MSQ_ERRRTN( err ); 00405 00406 // chose coordinate plane to plot in 00407 Vector3D range = max - min; 00408 int haxis = 0, vaxis = 1; 00409 if( range[0] < range[1] && range[1] < range[2] ) 00410 { 00411 haxis = 1; 00412 vaxis = 2; 00413 } 00414 else if( range[1] < range[2] ) 00415 { 00416 vaxis = 2; 00417 } 00418 00419 // open output file 00420 string base( basename ); 00421 FILE* file = fopen( ( string( basename ) + ".gnuplot" ).c_str(), "w" ); 00422 if( !file ) 00423 { 00424 MSQ_SETERR( err )( MsqError::FILE_ACCESS ); 00425 return; 00426 } 00427 00428 // write header 00429 fprintf( file, "#!gnuplot\n" ); 00430 fprintf( file, "#\n" ); 00431 fprintf( file, "# Mesquite Overlay of %s.0 to %s.%d\n", basename, basename, count ); 00432 fprintf( file, "#\n" ); 00433 00434 // write plot settings 00435 fprintf( file, "set term gif size %d,%d\n", WIDTH, HEIGHT ); 00436 fprintf( file, "set xrange [%f:%f]\n", min[haxis] - 0.05 * range[haxis], max[haxis] + 0.05 * range[haxis] ); 00437 fprintf( file, "set yrange [%f:%f]\n", min[vaxis] - 0.05 * range[vaxis], max[vaxis] + 0.05 * range[vaxis] ); 00438 fprintf( file, "set title '%s'\n", basename ); 00439 fprintf( file, "set output '%s.gif'\n", basename ); 00440 00441 // plot data 00442 fprintf( file, "plot '%s.0.gpt' using %d:%d w l lc rgb '#%02x%02x%02x' title 't0'", basename, haxis + 1, 00443 vaxis + 1, red( 0, count ), green( 0, count ), blue( 0, count ) ); 00444 for( int i = 1; i <= count; ++i ) 00445 { 00446 fprintf( file, ", \\\n '%s.%d.gpt' using %d:%d w l lc rgb '#%02x%02x%02x' title 't%d'", basename, i, 00447 haxis + 1, vaxis + 1, red( i, count ), green( i, count ), blue( i, count ), i ); 00448 } 00449 fprintf( file, "\n" ); 00450 00451 fclose( file ); 00452 } 00453 00454 void write_stl( Mesh* mesh, const char* filename, MsqError& err ) 00455 { 00456 // Open the file 00457 ofstream file( filename ); 00458 if( !file ) 00459 { 00460 MSQ_SETERR( err )( MsqError::FILE_ACCESS ); 00461 return; 00462 } 00463 00464 // Write header 00465 char header[70]; 00466 sprintf( header, "Mesquite%d", rand() ); 00467 file << "solid " << header << endl; 00468 00469 MsqVertex coords[3]; 00470 std::vector< Mesh::VertexHandle > verts( 3 ); 00471 std::vector< size_t > offsets( 2 ); 00472 00473 // Iterate over all elements 00474 size_t count = 0; 00475 std::vector< Mesh::ElementHandle > elems; 00476 std::vector< Mesh::ElementHandle >::iterator iter; 00477 mesh->get_all_elements( elems, err );MSQ_ERRRTN( err ); 00478 for( iter = elems.begin(); iter != elems.end(); ++iter ) 00479 { 00480 // Skip non-triangles 00481 Mesh::ElementHandle elem = *iter; 00482 EntityTopology type; 00483 mesh->elements_get_topologies( &elem, &type, 1, err );MSQ_ERRRTN( err ); 00484 if( type != TRIANGLE ) continue; 00485 ++count; 00486 00487 // Get vertex coordinates 00488 mesh->elements_get_attached_vertices( &elem, 1, verts, offsets, err );MSQ_ERRRTN( err ); 00489 mesh->vertices_get_coordinates( arrptr( verts ), coords, 3, err );MSQ_ERRRTN( err ); 00490 00491 // Get triagnle normal 00492 Vector3D n = ( coords[0] - coords[1] ) * ( coords[0] - coords[2] ); 00493 n.normalize(); 00494 00495 // Write triangle 00496 file << "facet normal " << n.x() << " " << n.y() << " " << n.z() << endl; 00497 file << "outer loop" << endl; 00498 for( unsigned i = 0; i < 3; ++i ) 00499 file << "vertex " << coords[i].x() << " " << coords[i].y() << " " << coords[i].z() << endl; 00500 file << "endloop" << endl; 00501 file << "endfacet" << endl; 00502 } 00503 00504 file << "endsolid " << header << endl; 00505 00506 file.close(); 00507 if( count == 0 ) 00508 { 00509 std::remove( filename ); 00510 MSQ_SETERR( err )( "Mesh contains no triangles", MsqError::INVALID_STATE ); 00511 } 00512 } 00513 00514 Projection::Projection( PlanarDomain* domain ) 00515 { 00516 Vector3D normal; 00517 domain->vertex_normal_at( 0, normal ); 00518 init( normal ); 00519 } 00520 00521 Projection::Projection( const Vector3D& n ) 00522 { 00523 init( n ); 00524 } 00525 00526 Projection::Projection( const Vector3D& n, const Vector3D& up ) 00527 { 00528 init( n, up ); 00529 } 00530 00531 Projection::Projection( Axis h, Axis v ) 00532 { 00533 Vector3D horiz( 0, 0, 0 ), vert( 0, 0, 0 ); 00534 horiz[h] = 1.0; 00535 vert[v] = 1.0; 00536 init( horiz * vert, vert ); 00537 } 00538 00539 void Projection::init( const Vector3D& n ) 00540 { 00541 // Choose an "up" direction 00542 00543 Axis max = X; 00544 for( Axis i = Y; i <= Z; i = (Axis)( i + 1 ) ) 00545 if( fabs( n[i] ) > fabs( n[max] ) ) max = i; 00546 00547 Axis up; 00548 if( max == Y ) 00549 up = Z; 00550 else 00551 up = Y; 00552 00553 // Initialize rotation matrix 00554 00555 Vector3D up_vect( 0, 0, 0 ); 00556 up_vect[up] = 1.0; 00557 init( n, up_vect ); 00558 } 00559 00560 void Projection::init( const Vector3D& n1, const Vector3D& up1 ) 00561 { 00562 MsqError err; 00563 const Vector3D n = n1 / n1.length(); 00564 const Vector3D u = up1 / up1.length(); 00565 00566 // Rotate for projection 00567 const Vector3D z( 0., 0., 1. ); 00568 Vector3D r = n * z; 00569 double angle = r.interior_angle( n, z, err ); 00570 Matrix3D rot1 = rotation( r, angle ); 00571 00572 // In-plane rotation for up vector 00573 Vector3D pu = u - n * ( n % u ); 00574 Vector3D y( 0., 1., 0. ); 00575 angle = z.interior_angle( pu, y, err ); 00576 Matrix3D rot2 = rotation( z, angle ); 00577 00578 this->myTransform = rot1 * rot2; 00579 } 00580 00581 Matrix3D Projection::rotation( const Vector3D& axis, double angle ) 00582 { 00583 const double c = cos( angle ); 00584 const double s = sin( angle ); 00585 const double x = axis.x(); 00586 const double y = axis.y(); 00587 const double z = axis.z(); 00588 00589 const double xform[9] = { c + x * x * ( 1 - c ), -z * s + x * y * ( 1 - c ), y * s + x * z * ( 1 - c ), 00590 z * s + x * y * ( 1 - c ), c + y * y * ( 1 - c ), -x * s + y * z * ( 1 - c ), 00591 -y * s + x * z * ( 1 - c ), x * s + y * z * ( 1 - c ), c + z * z * ( 1 - c ) }; 00592 return Matrix3D( xform ); 00593 } 00594 00595 void Projection::project( const Vector3D& p, float& h, float& v ) 00596 { 00597 Vector3D pr = myTransform * p; 00598 h = (float)pr.x(); 00599 v = (float)pr.y(); 00600 } 00601 00602 Transform2D::Transform2D( PatchData* pd, Projection& projection, unsigned width, unsigned height, bool flip ) 00603 : myProj( projection ), vertSign( flip ? -1 : 1 ) 00604 { 00605 // Get the bounding box of the projected points 00606 float w_max, w_min, h_max, h_min; 00607 w_max = h_max = -std::numeric_limits< float >::max(); 00608 w_min = h_min = std::numeric_limits< float >::max(); 00609 MsqError err; 00610 const MsqVertex* verts = pd->get_vertex_array( err ); 00611 const size_t num_vert = pd->num_nodes(); 00612 for( unsigned i = 0; i < num_vert; ++i ) 00613 { 00614 float w, h; 00615 myProj.project( verts[i], w, h ); 00616 if( w > w_max ) w_max = w; 00617 if( w < w_min ) w_min = w; 00618 if( h > h_max ) h_max = h; 00619 if( h < h_min ) h_min = h; 00620 } 00621 00622 // Determine the scale factor 00623 const float w_scale = (float)width / ( w_max - w_min ); 00624 const float h_scale = (float)height / ( h_max - h_min ); 00625 myScale = w_scale > h_scale ? h_scale : w_scale; 00626 00627 // Determine offset 00628 horizOffset = -(int)( myScale * w_min ); 00629 vertOffset = -(int)( myScale * ( flip ? -h_max : h_min ) ); 00630 00631 // Determine bounding box 00632 horizMax = (int)( w_max * myScale ) + horizOffset; 00633 vertMax = (int)( ( flip ? -h_min : h_max ) * myScale ) + vertOffset; 00634 } 00635 00636 Transform2D::Transform2D( const Vector3D* verts, 00637 size_t num_vert, 00638 Projection& projection, 00639 unsigned width, 00640 unsigned height ) 00641 : myProj( projection ), vertSign( 1 ) 00642 { 00643 // Get the bounding box of the projected points 00644 float w_max, w_min, h_max, h_min; 00645 w_max = h_max = -std::numeric_limits< float >::max(); 00646 w_min = h_min = std::numeric_limits< float >::max(); 00647 for( unsigned i = 0; i < num_vert; ++i ) 00648 { 00649 float w, h; 00650 myProj.project( verts[i], w, h ); 00651 if( w > w_max ) w_max = w; 00652 if( w < w_min ) w_min = w; 00653 if( h > h_max ) h_max = h; 00654 if( h < h_min ) h_min = h; 00655 } 00656 00657 // Determine the scale factor 00658 const float w_scale = (float)width / ( w_max - w_min ); 00659 const float h_scale = (float)height / ( h_max - h_min ); 00660 myScale = w_scale > h_scale ? h_scale : w_scale; 00661 00662 // Determine offset 00663 horizOffset = -(int)( myScale * w_min ); 00664 vertOffset = -(int)( myScale * h_min ); 00665 00666 // Determine bounding box 00667 horizMax = (int)( w_max * myScale ) + horizOffset; 00668 vertMax = (int)( h_max * myScale ) + vertOffset; 00669 } 00670 00671 void Transform2D::transform( const Vector3D& coords, int& horizontal, int& vertical ) const 00672 { 00673 float horiz, vert; 00674 myProj.project( coords, horiz, vert ); 00675 horizontal = (int)( myScale * horiz ) + horizOffset; 00676 vertical = vertSign * (int)( myScale * vert ) + vertOffset; 00677 } 00678 00679 /** Write quadratic edge shape in PostScript format. 00680 * 00681 * Given the three points composing a quadratic mesh edge, 00682 * write the cubic Bezier curve of the same shape in 00683 * PostScript format. The formulas for P1 and P2 00684 * at the start of this function will result in the cubic 00685 * terms of the Bezier curve dropping out, leaving the 00686 * quadratic curve matching the edge shape function as 00687 * described in Section 3.6 of Hughes. (If you're attempting 00688 * to verify this, don't forget to adjust for the different 00689 * parameter ranges: \f$ \xi = 2 t - 1 \f$). 00690 */ 00691 static void write_eps_quadratic_edge( ostream& s, Transform2D& xform, Vector3D start, Vector3D mid, Vector3D end ) 00692 { 00693 Vector3D P1 = 1. / 3 * ( 4 * mid - end ); 00694 Vector3D P2 = 1. / 3 * ( 4 * mid - start ); 00695 00696 int x, y; 00697 xform.transform( start, x, y ); 00698 s << x << ' ' << y << " moveto" << endl; 00699 xform.transform( P1, x, y ); 00700 s << x << ' ' << y << ' '; 00701 xform.transform( P2, x, y ); 00702 s << x << ' ' << y << ' '; 00703 xform.transform( end, x, y ); 00704 s << x << ' ' << y << " curveto" << endl; 00705 } 00706 00707 void write_eps( Mesh* mesh, const char* filename, Projection proj, MsqError& err, int width, int height ) 00708 { 00709 // Get a global patch 00710 PatchData pd; 00711 pd.set_mesh( mesh ); 00712 pd.fill_global_patch( err );MSQ_ERRRTN( err ); 00713 00714 Transform2D transf( &pd, proj, width, height, false ); 00715 00716 // Open the file 00717 ofstream s( filename ); 00718 if( !s ) 00719 { 00720 MSQ_SETERR( err )( MsqError::FILE_ACCESS ); 00721 return; 00722 } 00723 00724 // Write header 00725 s << "%!PS-Adobe-2.0 EPSF-2.0" << endl; 00726 s << "%%Creator: Mesquite" << endl; 00727 s << "%%Title: Mesquite " << endl; 00728 s << "%%DocumentData: Clean7Bit" << endl; 00729 s << "%%Origin: 0 0" << endl; 00730 s << "%%BoundingBox: 0 0 " << transf.max_horizontal() << ' ' << transf.max_vertical() << endl; 00731 s << "%%Pages: 1" << endl; 00732 00733 s << "%%BeginProlog" << endl; 00734 s << "save" << endl; 00735 s << "countdictstack" << endl; 00736 s << "mark" << endl; 00737 s << "newpath" << endl; 00738 s << "/showpage {} def" << endl; 00739 s << "/setpagedevice {pop} def" << endl; 00740 s << "%%EndProlog" << endl; 00741 00742 s << "%%Page: 1 1" << endl; 00743 s << "1 setlinewidth" << endl; 00744 s << "0.0 setgray" << endl; 00745 00746 // Write mesh edges 00747 EdgeIterator iter( &pd, err );MSQ_ERRRTN( err ); 00748 while( !iter.is_at_end() ) 00749 { 00750 int s_w, s_h, e_w, e_h; 00751 transf.transform( iter.start(), s_w, s_h ); 00752 transf.transform( iter.end(), e_w, e_h ); 00753 00754 s << "newpath" << endl; 00755 s << s_w << ' ' << s_h << " moveto" << endl; 00756 00757 if( !iter.mid() ) 00758 { 00759 s << e_w << ' ' << e_h << " lineto" << endl; 00760 } 00761 else 00762 { 00763 write_eps_quadratic_edge( s, transf, iter.start(), *iter.mid(), iter.end() ); 00764 // draw rings at mid-edge node location 00765 // transf.transform( *(iter.mid()), w1, h1 ); 00766 // s << w1+2 << ' ' << h1 << " moveto" << endl; 00767 // s << w1 << ' ' << h1 << " 2 0 360 arc" << endl; 00768 } 00769 s << "stroke" << endl; 00770 00771 iter.step( err );MSQ_ERRRTN( err ); 00772 } 00773 00774 // Write footer 00775 s << "%%Trailer" << endl; 00776 s << "cleartomark" << endl; 00777 s << "countdictstack" << endl; 00778 s << "exch sub { end } repeat" << endl; 00779 s << "restore" << endl; 00780 s << "%%EOF" << endl; 00781 } 00782 00783 /** Quadratic triangle shape function for use in write_eps_triangle */ 00784 static double tN0( double r, double s ) 00785 { 00786 double t = 1 - r - s; 00787 return t * ( 2 * t - 1 ); 00788 } 00789 static double tN1( double r, double ) 00790 { 00791 return r * ( 2 * r - 1 ); 00792 } 00793 static double tN2( double, double s ) 00794 { 00795 return s * ( 2 * s - 1 ); 00796 } 00797 static double tN3( double r, double s ) 00798 { 00799 double t = 1 - r - s; 00800 return 4 * r * t; 00801 } 00802 static double tN4( double r, double s ) 00803 { 00804 return 4 * r * s; 00805 } 00806 static double tN5( double r, double s ) 00807 { 00808 double t = 1 - r - s; 00809 return 4 * s * t; 00810 } 00811 /** Quadratic triangle shape function for use in write_eps_triangle */ 00812 static Vector3D quad_tri_pt( double r, double s, const Vector3D* coords ) 00813 { 00814 Vector3D result = tN0( r, s ) * coords[0]; 00815 result += tN1( r, s ) * coords[1]; 00816 result += tN2( r, s ) * coords[2]; 00817 result += tN3( r, s ) * coords[3]; 00818 result += tN4( r, s ) * coords[4]; 00819 result += tN5( r, s ) * coords[5]; 00820 return result; 00821 } 00822 00823 void write_eps_triangle( Mesh* mesh, 00824 Mesh::ElementHandle elem, 00825 const char* filename, 00826 bool draw_iso_lines, 00827 bool draw_nodes, 00828 MsqError& err, 00829 int width, 00830 int height ) 00831 { 00832 // Get triangle vertices 00833 MsqVertex coords[6]; 00834 EntityTopology type; 00835 mesh->elements_get_topologies( &elem, &type, 1, err );MSQ_ERRRTN( err ); 00836 if( type != TRIANGLE ) 00837 { 00838 MSQ_SETERR( err )( "Invalid element type", MsqError::UNSUPPORTED_ELEMENT ); 00839 return; 00840 } 00841 std::vector< Mesh::VertexHandle > verts; 00842 std::vector< size_t > junk; 00843 mesh->elements_get_attached_vertices( &elem, 1, verts, junk, err );MSQ_ERRRTN( err ); 00844 if( verts.size() != 3 && verts.size() != 6 ) 00845 { 00846 MSQ_SETERR( err )( "Invalid element type", MsqError::UNSUPPORTED_ELEMENT ); 00847 return; 00848 } 00849 mesh->vertices_get_coordinates( arrptr( verts ), coords, verts.size(), err );MSQ_ERRRTN( err ); 00850 00851 Vector3D coords2[6]; 00852 std::copy( coords, coords + verts.size(), coords2 ); 00853 00854 std::vector< bool > fixed( verts.size(), false ); 00855 if( draw_nodes ) 00856 { 00857 mesh->vertices_get_fixed_flag( arrptr( verts ), fixed, verts.size(), err );MSQ_ERRRTN( err ); 00858 } 00859 write_eps_triangle( coords2, verts.size(), filename, draw_iso_lines, draw_nodes, err, fixed, width, height ); 00860 } 00861 00862 void write_eps_triangle( const Vector3D* coords, 00863 size_t num_vtx, 00864 const char* filename, 00865 bool draw_iso_lines, 00866 bool draw_nodes, 00867 MsqError& err, 00868 const std::vector< bool >& fixed, 00869 int width, 00870 int height ) 00871 { 00872 const int PT_RAD = 3; // radius of circles for drawing nodes, in points 00873 const int PAD = PT_RAD + 2; // margin in points 00874 const double EDGE_GRAY = 0.0; // color for triangle edges, 0.0 => black 00875 const double ISO_GRAY = 0.7; // color for parameter iso-lines 00876 const double NODE_GRAY = 0.0; // color for node circle 00877 const double FIXED_GRAY = 1.0; // color to fill fixed nodes with, 1.0 => white 00878 const double FREE_GRAY = 0.0; // color to fill free nodes with 00879 00880 Projection proj( X, Y ); 00881 Transform2D transf( coords, num_vtx, proj, width, height ); 00882 00883 // Open the file 00884 ofstream str( filename ); 00885 if( !str ) 00886 { 00887 MSQ_SETERR( err )( MsqError::FILE_ACCESS ); 00888 return; 00889 } 00890 00891 // Write header 00892 str << "%!PS-Adobe-2.0 EPSF-2.0" << endl; 00893 str << "%%Creator: Mesquite" << endl; 00894 str << "%%Title: Mesquite " << endl; 00895 str << "%%DocumentData: Clean7Bit" << endl; 00896 str << "%%Origin: 0 0" << endl; 00897 str << "%%BoundingBox: " << -PAD << ' ' << -PAD << ' ' << transf.max_horizontal() + PAD << ' ' 00898 << transf.max_vertical() + PAD << endl; 00899 str << "%%Pages: 1" << endl; 00900 00901 str << "%%BeginProlog" << endl; 00902 str << "save" << endl; 00903 str << "countdictstack" << endl; 00904 str << "mark" << endl; 00905 str << "newpath" << endl; 00906 str << "/showpage {} def" << endl; 00907 str << "/setpagedevice {pop} def" << endl; 00908 str << "%%EndProlog" << endl; 00909 00910 str << "%%Page: 1 1" << endl; 00911 str << "1 setlinewidth" << endl; 00912 str << EDGE_GRAY << " setgray" << endl; 00913 00914 const double h = 0.5, t = 1.0 / 3.0, w = 2.0 / 3.0, s = 1. / 6, f = 5. / 6; 00915 const int NUM_ISO = 15; 00916 const double iso_params[NUM_ISO][2][2] = { 00917 { { h, 0 }, { h, h } }, // r = 1/2 00918 { { t, 0 }, { t, w } }, // r = 1/3 00919 { { w, 0 }, { w, t } }, // r = 2/3 00920 { { s, 0 }, { s, f } }, // r = 1/6 00921 { { f, 0 }, { f, s } }, // r = 5/6 00922 { { 0, h }, { h, h } }, // s = 1/2 00923 { { 0, t }, { w, t } }, // s = 1/3 00924 { { 0, w }, { t, w } }, // s = 2/3 00925 { { 0, s }, { f, s } }, // s = 1/6 00926 { { 0, f }, { s, f } }, // s = 5/6 00927 { { 0, h }, { h, 0 } }, // t = 1 - r - s = 1/2 00928 { { 0, w }, { w, 0 } }, // t = 1 - r - s = 1/3 00929 { { 0, t }, { t, 0 } }, // t = 1 - r - s = 2/3 00930 { { 0, f }, { f, 0 } }, // t = 1 - r - s = 1/6 00931 { { 0, s }, { s, 0 } } // t = 1 - r - s = 5/6 00932 }; 00933 00934 if( num_vtx == 3 ) 00935 { 00936 int x[3], y[3]; 00937 for( size_t i = 0; i < 3; ++i ) 00938 transf.transform( coords[i], x[i], y[i] ); 00939 00940 str << "newpath" << endl; 00941 str << x[0] << ' ' << y[0] << " moveto" << endl; 00942 str << x[1] << ' ' << y[1] << " lineto" << endl; 00943 str << x[2] << ' ' << y[2] << " lineto" << endl; 00944 str << x[0] << ' ' << y[0] << " lineto" << endl; 00945 str << "stroke" << endl; 00946 00947 if( draw_iso_lines ) 00948 { 00949 str << ISO_GRAY << " setgray" << endl; 00950 str << "newpath" << endl; 00951 for( int i = 0; i < NUM_ISO; ++i ) 00952 { 00953 double R[2] = { iso_params[i][0][0], iso_params[i][1][0] }; 00954 double S[2] = { iso_params[i][0][1], iso_params[i][1][1] }; 00955 double T[2] = { 1 - R[0] - S[0], 1 - R[1] - S[1] }; 00956 Vector3D p[2] = { T[0] * coords[0] + R[0] * coords[1] + S[0] * coords[2], 00957 T[1] * coords[0] + R[1] * coords[1] + S[1] * coords[2] }; 00958 transf.transform( p[0], x[0], y[0] ); 00959 transf.transform( p[1], x[1], y[1] ); 00960 str << x[0] << ' ' << y[0] << " moveto" << endl; 00961 str << x[1] << ' ' << y[1] << " lineto" << endl; 00962 } 00963 str << " stroke" << endl; 00964 } 00965 } 00966 else if( num_vtx == 6 ) 00967 { 00968 str << "newpath" << endl; 00969 write_eps_quadratic_edge( str, transf, coords[0], coords[3], coords[1] ); 00970 write_eps_quadratic_edge( str, transf, coords[1], coords[4], coords[2] ); 00971 write_eps_quadratic_edge( str, transf, coords[2], coords[5], coords[0] ); 00972 str << "stroke" << endl; 00973 00974 if( draw_iso_lines ) 00975 { 00976 str << ISO_GRAY << " setgray" << endl; 00977 str << "newpath" << endl; 00978 for( int i = 0; i < NUM_ISO; ++i ) 00979 { 00980 double R[3] = { iso_params[i][0][0], 0, iso_params[i][1][0] }; 00981 double S[3] = { iso_params[i][0][1], 0, iso_params[i][1][1] }; 00982 R[1] = 0.5 * ( R[0] + R[2] ); 00983 S[1] = 0.5 * ( S[0] + S[2] ); 00984 Vector3D p[3] = { quad_tri_pt( R[0], S[0], coords ), quad_tri_pt( R[1], S[1], coords ), 00985 quad_tri_pt( R[2], S[2], coords ) }; 00986 write_eps_quadratic_edge( str, transf, p[0], p[1], p[2] ); 00987 } 00988 str << " stroke" << endl; 00989 } 00990 } 00991 00992 if( draw_nodes ) 00993 { 00994 for( size_t i = 0; i < num_vtx; ++i ) 00995 { 00996 int iw, ih; 00997 // fill interior with either white or black depending 00998 // on whether or not the vertex is fixed. 00999 if( fixed[i] ) 01000 str << FIXED_GRAY << " setgray" << endl; 01001 else 01002 str << FREE_GRAY << " setgray" << endl; 01003 transf.transform( coords[i], iw, ih ); 01004 str << w + PT_RAD << ' ' << ih << " moveto" << endl; 01005 str << iw << ' ' << ih << ' ' << PT_RAD << " 0 360 arc" << endl; 01006 str << "closepath fill" << endl; 01007 str << NODE_GRAY << " setgray" << endl; 01008 str << "newpath" << endl; 01009 str << iw << ' ' << ih << ' ' << PT_RAD << " 0 360 arc" << endl; 01010 str << "stroke" << endl; 01011 } 01012 } 01013 01014 // Write footer 01015 str << "%%Trailer" << endl; 01016 str << "cleartomark" << endl; 01017 str << "countdictstack" << endl; 01018 str << "exch sub { end } repeat" << endl; 01019 str << "restore" << endl; 01020 str << "%%EOF" << endl; 01021 } 01022 01023 void write_svg( Mesh* mesh, const char* filename, Projection proj, MsqError& err ) 01024 { 01025 // Get a global patch 01026 PatchData pd; 01027 pd.set_mesh( mesh ); 01028 pd.fill_global_patch( err );MSQ_ERRRTN( err ); 01029 01030 Transform2D transf( &pd, proj, 400, 400, true ); 01031 01032 // Open the file 01033 ofstream file( filename ); 01034 if( !file ) 01035 { 01036 MSQ_SETERR( err )( MsqError::FILE_ACCESS ); 01037 return; 01038 } 01039 01040 // Write header 01041 file << "<?xml version=\"1.0\" standalone=\"no\"?>" << endl; 01042 file << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" " 01043 << "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" << endl; 01044 file << endl; 01045 file << "<svg width=\"" << transf.max_horizontal() << "\" height=\"" << transf.max_vertical() 01046 << "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">" << endl; 01047 01048 // Write mesh edges 01049 EdgeIterator iter( &pd, err );MSQ_ERRRTN( err ); 01050 while( !iter.is_at_end() ) 01051 { 01052 int s_w, s_h, e_w, e_h; 01053 transf.transform( iter.start(), s_w, s_h ); 01054 transf.transform( iter.end(), e_w, e_h ); 01055 01056 file << "<line " 01057 << "x1=\"" << s_w << "\" " 01058 << "y1=\"" << s_h << "\" " 01059 << "x2=\"" << e_w << "\" " 01060 << "y2=\"" << e_h << "\" " 01061 << " style=\"stroke:rgb(99,99,99);stroke-width:2\"" 01062 << "/>" << endl; 01063 01064 iter.step( err );MSQ_ERRRTN( err ); 01065 } 01066 01067 // Write footer 01068 file << "</svg>" << endl; 01069 } 01070 01071 } // namespace MeshWriter 01072 01073 } // namespace MBMesquite 01074 01075 #endif