CGAL - 2D Delaunay triangulation - remove is not removing - Part 2 - 2d

This question is continuing of the question
CGAL - 2D Delaunay triangulation - remove is not removing
Trying to solve the problem I decide to use for deletion the point returned on cdt.locate() so there will be not reason for the point do not be removed:
//---TYPEDEFS
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Triangulation_vertex_base_2<K> Vb;
typedef CGAL::Constrained_triangulation_face_base_2<K> Fb;
typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef CGAL::Exact_predicates_tag Itag;
typedef CGAL::Constrained_Delaunay_triangulation_2<K,TDS,Itag> CDT;
typedef CDT::Point Point;
typedef CDT::Vertex_handle Vertex_handle;
typedef CDT::Vertex_circulator Vertex_circulator;
typedef CDT::Face_handle Face_handle;
...
int RemovePontosDesabilitados(CDT& cdt, tysetPonto3D& SetPonDesabilitados)
{
for (auto& RPonto : SetPonDesabilitados)
{
Point PonRemover(RPonto.x, RPonto.y);
auto Face(cdt.locate(PonRemover));
if (Face != NULL)
{
int C(0);
bool Achou(false);
while (C < 3 && !Achou)
{
if((fabs(Face->vertex(C)->point().x()) - RPonto.x) < 1e-5 &&
(fabs(Face->vertex(C)->point().y()) - RPonto.y) < 1e-5)
{
Achou = true;
}
else
{
C++;
}
}
if (Achou)
{
Point PonRemover(Face->vertex(C)->point().x(), Face->vertex(C)->point().y());
auto Tam = cdt.number_of_vertices();
auto PonIns = cdt.insert(PonRemover);
Tam = cdt.number_of_vertices();
cdt.remove(PonIns);
Tam = cdt.number_of_vertices();
}
}
}
return 1;
}
But on cdt.insert(PonRemover) the point is inserted (See that this point is exactly the same point found in cdt.locate(PonRemover)) and on cdt.remove(PonIns) an exception is raised and the program finished.
Why is this exception being raised?
Thank you

Related

CGAL - 2D Delaunay triangulation - remove is not removing

I need to remove some points from a 2d Delaunay triangulation. I´m using remove but it is not working, the code is:
//---TYPEDEFS
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Triangulation_vertex_base_2<K> Vb;
typedef CGAL::Constrained_triangulation_face_base_2<K> Fb;
typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS;
typedef CGAL::Exact_predicates_tag Itag;
typedef CGAL::Constrained_Delaunay_triangulation_2<K,TDS,Itag> CDT;
typedef CDT::Point Point;
typedef CDT::Vertex_handle Vertex_handle;
typedef CDT::Vertex_circulator Vertex_circulator;
typedef CDT::Face_handle Face_handle;
...
int RemovePontosDesabilitados(CDT& cdt, tysetPonto3D& SetPonDesabilitados)
{
for (auto& RPonto : SetPonDesabilitados)
{
auto Face(cdt.locate(Point(RPonto.x,RPonto.y)));
if (Face != NULL)
{
auto Tam = cdt.number_of_vertices();
auto Pon = cdt.insert(Point(RPonto.x, RPonto.y)); //--- return the existing point
cdt.remove(Pon);
Tam = cdt.number_of_vertices();
}
}
return 1;
}
after the remove Tam returns the same value.
How to correctly remove points from a 2d Delaunay triangulations?

can coroutine be used to solve stack overflow due to too deep recursive function call?

Has anyone tried to use coroutine to solve stack overflow caused by too deep recursive function call? according to the document on coroutines, the coroutine state will be saved on heap instead of on stack, which could have the potential to avoid the limitation imposed by the limited stack size and thus provide a way to solve the stack overflow issue in a generic way. i have tried with some code but it looks like the stack over flow issue persists. anyone has any tips/advice to share? or point me to some tutorial? thanks in advance.
// file main
#include "RecursiveCall.h"
// coroutine
static ReturnObject DoIntegration(Context& ctx, ReturnObject::promise_type* parent, double x_n)
{
double* dummyA = new double[(int)((x_n + 1) * 2)]; // an effort to prevent heap allocation from "optimized out"
co_await AwaitableBase(ctx, parent, x_n);
ctx._dummyVec.push_back(dummyA); // part of the effort to prevent heap allocation from "optimized out"
}
// caller
static double Invoke(Context& ctx, ReturnObject::promise_type* parent, double x_n)
{
auto ret = DoIntegration(ctx, parent, x_n);
std::coroutine_handle<ReturnObject::promise_type> h = ret._coroH;
auto p = h.promise();
while (!h.done())
{
if (p.AreChildrenReady())
{
h();
break;
}
}
return p._area;
}
bool AwaitableBase::await_suspend(std::coroutine_handle<PromiseType> h)
{
_promise = &h.promise();
if (_parent)
{
_parent->RegisterChild(h);
}
if (_x_n <= _ctx._b)
{
_promise->_x_n = 0.0;
_promise->_area = 0.0;
return false;
}
_promise->_area = GetArea(_x_n, _ctx._incr);
double newX = _x_n - _ctx._incr;
_promise->_x_n = newX;
double area = Invoke(_ctx, &h.promise(), newX);
//post calculation
_promise->_area += area;
return true;
}
double CallRecursive(double x0, double x_n, double incr)
{
Context ctx{ x0, incr };
return Invoke(ctx, nullptr, x_n);
}
int main()
{
double x0 = 0.0;
double x_n = 4.5;
double incr = 0.5; // no stackoveflow
//double incr = 0.0015; // stack oveflow
auto area = CallRecursive(x0, x_n, incr);
std::cout << "integrated result: " << area << "\n";
}
// file RecrusiveCall.h
#include <coroutine>
#include <exception>
#include <map>
#include <iostream>
#include <vector>
/* integration certainly can and should be done in a sequencial way in real world. but here is just use it as a simple example of recursive call, so the integration is implemented as a recursive function call and is done from high limit of x to the lower limit */
static double GetY(double x)
{
using CurvePoint = std::pair<double, double>;
constexpr CurvePoint curve[10] = { {0.0, 1.0}, {0.5, 1.2}, {1.0, 1.0}, {1.5, 1.2}, {2.0, 1.0},
{2.5, 1.2}, {3.0, 1.0}, {3.5, 1.2}, {4.0, 1.0}, {4.5, 1.2} };
if (x < curve[0].first || x > curve[9].first)
return 0.0;
CurvePoint newPoint;
const auto p1 = std::lower_bound(&curve[0], &curve[10], x, [](const auto& a, const auto& b) constexpr { return a.first < b; });
// check for special cases: first
const auto p0 = p1 - 1;
return (p1->second - p0->second) * (x - p0->first) / (p1->first - p0->first) + p0->second;
}
static double GetArea(double end, double incr)
{
return (GetY(end) + GetY(end - incr)) * 0.5 * incr;
}
struct Context
{
double _b; // lower limit of the integration range
double _incr; // increment steplength
std::vector<double*> _dummyVec; // effort to prevent heap allocation from being optimzed out
~Context()
{
for (auto p : _dummyVec)
delete p;
}
};
struct ReturnObject
{
struct promise_type
{
using Handle = std::coroutine_handle<promise_type>;
ReturnObject get_return_object() {
return { std::coroutine_handle<promise_type>::from_promise(*this) };
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() {}
void return_void() {}
void RegisterChild(Handle& childH)
{
_children.push_back(childH);
}
bool AreChildrenReady()
{
for (auto c : _children)
{
if (!c.done())
return false;
}
return true;
}
double GetValue() const { return _area; }
std::vector<Handle> _children;
double _area{ 0 };
double _x_n{ 0 };
};
ReturnObject(promise_type::Handle coro) : _coroH(coro)
{
}
operator std::coroutine_handle<promise_type>() const { return _coroH; }
// A coroutine_handle<promise_type> converts to coroutine_handle<>
operator std::coroutine_handle<>() const { return _coroH; }
std::coroutine_handle<promise_type> _coroH;
};
struct AwaitableBase
{
typedef Context Ctx;
using PromiseType = ReturnObject::promise_type; // todo: remove
bool await_ready()
{
return false;
}
bool await_suspend(std::coroutine_handle<PromiseType> h);
PromiseType* await_resume()
{
return _promise;
}
AwaitableBase(Ctx& ctx, PromiseType* parent, double x_n) : _ctx(ctx), _x_n(x_n), _parent(parent)
{
}
~AwaitableBase()
{
}
Ctx& _ctx;
PromiseType* _parent{ nullptr };
PromiseType* _promise{ nullptr };
double _x_n{ 0.0 };
};
no.
the coroutine's stack frame remains allocated. what's pushed to heap (as custom handle struct) is minimal register state plus struct data, to later resume the stack frame.
that is how you can access all local variables after resuming where you left off.

undirected Graph adjacency list implementation using C

Code :
#include <stdio.h>
#include <stdlib.h>
#define N 5
typedef struct list
{
int data;
struct list * next;//self referenced structure
}slist;
void displayList(slist * start)
{
slist * temp;
if(start==NULL)
{
printf("Empty Linked List");
return;
}
for(temp=start;temp!=NULL;temp=temp->next)
printf("%d ",temp->data);
return;
}
void insertLast(slist * * start,slist * node)
{
slist * temp;
if(start==NULL){
(* start)=node;
return;
}
temp=(* start);
while((temp->next)!= NULL)
temp=temp->next;
temp->next=node;
return;
}
int main()
{
int i,j;
//slist * node;
char Ans;
/*printf("Write the number of vertices\n");
scanf("%d",&N);*/
slist * start[N];
for(i=0;i<N;i++)
start[i]=NULL;
for(i=0;i<N;i++)
{
for(j=i+1;j<N;j++)
{
printf("Is there a connection between V[%d] and V[%d]\n",(i+1),(j+1));
scanf(" %c",&Ans);
if(Ans=='y'||Ans=='Y')
{
slist * node1=(slist *)malloc(sizeof(slist));
node1->data=(j+1); node1->next=NULL;
insertLast(&start[i],node1);enter code here
slist * node2=(slist *)malloc(sizeof(slist));
node2->data=(i+1); node2->next=NULL;
insertLast(&start[j],node2);
}
}
}
for(i=0;i<N;i++)
{
displayList(start[i]);
printf("\n");
}
return 0;
}
The above code is showing segmentation fault at the line where while((temp->next)!=NULL) is written whereas while creation of linked lists, the same insertLast worked just fine. What is the fault in the code?
Your program crashed as you are checking if start is NULL or not. But that does not guarantee that *start is also not NULL. In such situation, temp gets NULL and in while loop temp->next actually trying to access next element of NULL pointer and that is why the crash.
Changing this line -
if(start==NULL)
to
if(*start==NULL)
in insertLast() will fix the crash.
I also recommend to use a debugger like gdb to debug such issues.

Bridges in an Undirected Graph

A bridge can only exist between two articulation points or between an articulation point and a node with degree 1.
This is just an observation because if a vertex is articulation point then simply disconnecting from that node will give us bridges only.
Please tell me if this is true or false.
/*A sample file should contain edges as follow
1,2
2,3
4,5
5,3
Output will give the set of bridge nodes
*/
#include<stdio.h>
#include<stdlib.h>
#define MAX 100
typedef struct graph
{
int node;
struct graph *next;
}graph;
void init(int group[],int index[],graph *g[]);
void create_graph(graph *g[],int node1,int node2,int group[],int index[],int max_nodes);
int max(int a,int b);
int min(int a,int b);
void display(graph *g[],int group[],int max_nodes);
int query(graph *g[],int group[],int max_nodes,int first,int second);
void delete_node(graph *g[],int node1,int node2,int group[],int index[],int max_nodes);
int team=0;
int main()
{
char filename[50],ch;
int nodes[MAX][2],temp=0,node1,node2,group[MAX],index[MAX],max_nodes=0,i;
FILE *fp;
graph *g[MAX],*p;
init(group,index,g);
printf("Enter the filename - ");
scanf("%s",filename);
fp=fopen(filename,"r");
if(fp==NULL)
{
printf("File does not exist");
exit(1);
}
while(1)
{
ch=fgetc(fp);
if(isdigit(ch))
temp=temp*10+(ch-48);
else
{
if(ch!=','&&ch!='\n'&&ch!=EOF)
exit(1);
if(ch==',')
node1=temp;
else
{
node2=temp;
if(node1>max_nodes)
max_nodes=node1;
if(node2>max_nodes)
max_nodes=node2;
if(node1!=node2&&node1>0&&node2>0)
create_graph(g,node1,node2,group,index,max_nodes);
}
temp=0;
}
if(ch==EOF)
{
break;
}
}
display(g,group,max_nodes);
temp=0;
fclose(fp);
fp=fopen(filename,"r");
printf("Set of bridges existing - \n\n");
while(1)
{
ch=fgetc(fp);
if(isdigit(ch))
temp=temp*10+(ch-48);
else
{
if(ch!=','&&ch!='\n'&&ch!=EOF)
exit(1);
if(ch==',')
node1=temp;
else
{
node2=temp;
if(node1>max_nodes)
max_nodes=node1;
if(node2>max_nodes)
max_nodes=node2;
if(node1!=node2&&node1>0&&node2>0)
delete_node(g,node1,node2,group,index,max_nodes);
}
temp=0;
}
if(ch==EOF)
{
break;
}
}
return 0;
}
void init(int group[],int index[],graph *g[])
{
int i;
graph *p=NULL;
for(i=0;i<MAX;i++)
{
group[i]=index[i]=0;
g[i]=(graph *)malloc(sizeof(graph));
g[i]->node=0;
g[i]->next=NULL;
}
}
void create_graph(graph *g[],int node1,int node2,int group[],int index[],int max_nodes)
{
int temp_index,other_index,i,minimum;
int stack[MAX],stack_index=0;
graph *p;
p=g[node1];
if(p->next)
{
p=p->next;
while(p)
{
if(p->node==node2)
return;
p=p->next;
}
}
if(g[node1]->node<g[node2]->node)
{
temp_index=node1;
other_index=node2;
}
else
{
temp_index=node2;
other_index=node1;
}
p=g[temp_index];
p->node=p->node+1;
while(p->next!=NULL)
p=p->next;
p->next=(graph *)malloc(sizeof(graph));
p=p->next;
p->node=other_index;
p->next=NULL;
p=g[other_index];
p->node=p->node+1;
while(p->next)
p=p->next;
p->next=(graph *)malloc(sizeof(graph));
p=p->next;
p->node=temp_index;
p->next=NULL;
if(group[temp_index]==group[other_index]&&group[temp_index]==0)
{
{
team++;
group[temp_index]=group[other_index]=team;
}
}
else
{
if(group[temp_index]==0)
{
group[temp_index]=group[other_index];
}
{
minimum=min(group[temp_index],group[other_index]);
group[temp_index]=max(group[temp_index],group[other_index]);
for(i=1;i<=max_nodes;i++)
{
if(group[i]==minimum)
group[i]=max(group[temp_index],group[other_index]);
}
}
}
}
int max(int a,int b)
{
if(a>b)
return a;
return b;
}
int min(int a,int b)
{
if(a<b)
return a;
return b;
}
void display(graph *g[],int group[],int max_nodes)
{
int i;
graph *p;
printf("Graph\n");
printf("Id\tGrp\tNodes\n");
for(i=1;i<=max_nodes;i++)
{
printf("%d\t%d\t%d",i,group[i],g[i]->node);
p=g[i]->next;
while(p)
{
printf(" %d",p->node);
p=p->next;
}
printf("\n");
}
printf("\n\n");
}
int query(graph *g[],int group[],int max_nodes,int first,int second)
{
if(group[first]==group[second])
return 1;
else
return 0;
}
void delete_node(graph *g[],int node1,int node2,int group[],int index[],int max_nodes)
{
graph *p=NULL;
int i,temp,stack_index=0,stack[MAX];
p=g[node1];
while(p->next->node!=node2&&p->next)
p=p->next;
if(p->next)
{
p->next=p->next->next;
g[node1]->node=g[node1]->node-1;
}
p=g[node2];
while(p->next->node!=node1&&p->next)
p=p->next;
if(p->next)
{
p->next=p->next->next;
g[node2]->node=g[node2]->node-1;
}
team++;
group[node2]=team;
stack_index=0;
temp=node2;
p=g[temp]->next;
while(p)
{
stack[stack_index++]=p->node;
p=p->next;
}
for(i=0;i<stack_index;i++)
{
if(group[stack[i]]!=team)
{
group[stack[i]]=team;
p=g[stack[i]]->next;
while(p)
{
stack[stack_index++]=p->node;
p=p->next;
}
}
}
if(query(g,group,max_nodes,node1,node2)==0)
printf("%d %d\n",node1,node2);
create_graph(g,node1,node2,group,index,max_nodes);
}
In an undirected graph, a bridge can only exist between two articulation points. That goes straight from the definitions:
Bridge is an edge, such that removing it from the graph increases the number of connected components.
If you remove any of its endpoints, you therefore increase the number of connected components as well, and that's exactly the definition of articulation point.
I'm not sure what you mean with the part or between an articulation point and a node with out-degree 0. What would out-degree mean in an undirected graph?
Here are nice slides on Tarjan's algorithm, including its usage for detecting bridges and articulation points in graphs.
"The key issue is determining how the reachability relation impacts whether
vertex v is an articulation vertex. There are three cases:
• Root cut-nodes – If the root of the DFS tree has two or more children, it must
be an articulation vertex. No edges from the subtree of the second child can
possibly connect to the subtree of the first child.
• Bridge cut-nodes – If the earliest reachable vertex from v is v, then deleting
the single edge (parent[v], v) disconnects the graph. Clearly parent[v] must
be an articulation vertex, since it cuts v from the graph. Vertex v is also an
articulation vertex unless it is a leaf of the DFS tree. For any leaf, nothing
falls off when you cut it.
• Parent cut-nodes – If the earliest reachable vertex from v is the parent of v,
then deleting the parent must sever v from the tree unless the parent is the
root." - The algorithm design manual by Steve Skiena

Qt: Extract intensity values clipped by a region of interest polygon

Based on a grayscale image and an ordered closed polygon (may be concave), I want to get all grayscale values that lie inside a region of interest polygon (as likewise described in SciPy Create 2D Polygon Mask). What is the most performant realisation of that in Qt 4.8? Endpoint should be some kind of QList<double>. Thanks for your advices.
In addition, is it possible to compute a floating point mask (e.g. 0 for outside the polygon, 0.3 for 30% of the pixel area is within the polygon, 1 for completely inside the polygon)? However, that's just an extra, endpoint would be QPair<double, double> (percentage, value) then. Thanks.
First you need to scanline convert the polygon into horizontal lines -- this is done by getScanLines(). The scanlines are used to sample the image to get all the values within the endpoints of each line, using scanlineScanner().
Below is a complete, standalone and compileable example I had laying around to show that the scanline algorithm is well behaved. It could be tweaked to calculate the fixed point mask as well. So far a point is included if the scanline covers more than half of it in the horizontal extent (due to round()s in scanlineScanner).
Upon startup, resize the window and click around to define consecutive points in the polygon. The polygon you see is rendered solely using the scanlines. For comparison, you can enable the outline of the polygon.
I'm sure the scanline converter could be further optimized. I'm not doing any image sampling, but the scanlineScanner is there to show that it's a trivial thing to do.
// https://github.com/KubaO/stackoverflown/tree/master/questions/scanline-converter-11037252
#define QT_DISABLE_DEPRECATED_BEFORE 5
#include <QtGui>
#if QT_VERSION > QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
#include <algorithm>
typedef QVector<QPointF> PointVector;
typedef QVector<QLineF> LineVector;
// A list of vertex indices in the polygon, sorted ascending in their y coordinate
static QVector<int> sortedVertices(const QPolygonF & poly)
{
Q_ASSERT(! poly.isEmpty());
QVector<int> vertices;
vertices.reserve(poly.size());
for (int i = 0; i < poly.size(); ++i) { vertices << i; }
std::sort(vertices.begin(), vertices.end(), [&](int i, int j){
return poly[i].y() < poly[j].y();
});
return vertices;
}
// Returns point of intersection of infinite lines ref and target
static inline QPointF intersect(const QLineF & ref, const QLineF & target)
{
QPointF p;
target.intersect(ref, &p);
return p;
}
// Allows accessing polygon vertices using an indirect index into a vector of indices.
class VertexAccessor {
const QPolygonF & p;
const QVector<int> & i;
public:
VertexAccessor(const QPolygonF & poly, const QVector<int> & indices) :
p(poly), i(indices) {}
inline QPointF operator[](int ii) const {
return p[i[ii]];
}
inline QPointF prev(int ii) const {
int index = i[ii] - 1;
if (index < 0) index += p.size();
return p[index];
}
inline QPointF next(int ii) const {
int index = i[ii] + 1;
if (index >= p.size()) index -= p.size();
return p[index];
}
};
// Returns a horizontal line scanline rendering of an unconstrained polygon.
// The lines are generated on an integer grid, but this could be modified for any other grid.
static LineVector getScanlines(const QPolygonF & poly)
{
LineVector lines;
if (poly.isEmpty()) return lines;
const QVector<int> indices = sortedVertices(poly);
VertexAccessor vertex{poly, indices};
const QRectF bound = poly.boundingRect();
const auto l = bound.left();
const auto r = bound.right();
int ii = 0;
int yi = qFloor(vertex[0].y());
QList<int> active;
PointVector points;
forever {
const qreal y = yi;
const QLineF sweeper{l, y, r, y};
// Remove vertex from the active list if both lines extending from it are above sweeper
for (int i = 0; i < active.size(); ) {
const int ii = active.at(i);
// Remove vertex
if (vertex.prev(ii).y() < y && vertex.next(ii).y() < y) {
active.removeAt(i);
} else {
++ i;
}
}
// Add new vertices to the active list
while (ii < poly.count() && vertex[ii].y() < y) {
active << ii++;
}
if (ii >= poly.count() && active.isEmpty()) break;
// Generate sorted intersection points
points.clear();
for (auto ii : active) {
const auto a = vertex[ii];
auto b = vertex.prev(ii);
if (b.y() >= y)
points << intersect(sweeper, QLineF{a, b});
b = vertex.next(ii);
if (b.y() >= y)
points << intersect(sweeper, QLineF{a, b});
}
std::sort(points.begin(), points.end(), [](const QPointF & p1, const QPointF & p2){
return p1.x() < p2.x();
});
// Generate horizontal fill segments
for (int i = 0; i < points.size() - 1; i += 2) {
lines << QLineF{points.at(i).x(), y, points.at(i+1).x(), y};
}
yi++;
};
return lines;
}
QVector<int> scanlineScanner(const QImage & image, const LineVector & tess)
{
QVector<int> values;
for (auto & line : tess) {
for (int x = round(line.x1()); x <= round(line.x2()); ++ x) {
values << qGray(image.pixel(x, line.y1()));
}
}
return values;
}
class Ui : public QWidget
{
Q_OBJECT
QPointF lastPoint;
QPolygonF polygon;
LineVector scanlines;
QGridLayout layout{this};
QPushButton reset{"Reset"};
QCheckBox outline{"Outline"};
public:
Ui() {
setMinimumSize(200, 200);
layout.addItem(new QSpacerItem{0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding}, 0, 0, 1, 3);
layout.addWidget(&reset, 1, 0);
layout.addWidget(&outline, 1, 1);
layout.addItem(new QSpacerItem{0, 0, QSizePolicy::Expanding}, 1, 2);
reset.setObjectName("reset");
outline.setObjectName("outline");
QMetaObject::connectSlotsByName(this);
}
protected:
Q_SLOT void on_reset_clicked() {
polygon.clear();
scanlines.clear();
lastPoint = QPointF{};
update();
}
Q_SLOT void on_outline_stateChanged() {
update();
}
void paintEvent(QPaintEvent *) override {
QPainter p{this};
if (false) p.setRenderHint(QPainter::Antialiasing);
p.setPen("cadetblue");
if (!polygon.isEmpty() && scanlines.isEmpty()) {
scanlines = getScanlines(polygon);
qDebug() << "new scanlines";
}
p.drawLines(scanlines);
if (outline.isChecked()) {
p.setPen("orangered");
p.setBrush(Qt::NoBrush);
p.drawPolygon(polygon);
}
if (!lastPoint.isNull()) {
p.setPen("navy");
p.drawEllipse(lastPoint, 3, 3);
}
}
void mousePressEvent(QMouseEvent * ev) override {
lastPoint = ev->posF();
polygon << ev->posF();
scanlines.clear();
update();
}
};
int main(int argc, char** argv)
{
QApplication app{argc, argv};
Ui ui;
ui.show();
return app.exec();
}
#include "main.moc"

Resources