boost::polygon boolean subtract results in additional lines - c++
I have a rectangle with a circle that is located inside the rectangle. Now I do a boolean subtract operation where the circle is subtracted from the larger rectangle using boost::polygon and the following (pseudo) code:
boost::polygon::polygon_with_holes_data<int> rect;
boost::polygon::polygon_with_holes_data<int> circle;
// fill both with data here
boost::polygon::polygon_set_data<int> result=rect-circle;
After that "result" contains a bunch of coordinate-pairs where each of them are marked with an additional value -1 or 1. Then I fetch the resulting data the following way:
std::vector<boost::polygon::polygon_data<int>> outputData;
result.get<std::vector<boost::polygon::polygon_data<int>>>(outputData);
Now "outputData" contains a list of coordinates which form the following result:
So the hole is incorporated within one complete, closed polygon coordinate array where I would have expected something like boost::polygon::polygon_with_holes_data where the outline and the holes would have been separated.
So my question: am I doing something wrong here? How do I get the outline and the hole separated so that I can get rid of this extra line which connects them? Is there a function to fetch boost::polygon::polygon_with_holes_data out of the returned boost::polygon::polygon_set_data or what else is the magic here?
Thanks :-)
I took the trouble to generate some data myself. I don't know what data you use, nor how you render it. My suspicion is something is wrong in those processes:
Let's create a polygon_data factories:
template <typename T>
auto make_rect(T x1, T y1, T x2, T y2) {
auto r = bp::rectangle_data(x1, y1, x2, y2);
bp::polygon_data<T> p;
bp::assign(p, r);
return p;
}
So far so good. For the circle I used your new discovery from your other question:
template <typename T>
auto make_circle(T x, T y, T radius) {
std::vector<bp::polygon_data<T> > ps;
bp::assign(ps, bp::rectangle_data(x-1, y-1, x+1, y+1));
bp::resize(ps, radius, true, 512);
assert(ps.size() == 1);
return ps.front();
}
Now let's start out with the straight-forward:
int main() {
auto rect = make_rect(0, 0, 1000, 1000);
auto circle = make_circle(500, 500, 400);
auto difference = rect - circle;
std::cout << std::fixed;
std::cout << "rect area: " << area(rect) << "\n";
std::cout << "circle area: " << area(circle) << "\n";
std::cout << "difference area: " << area(difference) << "\n";
Prints
rect area: 1000000.000000
circle area: 505481.000000
difference area: 494519.000000
Convert And Check
Now to see whether any of these geometries would render correctly, I know no other technique than via Boost Geometry¹
So, let's convert to a polygon_with_holes_data and do some checks:
bp::polygon_with_holes_data<int> result(rect.begin(), rect.end(),
&circle, &circle+1);
std::cout << "result area: " << area(result) << "\n";
assert(area(rect) - area(circle) == area(difference));
assert(bp::equivalence(result, rect - circle));
The asserts pass, and this prints the expected:
result area: 494519.000000
Which correctly matches the difference area.
Rendering SVG
Now we adapt in Boost Geometry and write a SVG:
namespace bg = boost::geometry;
std::cout << bg::wkt(rect);
std::cout << bg::wkt(result);
{
using C = bg::coordinate_type<decltype(result)>::type;
using PT = bg::model::d2::point_xy<C>;
std::ofstream svg("output.svg");
boost::geometry::svg_mapper<PT> mapper(svg, 400, 400);
mapper.add(result);
mapper.map(result, "fill-opacity:0.3;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
}
This writes the WKT and output.svg:
Looks fine here.
What Could Be Wrong? How To Debug?
Like I mentioned in my comments, many library/tools have restrictions/invariants on the input they take. Things like the orientation of outer vs inner rings, self intersections and the presence of closing points on polygon rings.
Now, if you don't know what data is acceptable, you can hit the docs, or you can use the library API to diagnose. For example in Boost Geometry I like to make a quick-and-dirty diagnostic function:
template<typename G>
void auto_correct(G& geo) {
std::string reason;
while (!bg::is_valid(geo, reason)) {
std::cout << "Trying to auto-correct broken input data: " << reason << "\n";
bg::correct(geo);
}
}
You can add wkt printing to debug what changes are actually applied.
See e.g. boost read_wkt produced invalid polygon for examples of diagnostics and fixes.
(Note: don't use this in production, the corrections may not do what you expect and the loop may even be infinite for some inputs)
Live Demo
As always: Live On Coliru
#include <boost/polygon/polygon.hpp>
#include <boost/polygon/gtl.hpp>
#include <boost/polygon/rectangle_data.hpp>
#include <boost/polygon/polygon_set_data.hpp>
#include <boost/polygon/polygon_data.hpp>
#include <iostream>
namespace bp = boost::polygon;
#ifndef NO_GEO
#include <fstream>
#include <boost/geometry.hpp>
#include <boost/geometry/io/io.hpp>
#include <boost/geometry/geometries/adapted/boost_polygon.hpp>
namespace bg = boost::geometry;
/*
* template<typename G>
* void auto_correct(G& geo) {
* std::string reason;
* while (!bg::is_valid(geo, reason)) {
* std::cout << "Trying to auto-correct broken input data: " << reason << "\n";
* bg::correct(geo);
* }
* }
*/
#endif
template <typename T>
auto make_rect(T x1, T y1, T x2, T y2) {
auto r = bp::rectangle_data(x1, y1, x2, y2);
bp::polygon_data<T> p;
bp::assign(p, r);
return p;
}
template <typename T>
auto make_circle(T x, T y, T radius) {
std::vector<bp::polygon_data<T> > ps;
bp::assign(ps, bp::rectangle_data(x-1, y-1, x+1, y+1));
bp::resize(ps, radius, true, 512);
assert(ps.size() == 1);
return ps.front();
}
int main() {
auto rect = make_rect(0, 0, 1000, 1000);
auto circle = make_circle(500, 500, 400);
auto difference = rect - circle;
std::cout << std::fixed;
std::cout << "rect area: " << area(rect) << "\n";
std::cout << "circle area: " << area(circle) << "\n";
std::cout << "difference area: " << area(difference) << "\n";
bp::polygon_with_holes_data<int> result(rect.begin(), rect.end(),
&circle, &circle+1);
std::cout << "result area: " << area(result) << "\n";
assert(area(rect) - area(circle) == area(difference));
assert(bp::equivalence(result, rect - circle));
#ifndef NO_GEO
std::cout << bg::wkt(rect) << "\n";
std::cout << bg::wkt(result) << "\n";
{
using C = bg::coordinate_type<decltype(result)>::type;
using PT = bg::model::d2::point_xy<C>;
std::ofstream svg("output.svg");
boost::geometry::svg_mapper<PT> mapper(svg, 400, 400);
mapper.add(result);
mapper.map(result, "fill-opacity:0.3;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:2");
}
#endif
}
Prints
rect area: 1000000.000000
circle area: 505481.000000
difference area: 494519.000000
result area: 494519.000000
POLYGON((0 0,1000 0,1000 1000,0 1000))
POLYGON((0 0,1000 0,1000 1000,0 1000,0 0),(900 527,899 527,899 530,899 532,899 535,899 537,898 537,898 540,898 542,898 545,898 547,897 547,897 550,897 552,897 555,897 557,896 557,896 560,896 562,895 562,895 564,895 566,894 566,894 569,894 571,893 571,893 574,893 576,892 576,892 579,892 581,891 581,891 584,891 586,890 586,890 589,890 591,889 591,889 593,889 595,888 595,888 598,888 600,887 600,887 603,887 605,886 605,885 608,885 610,884 610,884 612,884 614,883 614,883 617,883 619,882 619,881 622,881 624,880 624,880 626,880 628,879 628,878 631,878 633,877 633,876 636,876 638,875 638,875 640,875 642,874 642,873 645,873 647,872 647,871 649,871 651,870 651,869 654,869 656,868 656,867 658,867 660,866 660,865 663,865 665,864 665,863 667,863 669,862 669,861 672,861 674,860 674,859 676,859 678,858 678,857 681,857 683,854 685,854 687,853 687,852 689,852 691,851 691,850 694,850 696,847 698,847 700,846 700,845 702,845 704,842 706,842 708,841 708,840 710,840 712,837 715,837 717,834 719,834 721,833 721,832 723,832 725,829 727,829 729,826 731,826 733,823 735,823 737,820 739,820 741,817 743,817 745,814 747,814 749,811 752,808 754,808 756,805 758,805 760,802 762,802 764,799 767,795 771,792 773,792 775,789 778,785 782,782 785,778 789,775 792,773 792,771 795,767 799,764 802,762 802,760 805,758 805,756 808,754 808,752 811,749 814,747 814,745 817,743 817,741 820,739 820,737 823,735 823,733 826,731 826,729 829,727 829,725 832,723 832,721 833,721 834,719 834,717 837,715 837,712 840,710 840,708 841,708 842,706 842,704 845,702 845,700 846,700 847,698 847,696 850,694 850,691 851,691 852,689 852,687 853,687 854,685 854,683 857,681 857,678 858,678 859,676 859,674 860,674 861,672 861,669 862,669 863,667 863,665 864,665 865,663 865,660 866,660 867,658 867,656 868,656 869,654 869,651 870,651 871,649 871,647 872,647 873,645 873,642 874,642 875,640 875,638 875,638 876,636 876,633 877,633 878,631 878,628 879,628 880,626 880,624 880,624 881,622 881,619 882,619 883,617 883,614 883,614 884,612 884,610 884,610 885,608 885,605 886,605 887,603 887,600 887,600 888,598 888,595 888,595 889,593 889,591 889,591 890,589 890,586 890,586 891,584 891,581 891,581 892,579 892,576 892,576 893,574 893,571 893,571 894,569 894,566 894,566 895,564 895,562 895,562 896,560 896,557 896,557 897,555 897,552 897,550 897,547 897,547 898,545 898,542 898,540 898,537 898,537 899,535 899,532 899,530 899,527 899,527 900,525 900,523 900,521 900,518 900,516 900,513 900,511 900,508 900,506 900,503 900,501 900,498 900,496 900,493 900,491 900,488 900,486 900,483 900,481 900,478 900,476 900,474 900,472 900,469 899,467 899,464 899,462 899,459 898,457 898,454 898,452 898,449 897,447 897,444 897,442 897,439 896,437 896,435 895,433 895,430 894,428 894,425 893,423 893,420 892,418 892,415 891,413 891,410 890,408 890,406 889,404 889,401 888,399 888,396 887,394 887,391 885,389 885,387 884,385 884,382 883,380 883,377 881,375 881,373 880,371 880,368 878,366 878,363 876,361 876,359 875,357 875,354 873,352 873,348 871,345 869,343 869,339 867,336 865,334 865,330 863,327 861,325 861,321 859,318 857,316 857,312 854,308 852,305 850,303 850,299 847,295 845,291 842,287 840,282 837,278 834,274 832,270 829,266 826,262 823,258 820,254 817,250 814,247 811,243 808,239 805,235 802,232 799,228 795,224 792,221 789,217 785,214 782,210 778,207 775,204 771,200 767,197 764,194 760,191 756,188 752,185 749,182 745,179 741,176 737,173 733,170 729,167 725,165 721,162 717,159 712,157 708,154 704,152 700,149 696,147 691,145 687,142 683,140 678,138 674,136 669,134 665,132 660,130 656,128 651,126 647,124 642,123 638,121 633,119 628,118 624,116 619,115 614,114 610,112 605,111 600,110 595,109 591,108 586,107 581,106 576,105 571,104 566,103 562,102 557,102 555,102 552,101 547,101 545,101 542,100 537,100 535,100 532,99 527,99 525,99 523,99 521,99 518,99 516,99 513,99 511,99 508,99 506,99 503,99 501,99 498,99 496,99 493,99 491,99 488,99 486,99 483,99 481,99 478,99 476,99 474,99 472,100 467,100 464,100 462,101 457,101 454,101 452,102 447,102 444,102 442,103 437,104 433,105 428,106 423,107 418,108 413,109 408,110 404,111 399,112 394,114 389,115 385,116 380,118 375,119 371,121 366,123 361,124 357,126 352,128 348,130 343,132 339,134 334,136 330,138 325,140 321,142 316,145 312,147 308,149 303,152 299,154 295,157 291,159 287,162 282,165 278,167 274,170 270,173 266,176 262,179 258,182 254,185 250,188 247,191 243,194 239,197 235,200 232,204 228,207 224,210 221,214 217,217 214,221 210,224 207,228 204,232 200,235 197,239 194,243 191,247 188,250 185,254 182,258 179,262 176,266 173,270 170,274 167,278 165,282 162,287 159,291 157,295 154,299 152,303 149,308 147,312 145,316 142,321 140,325 138,330 136,334 134,339 132,343 130,348 128,352 126,357 124,361 123,366 121,371 119,375 118,380 116,385 115,389 114,394 112,399 111,404 110,408 109,413 108,418 107,423 106,428 105,433 104,437 103,442 102,444 102,447 102,452 101,454 101,457 101,462 100,464 100,467 100,472 99,474 99,476 99,478 99,481 99,483 99,486 99,488 99,491 99,493 99,496 99,498 99,501 99,503 99,506 99,508 99,511 99,513 99,516 99,518 99,521 99,523 99,525 99,527 99,532 100,535 100,537 100,542 101,545 101,547 101,552 102,555 102,557 102,562 103,566 104,571 105,576 106,581 107,586 108,591 109,595 110,600 111,605 112,610 114,614 115,619 116,624 118,628 119,633 121,638 123,642 124,647 126,651 128,656 130,660 132,665 134,669 136,674 138,678 140,683 142,687 145,691 147,696 149,700 152,704 154,708 157,712 159,717 162,721 165,725 167,729 170,733 173,737 176,741 179,745 182,749 185,752 188,756 191,760 194,764 197,767 200,771 204,775 207,778 210,782 214,785 217,789 221,792 224,795 228,799 232,802 235,805 239,808 243,811 247,814 250,817 254,820 258,823 262,826 266,829 270,832 274,834 278,837 282,840 287,842 291,845 295,847 299,850 303,850 305,852 308,854 312,857 316,857 318,859 321,861 325,861 327,863 330,865 334,865 336,867 339,869 343,869 345,871 348,873 352,873 354,875 357,875 359,876 361,876 363,878 366,878 368,880 371,880 373,881 375,881 377,883 380,883 382,884 385,884 387,885 389,885 391,887 394,887 396,888 399,888 401,889 404,889 406,890 408,890 410,891 413,891 415,892 418,892 420,893 423,893 425,894 428,894 430,895 433,895 435,896 437,896 439,897 442,897 444,897 447,897 449,898 452,898 454,898 457,898 459,899 462,899 464,899 467,899 469,900 472,900 474,900 476,900 478,900 481,900 483,900 486,900 488,900 491,900 493,900 496,900 498,900 501,900 503,900 506,900 508,900 511,900 513,900 516,900 518,900 521,900 523,900 525,900 527))
¹ (I don't know how to output/input any geometry data using Boost Polygon, and I don't have any WKT/DSV capable rendering tools).
Related
CGAL How can I copy properties from Point_set to Surface mesh
First off, I'm aware of the CGAL GIS tutorial, but I just can't seem to copy properties from Point_set to surface mesh. Any way, I'm loading the LIDAR point cloud to the point set as follows: using Kernel = CGAL::Exact_predicates_inexact_constructions_kernel; using Point = Kernel::Point_3; using Point_set = CGAL::Point_set_3<Point>; std::ifstream ifile("input.ply", std::ios_base::binary); ifile >> point_set; std::cerr << point_set.size() << " point(s) read" << std::endl; ifile.close(); I can get the properties via auto props = point_set.properties(); for (const auto& item : props) std::cerr << item << std::endl; // I do know that there exist property "classification" that is of unsigned char type Point_set::Property_map<unsigned char> original_class_map = point_set.property_map<unsigned char>("classification").first; Then, I had tried to set the mesh and had added vertex property, using the code from above mentioned CGAL tutorial. The code below set the point's z coordinate as a property. auto idx_to_point_with_info = [&](const Point_set::Index& idx) -> std::pair<Point, Point_set::Index> { return std::make_pair(point_set.point(idx), idx); }; using Vbi = CGAL::Triangulation_vertex_base_with_info_2<Point_set::Index, Projection_traits>; using Fbi = CGAL::Triangulation_face_base_with_info_2<int, Projection_traits>; using TDS = CGAL::Triangulation_data_structure_2<Vbi, Fbi>; using TIN_with_info = CGAL::Delaunay_triangulation_2<Projection_traits, TDS>; TIN_with_info tin_with_info( boost::make_transform_iterator(point_set.begin(), idx_to_point_with_info), boost::make_transform_iterator(point_set.end(), idx_to_point_with_info)); auto classification_value = [&](const TIN_with_info::Vertex_handle vh) -> double { return vh->point().z(); }; for (TIN_with_info::Vertex_handle vh : tin_with_info.all_vertex_handles()) { // should work without classification_value, just plain vh->info() = vh->point().z(); vh->info() = classification_value(vh); } using Mesh = CGAL::Surface_mesh<Point>; Mesh tin_class_mesh; Mesh::Property_map<Mesh::Vertex_index, double> class_map = tin_class_mesh.add_property_map<Mesh::Vertex_index, double>("v:class").first; CGAL::copy_face_graph(tin_with_info, tin_class_mesh, CGAL::parameters::vertex_to_vertex_output_iterator( boost::make_function_output_iterator(class_lambda))); std::cerr << tin_class_mesh.number_of_vertices() << " vs " << point_set.size() <<std::endl; Now, this works just fine, I had successfully set the z coordinate as a property on a mesh. But, I just can't figure out how can I copy the classification property from the point_set to the tin_class_mesh. I know that I'd need to change double to unsigned char in the code, but I don't know how to access the property from the point_set and assign it to the corresponding vertex in tin_class_mesh. What am I doing wrong? As a side note, the interesting part here is that the number of tin_colored_mesh.number_of_vertices() differs slightly from the point_set.size(). Why is that?
Different output from Libtorch C++ and pytorch
I'm using the same traced model in pytorch and libtorch but I'm getting different outputs. Python Code: import cv2 import numpy as np import torch import torchvision from torchvision import transforms as trans # device for pytorch device = torch.device('cuda:0') torch.set_default_tensor_type('torch.cuda.FloatTensor') model = torch.jit.load("traced_facelearner_model_new.pt") model.eval() # read the example image used for tracing image=cv2.imread("videos/example.jpg") test_transform = trans.Compose([ trans.ToTensor(), trans.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) ]) resized_image = cv2.resize(image, (112, 112)) tens = test_transform(resized_image).to(device).unsqueeze(0) output = model(tens) print(output) C++ Code: #include <iostream> #include <algorithm> #include <opencv2/opencv.hpp> #include <torch/script.h> int main() { try { torch::jit::script::Module model = torch::jit::load("traced_facelearner_model_new.pt"); model.to(torch::kCUDA); model.eval(); cv::Mat visibleFrame = cv::imread("example.jpg"); cv::resize(visibleFrame, visibleFrame, cv::Size(112, 112)); at::Tensor tensor_image = torch::from_blob(visibleFrame.data, { 1, visibleFrame.rows, visibleFrame.cols, 3 }, at::kByte); tensor_image = tensor_image.permute({ 0, 3, 1, 2 }); tensor_image = tensor_image.to(at::kFloat); tensor_image[0][0] = tensor_image[0][0].sub(0.5).div(0.5); tensor_image[0][1] = tensor_image[0][1].sub(0.5).div(0.5); tensor_image[0][2] = tensor_image[0][2].sub(0.5).div(0.5); tensor_image = tensor_image.to(torch::kCUDA); std::vector<torch::jit::IValue> input; input.emplace_back(tensor_image); // Execute the model and turn its output into a tensor. auto output = model.forward(input).toTensor(); output = output.to(torch::kCPU); std::cout << "Embds: " << output << std::endl; std::cout << "Done!\n"; } catch (std::exception e) { std::cout << "exception" << e.what() << std::endl; } } The model gives (1x512) size output tensor as shown below. Python output tensor([[-1.6270e+00, -7.8417e-02, -3.4403e-01, -1.5171e+00, -1.3259e+00, -1.1877e+00, -2.0234e-01, -1.0677e+00, 8.8365e-01, 7.2514e-01, 2.3642e+00, -1.4473e+00, -1.6696e+00, -1.2191e+00, 6.7770e-01, ... -7.1650e-01, 1.7661e-01]], device=‘cuda:0’, grad_fn=) C++ output Embds: Columns 1 to 8 -84.6285 -14.7203 17.7419 47.0915 31.8170 57.6813 3.6089 -38.0543 Columns 9 to 16 3.3444 -95.5730 90.3788 -10.8355 2.8831 -14.3861 0.8706 -60.7844 ... Columns 505 to 512 36.8830 -31.1061 51.6818 8.2866 1.7214 -2.9263 -37.4330 48.5854 [ CPUFloatType{1,512} ] Using Pytorch 1.6.0 Libtorch 1.6.0 Visual studio 2019 Windows 10 Cuda 10.1
before the final normalization, you need to scale your input to the range 0-1 and then carry on the normalization you are doing. convert to float and then divide by 255 should get you there. Here is the snippet I wrote, there might be some syntaax errors, that should be visible. Try this : #include <iostream> #include <algorithm> #include <opencv2/opencv.hpp> #include <torch/script.h> int main() { try { torch::jit::script::Module model = torch::jit::load("traced_facelearner_model_new.pt"); model.to(torch::kCUDA); cv::Mat visibleFrame = cv::imread("example.jpg"); cv::resize(visibleFrame, visibleFrame, cv::Size(112, 112)); at::Tensor tensor_image = torch::from_blob(visibleFrame.data, { visibleFrame.rows, visibleFrame.cols, 3 }, at::kByte); tensor_image = tensor_image.to(at::kFloat).div(255).unsqueeze(0); tensor_image = tensor_image.permute({ 0, 3, 1, 2 }); ensor_image.sub_(0.5).div_(0.5); tensor_image = tensor_image.to(torch::kCUDA); // Execute the model and turn its output into a tensor. auto output = model.forward({tensor_image}).toTensor(); output = output.cpu(); std::cout << "Embds: " << output << std::endl; std::cout << "Done!\n"; } catch (std::exception e) { std::cout << "exception" << e.what() << std::endl; } } I don't have access to a system to run this so if you face anything comment below.
simplify combinatorial map using CGAL
I want to simplify or edge collapse a mesh read from .off file as a combinatorial map using CGAL std::ifstream ifile(fileName.toStdString().c_str()); if (ifile) { CGAL::load_off(lcc, ifile); lcc.display_characteristics(std::cout)<<", is_valid="<<CGAL::is_valid(lcc)<<std::endl; } namespace SMS = CGAL::Surface_mesh_simplification ; SMS::Count_stop_predicate<LCC> stop(lcc.number_of_halfedges()/2 - 1); int r = SMS::edge_collapse (lcc ,stop ,CGAL::parameters::halfedge_index_map(get(CGAL::halfedge_index, lcc)) .vertex_index_map(get(boost::vertex_index, lcc)) .get_cost(SMS::Edge_length_cost<LCC>()) .get_placement(SMS::Midpoint_placement<LCC>()) ); std::cout << "\nFinished...\n" << r << " edges removed.\n" << (lcc.number_of_darts()/2) << " final edges.\n" ; lcc.display_characteristics(std::cout)<<", is_valid="<<CGAL::is_valid(lcc)<<std::endl; the output : #Darts=16674, #0-cells=2775, #1-cells=8337, #2-cells=5558, #ccs=1, is_valid=1 Finished... 0 edges removed. 8337 final edges. #Darts=16674, #0-cells=2775, #1-cells=8337, #2-cells=5558, #ccs=1, is_valid=1 the method do nothing , I tried more than .off file and it's preview it properly but it cannot simplify it I appreciate any help .
See the example given here, it works perfectly.
Precision problems with `boost::geometry::difference`
Most of the time when I use boost::geometry::difference it does what I'd expect. However, when I have two polygons whose edges are almost coincident, I get some weird behavior. Consider this example: #include <iostream> #include <fstream> #include <list> #include <boost/geometry.hpp> #include <boost/geometry/geometries/point_xy.hpp> #include <boost/geometry/geometries/polygon.hpp> #include <boost/geometry/geometries/multi_polygon.hpp> using PointType = boost::geometry::model::d2::point_xy<double>; using PolygonType = boost::geometry::model::polygon<PointType>; using MultiPolygonType = boost::geometry::model::multi_polygon<PolygonType>; template <typename TPolygon> void printValidity(const TPolygon& polygons) { boost::geometry::validity_failure_type failure; bool valid = boost::geometry::is_valid(polygons, failure); if(!valid) { std::cout << "not valid: " << failure << std::endl; } else { std::cout << "valid." << std::endl; } } template <typename TPolygon> void WritePolygonsToSVG(const std::vector<TPolygon>& polygons, const std::string& filename) { std::ofstream svg(filename); boost::geometry::svg_mapper<PointType> mapper(svg, 400, 400); for(size_t polygonID = 0; polygonID < polygons.size(); ++polygonID) { mapper.add(polygons[polygonID]); int redValue = 50 * polygonID; // NOTE: This will break with more than 5 polygons mapper.map(polygons[polygonID], "fill:rgb(" + std::to_string(redValue) + ",128,0);stroke:rgb(0,0,100);stroke-width:1"); } } int main() { MultiPolygonType polygon1; boost::geometry::read_wkt("MULTIPOLYGON(((-23.8915 -12.2238,-23.7604 -10.2739,-23.1774 -7.83411,-22.196 -5.52561,-20.8436 -3.41293,-19.7976 -2.26009,-19.8108 -2.00306,24.8732 2.51519,26.0802 -9.42191,-23.6662 -14.452,-23.8915 -12.2238)))", polygon1); WritePolygonsToSVG(polygon1, "polygon1.svg"); MultiPolygonType polygon2; boost::geometry::read_wkt("MULTIPOLYGON(((-8.85138 -12.954,-8.89712 -12.7115,-8.9307 -12.5279,-3.35773 -12.078,-3.42937 -11.5832,-3.50013 -11.0882,-3.57007 -10.5932,-3.63925 -10.098,-3.70775 -9.60273,-3.77561 -9.10737,-3.84289 -8.61192,-3.90967 -8.1164,-3.976 -7.62081,-4.04194 -7.12517,-4.10756 -6.62948,-4.17291 -6.13375,-4.23805 -5.63799,-4.30305 -5.14222,-4.36797 -4.64643,-4.43287 -4.15064,-4.49781 -3.65485,-4.56285 -3.15909,-4.62806 -2.66331,-4.69349 -2.16756,-4.75921 -1.67185,-4.82528 -1.17619,-4.89175 -0.680597,-4.91655 -0.497015,17.8166 1.80166,17.8143 1.61653,17.8078 1.11656,17.8009 0.61658,17.7937 0.116605,17.7863 -0.383369,17.7786 -0.883343,17.7707 -1.3833,17.7627 -1.88324,17.7545 -2.38318,17.7463 -2.88312,17.7381 -3.38307,17.7299 -3.88301,17.7218 -4.38295,17.7139 -4.8829,17.706 -5.38285,17.6984 -5.88279,17.6911 -6.38274,17.684 -6.88269,17.6772 -7.38265,17.6709 -7.8826,17.6649 -8.38256,17.6595 -8.88251,17.6545 -9.38247,17.6501 -9.88244,17.6471 -10.2746,-8.85138 -12.954)))", polygon2); WritePolygonsToSVG(polygon2, "polygon2.svg"); MultiPolygonType differencePolygon; boost::geometry::difference(polygon1, polygon2, differencePolygon); WritePolygonsToSVG(differencePolygon, "difference.svg"); printValidity(differencePolygon); MultiPolygonType realDifference; boost::geometry::read_wkt("MULTIPOLYGON(((-23.8915 -12.2238,-23.7604 -10.2739,-23.1774 -7.83411,-22.196 -5.52561,-20.8436 -3.41293,-19.7976 -2.26009,-19.8108 -2.00306,24.8732 2.51519,26.0802 -9.42191,-23.6662 -14.452,-23.8915 -12.2238),(-8.85138 -12.954,17.6471 -10.2746,17.6501 -9.88244,17.6545 -9.38247,17.6595 -8.88251,17.6649 -8.38256,17.6709 -7.8826,17.6772 -7.38265,17.684 -6.88269,17.6911 -6.38274,17.6984 -5.88279,17.706 -5.38285,17.7139 -4.8829,17.7218 -4.38295,17.7299 -3.88301,17.7381 -3.38307,17.7463 -2.88312,17.7545 -2.38318,17.7627 -1.88324,17.7707 -1.3833,17.7786 -0.883343,17.7863 -0.383369,17.7937 0.116605,17.8009 0.61658,17.8078 1.11656,17.8143 1.61653,17.8166 1.80166,-4.91655 -0.497015,-4.89175 -0.680597,-4.82528 -1.17619,-4.75921 -1.67185,-4.69349 -2.16756,-4.62806 -2.66331,-4.56285 -3.15909,-4.49781 -3.65485,-4.43287 -4.15064,-4.36797 -4.64643,-4.30305 -5.14222,-4.23805 -5.63799,-4.17291 -6.13375,-4.10756 -6.62948,-4.04194 -7.12517,-3.976 -7.62081,-3.90967 -8.1164,-3.84289 -8.61192,-3.77561 -9.10737,-3.70775 -9.60273,-3.63925 -10.098,-3.57007 -10.5932,-3.50013 -11.0882,-3.42937 -11.5832,-3.35773 -12.078,-8.9307 -12.5279,-8.89712 -12.7115,-8.85138 -12.954)))", realDifference); WritePolygonsToSVG(realDifference, "realDifference.svg"); printValidity(realDifference); return 0; } The 'differencePolygon' computed using boost::geometry::difference in this code is "valid", though it has an infinitesimally thin region: The 'realDifference' polygon is what I output from my "real code" (where the input was slightly different due to (what I'm guessing) is the precision of the WKT writer). You can see this polygon has two infinitesimally small regions, and actually returns not valid (21: failure_self_intersections): Is there some kind of tolerance parameter than can be set to avoid these infinitesimally small regions during the difference operation (which apparently sometimes break the polygon)? Or is there a function to "correct" the polygon by removing these self intersections? (I know the correct() function only fixes ring ordering).
Boost.thread code presents different behaviour in Ubuntu and in Windows
I have a little simple program to test wether I can visualize a point cloud from a different thread and continue working in the main thread until typing 'q' in the terminal. In Ubuntu 10.04, the code works, letting me visualize the cloud as new points are added to it in each iteration. However, in Windows 7 this dosn't work (I'm compiling it with QtCreator). The cloud is shown and new points are computed in each turn, but this never exits. When typing 'q', the loop stops but the visualization thread keeps running. The only way to stop execution is to explicitly use CTRL+C. More things: If I don't uncomment the addPointCloud line before the !viewer->wasStopped() loop in the Visualize function, the point cloud is never shown. It doesn't matter that later in the loop I explicitly add it. It has to be done before the loop (now that line is commented to demonstrate that behaviour). I also tried to use boost::mutex instead of *tbb::queuing_mutex*, but again, the program won't exit. Do you have any idea why the thread is never joining?. Also, constructive critics about my thread usage are always welcomed, I want to keep improving. Here's the code: #include <boost/thread/thread.hpp> #include <iostream> #include <pcl/point_types.h> #include <pcl/visualization/pcl_visualizer.h> #include "tbb/queuing_mutex.h" typedef pcl::PointXYZ PointType; typedef pcl::PointCloud<PointType> PointCloudType; typedef tbb::queuing_mutex MutexType; //typedef boost::mutex MutexType; MutexType safe_update; const unsigned int HEIGHT = 100; const unsigned int WIDTH = 100; bool has_to_update(true); void Visualize(PointCloudType::Ptr cloud) { pcl::visualization::PCLVisualizer* viewer = new pcl::visualization::PCLVisualizer("Vis in thread",true); viewer->setBackgroundColor(1.0,0.0,0.0); // viewer->addPointCloud<PointType>(cloud, "sample cloud"); viewer->addCoordinateSystem(1.0); viewer->initCameraParameters(); viewer->resetCamera(); while(!viewer->wasStopped()) { viewer->spinOnce(100); { // boost::lock_guard<MutexType> lock(safe_update); MutexType::scoped_lock lock(safe_update); if(has_to_update) { if(!viewer->updatePointCloud<PointType>(cloud, "sample cloud")) { viewer->addPointCloud<PointType>(cloud, "sample cloud"); viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud"); viewer->resetCamera(); } has_to_update = false; } } // end scoped_lock } delete viewer; }; int main(int argc, char** argv) { PointCloudType::Ptr c(new PointCloudType); c->height=HEIGHT; c->width=WIDTH; const unsigned int size( c->height*c->width); c->points.resize(size); for(unsigned int i(0);i<size;++i){ c->points[i].x = 1024 * rand () / (RAND_MAX + 1.0f); c->points[i].y = 1024 * rand () / (RAND_MAX + 1.0f); c->points[i].z = 1024 * rand () / (RAND_MAX + 1.0f); } std::cout << "Filled cloud height: " << c->height << " ** widht = " << c->width << " ** size: " << c->points.size() << "\n" ; boost::thread vis_thread( boost::bind( &Visualize, boost::ref(c) ) ); char exit; std::vector<PointType> new_points; new_points.resize(10); PointType new_point; while(exit!='q') { for(unsigned int i(0);i<10;++i) { new_point.x = 2000 * rand () / (RAND_MAX + 1.0f); new_point.y = 2000 * rand () / (RAND_MAX + 1.0f); new_point.z = 2000 * rand () / (RAND_MAX + 1.0f); std::cout << "New point " << i << " with x = " << new_point.x << " ; y = " << new_point.y << " ; z = " << new_point.z << "\n" ; new_points.push_back(new_point); } { // boost::lock_guard<MutexType> lock(safe_update); MutexType::scoped_lock lock(safe_update); c->insert( c->points.end(), new_points.begin(), new_points.end() ); has_to_update = true; } // end scoped_lock std::cout << "Exit?: "; std::cin>>exit; } vis_thread.join(); return 0; } Thanks for your time!. EDIT: Since I can't use a debugger due to Windows not recognizing the executable format(?) I've put some qDebug() lines over the Visualize function (also, instead of directly calling viewer->wasStopped() now I'm using a volatile intermediate var, stopped): void Visualize(PointCloudType::Ptr cloud) { pcl::visualization::PCLVisualizer* viewer = new pcl::visualization::PCLVisualizer("Vis in thread",true); viewer->setBackgroundColor(1.0,0.0,0.0); viewer->addPointCloud<PointType>(cloud, "sample cloud"); viewer->addCoordinateSystem(1.0); viewer->initCameraParameters(); viewer->resetCamera(); volatile bool stopped( false ); int iterations( -1 ); while(!stopped) { ++iterations; qDebug() << "Before spinOnce - it: << iteration << "\n"; viewer->spinOnce(100); { // boost::lock_guard<MutexType> lock(safe_update); MutexType::scoped_lock lock(safe_update); if(has_to_update) { if(!viewer->updatePointCloud<PointType>(cloud, "sample cloud")) { viewer->addPointCloud<PointType>(cloud, "sample cloud"); viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud"); viewer->resetCamera(); } has_to_update = false; } } // end scoped_lock stopped = viewer->wasStopped(); qDebug() << "Before a new loop - it:" << iteration << "\n"; } delete viewer; }; Well, Before spinOnce is only displayed once, with iteration=0. The Before a new loop line is never printed. On the other hand, the main thread keeps calculating and printing those points to the standard output until 'q' is inputted. It seems that the visualization thread frozens in the viewer->spinOnce(100) call. If instead of spinOnce(100) I use the other visualization method, spin(), nothing changes. Maybe there's a data race in my code, but for much I keep checking it, I can't find the race myself. NOTE: According to the PCL library doc, spinOnce(int time) calls the interactor and updates the screen once, whereas spin() calls the interactor and runs an internal loop. EDIT #2: Today I tried to execute the code again in Ubuntu and resulted in a deadlock with the PCL visualizer. I added some volatile keywords and a new loop check. Now it seems it goes well (at least it worked as expected, no wrong turns...). Here's the new version: Global vars: volatile bool has_to_update(true); // as suggested by #daramarak volatile bool quit(false); // new while loop control var Visualize method: void Visualize(PointCloudType::Ptr cloud) { pcl::visualization::PCLVisualizer* viewer = new pcl::visualization::PCLVisualizer("Vis in thread",true); viewer->setBackgroundColor(1.0,0.0,0.0); viewer->addPointCloud<PointType>(cloud, "sample cloud"); viewer->addCoordinateSystem(1.0); viewer->initCameraParameters(); viewer->resetCamera(); while(!viewer->wasStopped() && !quit ) { viewer->spinOnce(100); { MutexType::scoped_lock lock(safe_update); if(has_to_update) { if(!viewer->updatePointCloud<PointType>(cloud, "sample cloud")) { viewer->addPointCloud<PointType>(cloud, "sample cloud"); viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud"); viewer->resetCamera(); } has_to_update = false; } } // end scoped_lock } delete viewer; }; Main function: // everything the same until... std::cin>>exit; quit = (exit=='q'); // no more changes I dont' like, however, the new control loop var hack. Isn't there a better way to know when to exit?. Right now, I can't realize any other way...
I believe that the wasStopped() function is a const member function thereby not changing the state of the object, so there might be an optimization in play here (It might cache the wasStopped() value as the compiler assumes the answer won't change. I suggest you try to wrap the viewer in another object with a function bool wasStopped() volatile, that might prevent such optimizations.