Is it possible to draw 3d segments with CGAL? - c++

I want to draw 3d segments, and the camera can rotate, so that I can observe the segments from various perspectives. I wonder if there is a way to draw them with CGAL? I know that CGAL is not specific for visualization, so the question itself may be some kind of silly. But it will be really helpful for me if it has this function, because I have some experience with CGAL. I have tried to learn OpenGL, but it's not possible for me to master it in a short time. And I don't want to spend much time to learn OpenGL, because I will not use it again in future work.
If CGAL doesn't have this function, could you please recommend some lightweight open source libraries which can draw 3d segments? I don't need a very feature-rich, but huge library. One easy to use and lightweight is best for me. Thanks a lot!

CGAL::Basic_viewer_qt allows to draw points, segments and faces in 2D/3D.
You can define your own viewer inheriting from this class.
As suggested by Marc, have a look at the different draw_XXX.h files to see how this is achieved for several viewers in CGAL.

Following Marc Glisse's and gdamiand's advice, I imitated the class SimpleTriangulation3ViewerQt in file draw_triangulation3.h and wrote a class named SimpleSegments3ViewerQt. It does work. Thanks for your advice! Here is the code.
#pragma once
#include<CGAL/Qt/Basic_viewer_qt.h>
//a struct that describes 3d segment, Point is the data structure of vertex of segment.
template<typename Point>
struct mySegment3d {
Point begin;
Point end;
mySegment3d() {}
mySegment3d(Point b,Point e):begin(b),end(e){}
};
#ifdef CGAL_USE_BASIC_VIEWER
#include<CGAL/Qt/init_ogl_context.h>
//viewer for mySegment3d
//Segs3 is an array of mySegment3d which can be traveled by iterator, such as std::veector<mySegment3d>.
template<class Segs3,class point>
class SimpleSegments3ViewerQt :public CGAL::Basic_viewer_qt
{
typedef Basic_viewer_qt Base;
typedef mySegment3d<point> mySegment3d;
public:
//construct the viewer
SimpleSegments3ViewerQt(QWidget* parent,
const Segs3& seg3,
const char* title = "Basic Segs3 Viewer") :
//First draw: vertices, edges, faces, multi-color, no inverse normal
Base(parent, title, true, true, false, false, true),
s3(seg3)
{
compute_elements();
}
protected:
const Segs3& s3;
protected:
void compute_edge(const mySegment3d& seg) {
add_segment(seg.begin, seg.end, CGAL::IO::blue());
}
void compute_vertex(const mySegment3d& seg) {
add_point(seg.begin, CGAL::IO::red());
add_point(seg.end,CGAL::IO::red());
}
void compute_elements() {
clear();
for (auto itor = s3.begin(); itor != s3.end(); ++itor) {
compute_vertex(*itor);
compute_edge(*itor);
}
}
virtual void keyPressEvent(QKeyEvent* e) {
Base::keyPressEvent(e);
}
};
//draw function
template<typename Segs3,typename Point>
void draw(const Segs3& s3, const char* title = "Segs3 Basic Viewer") {
#if defined(CGAL_TEST_SUITE)
bool cgal_test_suite = true;
#else
bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE");
#endif
if (!cgal_test_suite) {
CGAL::Qt::init_ogl_context(4, 3);
int argc = 1;
const char* argv[2] = { "segs3_viewer","\0" };
QApplication app(argc, const_cast<char**>(argv));
SimpleSegments3ViewerQt<Segs3,Point> mainwindow(app.activeWindow(),
s3, title);
mainwindow.show();
app.exec();
}
}
#endif
Here is an example of usage.
#include<vector>
#include<CGAL/Simple_cartesian.h>
#include"draw_segments_3.h"
typedef CGAL::Simple_cartesian<double> kernel;
typedef kernel::Point_3 Point_3;
//draw_segments3 test
int main() {
std::vector<mySegment3d<Point_3>> segs;
Point_3 p0(0, 0, 0), p1(1, 2, 3), p2(5, 3, 1), p3(3, 1, 10);
mySegment3d<Point_3> s0(p0, p1), s1(p0, p2), s2(p0, p3);
segs.emplace_back(s0);
segs.emplace_back(s1);
segs.emplace_back(s2);
draw<std::vector<mySegment3d<Point_3>>,Point_3>(segs);
}

Related

How to use wrapper class?

I have a QgraphicsScene which contains many QGraphicsItem. I am reading line, arc and circle's co-ordinates from a file and storing them in a map and when time comes to draw them, I iterate over that map and give those co-ordinates to respective Qt's Api.
But the problem is that, the symbol which is getting generated is not fully selectable.
Means if a symbol is created by let's say 4 lines, 1 arc and 1 circle. Then if I click on a particular line, then only that line gets selected. Or that particular arc gets selected.
But I am expecting, on clicking, the full symbol should be selected.
For that I was suggested to use wrapper class. But not understanding
how to use that ?
Here is my structure:
class mySymbol
{
struct Line {
std::tuple<int, int, int, int> line;
};
struct Arc {
std::tuple<int, int, int, int, int, int> arc;
};
struct Circle {
std::tuple<int, int, int> circle;
};
struct Rect
{
std::vector<Line> allLines;
std::vector<Arc> allArcs;
std::vector<Circle> allCircles;
}
std::map<std::string, Rect> allShapes;
}
DrawShapes.h
class DrawShapes
{
void createSymbol(map<std::string, Rect>);
}
DrawShapes.cpp
void DrawShapes :: createSymbol(map<std::string, Rect> allShapes)
{
QGraphicsItemGroup * parentGroup = new QGraphicsItemGroup;
for (auto iter = allShapes.begin(); iter != allShapes.end(); ++iter)
{
if (iter->second.allLines.size() > 0)
{
for (unsigned int i = 0; i < iter->second.allLines.size(); i++)
{
// getting 4 co-ordinates of line as first,second,third and fourth
myLine* line = new myLine(first,second,third,fourth);
line->DrawLine();
parentGroup->addToGroup(line);
scene->addItem(static_cast<QGraphicsLineItem*>(line));
}
}
// same logic for arc and circle
}
myLine.h
class myLine: public QGraphicsLineItem
{
public:
myLine();
myLine(int x1,int y1,int x2 ,int y2,QGraphicsItem *parent = nullptr)
: QGraphicsLineItem(x1,y1,x2,y2,parent)
{}
void DrawLine();
};
myLine.cpp
void myLine::DrawLine()
{
this->setPen(QPen(QColor("blue"), 1));
this->setFlag(QGraphicsItem::ItemIsSelectable);
}

Compute Reock geometric compactness C++

I need to be able to calculate the Reock compactness value for an arbitrary polygon (guaranteed to be simple). This value is defined as the ratio of the polygon's area to the area of its minimum bounding circle. I have the following data structure:
#include <vector>
#include <array>
using namespace std;
typedef array<int, 2> coordinate;
class Polygon {
vector<coordinate> border; // contains all coordinates
double get_area();
// a bunch of other data here...
};
class Polygon_Group {
vector<Polygon> borders;
double get_area();
};
This structure cannot be changed. I need a way to find the Reock score for an object of type Polygon_Group, and I need it to be as fast as possible. I have played around with Miniball for this, but it is really quite slow for me with large amounts of points. (Here's a small example of my miniball code)
#include "Miniball.hpp"
typedef std::vector<std::vector<double> >::const_iterator PointIterator;
typedef std::vector<double>::const_iterator CoordIterator;
typedef Miniball::Miniball <Miniball::CoordAccessor<PointIterator, CoordIterator> > MB;
double get_reock(Polygon_Group pg) {
// the list of points as vector<int, 2>
vector<coordinate> lp;
// the list of points as vector<double>, because
// through experimentation, this was all that worked
vector<vector<double> > p;
for (Polygon poly : pg.borders) {
lp.insert(lp.end(), poly.border.begin(), poly.border.end());
}
p.reserve(lp.size());
for (int i = 0; i < lp.size(); i++) {
p.emplace_back(lp[i].begin(), lp[i].end());
}
lp.clear();
MB mb (2, p.begin(), p.end());
return (pg.get_area() / (mb.squared_radius() * PI));
}
As stated above, this function was too slow for my needs (taking 0.4 seconds in some cases).
Really, what I need to know is how to quickly collapse the all the points in Polygon_Group into some sort of list, and then have some way to find the minimum bounding circle of those points. I can handle the area myself.

Fast Ray and Polygon Intersection

I'm working on programming my own little game which should have a visibility effect as described here. My world consists of Polygons which each have a list of Edges (sorted CW). I now want (as described in the article) to cast Rays towards the Edges of the polygons, find the intersections and retrieve a Polygon that defines the visible area.
So I wrote a classes for Vectors, Points, Edges and Polygons and adjusted the intersection-algorithm so it works with my code.
I then tested it and everything worked fine, but as I ran the Intersection algorithm in a for-loop to simulate a large amount of Edges processed(starting with 100, until 1000) the fps dropped drastically, with 100 Edges "only" 300fps (3000 before), and with 300 it dropped below 60 i think. This seems to be way to much drop for me as i wanted to reuse this code for my Lightsources and then i think i would quickly come up with processing way more than 300 Edges and it should run fast on way less powerful processors(i got an xeon e1230v3).
I figured out that only calling the EdgeIntersection the program runs many times faster, but I definitely need to loop through the Edges in my polygons so this is no option.
My Source-Code:
Vector.h/.cpp: Basic Vector class with two floats(X,Y), getters&setters, rotating
Vertex.h/.cpp: Basic Point class with a Position Vector, getters&setters and a boolean that indicates whether it is a Intersection Vertex
Edge.h/.cpp Basic Edge class with start/end-Verticies, getters&setters and rotating function(uses Vector.rotate())
Polygon.h:
#pragma once
#include <vector>
#include "Edge.h"
namespace geo
{
class Polygon
{
private:
std::vector<Edge> edges;
public:
Polygon();
Polygon(std::vector<Edge> edges);
~Polygon();
std::vector<Edge> getEdges();
Edge getEdge(int index);
int getEdgeCount();
void setEdges(std::vector<Edge> edges);
void setEdge(Edge e, int index);
void addEdge(Edge e);
void removeEdge(int index);
};
}
Ray.h:
#pragma once
#include "Vertex.h"
class Ray
{
private:
geo::Vertex origin;
geo::Vector dir;
public:
Ray();
Ray(geo::Vertex origin, geo::Vector dir);
~Ray();
geo::Vertex getOrigin();
geo::Vector getDirection();
void setOrigin(geo::Vertex origin);
void setDirection(geo::Vector dir);
};
LightModule.h:
#pragma once
#include "Polygon.h"
#include "Ray.h"
class LightModule
{
private:
//List of blocking Polygons
std::vector<geo::Polygon>* blockingPolygons;
std::vector<Ray> rays;
geo::Polygon bounds;
geo::Polygon visible;
/*geo::Polygon blocked;*/
//HitDetection Class later
geo::Vertex getIntersection(Ray r, geo::Edge* e);
geo::Vertex getClosestIntersection(Ray r, geo::Polygon *p);
public:
LightModule();
LightModule(std::vector<geo::Polygon>* blockingPolygons);
~LightModule();
//Set the Blocking Polygons
void setBlockingPolygons(std::vector<geo::Polygon>* blockingPolygons);
geo::Vertex callCI(Ray r, geo::Polygon* p);
geo::Vertex callI(Ray r, geo::Edge* e);
//Cast Rays towards Vertecies and store them in rays
void updateRays();
//Update Visibility Polygon
void updateVisible();
//Return Visibility Polygon
geo::Polygon* getVisible();
};
LightMModule.cpp:
#include "LightModule.h"
LightModule::LightModule()
{
rays.clear();
}
LightModule::LightModule(std::vector<geo::Polygon>* blockingPolygons)
{
this->blockingPolygons = blockingPolygons;
rays.clear();
}
LightModule::~LightModule()
{
}
void LightModule::setBlockingPolygons(std::vector<geo::Polygon>* blockingPolygons)
{
this->blockingPolygons = blockingPolygons;
}
//Test-cast a Ray (will follow mouse in the Test)
void LightModule::updateRays()
{
Ray r(geo::Vertex(geo::Vector(200, 100)), geo::Vector(-100, 0));
rays.push_back(r);
}
void LightModule::updateVisible()
{
}
//Both for Testing will later be part of a seperate class
geo::Vertex LightModule::callCI(Ray r, geo::Polygon *p)
{
return this->getClosestIntersection(r, p);
}
geo::Vertex LightModule::callI(Ray r, geo::Edge* e)
{
return this->getIntersection(r, e);
}
//TEST
geo::Vertex LightModule::getIntersection(Ray r, geo::Edge* e)
{
geo::Vertex v;
v.setIntersectVert(false);
float r_px = r.getOrigin().getPosition().getX();
float r_py = r.getOrigin().getPosition().getY();
float r_dx = r.getDirection().getX();
float r_dy = r.getDirection().getY();
float s_px = e->getOrigin().getPosition().getX();
float s_py = e->getOrigin().getPosition().getY();
float s_dx = e->getDirection().getX();
float s_dy = e->getDirection().getY();
float r_mag = sqrt(r_dx*r_dx + r_dy*r_dy);
float s_mag = sqrt(s_dx*s_dx + s_dy*s_dy);
if (r_dx / r_mag == s_dx / s_mag && r_dy / r_mag == s_dy / s_mag)
{
return v;
}
float T2 = (r_dx*(s_py - r_py) + r_dy*(r_px - s_px)) / (s_dx*r_dy - s_dy*r_dx);
float T1 = (s_px + s_dx*T2 - r_px) / r_dx;
if (T1 < 0 /*|| T1 > 1 For Lines*/)
{
return v;
}
if (T2 < 0 || T2 > 1)
{
return v;
}
v.setIntersectVert(true);
v.setPosition(geo::Vector(r_px + r_dx*T1, r_py + r_dy*T1));
return v;
}
geo::Vertex LightModule::getClosestIntersection(Ray r, geo::Polygon *p)
{
geo::Vertex v;
v.setIntersectVert(false);
geo::Vertex v_nearest(geo::Vector(0, 0));
v_nearest.setIntersectVert(false);
geo::Vector h1;
geo::Vector h2;
for (int i = 0; i < p->getEdges().size(); i++)
{
v = this->getIntersection(r, &p->getEdges().at(i));
h1.setX(v.getPosition().getX() - r.getOrigin().getPosition().getX());
h1.setY(v.getPosition().getY() - r.getOrigin().getPosition().getY());
h2.setX(v_nearest.getPosition().getX() - r.getOrigin().getPosition().getX());
h2.setY(v_nearest.getPosition().getY() - r.getOrigin().getPosition().getY());
if (i < 1)
v_nearest = v;
else if (v.isIntersectVert() == true && h1.getLength() < h2.getLength())
{
v_nearest = v;
}
}
return v_nearest;
}
For the Testing i create a Polygon a LightModule and call updateRays and then call the helper-Function callCI().
I know my code gets pretty messy when i have to cascade my getters and setters, ill have to fix that but for the Rest i hope everything is understandable and if not feel free to ask. And just to have mentioned it, I Test-draw my Objects with Vertex-Arrays but I don't need Graphical output of the intersection process, i just need the visible polygon.
Just to point out again: I need a faster way of finding the Intersection-Point between a Ray and a Polygon and as I didn't know if i did something wrong in my code I posted it all here so someone can maybe help me making my code more efficient or show me a different method to solve my problem.
Have a nice day and thank you for your answers :)
Paul
EDIT: Would it be meaningfully faster to first triangulate my polygons and then do a Ray-Triangle intersection Test?
I can't speak to the algorithm (which is possibly what you need) but some immediate thoughts on speeding up what you have.
First off you can define all your getters and setters inline (put them in the class in the header, not the separate source file) so the compiler can optimize the function calls away.
Then these changes might buy you a few frames:
// make sure your getters and setters are inline so the compiler
// can optimize them away
geo::Vertex LightModule::getClosestIntersection(Ray r, geo::Polygon* p)
{
geo::Vertex v;
v.setIntersectVert(false);
geo::Vector h1;
geo::Vector h2;
// cache these
Vector ray_position = r.getOrigin().getPosition();
geo::Vertex v_nearest(geo::Vector(0, 0));
v_nearest.setIntersectVert(false);
// cache size (don't dereference each time)
size_t size = p->getEdges().size();
// avoid acces violation
if(!size)
return v_nearest;
// preset item 0
v_nearest = this->getIntersection(r, &p->getEdges()[0]);
// start from 1 not 0
for(int i = 1; i < size; i++)
{
// don't use at() its slower
// v = this->getIntersection(r, &p->getEdges().at(i));
v = this->getIntersection(r, &p->getEdges()[i]);
// used cached ray position rather than call functions
h1.setX(v.getPosition().getX() - ray_position.getX());
h1.setY(v.getPosition().getY() - ray_position.getY());
h2.setX(v_nearest.getPosition().getX() - ray_position.getX());
h2.setY(v_nearest.getPosition().getY() - ray_position.getY());
// this if not needed because presetting item 0
//if(i < 1)
// v_nearest = v;
if(v.isIntersectVert() == true && h1.getLength() < h2.getLength())
{
v_nearest = v;
}
}
return v_nearest;
}
I removed one of the if statements by calculating the 0 item before the loop and starting the loop from 1, the rest is just caching a much used value and avoiding at() which is slower because it does bound-checking.

How to use of "new" for polygons in C++

The definitions of Circle and Polygon are here in Graph.h and graph.cpp.
For some exercise I need to have some unnamed shapes which are made using the new keyword. Both Circle and polygon are kinds of Shape.
For example if I have a vector_ref<Circle> vc; I can using this statement add an unnamed Circle into that vector: vc.push_back(new Circle (Point (p), 50)); because I can supply parameters (which are a point and a radius) of a circle when defining it.
But for polygons the subject is different.
For having a polygon I must declare it first, e.g., Polygon poly; then add points to it, this way, poly.add(Point(p));. Now it has caused a problem for me.
Consider I have a vector of polygons, Vector_ref<Polygon> vp; Now how to add (that is push back) a polygon using the new keyword just like I did for circle please?
My code is this:
#include <GUI.h>
using namespace Graph_lib;
//---------------------------------
class Math_shapes : public Window {
public:
Math_shapes(Point, int, int, const string&);
private:
//Widgets
Menu menu;
Button quit_button;
In_box x_coor;
In_box y_coor;
Vector_ref<Circle> vc;
Vector_ref<Graph_lib::Rectangle> vr;
Vector_ref<Graph_lib::Polygon> vt;
Vector_ref<Graph_lib::Polygon> vh;
//Action fucntions
void circle_pressed() {
int x = x_coor.get_int();
int y = y_coor.get_int();
vc.push_back(new Circle (Point(x,y), 50));
attach(vc[vc.size()-1]);
redraw();
}
void square_pressed() {
int x = x_coor.get_int();
int y = y_coor.get_int();
vr.push_back(new Graph_lib::Rectangle (Point(x,y), Point(x+100,y+100)));
attach(vr[vr.size()-1]);
redraw();
}
void triangle_pressed() {
int x = x_coor.get_int();
int y = y_coor.get_int();
vt.push_back(new Graph_lib::Polygon); // Problem is here!!
attach(vt[vt.size()-1]);
redraw();
}
void hexagon_pressed() {
int x = x_coor.get_int();
int y = y_coor.get_int();
Graph_lib::Polygon h;
h.add(Point(x,y)); h.add(Point(x+50,y+50)); h.add(Point(x+50,y+80));
h.add(Point(x,y+100)); h.add(Point(x-50,y+80)); h.add(Point(x-50,y+50));
vh.push_back(h);
attach(vh[vh.size()-1]);
redraw();
}
void quit() { hide(); }
// Call-back functions
static void cb_circle (Address, Address pw) { reference_to<Math_shapes>(pw).circle_pressed(); }
static void cb_square (Address, Address pw) { reference_to<Math_shapes>(pw).square_pressed(); }
static void cb_triangle (Address, Address pw) { reference_to<Math_shapes>(pw).triangle_pressed(); }
static void cb_hexagon (Address, Address pw) { reference_to<Math_shapes>(pw).hexagon_pressed(); }
static void cb_quit (Address, Address pw) { reference_to<Math_shapes>(pw).quit(); }
};
//----------------------------------------------------------------------------------
Math_shapes::Math_shapes(Point xy, int w, int h, const string& title):
Window(xy, w, h, title),
menu (Point(x_max()-150,70),120,30,Menu::vertical, "MathShapes"),
quit_button (Point(x_max()-100, 20), 70,20, "Quit", cb_quit),
x_coor(Point(x_max()-450,30),50,20,"x coordinate: "),
y_coor(Point(x_max()-250,30),50,20,"y coordinate: ")
{
attach(x_coor);
attach(y_coor);
attach(quit_button);
menu.attach(new Button(Point(0,0),0,0,"Circle",cb_circle));
menu.attach(new Button(Point(0,0),0,0,"Square",cb_square));
menu.attach(new Button(Point(0,0),0,0,"Equilateral triangle",cb_triangle));
menu.attach(new Button(Point(0,0),0,0,"Hexagon",cb_hexagon));
attach(menu);
}
//-------------------------------------------
int main()
try {
Math_shapes M_s(Point(100,100), 800, 600, "Math Shapes");
return gui_main();
}
catch(...)
{
return 0;
}
You simply need to hold a pointer to your polygon until you've put it in the conatiner:
Circle* pPoly = new Polygon();
// ...
pPoly->add(Point(p1));
// ...
pPoly->add(Point(p2));
// ...
vc.push_back(pPoly);
you probably want to use smart pointers rather than raw ones as above but this is where you can start.
Have you tried this
void triangle_pressed() {
int x = x_coor.get_int();
int y = y_coor.get_int();
Polygon *poly = new Polygon();
poly.add(Point(x));// add your points, I don't know if these are the right points
poly.add(Point(y));// but have you tried this way? creating it, adding, then calling
vt.push_back(poly); // push back without the move
attach(vt[vt.size()-1]);
redraw();
Well, in order to use the new operator, you would need to create a Polygon constructor that takes vector<Point> or initializer_list<Point> as an argument.
The other way around would be to create a helper function, e.g.
-- note that this is really suboptimal, there may be much better solution using move semantics, etc. (or even variadic template function for the matter)
Polygon* make_polygon(initializer_list<Point>& points)
{
Polygon* poly = new Polygon();
for (auto point : points)
poly->Add(point);
return poly;
}
And then just call vp.push_back(make_polygon({p1, p2, ...});
Obviously you could change the function to work without pointers simply by removing them alongside with the new operator call, but then it wouldn't work with your vector_ref<Polygon> type. You would need to use vector<Polygon> instead, as I assume that vector_ref<T> is just a typedef for vector<T*>

CGAL, Cropped voronoi diagram confined in a rectangle

I'm using CGAL with Qt to draw Voronoi diagram. I used CGAL::Voronoi_diagram_2<DT,AT,AP>since I need the faces. This is the example code:
for(Face_iterator f = VD.faces_begin(); f != VD.faces_end(); f++)
{
Ccb_halfedge_circulator ec_start = (f)->ccb();
Ccb_halfedge_circulator ec = ec_start;
do {
if (!ec->has_source())
{
}
else
QpolyF << QPointF(((Halfedge_handle)ec)->source()->point().x(), ((Halfedge_handle)ec)->source()->point().y());
} while ( ++ec != ec_start );
VectPolygon.push_back(QpolyF);
QpolyF.clear();}
I need to clip the rays that has source or target in infinity. If I use the Cropped_voronoi_from_delaunay to generate voronoi it only gives the segments not the faces. these are the typedefs:
typedef K::Line_2 Line_2;
typedef CGAL::Delaunay_triangulation_2<K> Delaunay_triangulation_2;
typedef Delaunay_triangulation_2::Face_iterator dt_Face_iterator;
typedef Delaunay_triangulation_2::Edge_circulator dt_Edge_circulator;
// typedefs for defining the adaptor
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Delaunay_triangulation_2<K> DT;
typedef CGAL::Delaunay_triangulation_adaptation_traits_2<DT> AT;
typedef CGAL::Delaunay_triangulation_caching_degeneracy_removal_policy_2<DT> AP;
typedef CGAL::Voronoi_diagram_2<DT,AT,AP> VD;
// typedef for the result type of the point location
typedef AT::Site_2 Site_2;
typedef AT::Point_2 Point_2;
typedef VD::Locate_result Locate_result;
typedef VD::Vertex_handle Vertex_handle;
typedef VD::Face_handle Face_handle;
typedef VD::Face_iterator Face_iterator;
typedef VD::Halfedge_handle Halfedge_handle;
typedef VD::Ccb_halfedge_circulator Ccb_halfedge_circulator;
There is some experimental code here: http://code.google.com/p/cgal-voronoi-cropping that crop a voronoi diagram to a rectangle, the result being a HDS. See main.cpp in the test directory
I know it's possible to do it with CGAL, but I found a workaround for now. in Qt, QPolygon class has the function to find intersected polygons. Qpolygon::intersected(yourPolygon).
this is the results:
The following will generate a random point cloud, find its Voronoi diagram, crop that diagram to the cloud's bounding box, and generate well-known text polygons.
I'm not sure how to integrate this with Qt, but, presumably, once you have the polygons this part will be easy(ish).
//Finds the cropped Voronoi diagram of a set of points and saves it as WKT
//Compile with: g++ main.cpp -Wall -lCGAL -lgmp
//Author: Richard Barnes (rbarnes.org)
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Regular_triangulation_filtered_traits_2.h>
#include <CGAL/Regular_triangulation_adaptation_traits_2.h>
#include <CGAL/Regular_triangulation_adaptation_policies_2.h>
#include <CGAL/Regular_triangulation_2.h>
#include <CGAL/Voronoi_diagram_2.h>
#include <CGAL/Boolean_set_operations_2.h>
#include <CGAL/bounding_box.h>
#include <CGAL/Polygon_2.h>
#include <iostream>
#include <cstdint>
//Used to convert otherwise infinite rays into looooong line segments
const int RAY_LENGTH = 1000;
typedef CGAL::Exact_predicates_exact_constructions_kernel K;
typedef CGAL::Regular_triangulation_filtered_traits_2<K> Traits;
typedef CGAL::Regular_triangulation_2<Traits> RT2;
typedef CGAL::Regular_triangulation_adaptation_traits_2<RT2> AT;
typedef CGAL::Regular_triangulation_degeneracy_removal_policy_2<RT2> DRP;
typedef CGAL::Voronoi_diagram_2<RT2, AT, DRP> VD;
int main(int argc, char **argv){
std::vector<RT2::Weighted_point> wpoints;
std::cout.precision(4);
std::cout.setf(std::ios::fixed);
//Generated random points
for(int i=0;i<100;i++)
//Weight of 0 gives a Voronoi diagram. Non-zero weight gives a power diagram
wpoints.push_back(RT2::Weighted_point(K::Point_2(rand()%100,rand()%100), 0));
//Find the bounding box of the points. This will be used to crop the Voronoi
//diagram later.
const K::Iso_rectangle_2 bbox = CGAL::bounding_box(wpoints.begin(), wpoints.end());
//Create a Regular Triangulation from the points
RT2 rt(wpoints.begin(), wpoints.end());
rt.is_valid();
//Wrap the triangulation with a Voronoi diagram adaptor. This is necessary to
//get the Voronoi faces.
VD vd(rt);
//CGAL often returns objects that are either segments or rays. This converts
//these objects into segments. If the object would have resolved into a ray,
//that ray is intersected with the bounding box defined above and returned as
//a segment.
const auto ConvertToSeg = [&](const CGAL::Object seg_obj, bool outgoing) -> K::Segment_2 {
//One of these will succeed and one will have a NULL pointer
const K::Segment_2 *dseg = CGAL::object_cast<K::Segment_2>(&seg_obj);
const K::Ray_2 *dray = CGAL::object_cast<K::Ray_2>(&seg_obj);
if (dseg) { //Okay, we have a segment
return *dseg;
} else { //Must be a ray
const auto &source = dray->source();
const auto dsx = source.x();
const auto dsy = source.y();
const auto &dir = dray->direction();
const auto tpoint = K::Point_2(dsx+RAY_LENGTH*dir.dx(),dsy+RAY_LENGTH*dir.dy());
if(outgoing)
return K::Segment_2(
dray->source(),
tpoint
);
else
return K::Segment_2(
tpoint,
dray->source()
);
}
};
//First line of WKT CSV output
std::cout<<"\"id\",\"geom\"\n";
int fnum = 0;
//Loop over the faces of the Voronoi diagram in some arbitrary order
for(VD::Face_iterator fit = vd.faces_begin(); fit!=vd.faces_end();++fit,fnum++){
CGAL::Polygon_2<K> pgon;
//Edge circulators traverse endlessly around a face. Make a note of the
//starting point so we know when to quit.
VD::Face::Ccb_halfedge_circulator ec_start = fit->ccb();
//Current location of the edge circulator
VD::Face::Ccb_halfedge_circulator ec = ec_start;
do {
//A half edge circulator representing a ray doesn't carry direction
//information. To get it, we take the dual of the dual of the half-edge.
//The dual of a half-edge circulator is the edge of a Delaunay triangle.
//The dual of the edge of Delaunay triangle is either a segment or a ray.
// const CGAL::Object seg_dual = rt.dual(ec->dual());
const CGAL::Object seg_dual = vd.dual().dual(ec->dual());
//Convert the segment/ray into a segment
const auto this_seg = ConvertToSeg(seg_dual, ec->has_target());
pgon.push_back(this_seg.source());
//If the segment has no target, it's a ray. This means that the next
//segment will also be a ray. We need to connect those two rays with a
//segment. The following accomplishes this.
if(!ec->has_target()){
const CGAL::Object nseg_dual = vd.dual().dual(ec->next()->dual());
const auto next_seg = ConvertToSeg(nseg_dual, ec->next()->has_target());
pgon.push_back(next_seg.target());
}
} while ( ++ec != ec_start ); //Loop until we get back to the beginning
//In order to crop the Voronoi diagram, we need to convert the bounding box
//into a polygon. You'd think there'd be an easy way to do this. But there
//isn't (or I haven't found it).
CGAL::Polygon_2<K> bpoly;
bpoly.push_back(K::Point_2(bbox.xmin(),bbox.ymin()));
bpoly.push_back(K::Point_2(bbox.xmax(),bbox.ymin()));
bpoly.push_back(K::Point_2(bbox.xmax(),bbox.ymax()));
bpoly.push_back(K::Point_2(bbox.xmin(),bbox.ymax()));
//Perform the intersection. Since CGAL is very general, it believes the
//result might be multiple polygons with holes.
std::list<CGAL::Polygon_with_holes_2<K>> isect;
CGAL::intersection(pgon, bpoly, std::back_inserter(isect));
//But we know better. The intersection of a convex polygon and a box is
//always a single polygon without holes. Let's assert this.
assert(isect.size()==1);
//And recover the polygon of interest
auto &poly_w_holes = isect.front();
auto &poly_outer = poly_w_holes.outer_boundary();
//Print the polygon as a WKT polygon
std::cout<<fnum<<", "
"\"POLYGON ((";
for(auto v=poly_outer.vertices_begin();v!=poly_outer.vertices_end();v++)
std::cout<<v->x()<<" "<<v->y()<<", ";
std::cout<<poly_outer.vertices_begin()->x()<<" "<<poly_outer.vertices_begin()->y()<<"))\"\n";
}
return 0;
}