Shrink/Expand the outline of a polygon with holes - c++

I want to expand/shrink a polygon with holes using boost::polygon. So to clarify that a bit, I have a single data structure
boost::polygon::polygon_with_holes_data<int> inPoly
where inPoly contains data that describe a rectangular outline and a triangle which forms the hole within this rectangle (in picture below this is the left, black drawing).
Now I want to
a) expand the whole stuff so that the rectangle becomes bigger and the hole becomes smaller (resulting in the red polygon in image below) or
b) shrink it so that the rectangle becomes smaller and the hole bigger (resulting in the green image below).
The corners don't necessarily need to be straight, the also can be rounded or somehow "rough".
My question: how can this be done using boost::polygon?
Thanks!

I answered this Expand polygons with boost::geometry?
And yes you can teach Boost Geometry to act on Boost Polygon types:
#include <boost/geometry/geometries/adapted/boost_polygon.hpp>
I came up with a test polygon like you described:
boost::polygon::polygon_with_holes_data<int> inPoly;
bg::read_wkt("POLYGON ((0 0,0 1000,1000 1000,1000 0,0 0),(100 100,900 100,500 700,100 100))", inPoly);
Now, apparently we can't just buffer on the adapted polygon, nor can we bg::assign or bg::convert directly. So, I came up with an ugly workaround of converting to WKT and back. And then you can do the buffer, and conver back similarly.
It's not very elegant, but it does work:
poly in;
bg::read_wkt(boost::lexical_cast<std::string>(bg::wkt(inPoly)), in);
Full Demo
Include SVG output:
Live On Coliru
#include <boost/polygon/polygon.hpp>
#include <boost/polygon/polygon_set_data.hpp>
#include <boost/polygon/polygon_with_holes_data.hpp>
#include <boost/geometry.hpp>
#include <boost/geometry/strategies/buffer.hpp>
#include <boost/geometry/algorithms/buffer.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/adapted/boost_polygon.hpp>
#include <fstream>
namespace bp = boost::polygon;
namespace bg = boost::geometry;
using P = bp::polygon_with_holes_data<int>;
using PS = bp::polygon_set_data<int>;
using coordinate_type = bg::coordinate_type<P>::type;
int main() {
P inPoly, grow, shrink;
bg::read_wkt("POLYGON ((0 0,0 1000,1000 1000,1000 0,0 0),(100 100,900 100,500 700,100 100))", inPoly);
{
// define our boost geometry types
namespace bs = bg::strategy::buffer;
namespace bgm = bg::model;
using pt = bgm::d2::point_xy<coordinate_type>;
using poly = bgm::polygon<pt>;
using mpoly = bgm::multi_polygon<poly>;
// define our buffering strategies
using dist = bs::distance_symmetric<coordinate_type>;
bs::side_straight side_strategy;
const int points_per_circle = 12;
bs::join_round join_strategy(points_per_circle);
bs::end_round end_strategy(points_per_circle);
bs::point_circle point_strategy(points_per_circle);
poly in;
bg::read_wkt(boost::lexical_cast<std::string>(bg::wkt(inPoly)), in);
for (auto [offset, output_p] : { std::tuple(+15, &grow), std::tuple(-15, &shrink) }) {
mpoly out;
bg::buffer(in, out, dist(offset), side_strategy, join_strategy, end_strategy, point_strategy);
assert(out.size() == 1);
bg::read_wkt(boost::lexical_cast<std::string>(bg::wkt(out.front())), *output_p);
}
}
{
std::ofstream svg("output.svg");
using pt = bg::model::d2::point_xy<coordinate_type>;
boost::geometry::svg_mapper<pt> mapper(svg, 400, 400);
mapper.add(inPoly);
mapper.add(grow);
mapper.add(shrink);
mapper.map(inPoly, "fill-opacity:0.3;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
mapper.map(grow, "fill-opacity:0.05;fill:rgb(255,0,0);stroke:rgb(255,0,0);stroke-width:2");
mapper.map(shrink, "fill-opacity:0.05;fill:rgb(0,0,255);stroke:rgb(0,0,255);stroke-width:2");
}
}
The output.svg written:

More or less accidentally I found boost::polygon also provides a single function for that which is quite easy to use: boost::polygon::polygon_set_data offers a function resize() which is doing exactly what is described above. Using the additional, parameters corner_fill_arc and num_segments rounded corners can be created.
No idea why this function is located in boost::polygon::polygon_set_data and not in boost::polygon::polygon_with_holes_data which in my opinion would be the more logically place for such a function...

Related

How to correctly use VTK ConstrainedDelaunay2D?

I've started from the VTK ConstrainedDelaunay2D example and added my own points:
#include <vtkSmartPointer.h>
#include <vtkDelaunay2D.h>
#include <vtkCellArray.h>
#include <vtkProperty.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolygon.h>
#include <vtkMath.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkNamedColors.h>
#include <vtkVersionMacros.h> // For version macros
int main(int, char *[])
{
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
int ptsHeight = 400;
std::vector<std::vector<int>> pts{ {166, 127},{103, 220},{166, 190},{174, 291},{189, 226},{227, 282},{213, 187},{242, 105},{196, 131},{182, 83} };
for (size_t i = 0; i < pts.size(); i++)
{
// !important: flip y
int x = pts[i][0];
int y = ptsHeight - pts[i][1];
points->InsertNextPoint(x, y, 0);
}
vtkSmartPointer<vtkPolyData> aPolyData = vtkSmartPointer<vtkPolyData>::New();
aPolyData->SetPoints(points);
// Create a cell array to store the polygon in
vtkSmartPointer<vtkCellArray> aCellArray = vtkSmartPointer<vtkCellArray>::New();
// Define a polygonal hole with a clockwise polygon
vtkSmartPointer<vtkPolygon> aPolygon = vtkSmartPointer<vtkPolygon>::New();
for (unsigned int i = 0; i < pts.size(); i++)
{
aPolygon->GetPointIds()->InsertNextId(i);
}
aCellArray->InsertNextCell(aPolygon);
// Create a polydata to store the boundary. The points must be the
// same as the points we will triangulate.
vtkSmartPointer<vtkPolyData> boundary =
vtkSmartPointer<vtkPolyData>::New();
boundary->SetPoints(aPolyData->GetPoints());
boundary->SetPolys(aCellArray);
// Triangulate the grid points
vtkSmartPointer<vtkDelaunay2D> delaunay =
vtkSmartPointer<vtkDelaunay2D>::New();
delaunay->SetInputData(aPolyData);
delaunay->SetSourceData(boundary);
// Visualize
vtkSmartPointer<vtkPolyDataMapper> meshMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
meshMapper->SetInputConnection(delaunay->GetOutputPort());
vtkSmartPointer<vtkNamedColors> colors =
vtkSmartPointer<vtkNamedColors>::New();
vtkSmartPointer<vtkActor> meshActor =
vtkSmartPointer<vtkActor>::New();
meshActor->SetMapper(meshMapper);
meshActor->GetProperty()->EdgeVisibilityOn();
meshActor->GetProperty()->SetEdgeColor(colors->GetColor3d("Peacock").GetData());
meshActor->GetProperty()->SetInterpolationToFlat();
meshActor->GetProperty()->SetBackfaceCulling(true);
// Create a renderer, render window, and interactor
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);
// Add the actor to the scene
renderer->AddActor(meshActor);
//renderer->AddActor(boundaryActor);
renderer->SetBackground(colors->GetColor3d("Mint").GetData());
// Render and interact
renderWindow->SetSize(640, 480);
renderWindow->Render();
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}
I'm experiencing two issues:
I get different results if I flip the Y coordinates: why is that ?
Why are there faces pointing in the wrong direction (flipped normal / wrong winding )?
Here's what I mean by the 1st issue:
If I don't flip the Y coordinates I get this:
I get the same effect if I don't flip the Y axis but insert the boundary polygon in reverse order:
for (unsigned int i = 0; i < pts.size(); i++)
{
aPolygon->GetPointIds()->InsertNextId(pts.size() - 1 - i);
}
I don't think I fully understand how the boundary/constraint works.
I thoght that the same points should produce the same triangulation wether the vertices are flipped vertically or not. (I suspect the order of indices changes then ?)
Regarding the second issue (unpredictable flipped faces) I'm not sure what the best way forward is. I had a look at the vtkDelaunay2D class and couldn't find anything related.
(I've tried setting projection plane mode to VTK_DELAUNAY_XY_PLANE, but it didn't seem to affect the output)
I've also tried to use vtkPolyDataNormals but got no output:
vtkSmartPointer<vtkPolyDataNormals> normalGenerator = vtkSmartPointer<vtkPolyDataNormals>::New();
normalGenerator->SetInputData(delaunay->GetOutput());
normalGenerator->ComputePointNormalsOff();
normalGenerator->ComputeCellNormalsOn();
normalGenerator->FlipNormalsOn();
normalGenerator->Update();
(normalGenerator's output has 0 cells and points)
Is there a way to compute constrained delaunay triangulation for a list of 2d points and ensure all the faces point the same way ? (If so, how ? Would it be possible to do this with the vtkDelaunay2D class alone or is it necessary to use other filters?)
Any hints/tips are more than welcome :)
I'm using VTK 8.2 by the way.
the flipping in y effectively reverses the faces orientation (what is clockwise becomes anti-clockwise, like in a mirror).
I'm not sure I can reproduce your example above. A quick test in python seems to give the expected behavior, maybe you can start from this and map it to your c++ version:
import vedo
pts = [
[166, 127],
[103, 220],
[166, 190],
[174, 291],
[189, 226],
[227, 282],
[213, 187],
[242, 105],
[196, 131],
[182, 83],
]
ids = [[2,4,6], [0,2,8]] # faces to erase by pt-index (clockwise)
dly = vedo.delaunay2D(pts, mode='xy', boundaries=ids)
dly.c('grey5').lc('red4').lw(2)
labels = vedo.Points(pts).labels('id').z(1)
vedo.show(labels, dly, axes=1)

When implmenting shapes using structures and classes C++

Hi guys so I'm trying to draw a tower by implementing shapes using structures and classes in C++. Below is what I'm trying to draw using 6 different shapes: point, shape, rectangle, circle, triangle, square. I have to create .h and .cpp file for each shapes and I never really understood what to put on each .h and .cpp files for each shapes. I will include what I have so far on my main.cpp and I just would like to know if I'm going in the right direction as well as what kind of information/code would I have to write on each .h and .cpp file for each shapes.
Picture of what I'm trying to draw
main.cpp
#include <iostream>
#include "point.h"
#include "shape.h"
#include "rectangle.h"
#include "square.h"
#include "triangle.h"
#include "circle.h"
using namespace std;
int main(){
//create objects of the shapes
Rectangle r, r1;
Square s;
Triangle t;
Circle c, c1;
//first rectangle
r.setLineType('*');
r.moveBy(5, 5); //x and y coordinates
r.setHeight(5);
r.setWidth(20);
r.computeArea();
r.draw(); // draw a rectangle in 2D array
auto firstRectangleArea = r.computeArea();
auto firstRectangleCircumference = r.computerCircumference();
//second rectangle
r1.setLineType('*');
r1.moveBy(10, 0);
r1.setHeight(20);
r1.setWidth(5);
r1.computeArea();
r1.draw(); // draw a rectangle in 2D array
auto secondRectangleArea = r1.computeArea();
auto secondRectangleCircumference = r1.computeCircumference();
//triangle
t.setLineType('*');
t.moveBy(15, 0);
t.setHeight(5);
t.setBase(5);
//first circle
c.moveBy(15, 0);
c.setRadius(2);
auto firstCircleArea = c.computeArea();
auto firstCircleCircumference = c.computeCircumference();
//second circle
c1.setLineType('*');
c1.moveBy(6, 0);
c1.setRadius(4);
auto secondCircleArea = c1.computeArea();
auto secondCircleCircumference = c1.computeCircumference();

How to generate delaunay by some 3D coplanar vertices By CGAL

I'm new in developing with cgal library,I have tried the following code to generate delaunay in 2D.
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Constrained_Delaunay_triangulation_2.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <cassert>
#include <iostream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Delaunay_triangulation_2<K> Triangulation;
typedef Triangulation::Point Point;
int main()
{
std::vector<Point> PL;
PL.push_back(Point(0, 0));
PL.push_back(Point(1, 0));
PL.push_back(Point(1, 1));
PL.push_back(Point(0, 1));
auto a = PL.begin();
Triangulation T;
T.insert(PL.begin(),PL.end());
Triangulation::Finite_faces_iterator Finite_face_iterator;
for (Finite_face_iterator = T.finite_faces_begin(); Finite_face_iterator != T.finite_faces_end(); ++Finite_face_iterator)
{
std::cerr << T.triangle(Finite_face_iterator) << std::endl;
}
return 0;
}
those code output two faces,and if the vertices change to 3D like
Point(0,0,0),
Point(1,0,0),
Point(1,1,0),
Point(0,1,0)
those four vertices are in the same plane,how can I output two faces not intersected by CGAL?
You can use the Delaunay_triangulation_3 class for this purpose. It handles coplanar points as a special case of dimension 2. All your points must be exactly coplanar, then.
Another option is to use Delaunay_triangulation_2, by projecting your points to the plane they belong. This would handle points that are almost coplanar.

shortest path to all points of mesh

I am working on watertight meshes and what I am trying is to get the shortest path on the surface of the mesh from every vertex in the mesh to every other vertex in the mesh.
E.g. when there are 100 vertices in the mesh i would get 100X100 distances and I want to store those in an 100x100 distance Matrix.
I use CGAL (and therefore also BOOST) and I have nearly no experience in both of them.
This is what i have so far:
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <iterator>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Random.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/IO/Polyhedron_iostream.h>
#include <CGAL/Surface_mesh_shortest_path.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/boost/graph/iterator.h>
#include "boost/multi_array.hpp"
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron_3;
typedef CGAL::Surface_mesh_shortest_path_traits<Kernel, Polyhedron_3> Traits;
typedef CGAL::Surface_mesh_shortest_path<Traits> Surface_mesh_shortest_path;
typedef Surface_mesh_shortest_path::Shortest_path_result Shortest_path_result;
typedef boost::graph_traits<Polyhedron_3> Graph_traits;
typedef Graph_traits::vertex_iterator vertex_iterator;
typedef Graph_traits::vertex_descriptor vertex_desc;
typedef Graph_traits::face_iterator face_iterator;
typedef boost::multi_array<double, 2> distArray;
typedef distArray::index distArrayIndex;
class DistanceMeasure {
public:
static distArray getDistances(Polyhedron_3 p);
};
as my header file and:
#include "DistanceMeasure.h"
distArray DistanceMeasure::getDistances(Polyhedron_3 p) {
distArray dists(boost::extents[p.size_of_vertices()][p.size_of_vertices()]);
// pick up a random face
CGAL::set_halfedgeds_items_id(p);
vertex_iterator pit = vertices(p).first;
// construct a shortest path query object and add a source point
Surface_mesh_shortest_path shortest_paths(p);
//add all points from p to the source points
for ( pit = vertices(p).first; pit != vertices(p).end(); pit++)
shortest_paths.add_source_point(*pit);
//for all points in p get distance to all the other points
vertex_iterator vit, vit_end;
for ( boost::tie(vit, vit_end) = vertices(p);
vit != vit_end; ++vit)
{
//get distances
Shortest_path_result res = shortest_paths.shortest_distance_to_source_points(*vit);
//iterate over all query results
Surface_mesh_shortest_path::Source_point_iterator spit;
int count = 0;
for(spit = res.second; spit != shortest_paths.source_points_end(); spit++) {
count++;
}
}
return dists;
}
for my source file.
In my unterstanding i woult get the distances to all the other points in res and can iterate over them in
for(spit = res.second; spit != shortest_paths.source_points_end(); spit++) {
count++;
}
Therefore i got 2 questions:
First:
Am I getting this right? Do i have all the distances in res?
And second:
How do I get the id's of the vertices and distances to them in my result (and also from spit) to be able to identify them and store their distance in the dists array.
What I thought so far is, that maybe the order is in the order I put the points into the source points.
When I run it with e.g. 100 vertices and use count to count the number of vertices in res I woult get.
100
99
98
97
96
95
..
2
1
I thought that this might be because CGAL does not compute a distance twice.
Still I am not sure about the indexes.
Thank you for your Answers
Steffen
For everybody with a similar Problem:
I can't give a solution for the first Problem I stated but the Intersection Problem might be computed in CGAL with something like this:
http://doc.cgal.org/latest/AABB_tree/index.html

Trying to remove background and make it transparent in c++, using magick++

I am having an image "objects.png" with a red background and I'm trying to make the background transparent. The code is straight forward but somehow I am not able to get the desired result. I am quite new to this ImageMagick business and this is my first program using Magick++. So, it would be great if you can explain things in detail. Thanks in advance. Code goes like this,
#include <iostream>
#include <Magick++.h>
using namespace std;
using namespace Magick;
int main(){
Image my_image("objects.png");
Color bg_color = my_image.pixelColor(0,0);
Color new_bg_color(0, MaxRGB, 0, MaxRGB);
for (int i=0;i<my_image.columns();i++){
for (int j=0;j<my_image.rows();j++){
//cout<<"(i,j) : ("<<i<<","<<j<<")\n";
if (my_image.pixelColor(i,j) == bg_color){
my_image.pixelColor(i,j,new_bg_color);
}
}
}
my_image.write("new_objects.png");
}
Image objects.png is
and I'm getting this as output
I think there is a Magick::Image.transparent method that should handle transparency assignment.
#include <iostream>
#include <Magick++.h>
using namespace std;
using namespace Magick;
int main(){
Image my_image("objects.png");
Color bg_color = my_image.pixelColor(0,0);
my_image.transparent(bg_color);
my_image.write("new_objects.png");
}
Edit
To allow the Magick::Color's opacity to be respected. You need to enable the alpha channel of the image by setting Magick::Image.matte, or Magick::Image.opacity attributes.
#include <iostream>
#include <Magick++.h>
using namespace std;
using namespace Magick;
int main(){
Image my_image("objects.png");
Color bg_color = my_image.pixelColor(0,0);
Color new_bg_color(0, MaxRGB, 0, MaxRGB);
my_image.matte(true); // or my_image.opacity();
for (int i=0;i<my_image.columns();i++){
for (int j=0;j<my_image.rows();j++){
if (my_image.pixelColor(i,j) == bg_color){
my_image.pixelColor(i,j,new_bg_color);
}
}
}
my_image.write("new_objects.png");
}