Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
SimpleSVGMeshExporter.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* SimpleSVGMeshExporter.cc (C) 2000-2025 */
9/* */
10/* Mesh exporter in SVG format. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14// Must put this first for MSVC otherwise we don't have 'M_PI'
15#define _USE_MATH_DEFINES
16#include <cmath>
17
18#include "arcane/core/SimpleSVGMeshExporter.h"
19
20#include "arcane/utils/Iostream.h"
21#include "arcane/core/ItemGroup.h"
22#include "arcane/core/IMesh.h"
23#include "arcane/core/VariableTypes.h"
24
25#include <set>
26#include <map>
27
28/*---------------------------------------------------------------------------*/
29/*---------------------------------------------------------------------------*/
30
31namespace Arcane
32{
33using std::ostream;
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37
39{
40 public:
41
42 Impl(ostream& ofile)
43 : m_ofile(ofile)
44 {}
45 void _writeText(Real x, Real y, StringView color, StringView text, double rotation, bool do_background);
46 void write(const CellGroup& cells);
47
48 private:
49
50 ostream& m_ofile;
51 double m_font_size = 3.0;
52};
53
54/*---------------------------------------------------------------------------*/
55/*---------------------------------------------------------------------------*/
56
57void SimpleSVGMeshExporter::Impl::
58_writeText(Real x, Real y, StringView color, StringView text, double rotation, bool do_background)
59{
60 // Displays a white background beneath the text.
61 if (do_background) {
62 m_ofile << "<text x='" << x << "' y='" << y << "' dominant-baseline='central' text-anchor='middle'"
63 << " style='stroke:white; stroke-width:0.6em'";
64 if (rotation != 0.0)
65 m_ofile << " transform='rotate(" << rotation << "," << x << "," << y << ")'";
66 m_ofile << " font-size='" << m_font_size << "'>" << text << "</text>\n";
67 }
68 m_ofile << "<text x='" << x << "' y='" << y << "' dominant-baseline='central' text-anchor='middle'"
69 << " fill='" << color << "'";
70 if (rotation != 0.0)
71 m_ofile << " transform='rotate(" << rotation << "," << x << "," << y << ")'";
72 m_ofile << " font-size='" << m_font_size << "'>" << text << "</text>\n";
73}
74
75/*---------------------------------------------------------------------------*/
76/*---------------------------------------------------------------------------*/
77
78void SimpleSVGMeshExporter::Impl::
79write(const CellGroup& cells)
80{
81 if (cells.null())
82 return;
83 IMesh* mesh = cells.mesh();
84 Int32 mesh_dim = mesh->dimension();
85 if (mesh_dim != 2)
86 ARCANE_FATAL("Invalid dimension ({0}) for mesh. Only 2D mesh is allowed", mesh_dim);
87
88 // Note: since the origin in SVG is by default top-left, we take the opposite of each
89 // 'Y' value for display.
90 // NOTE: we could use SVG transformations, but that is more complicated to
91 // handle for text display.
92
93 VariableNodeReal3& nodes_coord = mesh->nodesCoordinates();
94 ostream& ofile = m_ofile;
95 Real mul_value = 1000.0;
96 const Real min_val = std::numeric_limits<Real>::lowest();
97 const Real max_val = std::numeric_limits<Real>::max();
98 Real2 min_bbox(max_val, max_val);
99 Real2 max_bbox(min_val, min_val);
100 // Calculates the center of the cells and the bounding box of the cell group.
101 std::map<Int32, Real2> cells_center;
102 ENUMERATE_CELL (icell, cells) {
103 Cell cell = *icell;
104 Real3 center;
105 Integer nb_node = cell.nbNode();
106 if (nb_node > 0) {
107 for (Integer i = 0; i < nb_node; ++i) {
108 Real3 node_coord_3d = nodes_coord[cell.node(i)] * mul_value;
109 Real2 node_coord(node_coord_3d.x, -node_coord_3d.y);
110 min_bbox = math::min(min_bbox, node_coord);
111 max_bbox = math::max(max_bbox, node_coord);
112 center += node_coord_3d;
113 }
114 center /= nb_node;
115 }
116 Real2 center_2d(center.x, -center.y);
117 cells_center[cell.localId()] = center_2d;
118 }
119
120 Real bbox_width = math::abs(max_bbox.x - min_bbox.x);
121 Real bbox_height = math::abs(max_bbox.y - min_bbox.y);
122 Real max_dim = math::max(bbox_width, bbox_height);
123 m_font_size = max_dim / 80.0;
124
125 // Adds 10% of the dimensions on both sides of the viewBox to ensure
126 // that the text is properly written (as it might overflow the bounding box)
127 ofile << "<?xml version=\"1.0\"?>\n";
128 ofile << "<svg"
129 << " viewBox='" << min_bbox.x - bbox_width * 0.1 << "," << min_bbox.y - bbox_height * 0.1 << "," << bbox_width * 1.2 << "," << bbox_height * 1.2 << "'"
130 << " xmlns='http://www.w3.org/2000/svg' version='1.1'>\n";
131 ofile << "<!-- V3 bbox min_x=" << min_bbox.x << " min_y=" << min_bbox.y << " max_x=" << max_bbox.x << " max_y=" << max_bbox.y << " -->";
132 ofile << "<title>Mesh</title>\n";
133 ofile << "<desc>MeshExample</desc>\n";
134
135 //ofile << "<g transform='matrix(1,0,0,-1,0,200)'>\n";
136 //ofile << "<g transform='translate(" << min_bbox.x << "," << -min_bbox.y << ")'>\n";
137 ofile << "<g>\n";
138
139 // Displays the contour and uniqueId() for each cell.
140 ENUMERATE_CELL (icell, cells) {
141 Cell cell = *icell;
142 Real2 cell_pos = cells_center[cell.localId()];
143 Integer nb_node = cell.nbNode();
144 ofile << "<path d='";
145 nb_node = cell.typeInfo()->linearTypeInfo()->nbLocalNode();
146 for (Integer i = 0; i < nb_node; ++i) {
147 Real3 node_coord_3d = nodes_coord[cell.node(i)];
148 Real2 node_coord(node_coord_3d.x, -node_coord_3d.y);
149 node_coord *= mul_value;
150 if (i == 0)
151 ofile << "M ";
152 else
153 ofile << "L ";
154 // performs a homothety to clearly see the faces in case of welding.
155 Real2 coord = cell_pos + (node_coord - cell_pos) * 0.98;
156 ofile << coord.x << " " << coord.y << " ";
157 }
158 ofile << "z'";
159 if (cell.isOwn())
160 ofile << " fill='yellow'";
161 else
162 ofile << " fill='orange'";
163 ofile << " stroke='black'";
164 ofile << " stroke-width='1'/>\n";
165 _writeText(cell_pos.x, cell_pos.y, "blue", String::fromNumber(cell.uniqueId().asInt64()), 0.0, false);
166 }
167
168 // Displays the uniqueId() for each node.
169 {
170 // Set of nodes already processed to display them only once.
171 std::set<Int32> nodes_done;
172 ENUMERATE_CELL (icell, cells) {
173 Cell cell = *icell;
174 Integer nb_node = cell.nbNode();
175 for (Integer i = 0; i < nb_node; ++i) {
176 Node node = cell.node(i);
177 Int32 lid = node.localId();
178 if (nodes_done.find(lid) != nodes_done.end())
179 continue;
180 nodes_done.insert(lid);
181 Real3 coord_3d = nodes_coord[node];
182 Real2 coord(coord_3d.x, -coord_3d.y);
183 coord *= mul_value;
184 _writeText(coord.x, coord.y, "green", String::fromNumber(node.uniqueId().asInt64()), 0.0, true);
185 }
186 }
187 }
188
189 // Displays the uniqueId() for each face.
190 // Performs a possible rotation so that the display of the face number is aligned
191 // with its segment.
192 {
193 // Set of faces already processed to display them only once.
194 std::set<Int32> faces_done;
195 ENUMERATE_CELL (icell, cells) {
196 Cell cell = *icell;
197 Integer nb_face = cell.nbFace();
198 for (Integer i = 0; i < nb_face; ++i) {
199 Face face = cell.face(i);
200 Int32 lid = face.localId();
201 if (faces_done.find(lid) != faces_done.end())
202 continue;
203 faces_done.insert(lid);
204 // In the case of multi-dimensional meshing, it is possible
205 // to have faces reduced to a point.
206 if (face.nbNode() < 2)
207 continue;
208 Real3 node0_coord = nodes_coord[face.node(0)];
209 Real3 node1_coord = nodes_coord[face.node(1)];
210 Real3 face_coord_3d = (node0_coord + node1_coord) / 2.0;
211
212 Real2 face_coord(face_coord_3d.x, -face_coord_3d.y);
213 face_coord *= mul_value;
214 Real3 direction = node1_coord - node0_coord;
215 direction = direction.normalize();
216 // TODO: check between -1.0 and 1.0
217 // Calculates the rotation angle so that the display of the face number is aligned with
218 // the edge of the face.
219 double angle = math::abs(std::asin(direction.y)) / M_PI * 180.0;
220 Real2 cell_center = cells_center[cell.localId()];
221 Real2 coord = cell_center + (face_coord - cell_center) * 0.92;
222 _writeText(coord.x, coord.y, "red", String::fromNumber(face.uniqueId().asInt64()), angle, true);
223 }
224 }
225 }
226
227 ofile << "</g>\n";
228 ofile << "</svg>\n";
229}
230
231/*---------------------------------------------------------------------------*/
232/*---------------------------------------------------------------------------*/
233
235SimpleSVGMeshExporter(std::ostream& ofile)
236: m_p(new Impl(ofile))
237{
238}
239
240/*---------------------------------------------------------------------------*/
241/*---------------------------------------------------------------------------*/
242
243SimpleSVGMeshExporter::
244~SimpleSVGMeshExporter()
245{
246 delete m_p;
247}
248
249/*---------------------------------------------------------------------------*/
250/*---------------------------------------------------------------------------*/
251
253write(const CellGroup& cells)
254{
255 m_p->write(cells);
256}
257
258/*---------------------------------------------------------------------------*/
259/*---------------------------------------------------------------------------*/
260
261} // End namespace Arcane
262
263/*---------------------------------------------------------------------------*/
264/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
#define ENUMERATE_CELL(name, group)
Generic enumerator for a cell group.
SimpleSVGMeshExporter(std::ostream &ofile)
Create an instance associated with the ofile stream.
void write(const CellGroup &cells)
Exports the entities of the cells group.
View of a UTF-8 character string.
Definition StringView.h:44
__host__ __device__ Real2 min(Real2 a, Real2 b)
Returns the minimum of two Real2.
Definition MathUtils.h:346
T max(const T &a, const T &b, const T &c)
Returns the maximum of three elements.
Definition MathUtils.h:407
ItemGroupT< Cell > CellGroup
Group of cells.
Definition ItemTypes.h:184
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Coordinate type quantity at node.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Int32 Integer
Type representing an integer.
double Real
Type representing a real number.
@ Cell
The mesh is AMR by cell.
Definition MeshKind.h:53
std::int32_t Int32
Signed integer type of 32 bits.