Arcane  v4.1.3.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
SimpleHTMLMeshAMRPatchExporter.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/* SimpleHTMLMeshAMRPatchExporter.cc (C) 2000-2026 */
9/* */
10/* Écrivain d'un maillage au format HTML, avec un SVG. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14// Il faut mettre cela en premier pour MSVC sinon on n'a pas 'M_PI'
15#define _USE_MATH_DEFINES
16#include <cmath>
17
18#include "arcane/cartesianmesh/SimpleHTMLMeshAMRPatchExporter.h"
19
20#include "arcane/cartesianmesh/AMRPatchPosition.h"
21
22#include "arcane/utils/Iostream.h"
23#include "arcane/utils/StringBuilder.h"
24
25#include "arcane/core/ItemGroup.h"
26#include "arcane/core/IMesh.h"
27#include "arcane/core/VariableTypes.h"
28
29#include <set>
30#include <map>
31
32/*---------------------------------------------------------------------------*/
33/*---------------------------------------------------------------------------*/
34
35namespace Arcane
36{
37using std::ostream;
38
39/*---------------------------------------------------------------------------*/
40/*---------------------------------------------------------------------------*/
41
43{
44 public:
45
46 Impl() = default;
47 void addPatch(const CartesianPatch& patch);
48 void write(ostream& ofile);
49
50 private:
51
52 void _writeJs(ostream& ofile);
53 void _writeCss(ostream& ofile);
54 void _writeBodyHtml(ostream& ofile);
55 void _writeHeaderSvg(const CartesianPatch& ground_patch);
56 void _writeText(Real x, Real y, StringView color, StringView text, Real rotation, bool do_background);
57 void _writePatch(const CartesianPatch& patch);
58
59 private:
60
61 Real m_font_size = 0;
62 bool m_has_ground = false;
63 bool m_has_patch = false;
64 StringBuilder m_header_svg{};
65 StringBuilder m_patches{};
66};
67
68/*---------------------------------------------------------------------------*/
69/*---------------------------------------------------------------------------*/
70
71/*---------------------------------------------------------------------------*/
72/*---------------------------------------------------------------------------*/
73
74void SimpleHTMLMeshAMRPatchExporter::Impl::
75addPatch(const CartesianPatch& patch)
76{
77 if (patch.position().isNull()) {
78 return;
79 }
80 if (patch.level() == 0) {
81 if (m_has_ground) {
82 ARCANE_FATAL("Ground patch already wrote");
83 }
84 _writeHeaderSvg(patch);
85 m_has_ground = true;
86 }
87 Int32 level = patch.level();
88 Int32 index = patch.index();
89
90 m_patches += String::format("<g class='level-{0}' id='patch-{1}'>\n", level, index);
91 _writePatch(patch);
92 m_patches += "</g>\n";
93 m_has_patch = true;
94}
95
96/*---------------------------------------------------------------------------*/
97/*---------------------------------------------------------------------------*/
98
99void SimpleHTMLMeshAMRPatchExporter::Impl::
100write(ostream& ofile)
101{
102 if (!m_has_patch) {
103 return;
104 }
105 if (!m_has_ground) {
106 ARCANE_FATAL("Need ground patch to write");
107 }
108 ofile << "<!DOCTYPE html>\n";
109 ofile << "<html>\n";
110
111 ofile << "<head>\n";
112 _writeJs(ofile);
113 _writeCss(ofile);
114 ofile << "</head>\n";
115
116 ofile << "<body>\n";
117 _writeBodyHtml(ofile);
118 ofile << "</body>\n";
119
120 ofile << "</html>\n";
121}
122
123/*---------------------------------------------------------------------------*/
124/*---------------------------------------------------------------------------*/
125
126void SimpleHTMLMeshAMRPatchExporter::Impl::
127_writeJs(ostream& ofile)
128{
129 ofile << "<script>\n";
130 //TODO
131 ofile << "</script>\n";
132}
133
134/*---------------------------------------------------------------------------*/
135/*---------------------------------------------------------------------------*/
136
137void SimpleHTMLMeshAMRPatchExporter::Impl::
138_writeCss(ostream& ofile)
139{
140 ofile << "<style>\n";
141
142 ofile << ".uid-text {font-size: " << m_font_size << "px;}\n";
143
144 ofile << "</style>\n";
145}
146
147/*---------------------------------------------------------------------------*/
148/*---------------------------------------------------------------------------*/
149
150void SimpleHTMLMeshAMRPatchExporter::Impl::
151_writeBodyHtml(ostream& ofile)
152{
153 ofile << m_header_svg;
154 ofile << m_patches;
155 ofile << "</g>\n";
156 ofile << "</svg>\n";
157}
158
159/*---------------------------------------------------------------------------*/
160/*---------------------------------------------------------------------------*/
161
162void SimpleHTMLMeshAMRPatchExporter::Impl::
163_writeHeaderSvg(const CartesianPatch& ground_patch)
164{
165 CellGroup cells = ground_patch.patchInterface()->cells();
166 if (ground_patch.level() != 0) {
167 ARCANE_FATAL("Not ground patch");
168 }
169 if (cells.null()) {
170 ARCANE_FATAL("Empty patch");
171 }
172 IMesh* mesh = cells.mesh();
173 Int32 mesh_dim = mesh->dimension();
174 if (mesh_dim != 2)
175 ARCANE_FATAL("Invalid dimension ({0}) for mesh. Only 2D mesh is allowed", mesh_dim);
176
177 // Note: comme par défaut en SVG l'origin est en haut à gauche, on prend pour chaque
178 // valeur de 'Y' son opposé pour l'affichage.
179 // NOTE: on pourrait utiliser les transformations de SVG mais c'est plus compliqué à
180 // traiter pour l'affichage du texte
181
182 VariableNodeReal3& nodes_coord = mesh->nodesCoordinates();
183 Real mul_value = 1000.0;
184 const Real min_val = std::numeric_limits<Real>::lowest();
185 const Real max_val = std::numeric_limits<Real>::max();
186 Real2 min_bbox(max_val, max_val);
187 Real2 max_bbox(min_val, min_val);
188 // Calcul le centre des mailles et la bounding box du groupe de maille.
189 std::map<Int32, Real2> cells_center;
190 ENUMERATE_CELL (icell, cells) {
191 Cell cell = *icell;
192 Real3 center;
193 Integer nb_node = cell.nbNode();
194 if (nb_node > 0) {
195 for (Integer i = 0; i < nb_node; ++i) {
196 Real3 node_coord_3d = nodes_coord[cell.node(i)] * mul_value;
197 Real2 node_coord(node_coord_3d.x, -node_coord_3d.y);
198 min_bbox = math::min(min_bbox, node_coord);
199 max_bbox = math::max(max_bbox, node_coord);
200 center += node_coord_3d;
201 }
202 center /= nb_node;
203 }
204 Real2 center_2d(center.x, -center.y);
205 cells_center[cell.localId()] = center_2d;
206 }
207
208 Real bbox_width = math::abs(max_bbox.x - min_bbox.x);
209 Real bbox_height = math::abs(max_bbox.y - min_bbox.y);
210 Real max_dim = math::max(bbox_width, bbox_height);
211 m_font_size = max_dim / 80.0;
212
213 // Ajoute 10% des dimensions de part et d'autre de la viewBox pour être sur
214 // que le texte est bien écrit (car il peut déborder de la bounding box)
215 m_header_svg += "<?xml version=\"1.0\"?>\n";
216 m_header_svg += String::format("<svg viewBox='{0},{1},{2},{3}' xmlns='http://www.w3.org/2000/svg' version='1.1'>\n",
217 (min_bbox.x - bbox_width * 0.1),
218 (min_bbox.y - bbox_height * 0.1),
219 (bbox_width * 1.2),
220 (bbox_height * 1.2));
221 m_header_svg += String::format("<!-- V3 bbox min_x={0} min_y={1} max_x={2} max_y={3} -->",
222 min_bbox.x,
223 min_bbox.y,
224 max_bbox.x,
225 max_bbox.y);
226
227 m_header_svg += "<title>Mesh</title>\n";
228 m_header_svg += "<desc>MeshExample</desc>\n";
229
230 //ofile << "<g transform='matrix(1,0,0,-1,0,200)'>\n";
231 //ofile << "<g transform='translate(" << min_bbox.x << "," << -min_bbox.y << ")'>\n";
232 m_header_svg += "<g>\n";
233}
234
235/*---------------------------------------------------------------------------*/
236/*---------------------------------------------------------------------------*/
237
238void SimpleHTMLMeshAMRPatchExporter::Impl::
239_writeText(Real x, Real y, StringView color, StringView text, Real rotation, bool do_background)
240{
241 // Affiche un fond blanc en dessous du texte.
242 if (do_background) {
243 m_patches += String::format("<text class='uid-text' x='{0}' y='{1}' dominant-baseline='central' text-anchor='middle' style='stroke:white; stroke-width:0.6em'", x, y);
244 if (rotation != 0.0) {
245 m_patches += String::format(" transform='rotate({0}, {1}, {2})'", rotation, x, y);
246 }
247 m_patches += String::format(">{0}</text>\n", text);
248 }
249
250 m_patches += String::format("<text class='uid-text' x='{0}' y='{1}' dominant-baseline='central' text-anchor='middle' fill='{2}'", x, y, color);
251
252 if (rotation != 0.0) {
253 m_patches += String::format(" transform='rotate({0}, {1}, {2})'", rotation, x, y);
254 }
255 m_patches += String::format(">{0}</text>\n", text);
256}
257
258/*---------------------------------------------------------------------------*/
259/*---------------------------------------------------------------------------*/
260
261void SimpleHTMLMeshAMRPatchExporter::Impl::
262_writePatch(const CartesianPatch& patch)
263{
264 CellGroup cells = patch.patchInterface()->cells();
265 if (cells.null())
266 return;
267 IMesh* mesh = cells.mesh();
268 Int32 mesh_dim = mesh->dimension();
269 if (mesh_dim != 2)
270 ARCANE_FATAL("Invalid dimension ({0}) for mesh. Only 2D mesh is allowed", mesh_dim);
271
272 // Note: comme par défaut en SVG l'origin est en haut à gauche, on prend pour chaque
273 // valeur de 'Y' son opposé pour l'affichage.
274 // NOTE: on pourrait utiliser les transformations de SVG mais c'est plus compliqué à
275 // traiter pour l'affichage du texte
276
277 VariableNodeReal3& nodes_coord = mesh->nodesCoordinates();
278 Real mul_value = 1000.0;
279 const Real min_val = std::numeric_limits<Real>::lowest();
280 const Real max_val = std::numeric_limits<Real>::max();
281 Real2 min_bbox(max_val, max_val);
282 Real2 max_bbox(min_val, min_val);
283
284 // Calcul le centre des mailles et la bounding box du groupe de maille.
285 std::map<Int32, Real2> cells_center;
286 ENUMERATE_CELL (icell, cells) {
287 Cell cell = *icell;
288 Real3 center;
289 Integer nb_node = cell.nbNode();
290 if (nb_node > 0) {
291 for (Integer i = 0; i < nb_node; ++i) {
292 Real3 node_coord_3d = nodes_coord[cell.node(i)] * mul_value;
293 Real2 node_coord(node_coord_3d.x, -node_coord_3d.y);
294 min_bbox = math::min(min_bbox, node_coord);
295 max_bbox = math::max(max_bbox, node_coord);
296 center += node_coord_3d;
297 }
298 center /= nb_node;
299 }
300 Real2 center_2d(center.x, -center.y);
301 cells_center[cell.localId()] = center_2d;
302 }
303
304 // Affiche pour chaque maille son contour et son uniqueId().
305 ENUMERATE_CELL (icell, cells) {
306 Cell cell = *icell;
307 Real2 cell_pos = cells_center[cell.localId()];
308 Integer nb_node = cell.nbNode();
309 m_patches += "<path d='";
310 nb_node = cell.typeInfo()->linearTypeInfo()->nbLocalNode();
311 for (Integer i = 0; i < nb_node; ++i) {
312 Real3 node_coord_3d = nodes_coord[cell.node(i)];
313 Real2 node_coord(node_coord_3d.x, -node_coord_3d.y);
314 node_coord *= mul_value;
315 if (i == 0)
316 m_patches += "M ";
317 else
318 m_patches += "L ";
319 // fait une homothétie pour bien voir les faces en cas de soudure.
320 Real2 coord = cell_pos + (node_coord - cell_pos) * 0.98;
321 m_patches += String::format("{0} {1} ", coord.x, coord.y);
322 }
323 m_patches += "z'";
324 if (icell->hasFlags(ItemFlags::II_Overlap) && icell->hasFlags(ItemFlags::II_InPatch)) {
325 if (cell.isOwn())
326 m_patches += " fill='magenta'";
327 else
328 m_patches += " fill='darkmagenta'";
329 }
330 if (icell->hasFlags(ItemFlags::II_Overlap)) {
331 if (cell.isOwn())
332 m_patches += " fill='orange'";
333 else
334 m_patches += " fill='darkorange'";
335 }
336 else {
337 if (cell.isOwn())
338 m_patches += " fill='yellow'";
339 else
340 m_patches += " fill='gold'";
341 }
342 m_patches += " stroke='black'";
343 m_patches += " stroke-width='1'/>\n";
344 _writeText(cell_pos.x, cell_pos.y, "blue", String::fromNumber(cell.uniqueId().asInt64()), 0.0, false);
345 }
346
347 // Affiche pour chaque noeud son uniqueId().
348 {
349 // Ensemble des noeuds déjà traités pour ne les afficher qu'une fois.
350 std::set<Int32> nodes_done;
351 ENUMERATE_CELL (icell, cells) {
352 Cell cell = *icell;
353 Integer nb_node = cell.nbNode();
354 for (Integer i = 0; i < nb_node; ++i) {
355 Node node = cell.node(i);
356 Int32 lid = node.localId();
357 if (nodes_done.find(lid) != nodes_done.end())
358 continue;
359 nodes_done.insert(lid);
360 Real3 coord_3d = nodes_coord[node];
361 Real2 coord(coord_3d.x, -coord_3d.y);
362 coord *= mul_value;
363 _writeText(coord.x, coord.y, "green", String::fromNumber(node.uniqueId().asInt64()), 0.0, true);
364 }
365 }
366 }
367
368 // Affiche pour chaque face son uniqueId().
369 // Fait une éventuelle rotation pour que l'affichage du numéro de la face soit aligné
370 // avec son segment.
371 {
372 // Ensemble des faces déjà traitées pour ne les afficher qu'une fois.
373 std::set<Int32> faces_done;
374 ENUMERATE_CELL (icell, cells) {
375 Cell cell = *icell;
376 Integer nb_face = cell.nbFace();
377 for (Integer i = 0; i < nb_face; ++i) {
378 Face face = cell.face(i);
379 Int32 lid = face.localId();
380 if (faces_done.find(lid) != faces_done.end())
381 continue;
382 faces_done.insert(lid);
383 // En cas de maillage multi-dim, il est possible
384 // d'avoir des faces réduites à un point.
385 if (face.nbNode() < 2)
386 continue;
387 Real3 node0_coord = nodes_coord[face.node(0)];
388 Real3 node1_coord = nodes_coord[face.node(1)];
389 Real3 face_coord_3d = (node0_coord + node1_coord) / 2.0;
390
391 Real2 face_coord(face_coord_3d.x, -face_coord_3d.y);
392 face_coord *= mul_value;
393 Real3 direction = node1_coord - node0_coord;
394 direction = math::mutableNormalize(direction);
395 // TODO: vérifier entre -1.0 et 1.0
396 // Calcule l'angle de la rotation pour que l'affichage numéro de la face soit aligné avec
397 // le trait du bord de la face.
398 double angle = math::abs(std::asin(direction.y)) / M_PI * 180.0;
399 Real2 cell_center = cells_center[cell.localId()];
400 Real2 coord = cell_center + (face_coord - cell_center) * 0.92;
401 _writeText(coord.x, coord.y, "red", String::fromNumber(face.uniqueId().asInt64()), angle, true);
402 }
403 }
404 }
405}
406
407/*---------------------------------------------------------------------------*/
408/*---------------------------------------------------------------------------*/
409
410/*---------------------------------------------------------------------------*/
411/*---------------------------------------------------------------------------*/
412
413SimpleHTMLMeshAMRPatchExporter::
414SimpleHTMLMeshAMRPatchExporter()
415: m_p(new Impl())
416{}
417
418/*---------------------------------------------------------------------------*/
419/*---------------------------------------------------------------------------*/
420
421SimpleHTMLMeshAMRPatchExporter::
422~SimpleHTMLMeshAMRPatchExporter()
423{
424 delete m_p;
425}
426
427/*---------------------------------------------------------------------------*/
428/*---------------------------------------------------------------------------*/
429
430void SimpleHTMLMeshAMRPatchExporter::
431addPatch(const CartesianPatch& patch)
432{
433 m_p->addPatch(patch);
434}
435
436/*---------------------------------------------------------------------------*/
437/*---------------------------------------------------------------------------*/
438
439void SimpleHTMLMeshAMRPatchExporter::
440write(std::ostream& ofile)
441{
442 m_p->write(ofile);
443}
444
445/*---------------------------------------------------------------------------*/
446/*---------------------------------------------------------------------------*/
447
448} // End namespace Arcane
449
450/*---------------------------------------------------------------------------*/
451/*---------------------------------------------------------------------------*/
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
#define ENUMERATE_CELL(name, group)
Enumérateur générique d'un groupe de mailles.
bool isNull() const
Méthode permettant de savoir si la position du patch est nulle.
Patch AMR d'un maillage cartésien.
Integer index() const
Index du patch dans le tableau des patchs.
AMRPatchPosition position() const
Méthode permettant de récupérer la position du patch dans le maillage cartesien.
Integer level() const
Niveau du patch.
virtual Integer dimension()=0
Dimension du maillage (1D, 2D ou 3D).
@ II_Overlap
[AMR Patch] L'entité est marquée comme étant de recouvrement par au moins un patch AMR.
Definition ItemFlags.h:87
@ II_InPatch
[AMR Patch] L'entité est marqué comme étant dans un patch AMR.
Definition ItemFlags.h:92
IMesh * mesh() const
Maillage auquel appartient ce groupe (0 pour le group nul)
Definition ItemGroup.h:126
Constructeur de chaîne de caractère unicode.
Vue sur une chaîne de caractères UTF-8.
Definition StringView.h:47
__host__ __device__ Real2 min(Real2 a, Real2 b)
Retourne le minimum de deux Real2.
Definition MathUtils.h:336
T max(const T &a, const T &b, const T &c)
Retourne le maximum de trois éléments.
Definition MathUtils.h:392
ItemGroupT< Cell > CellGroup
Groupe de mailles.
Definition ItemTypes.h:183
MeshVariableScalarRefT< Node, Real3 > VariableNodeReal3
Grandeur au noeud de type coordonnées.
Real2 & mutableNormalize(Real2 &v)
Normalise le couple.
Definition Real2.h:469
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
Int32 Integer
Type représentant un entier.
double Real
Type représentant un réel.
@ Cell
Le maillage est AMR par maille.
Definition MeshKind.h:52
std::int32_t Int32
Type entier signé sur 32 bits.