From 621481b356f85625656c2188f1b656b5dfab315f Mon Sep 17 00:00:00 2001 From: "christian.wuelker" Date: Thu, 21 Mar 2013 13:27:38 +0000 Subject: [PATCH] Added GridOut::write_svg git-svn-id: https://svn.dealii.org/trunk@28966 0785d39b-7218-0410-832d-ea1e28bc413d --- deal.II/doc/news/changes.h | 8 + deal.II/include/deal.II/grid/grid_out.h | 139 +++++- deal.II/source/grid/grid_out.cc | 635 +++++++++++++++++++++++- 3 files changed, 779 insertions(+), 3 deletions(-) diff --git a/deal.II/doc/news/changes.h b/deal.II/doc/news/changes.h index 120da077a6..7874501638 100644 --- a/deal.II/doc/news/changes.h +++ b/deal.II/doc/news/changes.h @@ -69,6 +69,14 @@ this function.
    +
  1. Added GridOut::write_svg to allow for the output of two-dimensional +triangulations in two space dimensions in the SVG format (Scalable Vector +Graphics, an XML-based vector image format recommended by the World +Wide Web Consortium W3C). This function also provides cell coloring +and cell labeling for the visualization of basic cell properties. +
    +(Christian Wülker, 2013/03/21) +
  2. Added TimerOutput::reset to remove the collected information so far and added a new frequency TimerOutput::never to only output information if triggered by print_summary(). diff --git a/deal.II/include/deal.II/grid/grid_out.h b/deal.II/include/deal.II/grid/grid_out.h index 910f47494c..967c991f6b 100644 --- a/deal.II/include/deal.II/grid/grid_out.h +++ b/deal.II/include/deal.II/grid/grid_out.h @@ -737,6 +737,93 @@ namespace GridOutFlags void parse_parameters (ParameterHandler ¶m); }; + + /** + * Flags controlling SVG output. + */ + struct Svg + { + /// Thickness of the lines between cells + unsigned int line_thickness; + /// Thickness of lines at the boundary + unsigned int boundary_line_thickness; + + /// Margin around the plotted area + unsigned int margin_in_percent; + + /** + * Background style. + */ + enum Background { + /// Use transparent value of SVG + transparent, + /// Use white background + white, + /// Use a gradient from white (top) to steelblue (bottom), and add date and time plus a "deal.II" logo. + dealii }; + + Background background; + + /** + * Cell coloring. + */ + enum Coloring { + /// No cell coloring + none, + /// Convert the material id into the cell color + material_id, + /// Convert the level number into the cell color + level_number, + /// Convert the subdomain id into the cell color + subdomain_id }; + + Coloring coloring; + + /** + * Cell labeling (fixed order). + * + * The following booleans determine which property of the cell + * shall be displayed as text in the middle of each cell. + */ + bool label_level_number; /// default: true + bool label_cell_index; /// default: true + bool label_material_id; /// default: false + bool label_subdomain_id; /// default: false + //bool label_level_subdomain_id; // TODO [CW]: not yet implemented ... + + /// Draw a colorbar next to the plotted grid with respect to the chosen coloring of the cells + bool draw_colorbar; + /// Draw a legend next to the plotted grid, explaining the label of the cells + bool draw_legend; + + /** + * Constructor. + */ + Svg (const unsigned int line_thickness = 3, + const unsigned int boundary_line_thickness = 7, + const unsigned int margin_in_percent = 7, + const Background background = dealii, + const Coloring coloring = material_id, + const bool label_level_number = true, + const bool label_cell_index = true, + const bool label_material_id = false, + const bool label_subdomain_id = false, + const bool draw_colorbar = true, + const bool draw_legend = true); + + /** + * Declare parameters in + * ParameterHandler. + */ +// static void declare_parameters (ParameterHandler ¶m); + + /** + * Parse parameters of + * ParameterHandler. + */ +// void parse_parameters (ParameterHandler ¶m); + }; + } @@ -840,7 +927,10 @@ public: /// write() calls write_xfig() xfig, /// write() calls write_msh() - msh + msh, + /// write() calls write_svg() + svg + /// write() calls write_svg() }; /** @@ -1110,6 +1200,43 @@ public: std::ostream &out, const Mapping *mapping=0) const; + /** + * Write the triangulation in the + * SVG format. + * + * SVG (Scalable Vector Graphics) is + * an XML-based vector image format + * recommended by the World Wide Web + * Consortium (W3C). This function + * conforms to the latest + * specification SVG 1.1, released + * on August 16, 2011. + * + * The cells of the triangulation + * are written as polygons with + * additional lines at the boundary + * of the triangulation. A coloring + * of the cells is further possible + * in order to visualize a + * certain property of the cells + * such as their level or material + * id. A colorbar can be drawn to + * encode the chosen coloring. + * Moreover, a cell label can be + * added, showing level index, etc. + * + * @note Only implemented for the + * two-dimensional grids in two + * space dimensions. + * + */ + template + void write_svg (const Triangulation &tria, + std::ostream &out) const; + + void write_svg (const Triangulation<2,2> &tria, + std::ostream &out) const; + /** * Write grid to @p out according * to the given data format. This @@ -1176,6 +1303,11 @@ public: */ void set_flags (const GridOutFlags::XFig &flags); + /** + * Set flags for SVG output + */ + void set_flags (const GridOutFlags::Svg &flags); + /** * Provide a function which tells us which * suffix with a given output format @@ -1319,6 +1451,11 @@ private: */ GridOutFlags::XFig xfig_flags; + /** + * Flags used for Svg output. + */ + GridOutFlags::Svg svg_flags; + /** * Write the grid information about * faces to @p out. Only those faces diff --git a/deal.II/source/grid/grid_out.cc b/deal.II/source/grid/grid_out.cc index 4aad4358bb..e3f21733a6 100644 --- a/deal.II/source/grid/grid_out.cc +++ b/deal.II/source/grid/grid_out.cc @@ -340,6 +340,30 @@ namespace GridOutFlags boundary_thickness = param.get_integer("Boundary width"); } + Svg::Svg(const unsigned int line_thickness, + const unsigned int boundary_line_thickness, + const unsigned int margin_in_percent, + const Background background, + const Coloring coloring, + const bool label_level_number, + const bool label_cell_index, + const bool label_material_id, + const bool label_subdomain_id, + const bool draw_colorbar, + const bool draw_legend) : + line_thickness(line_thickness), + boundary_line_thickness(boundary_line_thickness), + margin_in_percent(margin_in_percent), + background(background), + coloring(coloring), + label_level_number(label_level_number), + label_cell_index(label_cell_index), + label_material_id(label_material_id), + label_subdomain_id(label_subdomain_id), + draw_colorbar(draw_colorbar), + draw_legend(draw_legend) + {} + } // end namespace GridOutFlags @@ -406,6 +430,12 @@ void GridOut::set_flags (const GridOutFlags::XFig &flags) +void GridOut::set_flags (const GridOutFlags::Svg &flags) +{ + svg_flags = flags; +} + + std::string GridOut::default_suffix (const OutputFormat output_format) { @@ -425,6 +455,8 @@ GridOut::default_suffix (const OutputFormat output_format) return ".fig"; case msh: return ".msh"; + case svg: + return ".svg"; default: Assert (false, ExcNotImplemented()); return ""; @@ -465,6 +497,9 @@ GridOut::parse_output_format (const std::string &format_name) if (format_name == "msh") return msh; + if (format_name == "svg") + return svg; + AssertThrow (false, ExcInvalidState ()); // return something weird return OutputFormat(-1); @@ -474,7 +509,7 @@ GridOut::parse_output_format (const std::string &format_name) std::string GridOut::get_output_format_names () { - return "none|dx|gnuplot|eps|ucd|xfig|msh"; + return "none|dx|gnuplot|eps|ucd|xfig|msh|svg"; } @@ -1174,7 +1209,7 @@ void GridOut::write_xfig ( { //TODO[GK]: Simplify after deprecation period is over case GridOutFlags::XFig::material_id: - out << static_cast(cell->material_id()) + 32; + out << static_cast(cell->material_id()) + 32; break; case GridOutFlags::XFig::level_number: out << cell->level() + 8; @@ -1274,7 +1309,599 @@ void GridOut::write_xfig ( } +template +void GridOut::write_svg(const Triangulation &tria, std::ostream &out) const +{ + Assert(false, ExcNotImplemented()); +} + + +void GridOut::write_svg(const Triangulation<2,2> &tria, std::ostream &out) const +{ + + const int dim = 2; + +//------------------ determine the bounding box of the given triangulation, +//------------------ the materials being used, the levels being used, +//------------------ the subdomains being used (, and the level subdomains being used) + unsigned int n_materials = 0; + unsigned int n_levels = 0; + unsigned int n_subdomains = 0; + //unsigned int n_level_subdomains = 0; // TODO [CW]: CellAccessor does not + // seem to possess this attribute ... + + const unsigned int height = 4000; // TODO [CW]: consider other options ... + + unsigned int cell_label_font_size = round((height/100.) * 2.25); // initial font size for cell labels + unsigned int font_size = round((height/100.) * 2.); // font size for date, time, legend, and colorbar + + // get date and time + time_t time_stamp; + tm *now; + time_stamp = time(0); + now = localtime(&time_stamp); + + Triangulation<2,2>::active_cell_iterator cell = tria.begin_active(), endc = tria.end(); + + double x_min = cell->vertex(0)[0]; + double x_max = cell->vertex(0)[0]; + double y_min = cell->vertex(0)[1]; + double y_max = cell->vertex(0)[1]; + + unsigned int min_level = cell->level(); + unsigned int max_level = cell->level(); + + // array for the materials being used + unsigned int materials[256]; + for (unsigned int material_index = 0; material_index < 256; material_index++) materials[material_index] = 0; + + // array for the levels being used + unsigned int levels[256]; + for (unsigned int level_index = 0; level_index < 256; level_index++) levels[level_index] = 0; + + // array for the subdomains being used + unsigned int subdomains[256]; + for (unsigned int subdomain_index = 0; subdomain_index < 256; subdomain_index++) subdomains[subdomain_index] = 0; + + /* + // array for the level subdomains being used + int level_subdomains[256]; + for(int level_subdomain_index = 0; level_subdomain_index < 256; level_subdomain_index++) level_subdomains[level_subdomain_index] = 0; + */ + + // determine the bounding box of the triangulation and check the cells for material and subdomain + for (; cell != endc; ++cell) + { + for (unsigned int vertex_index = 0; vertex_index < GeometryInfo::vertices_per_cell; vertex_index++) + { + if (cell->vertex(vertex_index)[0] < x_min) x_min = cell->vertex(vertex_index)[0]; + if (cell->vertex(vertex_index)[0] > x_max) x_max = cell->vertex(vertex_index)[0]; + if (cell->vertex(vertex_index)[1] < y_min) y_min = cell->vertex(vertex_index)[1]; + if (cell->vertex(vertex_index)[1] > y_max) y_max = cell->vertex(vertex_index)[1]; + } + + if ((unsigned int)cell->level() < min_level) min_level = cell->level(); + if ((unsigned int)cell->level() > max_level) max_level = cell->level(); + + if ((unsigned int)cell->material_id()) materials[(unsigned int)cell->material_id()] = 1; + else if ((unsigned int)cell->material_id() == 0) materials[0] = 1; + + if ((int)cell->level()) levels[(unsigned int)cell->level()] = 1; + else if ((unsigned int)cell->level() == 0) levels[0] = 1; + + if ((unsigned int)cell->subdomain_id()) subdomains[(unsigned int)cell->subdomain_id()] = 1; + else if ((unsigned int)cell->subdomain_id() == 0) subdomains[0] = 1; + + // if((unsigned int)(cell->level_subdomain_id())) level_subdomains[(unsigned int)cell->level_subdomain_id()] = 1; + // else if((unsigned int)(cell->level_subdomain_id()) == 0) level_subdomains[0] = 1; + } + + const double x_dimension = x_max - x_min; + const double y_dimension = y_max - y_min; + + // count the materials being used + for (unsigned int material_index = 0; material_index < 256; material_index++) + { + if (materials[material_index]) n_materials++; + } + + // count the levels being used + for (unsigned int level_index = 0; level_index < 256; level_index++) + { + if (levels[level_index]) n_levels++; + } + + // count the subdomains being used + for (unsigned int subdomain_index = 0; subdomain_index < 256; subdomain_index++) + { + if (subdomains[subdomain_index]) n_subdomains++; + } + + /* + // count the level subdomains being used + for(int level_subdomain_index = 0; level_subdomain_index < 256; level_subdomain_index++) + { + if(level_subdomains[level_subdomain_index]) n_level_subdomains++; + } + */ + +//------------------ create the svg file with internal style sheet + const unsigned int width = round(height * (x_dimension/y_dimension)); + unsigned int additional_width = 0; + + if (svg_flags.draw_legend && (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id)) // || svg_flags.label_level_subdomain_id )) + { + additional_width = round(height * .4); // additional width for legend + } + else if (svg_flags.draw_colorbar && svg_flags.coloring) + { + additional_width = round(height * .175); // additional width for colorbar // TODO [CW]: not enough / automatic adjustment ... + } + + out << "" + << '\n' << '\n'; + + // basic svg header and inclusion of the corresponding style sheet + out << "" + << '\n' << '\n'; + + if (svg_flags.background == 2) + { + out << " " << '\n' + << " " << '\n' + << " " << '\n' + << " " + << '\n'; + } + + out << '\n'; + + out << "" + << '\n' << '\n'; + + out << " " + << '\n'; + + if (svg_flags.background == 2) + { + unsigned int x_offset = 0; + + if (svg_flags.margin_in_percent) x_offset = round((height/100.) * (svg_flags.margin_in_percent/2.)); + else x_offset = round(height * .025); + + out << " " + << "deal.II" << "" + << '\n'; + + out << " " + << now->tm_mday << '/' << now->tm_mon + 1 << '/' << now->tm_year + 1900 + << " - " << now->tm_hour << ':'; + + if (now->tm_min < 10) out << '0'; + + out << now->tm_min + << "" + << '\n' << '\n'; + } + +//------------------ draw the cells + out << " " + << '\n'; + + cell = tria.begin_active(); + endc = tria.end(); + + for (; cell != endc; ++cell) + { + // draw the current cell + out << " material_id(); + break; + case 2: + out << (unsigned int)cell->level(); + break; + case 3: + out << (unsigned int)cell->subdomain_id(); + break; + // case 4: out << (unsigned int)cell->level_subdomain_id(); break; + default: + break; + } + + out << '\"'; + } + + out << " d=\"" + << "M " << round(((cell->vertex(0)[0] - x_min) / x_dimension) * (width - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << ' ' << round(((cell->vertex(0)[1] - y_min) / y_dimension) * (height - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << " L " << round(((cell->vertex(1)[0] - x_min) / x_dimension) * (width - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << ' ' << round(((cell->vertex(1)[1] - y_min) / y_dimension) * (height - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << " L " << round(((cell->vertex(3)[0] - x_min) / x_dimension) * (width - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << ' ' << round(((cell->vertex(3)[1] - y_min) / y_dimension) * (height - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << " L " << round(((cell->vertex(2)[0] - x_min) / x_dimension) * (width - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << ' ' << round(((cell->vertex(2)[1] - y_min) / y_dimension) * (height - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << " L " << round(((cell->vertex(0)[0] - x_min) / x_dimension) * (width - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << ' ' << round(((cell->vertex(0)[1] - y_min) / y_dimension) * (height - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << "\"/>" + << '\n'; + + // label the current cell + if (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id) // || svg_flags.label_level_subdomain_id) + { + out << " level() + << "\" x=\"" << round(((cell->center()[0] - x_min) / x_dimension) * (width - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << "\" y=\"" << round(((cell->center()[1] - y_min) / y_dimension) * (height - (height/100.) * 2. * svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << "\">"; + + if (svg_flags.label_level_number) + { + out << cell->level(); + } + + if (svg_flags.label_cell_index) + { + if (svg_flags.label_level_number) out << ','; + out << cell->index(); + } + + if (svg_flags.label_material_id) + { + if (svg_flags.label_level_number || svg_flags.label_cell_index) out << ','; + out << (int)cell->material_id(); + } + + if (svg_flags.label_subdomain_id) + { + if (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id) out << ','; + out << cell->subdomain_id(); + } + /* + if(svg_flags.label_level_subdomain_id) + { + if(svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id) out << ','; + out << cell->level_subdomain_id(); + } + */ + out << "" + << '\n'; + } + +//------------------ draw the boundary + if (svg_flags.boundary_line_thickness) + { + for (unsigned int faceIndex = 0; faceIndex < GeometryInfo::faces_per_cell; faceIndex++) + { + if (cell->at_boundary(faceIndex)) + { + out << " face(faceIndex)->vertex(0)[0] - x_min)/x_dimension) * (width - (height/100.) *2.* svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << "\" y1=\"" << round(((cell->face(faceIndex)->vertex(0)[1] - y_min)/y_dimension) * (height - (height/100.) *2.* svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << "\" x2=\"" << round(((cell->face(faceIndex)->vertex(1)[0] - x_min)/x_dimension) * (width - (height/100.) *2.* svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << "\" y2=\"" << round(((cell->face(faceIndex)->vertex(1)[1] - y_min)/y_dimension) * (height - (height/100.) *2.* svg_flags.margin_in_percent) + ((height/100.) * svg_flags.margin_in_percent)) + << "\"/>" + << '\n'; + } + } + } + } + +//------------------ draw the legend + if (svg_flags.draw_legend && (svg_flags.label_level_number || svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id)) // || svg_flags.label_level_subdomain_id )) + { + out << '\n'; + + out << " " + << '\n'; + unsigned int additional_width = 0; + if (!svg_flags.margin_in_percent) additional_width = round((height/100.) * 2.5); + + out << " " + << '\n'; + + unsigned int line_offset = 0; + + out << " " + << "cell label" + << "" + << '\n'; + + if (svg_flags.label_level_number) + { + out << " " + << "level_number"; + + if (svg_flags.label_cell_index || svg_flags.label_material_id || svg_flags.label_subdomain_id) // || svg_flags.label_level_subdomain_id) + out << ','; + + out << "" + << '\n'; + } + + if (svg_flags.label_cell_index) + { + out << " " + << "cell_index"; + + if (svg_flags.label_material_id || svg_flags.label_subdomain_id) // || svg_flags.label_level_subdomain_id) + out << ','; + + out << "" + << '\n'; + } + + if (svg_flags.label_material_id) + { + out << " " + << "material_id"; + + if (svg_flags.label_subdomain_id) // || svg_flags.label_level_subdomain_id) + out << ','; + + out << "" + << '\n'; + } + + if (svg_flags.label_subdomain_id) + { + out << " " + << "subdomain_id"; + + // if(svg_flags.label_level_subdomain_id) + // out << ','; + + out << "" + << '\n'; + } + /* + if(svg_flags.label_level_subdomain_id) + { + out << " " + << "level_subdomain_id" + << "" << '\n'; + } + */ + } + +//------------------ draw the colorbar + if (svg_flags.draw_colorbar && svg_flags.coloring) + { + out << '\n'; + + out << " " + << '\n'; + + unsigned int additional_width = 0; + if (!svg_flags.margin_in_percent) additional_width = round((height/100.) * 2.5); + + unsigned int n = 0; + + out << " "; + + switch (svg_flags.coloring) + { + case 1: + out << "material_id", n = n_materials; + break; + case 2: + out << "level_number", n = n_levels; + break; + case 3: + out << "subdomain_id", n = n_subdomains; + break; + // case 4: out << "level_subdomain_id", n = n_level_subdomains; break; + default: + break; + } + + out << "" + << '\n'; + + unsigned int element_height = floor(((height/100.) * (72.5 - 2.*svg_flags.margin_in_percent)) / n); + unsigned int element_width = round((height/100.) * 2.5); + + int labeling_index = 0; + + for (unsigned int index = 0; index < n; index++) + { + switch (svg_flags.coloring) + { + case 1: + while (!materials[labeling_index]) labeling_index++; + break; + case 2: + while (!levels[labeling_index]) labeling_index++; + break; + case 3: + while (!subdomains[labeling_index]) labeling_index++; + break; + // case 4: while(!level_subdomains[labeling_index]) labeling_index++; break; + default: + break; + } + + out << " " + << '\n'; + + unsigned int additional_width = 0; + if (!svg_flags.margin_in_percent) additional_width = round((height/100.) * 2.5); + + out << " " << labeling_index; + + if (index == n-1) out << " max"; + if (index == 0) out << " min"; + + out << "" + << '\n'; + + labeling_index++; + } + } + +//------------------ finalize the svg file + out << '\n'; + out << ""; + + out.flush(); + +} unsigned int GridOut::n_boundary_faces (const Triangulation<1> &) const @@ -2612,6 +3239,10 @@ void GridOut::write (const Triangulation &tria, case msh: write_msh (tria, out); return; + + case svg: + write_svg (tria, out); + return; } Assert (false, ExcInternalError()); -- 2.39.5