libMesh
Loading...
Searching...
No Matches
postscript_io.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18// C++ includes
19#include <ctime>
20#include <iomanip>
21#include <iostream>
22#include <sstream>
23
24// Local includes
25#include "libmesh/postscript_io.h"
26#include "libmesh/mesh_tools.h"
27#include "libmesh/elem.h"
28#include "libmesh/int_range.h"
29
30namespace libMesh
31{
32
33
34// Transformation map between monomial (physical space) and Bezier bases.
35const float PostscriptIO::_bezier_transform[3][3] =
36 {
37 {-1.f/6.f, 1.f/6.f, 1.},
38 {-1.f/6.f, 0.5, 1.f/6.f},
39 {0., 1., 0.}
40 };
41
42
44 MeshOutput<MeshBase> (mesh_in),
45 shade_value(0.0),
46 line_width(0.5),
47 //_M(3,3),
48 _offset(0., 0.),
49 _scale(1.0),
50 _current_point(0., 0.)
51{
52 // This code is still undergoing some development.
53 libmesh_experimental();
54
55 // Entries of transformation matrix from physical to Bezier coords.
56 // _M(0,0) = -1./6.; _M(0,1) = 1./6.; _M(0,2) = 1.;
57 // _M(1,0) = -1./6.; _M(1,1) = 0.5 ; _M(1,2) = 1./6.;
58 // _M(2,0) = 0. ; _M(2,1) = 1. ; _M(2,2) = 0.;
59
60 // Make sure there is enough room to store Bezier coefficients.
61 _bezier_coeffs.resize(3);
62}
63
64
65
67
68
69
70void PostscriptIO::write (const std::string & fname)
71{
72 // We may need to gather a DistributedMesh to output it, making that
73 // const qualifier in our constructor a dirty lie
74 MeshSerializer serialize(const_cast<MeshBase &>(this->mesh()), !_is_parallel_format);
75
76 if (this->mesh().processor_id() == 0)
77 {
78 // Get a constant reference to the mesh.
79 const MeshBase & the_mesh = MeshOutput<MeshBase>::mesh();
80
81 // Only works in 2D
82 libmesh_assert_equal_to (the_mesh.mesh_dimension(), 2);
83
84 // Create output file stream.
85 // _out is now a private member of the class.
86 _out.open(fname.c_str());
87
88 // Make sure it opened correctly
89 if (!_out.good())
90 libmesh_file_error(fname.c_str());
91
92 // The mesh bounding box gives us info about what the
93 // Postscript bounding box should be.
95
96 // Add a little extra padding to the "true" bounding box so
97 // that we can still see the boundary
98 const Real percent_padding = 0.01;
99 const Real dx=bbox.second(0)-bbox.first(0); libmesh_assert_greater (dx, 0.0);
100 const Real dy=bbox.second(1)-bbox.first(1); libmesh_assert_greater (dy, 0.0);
101
102 const Real x_min = bbox.first(0) - percent_padding*dx;
103 const Real y_min = bbox.first(1) - percent_padding*dy;
104 const Real x_max = bbox.second(0) + percent_padding*dx;
105 const Real y_max = bbox.second(1) + percent_padding*dy;
106
107 // Width of the output as given in postscript units.
108 // This usually is given by the strange unit 1/72 inch.
109 // A width of 300 represents a size of roughly 10 cm.
110 const Real width = 300;
111 _scale = width / (x_max-x_min);
112 _offset(0) = x_min;
113 _offset(1) = y_min;
114
115 // Header writing stuff stolen from Deal.II
116 std::time_t time1= std::time (0);
117 std::tm * time = std::localtime(&time1);
118 _out << "%!PS-Adobe-2.0 EPSF-1.2" << '\n'
119 //<< "%!PS-Adobe-1.0" << '\n' // Lars' PS version
120 << "%%Filename: " << fname << '\n'
121 << "%%Title: LibMesh Output" << '\n'
122 << "%%Creator: LibMesh: A C++ finite element library" << '\n'
123 << "%%Creation Date: "
124 << time->tm_year+1900 << "/"
125 << time->tm_mon+1 << "/"
126 << time->tm_mday << " - "
127 << time->tm_hour << ":"
128 << std::setw(2) << time->tm_min << ":"
129 << std::setw(2) << time->tm_sec << '\n'
130 << "%%BoundingBox: "
131 // lower left corner
132 << "0 0 "
133 // upper right corner
134 << static_cast<unsigned int>( rint(double((x_max-x_min) * _scale )))
135 << ' '
136 << static_cast<unsigned int>( rint(double((y_max-y_min) * _scale )))
137 << '\n';
138
139 // define some abbreviations to keep
140 // the output small:
141 // m=move turtle to
142 // l=define a line
143 // s=set rgb color
144 // sg=set gray value
145 // lx=close the line and plot the line
146 // lf=close the line and fill the interior
147 _out << "/m {moveto} bind def" << '\n'
148 << "/l {lineto} bind def" << '\n'
149 << "/s {setrgbcolor} bind def" << '\n'
150 << "/sg {setgray} bind def" << '\n'
151 << "/cs {curveto stroke} bind def" << '\n'
152 << "/lx {lineto closepath stroke} bind def" << '\n'
153 << "/lf {lineto closepath fill} bind def" << '\n';
154
155 _out << "%%EndProlog" << '\n';
156 // << '\n';
157
158 // Set line width in the postscript file.
159 _out << line_width << " setlinewidth" << '\n';
160
161 // Set line cap and join options
162 _out << "1 setlinecap" << '\n';
163 _out << "1 setlinejoin" << '\n';
164
165 // allow only five digits for output (instead of the default
166 // six); this should suffice even for fine grids, but reduces
167 // the file size significantly
168 _out << std::setprecision (5);
169
170 // Loop over the active elements, draw lines for the edges. We
171 // draw even quadratic elements with straight sides, i.e. a straight
172 // line sits between each pair of vertices. Also we draw every edge
173 // for an element regardless of the fact that it may overlap with
174 // another. This would probably be a useful optimization...
175 for (const auto & elem : the_mesh.active_element_ptr_range())
176 this->plot_linear_elem(elem);
177
178 // Issue the showpage command, and we're done.
179 _out << "showpage" << std::endl;
180
181 } // end if (this->mesh().processor_id() == 0)
182}
183
184
185
186
187
188
190{
191 // Clear the string contents. Yes, this really is how you do that...
192 _cell_string.str("");
193
194 // The general strategy is:
195 // 1.) Use m := {moveto} to go to vertex 0.
196 // 2.) Use l := {lineto} commands to draw lines to vertex 1, 2, ... N-1.
197 // 3a.) Use lx := {lineto closepath stroke} command at vertex N to draw the last line.
198 // 3b.) lf := {lineto closepath fill} command to shade the cell just drawn
199 // All of our 2D elements' vertices are numbered in counterclockwise order,
200 // so we can just draw them in the same order.
201
202 // 1.)
203 _current_point = (elem->point(0) - _offset) * _scale;
204 _cell_string << _current_point(0) << " " << _current_point(1) << " "; // write x y
205 _cell_string << "m ";
206
207 // 2.)
208 const unsigned int nv=elem->n_vertices();
209 for (unsigned int v=1; v<nv-1; ++v)
210 {
211 _current_point = (elem->point(v) - _offset) * _scale;
212 _cell_string << _current_point(0) << " " << _current_point(1) << " "; // write x y
213 _cell_string << "l ";
214 }
215
216 // 3.)
217 _current_point = (elem->point(nv-1) - _offset) * _scale;
218 _cell_string << _current_point(0) << " " << _current_point(1) << " "; // write x y
219
220 // We draw the shaded (interior) parts first, if applicable.
221 if (shade_value > 0.0)
222 _out << shade_value << " sg " << _cell_string.str() << "lf\n";
223
224 // Draw the black lines (I guess we will always do this)
225 _out << "0 sg " << _cell_string.str() << "lx\n";
226}
227
228
229
230
232{
233 for (auto ns : elem->side_index_range())
234 {
235 // Build the quadratic side
236 std::unique_ptr<const Elem> side = elem->build_side_ptr(ns);
237
238 // Be sure it's quadratic (Edge2). Eventually we could
239 // handle cubic elements as well...
240 libmesh_assert_equal_to ( side->type(), EDGE3 );
241
242 _out << "0 sg ";
243
244 // Move to the first point on this side.
245 _current_point = (side->point(0) - _offset) * _scale;
246 _out << _current_point(0) << " " << _current_point(1) << " "; // write x y
247 _out << "m ";
248
249 // Compute _bezier_coeffs for this edge. This fills up
250 // the _bezier_coeffs vector.
251 this->_compute_edge_bezier_coeffs(side.get());
252
253 // Print curveto path to file
254 for (auto i : index_range(_bezier_coeffs))
255 _out << _bezier_coeffs[i](0) << " " << _bezier_coeffs[i](1) << " ";
256 _out << " cs\n";
257 }
258}
259
260
261
262
264{
265 // I only know how to do this for an Edge3!
266 libmesh_assert_equal_to (elem->type(), EDGE3);
267
268 // Get x-coordinates into an array, transform them,
269 // and repeat for y.
270 float phys_coords[3] = {0., 0., 0.};
271 float bez_coords[3] = {0., 0., 0.};
272
273 for (unsigned int i=0; i<2; ++i)
274 {
275 // Initialize vectors. Physical coordinates are initialized
276 // by their postscript-scaled values.
277 for (unsigned int j=0; j<3; ++j)
278 {
279 phys_coords[j] = static_cast<float>
280 ((elem->point(j)(i) - _offset(i)) * _scale);
281 bez_coords[j] = 0.; // zero out result vector
282 }
283
284 // Multiply matrix times vector
285 for (unsigned int j=0; j<3; ++j)
286 for (unsigned int k=0; k<3; ++k)
287 bez_coords[j] += _bezier_transform[j][k]*phys_coords[k];
288
289 // Store result in _bezier_coeffs
290 for (unsigned int j=0; j<3; ++j)
291 _bezier_coeffs[j](i) = phys_coords[j];
292 }
293}
294
295} // namespace libMesh
Defines a Cartesian bounding box by the two corner extremum.
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual unsigned int n_vertices() const =0
const Point & point(const unsigned int i) const
Definition elem.h:2462
virtual ElemType type() const =0
IntRange< unsigned short > side_index_range() const
Definition elem.h:2727
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
This is the MeshBase class.
Definition mesh_base.h:81
unsigned int mesh_dimension() const
Definition mesh_base.C:430
This class defines an abstract interface for Mesh output.
Definition mesh_output.h:54
const bool _is_parallel_format
Flag specifying whether this format is parallel-capable.
const MeshBase & mesh() const
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
void plot_quadratic_elem(const Elem *elem)
Draws an element with Bezier curves.
std::ostringstream _cell_string
Drawing style-independent data for a single cell.
Real _scale
Amount by which to stretch each point to place it in Postscript coordinates.
std::ofstream _out
Output file stream which will be opened when the file name is known.
virtual ~PostscriptIO()
Destructor.
static const float _bezier_transform[3][3]
Coefficients of the transformation from physical-space edge coordinates to Bezier basis coefficients.
std::vector< Point > _bezier_coeffs
Vector containing 3 points corresponding to Bezier coefficients, as computed by _compute_edge_bezier_...
Point _current_point
A point object used for temporary calculations.
Point _offset
Amount to add to every (x,y) point to place it in Postscript coordinates.
virtual void write(const std::string &) override
This method implements writing a mesh to a specified file.
PostscriptIO(const MeshBase &mesh)
Constructor.
Real shade_value
Controls greyscale shading of cells.
Real line_width
Control the thickness of the lines used.
void plot_linear_elem(const Elem *elem)
Draws an element with straight lines.
void _compute_edge_bezier_coeffs(const Elem *elem)
Given a quadratic edge Elem which lies in the x-y plane, computes the Bezier coefficients.
libMesh::BoundingBox create_bounding_box(const MeshBase &mesh)
Definition mesh_tools.C:566
The libMesh namespace provides an interface to certain functionality in the library.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real