Boost Geometry: segments intersection not yet implemented? - boost

I am trying a simple test: compute the intersection of 2 segments with Boost Geometry. It does not compile. I also tried with some variations (int points instead of float points, 2D instead of 3D) with no improvement.
Is it really possible that boost doesn't implement segment intersection ? Or what did I do wrong ? Missing some hpp ? Confusion between algorithms "intersects" & "intersection" ?
The code is very basic:
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/segment.hpp>
#include <boost/geometry/algorithms/intersection.hpp>
typedef boost::geometry::model::point<float, 3, boost::geometry::cs::cartesian> testPoint;
typedef boost::geometry::model::segment<testPoint> testSegment;
testSegment s1(
testPoint(-1.f, 0.f, 0.f),
testPoint(1.f, 0.f, 0.f)
);
testSegment s2(
testPoint(0.f, -1.f, 0.f),
testPoint(0.f, 1.f, 0.f)
);
std::vector<testPoint> output;
bool intersectionExists = boost::geometry::intersects(s1, s2, output);
But I got the following errors at compile time by Visual:
- Error C2039 'apply' n'est pas membre de 'boost::geometry::dispatch::disjoint<Geometry1,Geometry2,3,boost::geometry::segment_tag,boost::geometry::segment_tag,false>' CDCadwork C:\Program Files\Boost\boost_1_75_0\boost\geometry\algorithms\detail\disjoint\interface.hpp 54
- Error C2338 This operation is not or not yet implemented. CDCadwork C:\Program Files\Boost\boost_1_75_0\boost\geometry\algorithms\not_implemented.hpp 47

There are indeed two problems:
you're intersecting 3D geometries. That's not implemented
Instead you can do the same operation on a projection.
you're passing an "output" geometry to intersects (which indeed only returns the true/false value as your chosen name intersectionExists suggested). In the presence of a third parameter, it would be used as a Strategy - a concept for which output obviously doesn't satisfy.
Note intersection always returns true: What does boost::geometry::intersection return - although that's not part of the documented interface
Since your geometries are trivially projected onto 2d plane Z=0:
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/segment.hpp>
#include <iostream>
namespace bg = boost::geometry;
namespace bgm = bg::model;
using Point = bgm::point<float, 2, bg::cs::cartesian>;
using Segment = bgm::segment<Point>;
int main() {
Segment s1{{-1, 0}, {1, 0}};
Segment s2{{0, -1}, {0, 1}};
bool exists = bg::intersects(s1, s2);
std::vector<Point> output;
/*bool alwaysTrue = */ bg::intersection(s1, s2, output);
std::cout << bg::wkt(s1) << "\n";
std::cout << bg::wkt(s2) << "\n";
for (auto& p : output) {
std::cout << bg::wkt(p) << "\n";
}
return exists? 0:1;
}
Prints
LINESTRING(-1 0,1 0)
LINESTRING(0 -1,0 1)
POINT(0 0)

Related

Override floating point tolerance with boost intersection

Is it possible to introduce some tolerance with the intersection algorithm, such that close points or almost colinear lines are considered parallel?
To be concrete:
I have two line segments that should be considered parallel, however, due to some accuracy problems while doing floating point calculations, the line segments are not entirely parallel, the error is 3.78e-14 which should - by all means - be considered parallel in my case: so boost's intersection should give me two points.
However this is not the case and intersection regards those lines as not parallel. Example:
This is very similar to this question. This post is old and doesn't seem to satisfy me needs though. I'm confused as to how the intersection algorithm works in boost, too. I tried to find the code in boosts library but without success. Boosts code base is terrifying.
struct Point {
double x, y;
Point(double x_, double y_) : x(x_), y(y_) {};
}
BOOST_GEOMETRY_REGISTER_POINT_2D(Point, double, boost::geometry::cs::cartesian, x, y);
typedef boost::geometry::model::segment<Point> Segment;
Segment seg1({ -1012600, 9641189 }, { -935132, 9595186.14285714179277420043945 });
Segment seg2({ -1012600, 9641189 }, { -877031, 9560684 });
std::vector<Point> out;
boost::geometry::intersection(seg1, seg2, out);
Since I consider both segments as parallel, the expected output should be:
{ -1012600, 9641189 }, { -935132, 9595186.14285714179277420043945 }
Intersection indeed gives two points in a parallel case:
See for example:
Segment seg1({0, 0}, {6, 6});
Segment seg2({2, 2}, {3, 3});
boost::geometry::intersection(seg1, seg2, out);
Will give:
{2, 2}, {3, 3}
What do you expect the outcome to be? The segments still start in the same point.
And the resulting intersection is that point when I test it:
Live On Coliru
#include <boost/geometry.hpp>
#include <iostream>
#include <boost/geometry/geometries/register/point.hpp>
struct Point {
double x, y;
Point(double x_= 0, double y_= 0) : x(x_), y(y_) {};
};
BOOST_GEOMETRY_REGISTER_POINT_2D(Point, double, boost::geometry::cs::cartesian,
x, y)
typedef boost::geometry::model::segment<Point> Segment;
int main() {
Point P1{-1012600, 9641189};
std::cout << std::fixed;
Segment seg1(P1, {-935132, 9595186.14285714179277420043945});
Segment seg2(P1, {-877031, 9560684});
std::vector<Point> out;
boost::geometry::intersection(seg1, seg2, out);
for (auto& p : out) {
std::cout << boost::geometry::wkt(p) << "\n";
}
}
Print
POINT(-1012600.000000 9641189.000000)
On Precision
Otherwise, I've had success replacing double with long double or Boost Multiprecision types: https://stackoverflow.com/search?tab=newest&q=user%3a85371%20geometry%20multiprecision

Properly generate convex hull and get plane equations

I am completely new to Computational Geometry. I want to generate convex hull of a set of points and then get plane equations for the generated convex polyhedron so that I can check inclusion/exclusion of points. I have followed the docs and tried the whole procedure probably a dozen times but there is always some issue. Maybe I'm missing some subtle point here. The whole procedure is as follows. I have the following plot, generated in Mathematica.
I want to include every point that is on the plot inside a convex hull. So I take all the points lying on all corners of both planes and the origin (Maybe that's the problem. Maybe there is a way to properly choose points so that all points on the plot are covered). The points for this specific plot are as follows. Note that the points are generated using infinite precision so they are exact values.
pts = {
{-24298771/25000000000,-223461425901/50000000000,0},
{11285077/10000000000,-223461425901/50000000000,0},
{-24298771/25000000000,0,0},
{-24298771/25000000000,-11285077/10000000000,0},
{-24298771/25000000000,120551411529/25000000000,-24298771/25000000000},
{11285077/10000000000,120551411529/25000000000,11285077/10000000000},
{11285077/10000000000,0,11285077/10000000000},
{-24298771/25000000000,24298771/25000000000,-24298771/25000000000},
{0,0,0}
};
Then, I use the following CGAL program to generate the convex hull and plane equations. Again, trying to keep things in infinite precision.
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/GMP/Gmpq_type.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/convex_hull_3.h>
#include <CGAL/Side_of_triangle_mesh.h>
#include <CGAL/number_utils.h>
#include <unistd.h>
#include <iomanip>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef CGAL::Polyhedron_3<Kernel> Polyhedron_3;
typedef Kernel::Point_3 Point_3;
typedef Kernel::Plane_3 Plane_3;
typedef Kernel::Vector_3 Vector_3;
typedef CGAL::Side_of_triangle_mesh<Polyhedron_3, Kernel> Point_inside;
struct Plane_equation {
template <class Facet>
typename Facet::Plane_3 operator()( Facet& f) {
typename Facet::Halfedge_handle h = f.halfedge();
typedef typename Facet::Plane_3 Plane;
return Plane( h->vertex()->point(),
h->next()->vertex()->point(),
h->next()->next()->vertex()->point());
}
};
Point_3 create_point(std::vector<std::string> points) {
auto x = points[0], y = points[1], z = points[2];
Point_3 p;
std::istringstream input(x + " " + y + " " + z);
input >> p;
return p;
}
std::vector<std::string> create_coords_from_line(std::string line) {
std::vector<std::string> points;
std::istringstream stream(line);
std::string pt;
getline(stream, pt, ' ');
points.push_back(pt);
getline(stream, pt, ' ');
points.push_back(pt);
getline(stream, pt);
points.push_back(pt);
return points;
}
int main() {
std::vector<Point_3> points;
std::string line;
for (auto i = 0; i < 9; ++i) {
getline(std::cin, line);
points.push_back(create_point(create_coords_from_line(line)));
}
Polyhedron_3 poly;
CGAL::convex_hull_3(points.begin(), points.end(), poly);
// CGAL::draw(poly);
std::transform(poly.facets_begin(), poly.facets_end(), poly.planes_begin(), Plane_equation());
CGAL::set_pretty_mode(std::cout);
for (auto it = poly.planes_begin(); it != poly.planes_end(); ++it) {
if (isatty(fileno(stdin))) {
std::cout << "A = " << it->a().exact() << "\n";
std::cout << "B = " << it->b().exact() << "\n";
std::cout << "C = " << it->c().exact() << "\n";
std::cout << "D = " << it->d().exact() << "\n";
std::cout << "\n";
} else {
std::cout << it->a().exact() << " " << it->b().exact() << " "
<< it->c().exact() << " " << it->d().exact() << "\n";
}
}
return EXIT_SUCCESS;
}
Now, in order to make sure that the generated equations are correct and cover all the points, I create a Z3py script. In that, f is the function used to generate the plot, g is the conjunction of all plane equations with proper inequality (<, =, >). Then I check if f ---> g. I use the theory of reals for infinite precision. But it always comes up with a counter-example. And these counter examples are always on some edge of the plane. Here are a couple of pictures where red circle indicates the location of counterexample. This is a different plot from the one above but the process is the same. Just the input values to f is different.
Now, I don't really need infinite precision for my problem. But I would like to make sure that the procedure works with infinite precision so that I can be confident about correctness. But then I tried with CPLEX, which uses only 64-bits, and with that too, counterexamples were generated, in similar fashion to Z3. Here's an example
Now I have no idea in which step of the process am I making a mistake. My suspicion is the selection of initial points for convex hull. It would be great if someone can help me find the convex hull properly. As far as I have read, the convex hull algorithms are exact if infinite precision is used. That's why I didn't use Mathematica's convex hull feature, because it is not using infinite precision.
EDIT: There are two smaller planes which Mathematica is failing to show, as shown below. I want all points on those planes to be selected too. But the endpoints of the smaller planes coincide with the endpoints of larger planes. That's why I have taken only the corners of larger planes.
EDIT 2: Due to the range of y being so large compared to the other two variables, the convex hull generated for the above specified points looks just like a single line.
However, after diving the y values by 1000, we can see a clearer picture.

Keep patch numbers during tetrahedral meshing with CGAL

Input
I have several meshes in the .off format that together enclose a volume. For instance, take patch-01.off, patch-20.off and patch-30.off that are available with CGAL-4.11 in examples/Mesh_3/data/patches.
Desired output
I would like to get a tetrahedral mesh of this volume and save it in the .mesh format. The difficult part is that I want each line corresponding to a triangle to end with a number 0, 1 or 2 indicating to which of the input patches the triangle corresponds. Currently, I don't care about the tags of the vertices or tetrahedra.
Almost working solution
I tried modifying the CGAL example examples/Mesh_3/mesh_polyhedral_complex.cpp (the modified portion is marked):
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>
#include <CGAL/Polyhedral_complex_mesh_domain_3.h>
#include <CGAL/make_mesh_3.h>
#include <cstdlib>
// Domain
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Mesh_polyhedron_3<K>::type Polyhedron;
typedef CGAL::Polyhedral_complex_mesh_domain_3<K> Mesh_domain;
#ifdef CGAL_CONCURRENT_MESH_3
typedef CGAL::Parallel_tag Concurrency_tag;
#else
typedef CGAL::Sequential_tag Concurrency_tag;
#endif
// Triangulation
typedef CGAL::Mesh_triangulation_3<Mesh_domain,CGAL::Default,Concurrency_tag>::type Tr;
typedef CGAL::Mesh_complex_3_in_triangulation_3<
Tr,Mesh_domain::Corner_index,Mesh_domain::Curve_segment_index> C3t3;
// Criteria
typedef CGAL::Mesh_criteria_3<Tr> Mesh_criteria;
// To avoid verbose function and named parameters call
using namespace CGAL::parameters;
// THE MODIFICATION STARTS HERE
const char* const filenames[] = {
"data/patches/patch-01.off",
"data/patches/patch-20.off",
"data/patches/patch-30.off",
};
const std::pair<int, int> incident_subdomains[] = {
std::make_pair(0, 1),
std::make_pair(1, 0),
std::make_pair(1, 0),
};
// THE REMAINDER OF THE FILE IS UNCHANGED.
int main()
{
const std::size_t nb_patches = sizeof(filenames) / sizeof(const char*);
CGAL_assertion(sizeof(incident_subdomains) ==
nb_patches * sizeof(std::pair<int, int>));
std::vector<Polyhedron> patches(nb_patches);
for(std::size_t i = 0; i < nb_patches; ++i) {
std::ifstream input(filenames[i]);
if(!(input >> patches[i])) {
std::cerr << "Error reading " << filenames[i] << " as a polyhedron!\n";
return EXIT_FAILURE;
}
}
// Create domain
Mesh_domain domain(patches.begin(), patches.end(),
incident_subdomains, incident_subdomains+nb_patches);
domain.detect_features(); //includes detection of borders
// Mesh criteria
Mesh_criteria criteria(edge_size = 8,
facet_angle = 25, facet_size = 8, facet_distance = 0.2,
cell_radius_edge_ratio = 3, cell_size = 10);
// Mesh generation
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria);
// Output
std::ofstream medit_file("out.mesh");
c3t3.output_to_medit(medit_file);
return EXIT_SUCCESS;
}
This creates a well-looking tetrahedral mesh and saves it to out.mesh. However, all the triangles have a tag 1, as shown in the following excerpt (lines 1318--1328 in out.mesh).
52.527837077556413 58.272620021324407 30.13290265121827 1
0.06169736357779243 30.258121963438846 69.405198139655852 1
Triangles
2944
923 898 888 1
923 898 888 1
905 903 890 1
905 903 890 1
354 385 375 1
354 385 375 1
When I display the result in medit, all the triangles have the same colour, while (to put the question other way) I would like each of the input patches to be of different colour.
Question
What do I need to modify in the example above?
Side note
I noticed that out.mesh seems to contain two copies of each triangle. Is this related to the problem? How can I get rid of the copies?
Related questions
There already is a similar question. The difference is that they have a single file and try to convey the patch info through colour, whereas my patches are in separate files.
thanks for you precise question.
There is a very easy solution for your question, but that involves an undocumented feature of output_to_medit(). Just replace the line:
c3t3.output_to_medit(medit_file);
by
c3t3.output_to_medit(medit_file, false, true);
and that will tag the facets with surface patches IDs.

Element-wise multiplication in 2D vectors in C++11

I am trying to do element-wise multiplication (.*) between two 2D vectors in C++11 using the code below but I am getting the errors as Error C2664 'int std::multiplies::operator ()(const _Ty &,const _Ty &) const' : cannot convert argument 1 from 'std::vector>' to 'const int &' I could not figure out what the actual problem is?
The code I used is as follows
// Example program
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <iterator>
int main()
{
std::vector<std::vector<int32_t>> Route = { { 1,2 },{ 3,4 } };
std::vector<std::vector<int32_t>> Spectrum = { { 2,2 },{ 3,3 } };
std::vector<std::vector<int32_t>> score;
//and do element-wise multiplication of Spectrum and Route i.e. Spectrum .* Route
std::transform(Spectrum.begin() + 1, Spectrum.end(), Route.begin() + 1, std::back_inserter(score), std::multiplies<int32_t>());
std::vector< std::vector<int32_t> >::iterator row;
std::vector<int32_t>::iterator col;
for (row = score.begin(); row != score.end(); row++) {
for (col = row->begin() + 1; col != row->end(); col++) {
std::cout << *col << std::endl;
}
}
}
The elements of Route and Spectrum vectors are instances of std::vector<int32_t>. Your transform calls iterate over the mentioned vectors. Instead of a vector, std::multiplies<int32_t> expects the arguments to be integers. The error message tells you that there is no way to convert from a vector into an integer.
You could instead iterate over the "top" vectors, and transform each subvector into a subvector of the result "top" vector.

Eigen Library Euler Order Sequencing

I'm trying to understand the Eigen library's "eulerAngles" function, and I have some test code that doesn't make sense.
My testing includes determining if the Euler function call "eulerAngles(0, 1, 2)" corresponds to a "XYZ" Euler sequence of the static frame. For Eigen's column major matrix multiplication, it should correspond to "Z * Y * X". I have confirmed that with some simple tests, but for negative angles around the X axis I don't understand the results. Instead of using a Euler 3 Angle constructor, I'm using the "AngleAxisd" function:
rot3x3 = AngleAxisd( -M_PI, Vector3d::UnitX() );
Vector3d vec = rot3x3.eulerAngles(0, 1, 2);
For positive angles around the X axis, it works the way I expect. For the -M_PI angle (equivalent to 180 degrees), I'm seeing the following:
Euler angles: -0, 3.14159, -3.14159
I expect the first element in the array to be near -π, and the other two to be near zero. I expect the range of the first and last angles to be ±π, and the middle angle to be ±π/2. The middle angle is out of range.
The program is below:
#include "stdafx.h"
#include <iostream>
#include "<Eigen\Dense"
#include "<unsupported\Eigen\MatrixFunctions"
using namespace std;
using namespace Eigen;
int main(int argc, char* argv[])
{
Matrix3d rot3x3;
rot3x3 = AngleAxisd( -M_PI, Vector3d::UnitX() );
cout << "Here is rot3x3:" << endl << rot3x3 << endl << endl;
Vector3d vec = rot3x3.eulerAngles(0, 1, 2); // => 1-2-3 => XYZ => Z*Y*X
cout << "Euler angles: " << vec.x() << ", "
<< vec.y() << ", "
<< vec.z() << endl << endl;
system("pause");
return 0;
}
The output is:
Here is rot3x3:
1 0 0
0 -1 1.22465e-016
0 -1.22465e-016 -1
Euler angles: -0, 3.14159, -3.14159
This result is equivalent to a -π around the X axis (and it is the same as a positive π rotation around the X axis as well). Shouldn't the middle angle though be in the range that is equivalent to ±90 degrees?
Euler angles are not uniquely defined. In case of ambiguity, eulerAngles always pick the solution that minimizes the first angles. See also the following bug entries for related discussions: 609, 801, 947.

Resources