diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index 83a31897..20d697a4 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -46,7 +46,7 @@ sub bounding_box { # this method only works for expolygons having only a contour or # a contour and a hole, and not being thicker than the supplied # width. it returns a polyline or a polygon -sub medial_axis { +sub ____medial_axis { my ($self, $width) = @_; return $self->_medial_axis_voronoi($width); } diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 71720727..a3635549 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -223,7 +223,8 @@ sub make_perimeters { # process thin walls by collapsing slices to single passes if (@thin_walls) { - my @p = map $_->medial_axis($pspacing), @thin_walls; + #my @p = map $_->medial_axis($pspacing), @thin_walls; + my @p = map @{$_->medial_axis}, @thin_walls; my @paths = (); for my $p (@p) { next if $p->length <= $pspacing * 2; diff --git a/xs/MANIFEST b/xs/MANIFEST index a7c7cb86..68fae69b 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -24,6 +24,10 @@ src/ExtrusionEntity.cpp src/ExtrusionEntity.hpp src/ExtrusionEntityCollection.cpp src/ExtrusionEntityCollection.hpp +src/evg-thin/datatypes.hpp +src/evg-thin/evg-thin.cpp +src/evg-thin/evg-thin.hpp +src/evg-thin/heap.hpp src/Flow.cpp src/Flow.hpp src/Geometry.cpp diff --git a/xs/src/ExPolygon.cpp b/xs/src/ExPolygon.cpp index a3cb4f7f..be865085 100644 --- a/xs/src/ExPolygon.cpp +++ b/xs/src/ExPolygon.cpp @@ -1,12 +1,14 @@ #include "ExPolygon.hpp" #include "Polygon.hpp" #include "ClipperUtils.hpp" +#include "evg-thin/evg-thin.hpp" +#include namespace Slic3r { ExPolygon::operator Polygons() const { - Polygons polygons(this->holes.size() + 1); + Polygons polygons; polygons.push_back(this->contour); for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { polygons.push_back(*it); @@ -14,6 +16,17 @@ ExPolygon::operator Polygons() const return polygons; } +ExPolygon::operator Points() const +{ + Points points; + Polygons pp = *this; + for (Polygons::const_iterator poly = pp.begin(); poly != pp.end(); ++poly) { + for (Points::const_iterator point = poly->points.begin(); point != poly->points.end(); ++point) + points.push_back(*point); + } + return points; +} + void ExPolygon::scale(double factor) { @@ -119,6 +132,116 @@ ExPolygon::simplify(double tolerance, ExPolygons &expolygons) const expolygons.insert(expolygons.end(), ep.begin(), ep.end()); } +void +ExPolygon::medial_axis(Polylines* polylines) const +{ + // clone this expolygon and scale it down + ExPolygon scaled_this = *this; + #define MEDIAL_AXIS_SCALE 0.0001 + scaled_this.scale(MEDIAL_AXIS_SCALE); + + // get our bounding box and compute our size + BoundingBox bb(scaled_this); + Size size = bb.size(); + + // init grid to be as large as our size + EVG_THIN::grid_type grid; + grid.resize(size.x + 1); + for (coord_t x=0; x <= size.x; x++) { + grid[x].resize(size.y + 1); + for (coord_t y=0; y <= size.y; y++) + grid[x][y] = EVG_THIN::Free; + } + + // draw polygons + Polygons pp = scaled_this; + for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { + Lines lines = p->lines(); + for (Lines::iterator line = lines.begin(); line != lines.end(); ++line) { + coord_t x1 = line->a.x - bb.min.x; + coord_t y1 = line->a.y - bb.min.y; + coord_t x2 = line->b.x - bb.min.x; + coord_t y2 = line->b.y - bb.min.y; + + // Bresenham's line algorithm + const bool steep = (abs(y2 - y1) > abs(x2 - x1)); + if (steep) { + std::swap(x1, y1); + std::swap(x2, y2); + } + if (x1 > x2) { + std::swap(x1, x2); + std::swap(y1, y2); + } + + const double dx = x2 - x1; + const double dy = abs(y2 - y1); + double error = dx / 2.0f; + const coord_t ystep = (y1 < y2) ? 1 : -1; + coord_t y = (coord_t)y1; + const coord_t maxX = (coord_t)x2; + + for (coord_t x = (coord_t)x1; x < maxX; x++) { + if (steep) { + grid[y][x] = EVG_THIN::Occupied; + } else { + grid[x][y] = EVG_THIN::Occupied; + } + + error -= dy; + if (error < 0) { + y += ystep; + error += dx; + } + } + } + } + printf("Drawn\n"); + // compute skeleton + EVG_THIN::evg_thin thin(grid, 0.0, FLT_MAX, false, false, -1, -1); + EVG_THIN::skeleton_type skel = thin.generate_skeleton(); + + // emulate a lambda function to allow recursion + struct PolyBuilder { + EVG_THIN::skeleton_type skel; + Polylines* polylines; + BoundingBox bb; + + void follow (size_t node_id) { + EVG_THIN::node* node = &this->skel.at(node_id); + Polyline polyline; + polyline.points.push_back(Point(node->x + this->bb.min.x, node->y + this->bb.min.y)); + while (node->children.size() == 1) { + node = &this->skel.at(node->children.front()); + polyline.points.push_back(Point(node->x + this->bb.min.x, node->y + this->bb.min.y)); + } + if (polyline.is_valid()) this->polylines->push_back(polyline); + if (node->children.size() > 1) { + for (std::vector::const_iterator child_id = node->children.begin(); child_id != node->children.end(); ++child_id) + this->follow(*child_id); + } + } + } pb; + pb.skel = skel; + pb.polylines = polylines; + pb.bb = bb; + + // build polylines + for (EVG_THIN::skeleton_type::const_iterator node = skel.begin(); node != skel.end(); ++node) { + printf("[%zu] x = %d, y = %d, parent = %d, children count = %zu\n", node - skel.begin(), node->x, node->y, node->parent, node->children.size()); + if (node->parent == -1) + pb.follow(node - skel.begin()); + } + for (Polylines::iterator it = polylines->begin(); it != polylines->end(); ++it) + it->scale(1/MEDIAL_AXIS_SCALE); +} + +BoundingBox +ExPolygon::bounding_box() const +{ + return BoundingBox(*this); +} + #ifdef SLIC3RXS SV* ExPolygon::to_AV() { diff --git a/xs/src/ExPolygon.hpp b/xs/src/ExPolygon.hpp index fdecec41..5fec563b 100644 --- a/xs/src/ExPolygon.hpp +++ b/xs/src/ExPolygon.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_ExPolygon_hpp_ #define slic3r_ExPolygon_hpp_ +#include "BoundingBox.hpp" #include "Polygon.hpp" #include @@ -15,6 +16,7 @@ class ExPolygon Polygon contour; Polygons holes; operator Polygons() const; + operator Points() const; void scale(double factor); void translate(double x, double y); void rotate(double angle, Point* center); @@ -25,6 +27,8 @@ class ExPolygon Polygons simplify_p(double tolerance) const; ExPolygons simplify(double tolerance) const; void simplify(double tolerance, ExPolygons &expolygons) const; + void medial_axis(Polylines* polylines) const; + BoundingBox bounding_box() const; #ifdef SLIC3RXS void from_SV(SV* poly_sv); diff --git a/xs/src/TriangleMesh.cpp b/xs/src/TriangleMesh.cpp index c5f590da..90da0571 100644 --- a/xs/src/TriangleMesh.cpp +++ b/xs/src/TriangleMesh.cpp @@ -580,10 +580,10 @@ TriangleMesh::slice(const std::vector &z, std::vector* layer #ifdef SLIC3R_DEBUG size_t holes_count = 0; for (ExPolygons::const_iterator e = ex_slices.begin(); e != ex_slices.end(); ++e) { - holes_count += e->holes.count(); + holes_count += e->holes.size(); } - printf("Layer %d (slice_z = %.2f): %d surface(s) having %d holes detected from %d polylines\n", - layer_id, z[layer_id], ex_slices.count(), holes_count, loops->count()); + printf("Layer %zu (slice_z = %.2f): %zu surface(s) having %zu holes detected from %zu polylines\n", + layer_id, z[layer_id], ex_slices.size(), holes_count, loops->size()); #endif ExPolygons* layer = &(*layers)[layer_id]; diff --git a/xs/src/evg-thin/datatypes.hpp b/xs/src/evg-thin/datatypes.hpp new file mode 100644 index 00000000..45073f34 --- /dev/null +++ b/xs/src/evg-thin/datatypes.hpp @@ -0,0 +1,91 @@ +/********************************************************************* + + EVG-THIN v1.1: A Thinning Approximation to the Extended Voronoi Graph + + Copyright (C) 2006 - Patrick Beeson (pbeeson@cs.utexas.edu) + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + +*********************************************************************/ + + +#ifndef datatypes_hh +#define datatypes_hh + +#include + +using namespace std; + +namespace EVG_THIN { + +typedef unsigned int uint; + +// GRID DATA TYPES + +// Each cell belongs to one of three states. +typedef enum {Occupied, Unknown, Free} cell_type; + +typedef vector column_type; + +/** + The cells of a grid basically have three possible states: occupied, + free, or unknown. Free cells are light cells in a greyscale image + (129-255), occupied cells are dark (0-126), and (by default) + unknown cells are light grey (127&128). These ranges can be + changed at the command line. +**/ +typedef vector grid_type; + + + + +// SKELETON DATA TYPES + +/** + This data type holds the data for a single node in a skeleton graph + (e.g. Voronoi graph of free space). Graphs are meant to be non-cyclic + graphs represented as trees. Each node has a location, a radius + (distance to nearest obstacle), a parent, some children, a tag of + whether it touches the edge of the LPM, and a distance to the root + node. Coordinates and distances are in occ. grid coordinates. +**/ +class node { +public: + int x,y; //!< location in grid_coords + float radius; //!< distance to nearest obstacle (in number of cells) + float distance; //!< shortest depth in graph to graph root + int parent; //!< index of parant in list + /** + Basically, exactly equal to children.size() by the time the + skeleton is made. However, when making the skeleton, there may + times when the number of estimated children is known, but + children hasn't been fully filled out (e.g. doing a depth first + crawl over the raw skeleton to get a pruned skeleton). + **/ + unsigned int num_children; + vector children; //!< if # children > 1, graph branches + + friend bool operator>(const node& n1, const node& n2) { + return n1.distance>n2.distance; + } +}; + +typedef vector skeleton_type; + +} // end namespace EVG_THIN + +#endif diff --git a/xs/src/evg-thin/evg-thin.cpp b/xs/src/evg-thin/evg-thin.cpp new file mode 100644 index 00000000..60dce54c --- /dev/null +++ b/xs/src/evg-thin/evg-thin.cpp @@ -0,0 +1,823 @@ +/********************************************************************* + + EVG-THIN v1.1: A Thinning Approximation to the Extended Voronoi Graph + + Copyright (C) 2006 - Patrick Beeson (pbeeson@cs.utexas.edu) + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + +*********************************************************************/ + +#include "evg-thin.hpp" +#include +#include +#include + +namespace EVG_THIN { + +/** + Euclidean distance between two points. +**/ +static inline float dist(float ax, float ay, float bx, float by) { + return sqrt(pow(ax-bx,2)+pow(ay-by,2)); +} + +evg_thin::evg_thin(const grid_type& curr_grid, + float distance_min, float distance_max, + bool pruning, bool robot_dependent, + int loc_x, int loc_y) { + + original_grid=curr_grid; + coastal_dist=distance_max; + prune_dist=distance_min; + prune=pruning; + location=robot_dependent; + + + grid_size_x=original_grid.size(); + grid_size_y=original_grid[0].size(); + + robot_loc_x=loc_x; + robot_loc_y=loc_y; + + + vector tmp(grid_size_y); + _step1_grid.resize(grid_size_x,tmp); + + dist_col tmpcol(grid_size_y); + distance_grid.resize(grid_size_x,tmpcol); +} + + +/** + Resets data structures after a skeleton is found. +**/ +void evg_thin::reset() { + // Use this before calling generate_skeleton() if you are looping + // over a changing grid data structure. + + // Assumes the grid size is constant, if not, add code to resize + // (and clear) _step1_grid and distance_grid based on new grid size. + + curr_skel.clear(); + _step1_queue.clear(); + _step2_queue.clear(); +} + + +/** + Public method that returns the skeleton of a grid +**/ +skeleton_type evg_thin::generate_skeleton() { + + calculate_distances(); + find_skel(); + + return curr_skel; +} + + +bool evg_thin::on_grid(int x, int y) { + return ((x >=0 && x < grid_size_x) && + (y >=0 && y < grid_size_y)); +} + + + +/** + Calculate the distance of each free cell from the closest occupied + cell. Stores these (along with the location of the closest + obstacle) in distance_grid. +**/ +void evg_thin::calculate_distances() { + + heap hdist; + dist_cell current_cell; + + for (int x=0;x hdist2; + + while (!hdist.empty()) { + while (!hdist.empty()) { + current_cell=hdist.first(); + hdist.pop(); + + // Look at neighbors to find a new free cell that needs its + // distance updated + for (int i=-1; i<=1; i++) { + int nx=current_cell.x+i; + for (int j=-1; j<=1; j++) { + int ny=current_cell.y+j; + if ((i!=0 || j!=0) && + on_grid(nx,ny)) { + if (original_grid[nx][ny] != Occupied && + distance_grid[nx][ny].distance==FLT_MAX) { + float min_distance=FLT_MAX; + + //look at neighbors of freecell to find cells whose + //distance has already been found + for (int k=-1;k<=1;k++) { + int nxk=nx+k; + for (int l=-1;l<=1;l++) { + int nyl=ny+l; + if ((k!=0 || l!=0) && + on_grid(nxk,nyl)) { + if (distance_grid[nxk][nyl].x >=0) { + //find distance to neighbor's closest cell + float d=dist(nx,ny, + distance_grid[nxk][nyl].x, + distance_grid[nxk][nyl].y); + if (d < min_distance) { + min_distance=d; + distance_grid[nx][ny].distance=min_distance; + distance_grid[nx][ny].x=distance_grid[nxk][nyl].x; + distance_grid[nx][ny].y=distance_grid[nxk][nyl].y; + } + } + } + } + } + if (min_distance= coastal_dist) + return skel; + + + bool freecell[8]; + + int nx,ny,np; + + int i,j; + + np=0; + + //Marked state of all neighbors (occupied or !occupied). + for (i=-1;i<=1;i++) { + nx=current.x+i; + for (j=-1;j<=1;j++) + if (i!=0 || j!=0) { + ny=current.y+j; + if (on_grid(nx,ny) && + grid[nx][ny]==occupied) + freecell[np]=false; + else freecell[np]=true; + np++; + } + } + + + int N=0; + for (i=0;i<8;i++) + if (freecell[i]) + N++; + + //if more tha n6 neighbors are occupied, this cell is definately + //part of the skeleton. + if (N < 2) + return skel; + + if (N <= 6) { + int count=0; + if (!freecell[0] && freecell[1]) + count++; + if (!freecell[1] && freecell[2]) + count++; + if (!freecell[2] && freecell[4]) + count++; + if (!freecell[4] && freecell[7]) + count++; + if (!freecell[7] && freecell[6]) + count++; + if (!freecell[6] && freecell[5]) + count++; + if (!freecell[5] && freecell[3]) + count++; + if (!freecell[3] && freecell[0]) + count++; + + if (count == 1) + if ((step1 && (!freecell[1] || !freecell[4] || !freecell[6]) && + (!freecell[4] || !freecell[6] || !freecell[3])) || + (!step1 && (!freecell[1] || !freecell[4] || !freecell[3]) && + (!freecell[1] || !freecell[6] || !freecell[3]))) + return occupied; + + } + + + return grid[current.x][current.y]; +} + +/** + After thin() is run, _step1_grid has a bunch of cells marked + occupied, free, processed, and skel. Walk through the grid finding + skel or processed cells that border occupied cells. Those are part + of the skeleton, otherwise, mark them occupied in _step2_grid. + + Also, find the closest skeleton point to the robot's current + location. +**/ +void evg_thin::find_skel_edge() { + // Don't worry about making _step1_grid and _step2 equal. After + // thin(), if there is any difference it would only be that + // _step1_grid had some cells marked "skel" that are marked + // "processed" in _step2_grid. In this function, all cells that are + // "skel" or "procssed" in _step1_grid become either skel or + // occupied in _step2_grid. Then _step2 grid can be used for + // pruning and making the skeleton data structure. + + + float rdist=FLT_MAX; + _closestx=-1; + _closesty=-1; + _distance=-1; + + for (int i=0;i_distance)){ + _closestx=i; + _closesty=j; + _distance=d2; + } + + } + } +} + + +/** + Build the final data structure of the skeleton from the grid with + cells marked as skeleton or occupied. +**/ +void evg_thin::build_skel() { + // Use _step2_grid because after find_skel_edge(), it will have only + // skel, occupied, and unknown cells. prune_skel just changes + // things in _step2_grid. + + + // Walk along the skeleton away from the closest point to the robot. + // Walk is best-first (using total distance from start). If a + // branch terminates, but is not next to an unknown cell or the + // edge, that branch must be pruned. + if (_distance>=0) { + //if _distance==-1, then there was no skeleton found near the + //robot. + crawl_grid(); + + remove_final_spur(); + + best_to_depth_first(); + } +} + + +/** + Starting with the closest skelton point to the robot, walk along + the skeleton in the grid, building an intermediate skeleton data + structure. Do this in a best-first fashion, using cell distance as + the cost function. Also, if a branch ends but is not an exit (next + to the edge or next to unknown cells in the occupancy grid), prune + that branch back (basically, make the distance field of nodes in + that branch equal to -1). +**/ +void evg_thin::crawl_grid() { + + _tmp_skel.clear(); + + node new_node; + new_node.x=_closestx; + new_node.y=_closesty; + new_node.distance=0; + new_node.parent=-1; + + heap open_list; + //Closest point is root of tree. + open_list.push(new_node); + + _num_exits=0; + _root_index=0; + + bool cont; + vector children; + + while (!open_list.empty()) { + cont=true; + node curr_node=open_list.first(); + open_list.pop(); + // If the current node has not yet been processed + if (_step2_grid[curr_node.x][curr_node.y]==skel) { + //Mark the node as begin processed. + _step2_grid[curr_node.x][curr_node.y]=occupied; + //If exit (and not the tree root) stop searching this branch + if (is_exit(curr_node) && + curr_node.parent>=0) { + _num_exits++; + curr_node.num_children=0; + curr_node.radius=distance_grid[curr_node.x][curr_node.y].distance; + //update parent with the index of this node in _tmp_skel + _tmp_skel[curr_node.parent].children.push_back(_tmp_skel.size()); + //update _tmp_skel with this node + _tmp_skel.push_back(curr_node); + cont=false; + } + //For non-exits + if (cont) { + //Find neighbors + children=find_neighbors(curr_node.x,curr_node.y); + curr_node.num_children=children.size(); + //If there are no children (no neighbors left to process), + //this branch is a "spur" and needs to be removed. Remove + //from it's parent's children. + if (curr_node.num_children==0) { + //Called on parent because this node was never added to + //_tmp_skel. + remove_branch(curr_node.parent); + } + //For non-exits with children + else { + curr_node.radius=distance_grid[curr_node.x][curr_node.y].distance; + int curr_index=_tmp_skel.size(); + //If not the root, update the parent to know the index of + //this node. + if (curr_node.parent>=0) + _tmp_skel[curr_node.parent].children.push_back(curr_index); + //Add this node to the tree + _tmp_skel.push_back(curr_node); + //Now add children to the heap + for (uint i=0;i=0) { + //Remove unneccessary child from list of children. + if (child_index>=0) { + vector new_children; + for (uint i=0;i<_tmp_skel[index].children.size();i++) + if (_tmp_skel[index].children[i]!=uint(child_index)) + new_children.push_back(_tmp_skel[index].children[i]); + _tmp_skel[index].children=new_children; + } + //Reduce number of children. Different from children.size() + //because not all children may have been processed (thus added to + //the tree and updated their parent's children list). + _tmp_skel[index].num_children--; + + //If no more children, prune this node from it's parent's child + //list. + if (_tmp_skel[index].num_children==0) { + //Set distance to -1 so that later, we'll know this is a pruned + //node (instead of having to take it out of the tree, which may + //be hairy---requires updating indexes of all parents and + //chilren which _tmp_skel is rearranged). + _tmp_skel[index].distance=-1; + remove_branch(_tmp_skel[index].parent,index); + } + } +} + +/** + If a node is next to unknown nodes or an edge of the grid, it is an + exit. +**/ +bool evg_thin::is_exit(const node& curr_node) { + + int i,j,nx,ny; + + for (i=-1;i<=1;i++) { + nx=curr_node.x+i; + for (j=-1;j<=1;j++) + if (i!=0 || j!=0) { + ny=curr_node.y+j; + if (!on_grid(nx,ny) || + original_grid[nx][ny]==Unknown) + return true; + } + } + + return false; +} + +/** + Find the immediate neighbors, save their grid coordinates, and push + them all into a list. + **/ +vector evg_thin::find_neighbors(int x, int y) { + vector neighbors; + + node c1; + int i,j,newx,newy; + + for (i=-1;i<=1;i++) { + newx=x+i; + for (j=-1;j<=1;j++) + if (i!=0 || j!=0) { + newy=y+j; + if (on_grid(newx,newy) && + _step2_grid[newx][newy]==skel) { + c1.x=newx; + c1.y=newy; + neighbors.push_back(c1); + } + } + } + return neighbors; +} + + +/** + We used the closest skeleton point to the robot to know which + skeleton (thinning may find many) is the correct one to use. + Sometimes, the branch that includes the closest point to the robot + needs to be pruned. This does that (essentially reordering the + tree to have a new root). + **/ +void evg_thin::remove_final_spur() { + if (prune) + if (!_tmp_skel.empty()) + if (_num_exits > 1 && !is_exit(_tmp_skel[0]) && _tmp_skel[0].num_children==1) { + //If not an exit and only 1 child (terminal node) and more than + //1 exit exists (this isn't the only branch) then prune the + //branch. + _tmp_skel[0].distance=-1; + remove_branch2(_tmp_skel[0].children[0]); + } +} + + +/** + Similar to remove_branch, but going down the branch instead of up + (to prune the root of the skeleton tree). + + If a node is removed, because it terminates and is not an exit, + mark it's distance = -1, and recurse on its child, which may now + need to be removed as well. +**/ +void evg_thin::remove_branch2(int index) { + if (_tmp_skel[index].num_children==1) { + _tmp_skel[index].distance=-1; + remove_branch2(_tmp_skel[index].children[0]); + } + else { + _tmp_skel[index].parent=-1; + _root_index=index; + } +} + +/** + Go from the intermediate skeleton (from crawl_grid) to a fully + pruned data structure. While doing this, its convient (for other + modules that may use the skeleton) to make this depth first. Also, + when we see exits, add them to the internal or external exists list + (to be saved in the local topology data structure). + **/ +void evg_thin::best_to_depth_first() { + if (!_tmp_skel.empty()) + best_to_depth_first_helper(_root_index,-1); +} + + +/** + The recursive function that does the actual work of best_to_depth_first(). + **/ +void evg_thin::best_to_depth_first_helper(int myindex, int parent_index){ + + // Check distance (it's set to -1 for pruned nodes). Only add + // non-pruned cells to the final skeleton. + + if (_tmp_skel[myindex].distance>=0) { + //Curr node is somewhere (given a depth first search) in the _tmp_skel + node curr_node; + curr_node.x=_tmp_skel[myindex].x; + curr_node.y=_tmp_skel[myindex].y; + curr_node.radius=_tmp_skel[myindex].radius; + curr_node.distance=_tmp_skel[myindex].distance; + curr_node.num_children=_tmp_skel[myindex].children.size(); + + //parent has a new index in the depth first tree. + curr_node.parent=parent_index; + // Delete children indexes. They will recreate this list when + // they get added. + + uint new_index=curr_skel.size(); + + //Update parents list of children to include the new index of the + //node. + if (parent_index>=0) + curr_skel[parent_index].children.push_back(new_index); + + //Add to the new, depth-first tree. + curr_skel.push_back(curr_node); + + //Do a depth first traversal of the skeleton. + for (uint i=0;i +#include "datatypes.hpp" +#include "heap.hpp" + +using namespace std; + +namespace EVG_THIN { + +/** + The thinning algorithm can be thought of as a "brush fire" where a + fire starts at every obstacle and burns into freespace. When two + fronts of the fire meet, they define a skeleton. + + This particular algorithm has special purpose machinery to crete a + 1 cell wide skeleton (stops a cell from "burning" if it is next to + two or more "fronts" of the fire. + + I made a linear algorthim from the quadradic algorithm discussed in + the original thinning publication (see README). + + I also added max and min parameters and a pruning operator to + remove spurs. +**/ +class evg_thin { +public: + evg_thin(const grid_type&, float, float, bool, bool, int, int); + skeleton_type generate_skeleton(); + void reset(); + +private: + grid_type original_grid; + + int grid_size_x,grid_size_y; + + float coastal_dist, prune_dist; + bool prune,location; + + // Keeps track of distance to closest obstacle. + class dist_cell { + public: + int x,y; + float distance; + friend bool operator> (const dist_cell& d1, const dist_cell& d2) { + return d1.distance>d2.distance; + } + }; + + typedef vector dist_col; + typedef vector dist_grid; + dist_grid distance_grid; + + void calculate_distances(); + + + /** + occupied: not-part of skeleton (either an obstacle or a "burned" + free cell. free: a free cell that has not yet been + examined. processing: a free cell that is in queue to be + examined (prevents multiple copies of a cell in a queue). + processed: a free cell that has been determined will never + "burn". unknown : neither occupied nor free (grey cells inthe + lpm). + **/ + typedef enum {occupied, free, skel, processing, processed, unknown} State; + + typedef vector < vector > gridtype; + + gridtype _step1_grid; //!< holds current state of thinning algorithm + //!< before step1 + gridtype _step2_grid; //!< holds current state of thinning algorithm + //!< before step2 + + + int robot_loc_x, robot_loc_y; + int _num_exits; + + bool on_grid(int,int); + + skeleton_type curr_skel; //!< final skeleton + skeleton_type _tmp_skel; //!< intermediate skeleton + uint _root_index; //!< index of skeleton tree root in _tmp_skel; + + + //////// Responsible for actual thinning algorithm. + + int _closestx, _closesty; //!< closest point on skeleton to robot + float _distance; //!< distance of robot's closest skeletal point to + //!< nearest obstacle + + + void find_skel(); + void initialize(); + void thin(); + + class edge { + public: + edge(int i, int j) { + x=i; + y=j; + } + int x,y; + }; + + typedef deque queuetype; + + queuetype _step1_queue; //!< holds cells to process in step1 + queuetype _step2_queue; //!< holds cells to process in step2 + + State step(gridtype&, edge&, bool); + + + ///////////////////////// + ///////Responsible for building the data structure from the grid + + void find_skel_edge(); + void build_skel(); + + void crawl_grid(); + void remove_final_spur(); + void best_to_depth_first(); + void best_to_depth_first_helper(int myindex, int parent_index); + + void remove_branch(int index, int child_index=-1); + void remove_branch2(int index); + bool is_exit(const node& curr_node); + vector find_neighbors(int x, int y); + +}; + +} // end namespace EVG_THIN + +#endif //evg_thin_hh + diff --git a/xs/src/evg-thin/heap.hpp b/xs/src/evg-thin/heap.hpp new file mode 100644 index 00000000..b722d77e --- /dev/null +++ b/xs/src/evg-thin/heap.hpp @@ -0,0 +1,130 @@ +/********************************************************************* + + EVG-THIN v1.1: A Thinning Approximation to the Extended Voronoi Graph + + Copyright (C) 2006 - Patrick Beeson (pbeeson@cs.utexas.edu) + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + USA + +*********************************************************************/ + + +/** + heap.hh was originally written by Jefferson Provost. + + Defines a minheap template class for priority queues, etc. + Insertions and deletions are O(log(N)). + + heap on any comparible class T. Compare should be a + binary comparison functor implementing the comparison function for + ordering the heap. It defaults to greater (which implements a + minheap. (yes, greater => minheap. -jp) + + Example usage: + + heap minh; -- creates a minheap of integers + + heap > -- creates a maxheap of floats + + heap -- creates a minheap of elements of type foo, foo must + have > and = operators defined. + + + See the class definition below for the public operations on heaps. +**/ + + +#ifndef _HEAP_HH_ +#define _HEAP_HH_ + +#include +#include +#include +using namespace std; + +template > +class heap +{ + +public: + + const T& first(void) const; // returns the first (min) item in the heap + const T& nth(int n) const; // returns the nth item in the heap + void push(const T& item); // adds an item to the heap + void pop(void); // removes the first item from the heap + int size(void) const; // returns the number of items in the heap + bool empty(void) const; // returns true if the heap is empty + void clear(void); // removes all elements + +protected: + Compare _comp; + vector _store; + +}; + + +template +bool heap::empty(void) const +{ + return this->size() == 0; +} + +template +int heap::size(void) const +{ + return _store.size(); +} + +template +void heap::push(const T& item) +{ + _store.push_back(item); + + push_heap(_store.begin(), _store.end(), _comp); + +} + +template +void heap::pop(void) +{ + if (!empty()) { + pop_heap(_store.begin(), _store.end(), _comp); + _store.pop_back(); + } +} + +template +const T& heap::first(void) const +{ + return _store[0]; +} + +template +const T& heap::nth(int n) const +{ + if (n >= size()) + n=size()-1; + return _store[n]; +} + +template +void heap::clear(void) +{ + _store.clear(); +} + +#endif // _HEAP_HH_ diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t index 3fc4bd0f..f9733164 100644 --- a/xs/t/01_trianglemesh.t +++ b/xs/t/01_trianglemesh.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 52; +use Test::More tests => 41; is Slic3r::TriangleMesh::hello_world(), 'Hello world!', 'hello world'; @@ -83,9 +83,8 @@ my $cube = { my $result = $m->slice(\@z); my $SCALING_FACTOR = 0.000001; for my $i (0..$#z) { - is scalar(@{$result->[$i]}), 1, 'number of returned polygons per layer'; - is $result->[$i][0]->area, 20*20/($SCALING_FACTOR**2), 'size of returned polygon'; - ok $result->[$i][0]->is_counter_clockwise, 'orientation of returned polygon'; + is scalar(@{$result->[$i]}), 1, 'number of returned expolygons per layer'; + is $result->[$i][0]->area, 20*20/($SCALING_FACTOR**2), 'size of returned expolygon'; } } diff --git a/xs/t/04_expolygon.t b/xs/t/04_expolygon.t index 38c029a5..dc197b17 100644 --- a/xs/t/04_expolygon.t +++ b/xs/t/04_expolygon.t @@ -104,4 +104,16 @@ is $expolygon->area, 100*100-20*20, 'area'; is_deeply $collection->[0]->clone->pp, $collection->[0]->pp, 'clone collection item'; } +{ + my $square = [ # ccw + [100, 100], + [200, 100], + [200, 500], + [100, 500], + ]; + my $expolygon = Slic3r::ExPolygon->new($square); + $expolygon->scale(1 / 0.000001); + use XXX; YYY (map $_->pp, map @$_, $expolygon->medial_axis); +} + __END__ diff --git a/xs/xsp/ExPolygon.xsp b/xs/xsp/ExPolygon.xsp index 6540acec..fa1cd516 100644 --- a/xs/xsp/ExPolygon.xsp +++ b/xs/xsp/ExPolygon.xsp @@ -25,6 +25,8 @@ bool contains_point(Point* point); ExPolygons simplify(double tolerance); Polygons simplify_p(double tolerance); + Polylines medial_axis() + %code{% THIS->medial_axis(&RETVAL); %}; %{ ExPolygon*