From 4156b51c18ffc533994b083a4857cf813724a12f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 27 Mar 2016 10:53:59 +0200 Subject: [PATCH] Debugging visualization of the gap fills into a SVG format, if SLIC3R_DEBUG is set. --- xs/src/libslic3r/Geometry.cpp | 481 ++++++++++++++++++++++++++++++++++ xs/src/libslic3r/Geometry.hpp | 8 +- xs/src/libslic3r/SVG.cpp | 90 +++++-- xs/src/libslic3r/SVG.hpp | 21 +- 4 files changed, 573 insertions(+), 27 deletions(-) diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index d4528e864..a6171881a 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -11,12 +11,186 @@ #include #include #include +#include #include #ifdef SLIC3R_DEBUG #include "SVG.hpp" #endif +#ifdef SLIC3R_DEBUG +namespace boost { namespace polygon { + +// The following code for the visualization of the boost Voronoi diagram is based on: +// +// Boost.Polygon library voronoi_graphic_utils.hpp header file +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +template +class voronoi_visual_utils { + public: + // Discretize parabolic Voronoi edge. + // Parabolic Voronoi edges are always formed by one point and one segment + // from the initial input set. + // + // Args: + // point: input point. + // segment: input segment. + // max_dist: maximum discretization distance. + // discretization: point discretization of the given Voronoi edge. + // + // Template arguments: + // InCT: coordinate type of the input geometries (usually integer). + // Point: point type, should model point concept. + // Segment: segment type, should model segment concept. + // + // Important: + // discretization should contain both edge endpoints initially. + template class Point, + template class Segment> + static + typename enable_if< + typename gtl_and< + typename gtl_if< + typename is_point_concept< + typename geometry_concept< Point >::type + >::type + >::type, + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< Segment >::type + >::type + >::type + >::type, + void + >::type discretize( + const Point& point, + const Segment& segment, + const CT max_dist, + std::vector< Point >* discretization) { + // Apply the linear transformation to move start point of the segment to + // the point with coordinates (0, 0) and the direction of the segment to + // coincide the positive direction of the x-axis. + CT segm_vec_x = cast(x(high(segment))) - cast(x(low(segment))); + CT segm_vec_y = cast(y(high(segment))) - cast(y(low(segment))); + CT sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y; + + // Compute x-coordinates of the endpoints of the edge + // in the transformed space. + CT projection_start = sqr_segment_length * + get_point_projection((*discretization)[0], segment); + CT projection_end = sqr_segment_length * + get_point_projection((*discretization)[1], segment); + + // Compute parabola parameters in the transformed space. + // Parabola has next representation: + // f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y). + CT point_vec_x = cast(x(point)) - cast(x(low(segment))); + CT point_vec_y = cast(y(point)) - cast(y(low(segment))); + CT rot_x = segm_vec_x * point_vec_x + segm_vec_y * point_vec_y; + CT rot_y = segm_vec_x * point_vec_y - segm_vec_y * point_vec_x; + + // Save the last point. + Point last_point = (*discretization)[1]; + discretization->pop_back(); + + // Use stack to avoid recursion. + std::stack point_stack; + point_stack.push(projection_end); + CT cur_x = projection_start; + CT cur_y = parabola_y(cur_x, rot_x, rot_y); + + // Adjust max_dist parameter in the transformed space. + const CT max_dist_transformed = max_dist * max_dist * sqr_segment_length; + while (!point_stack.empty()) { + CT new_x = point_stack.top(); + CT new_y = parabola_y(new_x, rot_x, rot_y); + + // Compute coordinates of the point of the parabola that is + // furthest from the current line segment. + CT mid_x = (new_y - cur_y) / (new_x - cur_x) * rot_y + rot_x; + CT mid_y = parabola_y(mid_x, rot_x, rot_y); + + // Compute maximum distance between the given parabolic arc + // and line segment that discretize it. + CT dist = (new_y - cur_y) * (mid_x - cur_x) - + (new_x - cur_x) * (mid_y - cur_y); + dist = dist * dist / ((new_y - cur_y) * (new_y - cur_y) + + (new_x - cur_x) * (new_x - cur_x)); + if (dist <= max_dist_transformed) { + // Distance between parabola and line segment is less than max_dist. + point_stack.pop(); + CT inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) / + sqr_segment_length + cast(x(low(segment))); + CT inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) / + sqr_segment_length + cast(y(low(segment))); + discretization->push_back(Point(inter_x, inter_y)); + cur_x = new_x; + cur_y = new_y; + } else { + point_stack.push(mid_x); + } + } + + // Update last point. + discretization->back() = last_point; + } + + private: + // Compute y(x) = ((x - a) * (x - a) + b * b) / (2 * b). + static CT parabola_y(CT x, CT a, CT b) { + return ((x - a) * (x - a) + b * b) / (b + b); + } + + // Get normalized length of the distance between: + // 1) point projection onto the segment + // 2) start point of the segment + // Return this length divided by the segment length. This is made to avoid + // sqrt computation during transformation from the initial space to the + // transformed one and vice versa. The assumption is made that projection of + // the point lies between the start-point and endpoint of the segment. + template class Point, + template class Segment> + static + typename enable_if< + typename gtl_and< + typename gtl_if< + typename is_point_concept< + typename geometry_concept< Point >::type + >::type + >::type, + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< Segment >::type + >::type + >::type + >::type, + CT + >::type get_point_projection( + const Point& point, const Segment& segment) { + CT segment_vec_x = cast(x(high(segment))) - cast(x(low(segment))); + CT segment_vec_y = cast(y(high(segment))) - cast(y(low(segment))); + CT point_vec_x = x(point) - cast(x(low(segment))); + CT point_vec_y = y(point) - cast(y(low(segment))); + CT sqr_segment_length = + segment_vec_x * segment_vec_x + segment_vec_y * segment_vec_y; + CT vec_dot = segment_vec_x * point_vec_x + segment_vec_y * point_vec_y; + return vec_dot / sqr_segment_length; + } + + template + static CT cast(const InCT& value) { + return static_cast(value); + } +}; + +} } // namespace boost::polygon +#endif + using namespace boost::polygon; // provides also high() and low() namespace Slic3r { namespace Geometry { @@ -290,6 +464,294 @@ arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb) return positions; } +#ifdef SLIC3R_DEBUG +// The following code for the visualization of the boost Voronoi diagram is based on: +// +// Boost.Polygon library voronoi_visualizer.cpp file +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +namespace Voronoi { namespace Internal { + + typedef double coordinate_type; + typedef boost::polygon::point_data point_type; + typedef boost::polygon::segment_data segment_type; + typedef boost::polygon::rectangle_data rect_type; +// typedef voronoi_builder VB; + typedef boost::polygon::voronoi_diagram VD; + typedef VD::cell_type cell_type; + typedef VD::cell_type::source_index_type source_index_type; + typedef VD::cell_type::source_category_type source_category_type; + typedef VD::edge_type edge_type; + typedef VD::cell_container_type cell_container_type; + typedef VD::cell_container_type vertex_container_type; + typedef VD::edge_container_type edge_container_type; + typedef VD::const_cell_iterator const_cell_iterator; + typedef VD::const_vertex_iterator const_vertex_iterator; + typedef VD::const_edge_iterator const_edge_iterator; + + static const std::size_t EXTERNAL_COLOR = 1; + + inline void color_exterior(const VD::edge_type* edge) + { + if (edge->color() == EXTERNAL_COLOR) + return; + edge->color(EXTERNAL_COLOR); + edge->twin()->color(EXTERNAL_COLOR); + const VD::vertex_type* v = edge->vertex1(); + if (v == NULL || !edge->is_primary()) + return; + v->color(EXTERNAL_COLOR); + const VD::edge_type* e = v->incident_edge(); + do { + color_exterior(e); + e = e->rot_next(); + } while (e != v->incident_edge()); + } + + inline point_type retrieve_point(const std::vector &segments, const cell_type& cell) + { + assert(cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT || cell.source_category() == SOURCE_CATEGORY_SEGMENT_END_POINT); + return (cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT) ? low(segments[cell.source_index()]) : high(segments[cell.source_index()]); + } + + inline void clip_infinite_edge(const std::vector &segments, const edge_type& edge, coordinate_type bbox_max_size, std::vector* clipped_edge) + { + const cell_type& cell1 = *edge.cell(); + const cell_type& cell2 = *edge.twin()->cell(); + point_type origin, direction; + // Infinite edges could not be created by two segment sites. + if (cell1.contains_point() && cell2.contains_point()) { + point_type p1 = retrieve_point(segments, cell1); + point_type p2 = retrieve_point(segments, cell2); + origin.x((p1.x() + p2.x()) * 0.5); + origin.y((p1.y() + p2.y()) * 0.5); + direction.x(p1.y() - p2.y()); + direction.y(p2.x() - p1.x()); + } else { + origin = cell1.contains_segment() ? retrieve_point(segments, cell2) : retrieve_point(segments, cell1); + segment_type segment = cell1.contains_segment() ? segments[cell1.source_index()] : segments[cell2.source_index()]; + coordinate_type dx = high(segment).x() - low(segment).x(); + coordinate_type dy = high(segment).y() - low(segment).y(); + if ((low(segment) == origin) ^ cell1.contains_point()) { + direction.x(dy); + direction.y(-dx); + } else { + direction.x(-dy); + direction.y(dx); + } + } + coordinate_type koef = bbox_max_size / (std::max)(fabs(direction.x()), fabs(direction.y())); + if (edge.vertex0() == NULL) { + clipped_edge->push_back(point_type( + origin.x() - direction.x() * koef, + origin.y() - direction.y() * koef)); + } else { + clipped_edge->push_back( + point_type(edge.vertex0()->x(), edge.vertex0()->y())); + } + if (edge.vertex1() == NULL) { + clipped_edge->push_back(point_type( + origin.x() + direction.x() * koef, + origin.y() + direction.y() * koef)); + } else { + clipped_edge->push_back( + point_type(edge.vertex1()->x(), edge.vertex1()->y())); + } + } + + inline void sample_curved_edge(const std::vector &segments, const edge_type& edge, std::vector &sampled_edge, coordinate_type max_dist) + { + point_type point = edge.cell()->contains_point() ? + retrieve_point(segments, *edge.cell()) : + retrieve_point(segments, *edge.twin()->cell()); + segment_type segment = edge.cell()->contains_point() ? + segments[edge.twin()->cell()->source_index()] : + segments[edge.cell()->source_index()]; + ::boost::polygon::voronoi_visual_utils::discretize(point, segment, max_dist, &sampled_edge); + } + +} /* namespace Internal */ } // namespace Voronoi + +static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_diagram &vd, const ThickPolylines *polylines, const char *path) +{ + const double scale = 0.2; + const std::string inputSegmentPointColor = "lightseagreen"; + const coord_t inputSegmentPointRadius = coord_t(0.09 * scale / SCALING_FACTOR); + const std::string inputSegmentColor = "lightseagreen"; + const coord_t inputSegmentLineWidth = coord_t(0.03 * scale / SCALING_FACTOR); + + const std::string voronoiPointColor = "black"; + const coord_t voronoiPointRadius = coord_t(0.06 * scale / SCALING_FACTOR); + const std::string voronoiLineColorPrimary = "black"; + const std::string voronoiLineColorSecondary = "green"; + const std::string voronoiArcColor = "red"; + const coord_t voronoiLineWidth = coord_t(0.02 * scale / SCALING_FACTOR); + + const bool internalEdgesOnly = false; + const bool primaryEdgesOnly = false; + + BoundingBox bbox = BoundingBox(lines); + bbox.min.x -= coord_t(1. / SCALING_FACTOR); + bbox.min.y -= coord_t(1. / SCALING_FACTOR); + bbox.max.x += coord_t(1. / SCALING_FACTOR); + bbox.max.y += coord_t(1. / SCALING_FACTOR); + + ::Slic3r::SVG svg(path, bbox); + + if (polylines != NULL) + svg.draw(*polylines, "lime", "lime", voronoiLineWidth); + +// bbox.scale(1.2); + // For clipping of half-lines to some reasonable value. + // The line will then be clipped by the SVG viewer anyway. + const double bbox_dim_max = double(bbox.max.x - bbox.min.x) + double(bbox.max.y - bbox.min.y); + // For the discretization of the Voronoi parabolic segments. + const double discretization_step = 0.0005 * bbox_dim_max; + + // Make a copy of the input segments with the double type. + std::vector segments; + for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it) + segments.push_back(Voronoi::Internal::segment_type( + Voronoi::Internal::point_type(double(it->a.x), double(it->a.y)), + Voronoi::Internal::point_type(double(it->b.x), double(it->b.y)))); + + // Color exterior edges. + for (voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) + if (!it->is_finite()) + Voronoi::Internal::color_exterior(&(*it)); + + // Draw the end points of the input polygon. + for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) { + svg.draw(it->a, inputSegmentPointColor, inputSegmentPointRadius); + svg.draw(it->b, inputSegmentPointColor, inputSegmentPointRadius); + } + // Draw the input polygon. + for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) + svg.draw(Line(Point(coord_t(it->a.x), coord_t(it->a.y)), Point(coord_t(it->b.x), coord_t(it->b.y))), inputSegmentColor, inputSegmentLineWidth); + +#if 1 + // Draw voronoi vertices. + for (voronoi_diagram::const_vertex_iterator it = vd.vertices().begin(); it != vd.vertices().end(); ++it) + if (! internalEdgesOnly || it->color() != Voronoi::Internal::EXTERNAL_COLOR) + svg.draw(Point(coord_t(it->x()), coord_t(it->y())), voronoiPointColor, voronoiPointRadius); + + for (voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) { + if (primaryEdgesOnly && !it->is_primary()) + continue; + if (internalEdgesOnly && (it->color() == Voronoi::Internal::EXTERNAL_COLOR)) + continue; + std::vector samples; + std::string color = voronoiLineColorPrimary; + if (!it->is_finite()) { + Voronoi::Internal::clip_infinite_edge(segments, *it, bbox_dim_max, &samples); + if (! it->is_primary()) + color = voronoiLineColorSecondary; + } else { + // Store both points of the segment into samples. sample_curved_edge will split the initial line + // until the discretization_step is reached. + samples.push_back(Voronoi::Internal::point_type(it->vertex0()->x(), it->vertex0()->y())); + samples.push_back(Voronoi::Internal::point_type(it->vertex1()->x(), it->vertex1()->y())); + if (it->is_curved()) { + Voronoi::Internal::sample_curved_edge(segments, *it, samples, discretization_step); + color = voronoiArcColor; + } else if (! it->is_primary()) + color = voronoiLineColorSecondary; + } + for (std::size_t i = 0; i + 1 < samples.size(); ++i) + svg.draw(Line(Point(coord_t(samples[i].x()), coord_t(samples[i].y())), Point(coord_t(samples[i+1].x()), coord_t(samples[i+1].y()))), color, voronoiLineWidth); + } +#endif + + if (polylines != NULL) + svg.draw(*polylines, "blue", voronoiLineWidth); + + svg.Close(); +} +#endif /* SLIC3R_DEBUG */ + +// Euclidian distance of two boost::polygon points. +template +T dist(const boost::polygon::point_data &p1,const boost::polygon::point_data &p2) +{ + T dx = p2.x() - p1.x(); + T dy = p2.y() - p1.y(); + return sqrt(dx*dx+dy*dy); +} + +// Find a foot point of "px" on a segment "seg". +template +inline point_type project_point_to_segment(segment_type &seg, point_type &px) +{ + typedef typename point_type::coordinate_type T; + const point_type &p0 = low(seg); + const point_type &p1 = high(seg); + const point_type dir(p1.x()-p0.x(), p1.y()-p0.y()); + const point_type dproj(px.x()-p0.x(), px.y()-p0.y()); + const T t = (dir.x()*dproj.x() + dir.y()*dproj.y()) / (dir.x()*dir.x() + dir.y()*dir.y()); + assert(t >= T(-1e-6) && t <= T(1. + 1e-6)); + return point_type(p0.x() + t*dir.x(), p0.y() + t*dir.y()); +} + +template +inline const typename VD::point_type retrieve_cell_point(const typename VD::cell_type& cell, const SEGMENTS &segments) +{ + assert(cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT || cell.source_category() == SOURCE_CATEGORY_SEGMENT_END_POINT); + return (cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT) ? low(segments[cell.source_index()]) : high(segments[cell.source_index()]); +} + +template +inline std::pair +measure_edge_thickness(const VD &vd, const typename VD::edge_type& edge, const SEGMENTS &segments) +{ + typedef typename VD::coord_type T; + const typename VD::point_type pa(edge.vertex0()->x(), edge.vertex0()->y()); + const typename VD::point_type pb(edge.vertex1()->x(), edge.vertex1()->y()); + const typename VD::cell_type &cell1 = *edge.cell(); + const typename VD::cell_type &cell2 = *edge.twin()->cell(); + if (cell1.contains_segment()) { + if (cell2.contains_segment()) { + // Both cells contain a linear segment, the left / right cells are symmetric. + // Project pa, pb to the left segment. + const typename VD::segment_type segment1 = segments[cell1.source_index()]; + const typename VD::point_type p1a = project_point_to_segment(segment1, pa); + const typename VD::point_type p1b = project_point_to_segment(segment1, pb); + return std::pair(T(2.)*dist(pa, p1a), T(2.)*dist(pb, p1b)); + } else { + // 1st cell contains a linear segment, 2nd cell contains a point. + // The medial axis between the cells is a parabolic arc. + // Project pa, pb to the left segment. + const typename VD::point_type p2 = retrieve_cell_point(cell2, segments); + return std::pair(T(2.)*dist(pa, p2), T(2.)*dist(pb, p2)); + } + } else if (cell2.contains_segment()) { + // 1st cell contains a point, 2nd cell contains a linear segment. + // The medial axis between the cells is a parabolic arc. + const typename VD::point_type p1 = retrieve_cell_point(cell1, segments); + return std::pair(T(2.)*dist(pa, p1), T(2.)*dist(pb, p1)); + } else { + // Both cells contain a point. The left / right regions are triangular and symmetric. + const typename VD::point_type p1 = retrieve_cell_point(cell1, segments); + return std::pair(T(2.)*dist(pa, p1), T(2.)*dist(pb, p1)); + } +} + +// Converts the Line instances of Lines vector to VD::segment_type. +template +class Lines2VDSegments +{ +public: + Lines2VDSegments(const Lines &alines) : lines(alines) {} + typename VD::segment_type operator[](size_t idx) const { + return typename VD::segment_type( + typename VD::point_type(typename VD::coord_type(lines[idx].a.x), typename VD::coord_type(lines[idx].a.y)), + typename VD::point_type(typename VD::coord_type(lines[idx].b.x), typename VD::coord_type(lines[idx].b.y))); + } +private: + const Lines &lines; +}; + void MedialAxis::build(ThickPolylines* polylines) { @@ -373,6 +835,25 @@ MedialAxis::build(ThickPolylines* polylines) // append polyline to result polylines->push_back(polyline); } + + #ifdef SLIC3R_DEBUG + { + char path[2048]; + static int iRun = 0; + sprintf(path, "out/MedialAxis-%d.svg", iRun ++); + dump_voronoi_to_svg(this->lines, this->vd, polylines, path); + + + printf("Thick lines: "); + for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) { + ThickLines lines = it->thicklines(); + for (ThickLines::const_iterator it2 = lines.begin(); it2 != lines.end(); ++ it2) { + printf("%f,%f ", it2->a_width, it2->b_width); + } + } + printf("\n"); + } + #endif /* SLIC3R_DEBUG */ } void diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index af4667d42..0869f8065 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -52,7 +52,13 @@ class MedialAxis { void build(Polylines* polylines); private: - typedef voronoi_diagram VD; + class VD : public voronoi_diagram { + public: + typedef double coord_type; + typedef boost::polygon::point_data point_type; + typedef boost::polygon::segment_data segment_type; + typedef boost::polygon::rectangle_data rect_type; + }; VD vd; std::set edges, valid_edges; std::map > thickness; diff --git a/xs/src/libslic3r/SVG.cpp b/xs/src/libslic3r/SVG.cpp index af367ae76..444810044 100644 --- a/xs/src/libslic3r/SVG.cpp +++ b/xs/src/libslic3r/SVG.cpp @@ -19,18 +19,54 @@ SVG::SVG(const char* filename) ); } +SVG::SVG(const char* filename, const BoundingBox &bbox) + : arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min) +{ + this->f = fopen(filename, "w"); + float w = COORD(bbox.max.x - bbox.min.x); + float h = COORD(bbox.max.y - bbox.min.y); + fprintf(this->f, + "\n" + "\n" + "\n" + " \n" + " \n" + " \n", + h, w); +} + void -SVG::draw(const Line &line, std::string stroke) +SVG::draw(const Line &line, std::string stroke, coord_t stroke_width) { fprintf(this->f, - " arrows) fprintf(this->f, " marker-end=\"url(#endArrow)\""); fprintf(this->f, "/>\n"); } +void SVG::draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coord_t stroke_width) +{ + Pointf dir(line.b.x-line.a.x, line.b.y-line.a.y); + Pointf perp(-dir.y, dir.x); + coordf_t len = sqrt(perp.x*perp.x + perp.y*perp.y); + coordf_t da = coordf_t(0.5)*line.a_width/len; + coordf_t db = coordf_t(0.5)*line.b_width/len; + fprintf(this->f, + " \n", + COORD(line.a.x-da*perp.x-origin.x), + COORD(line.a.y-da*perp.y-origin.y), + COORD(line.b.x-db*perp.x-origin.x), + COORD(line.b.y-db*perp.y-origin.y), + COORD(line.b.x+db*perp.x-origin.x), + COORD(line.b.y+db*perp.y-origin.y), + COORD(line.a.x+da*perp.x-origin.x), + COORD(line.a.y+da*perp.y-origin.y), + fill.c_str(), stroke.c_str(), + (stroke_width == 0) ? 1.f : COORD(stroke_width)); +} + void SVG::draw(const Lines &lines, std::string stroke) { @@ -80,31 +116,45 @@ SVG::draw(const Polygons &polygons, std::string fill) } void -SVG::draw(const Polyline &polyline, std::string stroke) +SVG::draw(const Polyline &polyline, std::string stroke, coord_t stroke_width) { this->stroke = stroke; - this->path(this->get_path_d(polyline, false), false); + this->path(this->get_path_d(polyline, false), false, stroke_width); } void -SVG::draw(const Polylines &polylines, std::string stroke) +SVG::draw(const Polylines &polylines, std::string stroke, coord_t stroke_width) { for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) - this->draw(*it, stroke); + this->draw(*it, fill, stroke_width); +} + +void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std::string &stroke, coord_t stroke_width) +{ + for (ThickLines::const_iterator it = thicklines.begin(); it != thicklines.end(); ++it) + this->draw(*it, fill, stroke, stroke_width); } void -SVG::draw(const ThickPolylines &polylines, std::string stroke) +SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coord_t stroke_width) { for (ThickPolylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) - this->draw((Polyline)*it, stroke); + this->draw((Polyline)*it, stroke, stroke_width); +} + +void +SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coord_t stroke_width) +{ + for (ThickPolylines::const_iterator it = thickpolylines.begin(); it != thickpolylines.end(); ++ it) + draw(it->thicklines(), fill, stroke, stroke_width); } void -SVG::draw(const Point &point, std::string fill, unsigned int radius) +SVG::draw(const Point &point, std::string fill, coord_t iradius) { + float radius = (iradius == 0) ? 3.f : COORD(iradius); std::ostringstream svg; - svg << " "; @@ -112,22 +162,26 @@ SVG::draw(const Point &point, std::string fill, unsigned int radius) } void -SVG::draw(const Points &points, std::string fill, unsigned int radius) +SVG::draw(const Points &points, std::string fill, coord_t radius) { for (Points::const_iterator it = points.begin(); it != points.end(); ++it) this->draw(*it, fill, radius); } void -SVG::path(const std::string &d, bool fill) +SVG::path(const std::string &d, bool fill, coord_t stroke_width) { + float lineWidth = 0.f; + if (! fill) + lineWidth = (stroke_width == 0) ? 2.f : COORD(stroke_width); + fprintf( this->f, - " \n", + " \n", d.c_str(), fill ? this->fill.c_str() : "none", this->stroke.c_str(), - fill ? "0" : "2", + lineWidth, (this->arrows && !fill) ? " marker-end=\"url(#endArrow)\"" : "" ); } @@ -138,8 +192,8 @@ SVG::get_path_d(const MultiPoint &mp, bool closed) const std::ostringstream d; d << "M "; for (Points::const_iterator p = mp.points.begin(); p != mp.points.end(); ++p) { - d << COORD(p->x) << " "; - d << COORD(p->y) << " "; + d << COORD(p->x - origin.x) << " "; + d << COORD(p->y - origin.y) << " "; } if (closed) d << "z"; return d.str(); diff --git a/xs/src/libslic3r/SVG.hpp b/xs/src/libslic3r/SVG.hpp index f0ae06d04..94cfc27dd 100644 --- a/xs/src/libslic3r/SVG.hpp +++ b/xs/src/libslic3r/SVG.hpp @@ -13,27 +13,32 @@ class SVG public: bool arrows; std::string fill, stroke; - + Point origin; + SVG(const char* filename); - void draw(const Line &line, std::string stroke = "black"); + SVG(const char* filename, const BoundingBox &bbox); + void draw(const Line &line, std::string stroke = "black", coord_t stroke_width = 0); + void draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coord_t stroke_width = 0); void draw(const Lines &lines, std::string stroke = "black"); void draw(const IntersectionLines &lines, std::string stroke = "black"); void draw(const ExPolygon &expolygon, std::string fill = "grey"); void draw(const ExPolygons &expolygons, std::string fill = "grey"); void draw(const Polygon &polygon, std::string fill = "grey"); void draw(const Polygons &polygons, std::string fill = "grey"); - void draw(const Polyline &polyline, std::string stroke = "black"); - void draw(const Polylines &polylines, std::string stroke = "black"); - void draw(const ThickPolylines &polylines, std::string stroke = "black"); - void draw(const Point &point, std::string fill = "black", unsigned int radius = 3); - void draw(const Points &points, std::string fill = "black", unsigned int radius = 3); + void draw(const Polyline &polyline, std::string stroke = "black", coord_t stroke_width = 0); + void draw(const Polylines &polylines, std::string stroke = "black", coord_t stroke_width = 0); + void draw(const ThickLines &thicklines, const std::string &fill = "lime", const std::string &stroke = "black", coord_t stroke_width = 0); + void draw(const ThickPolylines &polylines, const std::string &stroke = "black", coord_t stroke_width = 0); + void draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coord_t stroke_width); + void draw(const Point &point, std::string fill = "black", coord_t radius = 0); + void draw(const Points &points, std::string fill = "black", coord_t radius = 0); void Close(); private: std::string filename; FILE* f; - void path(const std::string &d, bool fill); + void path(const std::string &d, bool fill, coord_t stroke_width = 0); std::string get_path_d(const MultiPoint &mp, bool closed = false) const; };