24 #include <QPainterPath>
35 bool PolygonUtils::IsClockwise(
const QPolygonF& polygon)
40 assert(!polygon.isClosed() &&
"The polygon isn't open.");
41 assert(polygon.size() >= 3 &&
"The polygon isn't valid.");
42 assert(IsSimplePolygon(polygon) &&
"The polygon isn't simple.");
44 double double_area = 0;
45 auto polygon_std = polygon.toStdVector();
48 double_area += (
next(cit)->x() - cit->x()) * (
next(cit)->y() + cit->y());
49 }
while (++cit != polygon_std.begin());
51 return (double_area > 0);
54 bool PolygonUtils::IsSimplePolygon(
const QPolygonF& polygon)
56 assert(!polygon.isClosed() &&
"The polygon isn't open.");
57 assert(polygon.size() >= 3 &&
"The polygon isn't valid.");
59 auto polygon_std = polygon.toStdVector();
62 std::list<QLineF> edges;
64 edges.push_back(QLineF((*cit), *
next(cit)));
65 }
while (++cit != polygon_std.begin());
67 for (
auto e1 = edges.begin(); e1 != edges.end(); e1++) {
68 for (
auto e2 =
std::next(e1); e2 != edges.end(); e2++) {
70 if (e1->intersect(*e2, &intersection) == QLineF::BoundedIntersection
71 && intersection != e1->p1()
72 && intersection != e1->p2()
73 && intersection != e2->p1()
74 && intersection != e2->p2()) {
83 QPolygonF PolygonUtils::OpenPolygon(
const QPolygonF &polygon)
85 assert(polygon.size() >= 3 &&
"The polygon isn't valid.");
87 if (polygon.isClosed()) {
88 QPolygonF opened = polygon;
96 QPolygonF PolygonUtils::Counterclockwise(
const QPolygonF& polygon)
98 assert(!polygon.isClosed() &&
"The polygon isn't open.");
99 assert(polygon.size() >= 3 &&
"The polygon isn't valid.");
100 assert(IsSimplePolygon(polygon) &&
"The polygon isn't simple.");
102 if (PolygonUtils::IsClockwise(polygon)) {
104 std::copy(polygon.begin(), polygon.end(), std::front_inserter(reversed));
111 double PolygonUtils::CalculateArea(
const QPolygonF& polygon)
113 assert(!polygon.isClosed() &&
"The polygon isn't open.");
114 assert(polygon.size() >= 3 &&
"The polygon isn't valid.");
115 assert(IsSimplePolygon(polygon) &&
"The polygon isn't simple.");
119 auto polygonStd = polygon.toStdVector();
122 area += (*cit).x() * (*
next(cit)).y() - (*
next(cit)).x() * (*cit).y();
123 }
while (++cit != polygonStd.begin());
130 std::list<QPolygonF> PolygonUtils::ClipPolygon(
const QPolygonF& polygon1,
const QPolygonF& polygon2)
132 assert(!polygon1.isClosed() && !polygon2.isClosed() &&
"The polygon aren't open.");
133 assert(polygon1.size() >= 3 && polygon2.size() >= 3 &&
"The polygon isn't valid.");
134 assert(IsSimplePolygon(polygon1) && IsSimplePolygon(polygon2) &&
"The polygon isn't simple.");
139 path1.addPolygon(polygon1);
141 path2.addPolygon(polygon2);
143 std::list<QPolygonF> subpathPolygons = path1.intersected(path2).toSubpathPolygons().toStdList();
144 std::transform(subpathPolygons.begin(), subpathPolygons.end(), subpathPolygons.begin(), &OpenPolygon);
149 auto is_perturbed = [](
const QPointF& p1,
const QPointF& p2) {
150 return fabs(p1.x() - p2.x()) < g_accuracy && fabs(p1.y() - p2.y()) < g_accuracy;
154 auto perturb_line = [](QLineF line) {
155 line.setLength(line.length() + g_accuracy);
156 QLineF reversed(line.p2(), line.p1());
157 reversed.setLength(reversed.length() + g_accuracy);
158 return QLineF(reversed.p2(), reversed.p1());
162 auto point_on_line = [perturb_line](
const QPointF& p,
const QLineF& l) {
163 QLineF normal = l.normalVector();
164 QLineF perpendicular(p, normal.p2() - normal.p1() + p);
166 QPointF intersection;
167 if (perturb_line(perpendicular).intersect(perturb_line(l), &intersection) == QLineF::BoundedIntersection) {
168 return QLineF(intersection, p).length() < g_accuracy;
178 for (
auto polygon_it = subpathPolygons.begin(); polygon_it != subpathPolygons.end(); polygon_it++) {
179 std::list<QPointF> polygonStd = polygon_it->toList().toStdList();
181 auto c_prev = [&polygonStd](
const decltype(polygonStd.begin())& it) {
186 for (
auto cit = polygonStd.begin(); cit != polygonStd.end(); cit++) {
187 QLineF line(*c_prev(cit), *cit);
188 for (
const QPointF& p : polygon1) {
189 if (point_on_line(p, line) && !is_perturbed(p, *c_prev(cit)) && !is_perturbed(p, *cit)) {
190 polygonStd.insert(cit, p);
192 line = QLineF(*c_prev(cit), *cit);
195 for (
const QPointF& p : polygon2) {
196 if (point_on_line(p, line) && !is_perturbed(p, *c_prev(cit)) && !is_perturbed(p, *cit)) {
197 polygonStd.insert(cit, p);
199 line = QLineF(*c_prev(cit), *cit);
203 *polygon_it = QPolygonF::fromList(QList<QPointF>::fromStdList(polygonStd));
209 for (QPolygonF& polygon : subpathPolygons) {
210 for (
int i = 0; i < polygon.size(); i++) {
211 for (
const QPointF& p2 : polygon1) {
212 if (is_perturbed(polygon[i], p2)) {
213 polygon.replace(i, p2);
216 for (
const QPointF& p2 : polygon2) {
217 if (is_perturbed(polygon[i], p2)) {
218 polygon.replace(i, p2);
224 return subpathPolygons;
227 std::list<QPolygonF> PolygonUtils::SlicePolygon(
const QPolygonF& polygon, QLineF cut)
229 assert(!polygon.isClosed() &&
"The polygon isn't open.");
230 assert(polygon.size() >= 3 &&
"The polygon isn't valid.");
231 assert(IsSimplePolygon(polygon) &&
"The polygon isn't simple.");
232 assert((!polygon.containsPoint(cut.p1(), Qt::OddEvenFill) || polygon.contains(cut.p1()))
233 && (!polygon.containsPoint(cut.p2(), Qt::OddEvenFill) || polygon.contains(cut.p2()))
234 &&
"One of the points of the cut is located inside the polygon.");
235 assert(cut.p1() != cut.p2() &&
"The cut isn't a line.");
238 QPolygonF initialPolygon;
239 std::list<QPointF> intersections;
240 auto edges = polygon.toStdVector();
244 auto is_perturbed = [](
const QPointF& p1,
const QPointF& p2) {
245 return fabs(p1.x() - p2.x()) < g_accuracy && fabs(p1.y() - p2.y()) < g_accuracy;
249 auto perturb_line = [](QLineF line) {
250 line.setLength(line.length() + g_accuracy);
251 QLineF reversed(line.p2(), line.p1());
252 reversed.setLength(reversed.length() + g_accuracy);
253 return QLineF(reversed.p2(), reversed.p1());
257 auto point_on_line = [perturb_line](
const QPointF& p,
const QLineF& l) {
258 QLineF normal = l.normalVector();
259 QLineF perpendicular(p, normal.p2() - normal.p1() + p);
261 QPointF intersection;
262 if (perturb_line(perpendicular).intersect(perturb_line(l), &intersection) == QLineF::BoundedIntersection) {
263 return QLineF(intersection, p).length() < g_accuracy;
271 auto line_on_line = [point_on_line](
const QLineF l1,
const QLineF l2) {
272 return ((point_on_line(l1.p1(), l2) && point_on_line(l1.p2(), l2)) || (point_on_line(l2.p1(), l1) && point_on_line(l2.p2(), l1)));
278 QPointF intersection;
279 QLineF edge(*cit, *
next(cit));
280 bool onLine = line_on_line(cut, edge);
282 initialPolygon.append(*cit);
284 if (perturb_line(cut).intersect(perturb_line(edge), &intersection) == QLineF::BoundedIntersection
289 if (is_perturbed(*cit, intersection)) {
292 else if (is_perturbed(*
next(cit), intersection)) {
293 intersection = *
next(cit);
296 initialPolygon.append(intersection);
299 if (intersection != *
next(cit)) {
301 intersections.push_back(intersection);
306 intersections.push_back(*cit);
308 }
while (++cit != edges.begin());
311 if (intersections.size() <= 1) {
312 return std::list<QPolygonF>({polygon});
316 auto cmp = [cut](
const QPointF& p1,
const QPointF& p2) {
317 return pow(cut.p1().x() - p1.x(), 2) + pow(cut.p1().y() - p1.y(), 2)
318 < pow(cut.p1().x() - p2.x(), 2) + pow(cut.p1().y() - p2.y(), 2);
320 intersections.sort(cmp);
324 std::list<QPolygonF> subpolygons({initialPolygon});
325 for (
auto it = intersections.begin(); it !=
std::prev(intersections.end()); it++) {
330 auto firstPoint = std::find(edges.begin(), edges.end(), line.p1());
331 if (firstPoint != edges.end()) {
332 std::rotate(edges.begin(), firstPoint, edges.end());
340 QPointF center = (line.p1() + line.p2()) / 2;
341 if (!polygon.containsPoint(center, Qt::OddEvenFill) && !polygon.contains(center)) {
346 auto currentPolygon = std::find_if(subpolygons.begin(), subpolygons.end(), [line](
const QPolygonF& p){
347 auto first = std::find(p.begin(), p.end(), line.p1());
348 auto second = std::find(p.begin(), p.end(), line.p2());
349 return first != p.end() && second != p.end();
355 QPolygonF* currentAppending = &polygon1;
356 for (
const QPointF& p : *currentPolygon) {
357 currentAppending->append(p);
359 if (p == line.p1() || p == line.p2()) {
360 currentAppending = (currentAppending == &polygon1 ? &polygon2 : &polygon1);
361 currentAppending->append(p);
364 subpolygons.erase(currentPolygon);
365 subpolygons.push_back(polygon1);
366 subpolygons.push_back(polygon2);
371 if (subpolygons.size() == 0) {
372 return std::list<QPolygonF>({polygon});
379 int PolygonUtils::Turn(
const QLineF& line1,
const QLineF& line2)
381 QPointF u = line1.p2() - line1.p1();
382 QPointF v = line2.p2() - line2.p1();
383 double z = u.x() * v.y() - u.y() * v.x();
384 return (z > 0) - (z < 0);
Namespace for SimPT tissue editor package.
Combo header for circular iterator.
Namespace for container related classes.
CircularIterator< T > next(CircularIterator< T > i)
Helper yields the position the iterator would have if moved forward (in circular fashion) by 1 positi...
ConstCircularIterator< typename T::const_iterator > make_const_circular(const T &c)
Helper produces const circular iterator whose range corresponds to the begin and end iterators of a c...
Interface for PolygonUtils.
CircularIterator< T > prev(CircularIterator< T > i)
Helper yields the position the iterator would have if moved backward (in circular fashion) by 1 posit...