Remove vector lines which are too close to each other? - c++
I'm currently using boost::polygon::detail::resize() function to enlarge or shrink the outline of a polygon by a specific size. This is working well and gives proper results.
Now in some cases, depending on the input shape, the resulting polygon (red) contains vector lines which are very close to each other (black line):
What I want to do is to have a minimum distance over all neighbouring lines of such a polygon. For this example this means, the resulting polygon needs to be modified and a vector line (blue) has to be added, replacing the red lines below of it. This of course would change the shape of the polygon but would keep it conform to the minimum-distance-rule.
In this example picture given above, the new, blue line would have the length of the required minimum distance.
My questions: are there any boost-functions available which could do that job? If yes: which ones and how have they to be used?
Thanks!
Converted your image into code using:
That's:
Point
A(-25.74, 2.5),
B( 14.96, 12.96),
C( 31.07, -2.16),
D( 37, -27.74),
E( 29.37, -51.63),
F( 46.62, -25.48),
G( 48.17, 3.63),
H( 41.53, 20.31),
I( 77.57, 32.04);
String ls{A, B, C, D, E, F, G, H, I};
Using
using Point = bgm::d2::point_xy<double>;
using String = bgm::linestring<Point>;
Let's reproduce the angle measurements:
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/views/segment_view.hpp>
#include <boost/geometry/geometries/point_xyz.hpp>
#include <iostream>
#include <fstream>
namespace bg = boost::geometry;
namespace bgm = bg::model;
using Point = bgm::d2::point_xy<double>;
using String = bgm::linestring<Point>;
using bg::wkt;
static inline auto angle_rad(Point a, Point b) {
bg::subtract_point(a, b);
return atan2(a.y(), a.x());
}
int main()
{
// clang-format off
Point
A(-25.74, 2.5),
B( 14.96, 12.96),
C( 31.07, -2.16),
D( 37, -27.74),
E( 29.37, -51.63),
F( 46.62, -25.48),
G( 48.17, 3.63),
H( 41.53, 20.31),
I( 77.57, 32.04);
// clang-format on
String ls{A, B, C, D, E, F, G, H, I};
if (std::string reason; !bg::is_valid(ls, reason)) {
std::cout << reason << "\n";
bg::correct(ls);
}
std::cout << wkt(ls) << "\n";
auto desc_p = [&ls](Point const& p) {
static auto names = "ABCDEFGHIJ";
assert(not std::less<>{}(&ls.back(), &p));
assert(not std::less<>{}(&p, &ls.front()));
return std::string(names + (&p - &ls.front()), 1);
};
auto desc = [=](auto it) {
return desc_p(*it->first) + desc_p(*it->second);
};
assert(ls.size() > 1);
auto a = bg::segments_begin(ls), b = std::next(a), e = bg::segments_end(ls);
for (; b != e; ++a, ++b) {
// segment pair *a and *b
auto pointing_angle = [](auto it) {
return angle_rad(*it->first, *it->second);
};
auto aa = pointing_angle(a);
auto ab = pointing_angle(b);
auto deg = [](auto rad) { return rad / M_PI * 180; };
std::cout << "angle " << deg(fmod(ab - aa + 3 * M_PI, 2 * M_PI))
<< " between " //
<< desc(a) << " " << deg(aa) << " vs " //
<< desc(b) << " " << deg(ab) << "\n";
}
{
// Declare a stream and an SVG mapper
std::ofstream svg("output.svg");
bg::svg_mapper<Point> mapper(svg, 400, 400);
// Add geometries such that all these geometries fit on the map
mapper.add(ls);
mapper.map(ls, "fill-opacity:0.3;fill:rgb(51,0,0);stroke:rgb(51,0,0);stroke-width:1");
for (auto& p : ls)
mapper.text(p, desc_p(p), "");
}
}
Prints
LINESTRING(-25.74 2.5,14.96 12.96,31.07 -2.16,37 -27.74,29.37 -51.63,46.62 -25.48,48.17 3.63,41.53 20.31,77.57 32.04)
angle 122.402 between AB -165.587 vs BC 136.816
angle 146.236 between BC 136.816 vs CD 103.052
angle 149.236 between CD 103.052 vs DE 72.2875
angle 344.301 between DE 72.2875 vs EF -123.411
angle 210.363 between EF -123.411 vs FG -93.0479
angle 204.754 between FG -93.0479 vs GH -68.2934
angle 86.322 between GH -68.2934 vs HI -161.971
With the side-effect of writing output.svg containing
Performing a cut-off
Detecting a sharp angle, we can check the cutoff length that would be required for the distance between the legs to grow beyond min_distance:
auto seg_angle = [](auto it) {
return angle_rad(*it->first, *it->second);
};
auto rel = fmod(seg_angle(b) - seg_angle(a) + 3 * M_PI, 2 * M_PI);
auto inner = fabs(2 * M_PI - rel);
bool is_sharp = inner < M_PI / 2;
if (is_sharp) {
std::cout << " ---- sharp angle, min_distance: " << min_distance
<< " (" << color << ")\n";
auto deg = [](auto rad) { return rad / M_PI * 180; };
std::cout << "angle " << deg(inner) << " between " << desc(a) << " and " << desc(b) << "\n";
auto length_a = bg::length(*a);
auto length_b = bg::length(*b);
std::cout << "len(" << desc(a) << "): " << length_a << "\n";
std::cout << "len(" << desc(b) << "): " << length_b << "\n";
std::cout << "distance(" << desc(P1) << " - " << desc(P3) << "): " << bg::distance(P1, P3) << "\n";
auto cutoff = min_distance / tan(inner);
std::cout << "cutoff: " << cutoff << "\n";
Let's create a helper to manually interpolate the cutoff points:
static inline String do_cutoff(String s, long double amount)
{
assert(s.size() >= 2);
auto delta = s[1];
bg::subtract_point(delta, s[0]);
auto l = bg::length(s);
if (l > 0) {
bg::multiply_value(delta, std::min(amount, l) / l);
bg::add_point(s[0], delta);
}
return s;
}
Now we can use it to get new mid-points instead of P2:
String fs = do_cutoff({P2, P1}, cutoff);
String ss = do_cutoff({P2, P3}, cutoff);
std::reverse(fs.begin(), fs.end());
// assert that end points didn't change
assert(bg::equals(fs.front(), P1));
assert(bg::equals(ss.back(), P3));
Now there's the degenerate case where both legs are too short for the cutoff, meaning the mid-point will disappear:
if (bg::length(*a) < cutoff && bg::length(*b) < cutoff) {
// both legs vanish, connect P1 to P3 directly
auto it = ls.begin() + index_of_p2;
std::cout << "Dropping " << desc(*it) << " (" << desc(P2) << " "
<< wkt(P2) << ")\n";
ls.erase(it);
// note that distance (P1, P3) might still exceed min_distance
// behaviour here is not specified in question
break; // invalidated the loop iterators!
}
Next up, there might be one leg that disappears:
if (bg::length(*a) < cutoff) { // P1 P2 P3 becomes P1 P3' P3
update(P2, ss.front());
} else if (bg::length(*b) < cutoff) { // P1 P2 P3 becomes P1 P1' P3
update(P2, fs.back());
} else {
So far so good. If none of the legs disappear, we need to cut-off the sharp point by introducing an extra point (and updating the original mid-point):
update(P2, ss.front());
// adding a point (invalidating the segment iterators)
std::cout << "Adding " << wkt(fs.back()) << " as X "
<< " before " << desc(P2) << "\n";
names.insert(names.begin() + index_of_p2, "X");
auto added = ls.insert(ls.begin() + index_of_p2, fs.back());
assert(bg::equals(*added, fs.back()));
break; // invalidated the loop iterators!
}
Live Demo
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xyz.hpp>
#include <boost/geometry/views/segment_view.hpp>
#include <fstream>
#include <iostream>
namespace bg = boost::geometry;
namespace bgm = bg::model;
using Point = bgm::d2::point_xy<double>;
using String = bgm::linestring<Point>;
using bg::wkt;
static inline auto angle_rad(Point a, Point b) {
bg::subtract_point(a, b);
return atan2(a.y(), a.x());
}
static inline String do_cutoff(String s, long double amount)
{
assert(s.size() >= 2);
auto delta = s[1];
bg::subtract_point(delta, s[0]);
auto l = bg::length(s);
if (l > 0) {
bg::multiply_value(delta, std::min(amount, l) / l);
bg::add_point(s[0], delta);
}
return s;
}
struct Test {
String ls;
std::vector<std::string> names;
double const min_distance;
std::string color, dash_pattern, other_style;
std::string desc(Point const& p) const
{
return names.at(&p - &ls.front());
};
std::string desc(bg::segment_iterator<String const> it) const
{
return desc(*it->first) + desc(*it->second);
};
void perform_cutoff();
};
void Test::perform_cutoff()
{
if (min_distance == 0)
return; // keep original
assert(ls.size() > 1);
auto a = bg::segments_begin(ls), b = std::next(a), e = bg::segments_end(ls);
for (; b != e; ++a, ++b) {
// segments a,b form a joint, let's call it P1 P2 P3
auto& P1 = *a->first;
auto& P2 = *a->second;
auto& P3 = *b->second;
// keep in mind a->second and b->first alias the same point here
assert(a->second == b->first);
auto seg_angle = [](auto it) {
return angle_rad(*it->first, *it->second);
};
auto rel = fmod(seg_angle(b) - seg_angle(a) + 3 * M_PI, 2 * M_PI);
auto inner = fabs(2 * M_PI - rel);
bool is_sharp = inner < M_PI / 2;
if (is_sharp) {
std::cout << " ---- sharp angle, min_distance: " << min_distance
<< " (" << color << ")\n";
auto deg = [](auto rad) { return rad / M_PI * 180; };
std::cout << "angle " << deg(inner) << " between " << desc(a) << " and " << desc(b) << "\n";
auto length_a = bg::length(*a);
auto length_b = bg::length(*b);
std::cout << "len(" << desc(a) << "): " << length_a << "\n";
std::cout << "len(" << desc(b) << "): " << length_b << "\n";
std::cout << "distance(" << desc(P1) << " - " << desc(P3) << "): " << bg::distance(P1, P3) << "\n";
auto cutoff = min_distance / tan(inner);
std::cout << "cutoff: " << cutoff << "\n";
String fs = do_cutoff({P2, P1}, cutoff);
String ss = do_cutoff({P2, P3}, cutoff);
std::reverse(fs.begin(), fs.end());
// assert that end points didn't change
assert(bg::equals(fs.front(), P1));
assert(bg::equals(ss.back(), P3));
std::cout << wkt(ls) << "\n";
auto update = [this](Point const& p, Point const& value) {
std::cout << "Updating " << desc(p) << " from " << wkt(p)
<< " to " << wkt(value) << "\n";
bg::assign(const_cast<Point&>(p), value);
names[&p - &ls.front()] += "'";
};
// For modifying we need the index to the middle point:
auto const index_of_p2 = &P2 - &ls.front();
if (bg::length(*a) < cutoff && bg::length(*b) < cutoff) {
// both legs vanish, connect P1 to P3 directly
auto it = ls.begin() + index_of_p2;
std::cout << "Dropping " << desc(*it) << " (" << desc(P2) << " "
<< wkt(P2) << ")\n";
ls.erase(it);
// note that distance (P1, P3) might still exceed min_distance
// behaviour here is not specified in question
break; // invalidated the loop iterators!
}
if (bg::length(*a) < cutoff) { // P1 P2 P3 becomes P1 P3' P3
update(P2, ss.front());
} else if (bg::length(*b) < cutoff) { // P1 P2 P3 becomes P1 P1' P3
update(P2, fs.back());
} else {
update(P2, ss.front());
// adding a point (invalidating the segment iterators)
std::cout << "Adding " << wkt(fs.back()) << " as X "
<< " before " << desc(P2) << "\n";
names.insert(names.begin() + index_of_p2, "X");
auto added = ls.insert(ls.begin() + index_of_p2, fs.back());
assert(bg::equals(*added, fs.back()));
break; // invalidated the loop iterators!
}
}
}
}
int main()
{
// clang-format off
Point
A(-25.74, 2.5),
B( 14.96, 12.96),
C( 31.07, -2.16),
D( 37, -27.74),
E( 29.37, -51.63),
F( 46.62, -25.48),
G( 48.17, 3.63),
H( 41.53, 20.31),
I( 77.57, 32.04);
// clang-format on
{
String const original_ls{A, B, C, D, E, F, G, H, I};
if (std::string reason; !bg::is_valid(original_ls, reason)) {
std::cout << reason << "\n";
return 1;
}
}
Test testcases[]{
{
{A, B, C, D, E, F, G, H, I},
{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"},
0, // keep original
"grey", "1 1",
},
{
{A, B, C, D, E, F, G, H, I},
{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"},
5,
"green", "2 1",
},
{
{A, B, C, D, E, F, G, H, I},
{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"},
8,
"red", "1 2",
},
{
{A, B, C, D, E, F, G, H, I},
{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"},
12,
"blue", "2 3",
},
};
std::ofstream svg("output.svg");
bg::svg_mapper<Point> mapper(svg, 400, 400);
for (auto& c : testcases) {
c.perform_cutoff();
std::cout << wkt(c.ls) << "\n";
mapper.add(c.ls);
mapper.map(
c.ls,
"stroke:" + c.color + ";stroke-width:1;stroke-dasharray:" + c.dash_pattern);
for (auto& p : c.ls)
mapper.text(p, c.desc(p), "fill:" + c.color + ";font-size:x-small");
}
}
Prints
LINESTRING(-25.74 2.5,14.96 12.96,31.07 -2.16,37 -27.74,29.37 -51.63,46.62 -25.48,48.17 3.63,41.53 20.31,77.57 32.04)
---- sharp angle, min_distance: 5 (green)
angle 15.6986 between DE and EF
len(DE): 25.0789
len(EF): 31.3271
distance(D - F): 9.8819
cutoff: 17.7897
LINESTRING(-25.74 2.5,14.96 12.96,31.07 -2.16,37 -27.74,29.37 -51.63,46.62 -25.48,48.17 3.63,41.53 20.31,77.57 32.04)
Updating E from POINT(29.37 -51.63) to POINT(39.1658 -36.7802)
Adding POINT(34.7824 -34.6836) as X before E'
LINESTRING(-25.74 2.5,14.96 12.96,31.07 -2.16,37 -27.74,34.7824 -34.6836,39.1658 -36.7802,46.62 -25.48,48.17 3.63,41.53 20.31,77.57 32.04)
---- sharp angle, min_distance: 8 (red)
angle 15.6986 between DE and EF
len(DE): 25.0789
len(EF): 31.3271
distance(D - F): 9.8819
cutoff: 28.4636
LINESTRING(-25.74 2.5,14.96 12.96,31.07 -2.16,37 -27.74,29.37 -51.63,46.62 -25.48,48.17 3.63,41.53 20.31,77.57 32.04)
Updating E from POINT(29.37 -51.63) to POINT(45.0432 -27.8703)
LINESTRING(-25.74 2.5,14.96 12.96,31.07 -2.16,37 -27.74,45.0432 -27.8703,46.62 -25.48,48.17 3.63,41.53 20.31,77.57 32.04)
---- sharp angle, min_distance: 12 (blue)
angle 15.6986 between DE and EF
len(DE): 25.0789
len(EF): 31.3271
distance(D - F): 9.8819
cutoff: 42.6953
LINESTRING(-25.74 2.5,14.96 12.96,31.07 -2.16,37 -27.74,29.37 -51.63,46.62 -25.48,48.17 3.63,41.53 20.31,77.57 32.04)
Dropping E (E POINT(29.37 -51.63))
LINESTRING(-25.74 2.5,14.96 12.96,31.07 -2.16,37 -27.74,46.62 -25.48,48.17 3.63,41.53 20.31,77.57 32.04)
And the resulting output.svg:
Conclusion
Hopefully that helps.
Note that the code has limitations as written, because for simplicity I used
segment_iterator.
Due to the segment-iterator loop, currently only the first sharp corner
might be treated, unless they can be treated without invalidating the
iterators. You'd either have to repeat the loop or rewrite the loop in
terms of indices instead of using the segment_iterators.
Also due to the segment iterators, I resorted to an ugly const_cast to
update existing points, instead of going throught the geometry interface.
This would not pass my own code review :)
Finally, nothing is decided when even the distance P1-P3 directly exceeded the
min_distance (you didn't specify anything about this potentiality in the
question).
Related
Proof that user compressed public key corresponds the curve equation (secp256k1)
I am trying to check if some compressed public key corresponds to an elliptic curve equation (secp256k1). As far as I know it should be valid once the following equation is fulfill y^2 = x^3 + ax + b or y^2 % p = (x^3 +ax +b) % p. Supposing that I have the following key: pubkey = 027d550bc2384fd76a47b8b0871165395e4e4d5ab9cb4ee286d1c60d074d7d60ef I am able to extract x-coordinate (do to it in this case I strip 02), in theory to calculate y without sqrt operation (due to losing precision) we can do in this case: y = (x^exp) % p, where exp = (p+1)/4 based on https://crypto.stackexchange.com/questions/101142/proof-that-user-compressed-public-key-corresponds-the-curve-equation-secp256k1 Now based on how I calculate y: bmp::uint1024_t const y = bmp::powm(x, pp, p); //or bmp::uint1024_t const yy = (x^pp) % p; I have other results, which impact later computations, generally the second example gives correct final result, but it seems, that for some reasons it doesn't work as it should... for power operation it gives the following result: bmp::pow(x, pp): 5668936922878426254536308284732873549115769122415677675761389126416312181579**1** x^pp: 5668936922878426254536308284732873549115769122415677675761389126416312181579**0** Even in python3 code for x^pp I have the same result as the second one, so should I use something other than boost::multiprecision ? or do these computation in other way ? Code can be test here: https://wandbox.org/permlink/JQ3ipCq6yQjptUet #include <numeric> #include <iostream> #include <string> #include <boost/multiprecision/cpp_int.hpp> namespace bmp = boost::multiprecision; bool verify(std::string const& address, std::size_t const stripped_prefix_size) { auto is_address_correct{false}; bmp::uint1024_t const p = bmp::uint1024_t{"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"} % 4;//3 % 4;//{"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"}; bmp::uint1024_t const a{"0x0000000000000000000000000000000000000000000000000000000000000000"}; bmp::uint1024_t const b{"0x0000000000000000000000000000000000000000000000000000000000000007"}; bmp::uint1024_t x{std::string{"0x"} + address.substr(2, address.size() - stripped_prefix_size)}; auto const right = (bmp::pow(x, 3) + (a * x) + b) % p; //bmp::uint1024_t const y = bmp::sqrt(right) % p; bmp::uint1024_t pp = (p + 1) / 4; bmp::uint1024_t const y = bmp::powm(x, pp, p); bmp::uint1024_t const yy = (x^pp) % p;//bmp::powm(x, pp, p); auto const left = bmp::powm(y, 2, p); auto const left2 = bmp::powm(yy, 2, p); std::cout << "x: " << x << std::endl; std::cout << "y pow pp: " << bmp::pow(x, pp.convert_to<int>()) << std::endl; std::cout << " y^pp: " << bmp::uint1024_t{x^pp} << std::endl; std::cout << "yy mod p: " << yy << std::endl; std::cout << " y mod p: " << y << std::endl; std::cout << "yy: " << yy << std::endl; std::cout << "right: " << right << std::endl; std::cout << " left: " << left << std::endl; std::cout << "left2: " << left2 << std::endl; is_address_correct = (left == right); return is_address_correct; } int main() { auto const res = verify("027d550bc2384fd76a47b8b0871165395e4e4d5ab9cb4ee286d1c60d074d7d60ef", 2); std::cout << "\nis valid: " << res << std::endl; return 0; } Output: x: 56689369228784262545363082847328735491157691224156776757613891264163121815791 y pow pp: 56689369228784262545363082847328735491157691224156776757613891264163121815791 y^pp: 56689369228784262545363082847328735491157691224156776757613891264163121815790 yy mod p: 2 y mod p: 0 yy: 2 right: 1 left: 0 left2: 1 is valid: 0
For the compressed key 027d550bc2384fd76a47b8b0871165395e4e4d5ab9cb4ee286d1c60d074d7d60ef the uncompressed representation is 047d550bc2384fd76a47b8b0871165395e4e4d5ab9cb4ee286d1c60d074d7d60effbb6217403fe57ff1b2f84f74086b413c7682027bd6ddde4538c340ba1a25638 i.e. x = 0x7d550bc2384fd76a47b8b0871165395e4e4d5ab9cb4ee286d1c60d074d7d60ef = 56689369228784262545363082847328735491157691224156776757613891264163121815791 y = 0xfbb6217403fe57ff1b2f84f74086b413c7682027bd6ddde4538c340ba1a25638 = 113852322045593354727100676608445520152048120867463853258291211042951302108728 This is also confirmed by the following code using the Boost library: bool verify(std::string const& address, std::size_t const stripped_prefix_size) { auto is_address_correct{false}; bmp::uint1024_t const p = bmp::uint1024_t{"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"}; bmp::uint1024_t const a{"0x0000000000000000000000000000000000000000000000000000000000000000"}; bmp::uint1024_t const b{"0x0000000000000000000000000000000000000000000000000000000000000007"}; bmp::uint1024_t x{std::string{"0x"} + address.substr(2, address.size() - stripped_prefix_size)}; bmp::uint1024_t right = (bmp::powm(x, 3, p) + (a * x) + b) % p; bmp::uint1024_t y = bmp::powm(right, (p + 1) / 4, p); // even, i.e. equals the searched y value because of leading 0x02 byte bmp::uint1024_t left = bmp::powm(y, 2, p); std::cout << "x: " << x << std::endl; // x: 56689369228784262545363082847328735491157691224156776757613891264163121815791 std::cout << "y: " << y << std::endl; // y: 113852322045593354727100676608445520152048120867463853258291211042951302108728 std::cout << "right: " << right << std::endl; // right: 33769945388650438579771708095049232540048570303667364755388658443270938208149 std::cout << "left: " << left << std::endl; // left: 33769945388650438579771708095049232540048570303667364755388658443270938208149 is_address_correct = (left == right); return is_address_correct; } The code contains the following fixes/considerations: When calculating y, not x but right must be applied. The solution has two values -y and +y, s. here. Because of the leading 0x02 byte, that value must be used which has an even parity in the positive, and this is true here for +y. The value for p must not be taken modulo 4 (see comment by President James K. Polk). The congruence of the modulus to 3 modulo 4 is only relevant for finding the correct solution path. A more detailed explanation can be found here.
How is it possible that double inequality succeeds on two equal doubles? [duplicate]
This question already has answers here: Is floating point math broken? (31 answers) Closed 2 years ago. I have a snippet of code that I don't manage to understand. To give some context: I have a line (defined by its angular coefficient m) and I search over a set of segments to find the intersecting one. A segment and a vector of segments are defined as: typedef std::pair<Eigen::Vector2d, Eigen::Vector2d> Vector2dPair; typedef std::vector<Vector2dPair> Vector2dPairVector; The mentioned code is (to provide a minimal working example): #include <iostream> #include <Eigen/Dense> typedef std::vector<Eigen::Vector2d> Vector2dVector; typedef std::pair<Eigen::Vector2d, Eigen::Vector2d> Vector2dPair; typedef std::vector<Vector2dPair> Vector2dPairVector; int main() { Vector2dPair segment; segment.first = Eigen::Vector2d(2, -2); segment.second = Eigen::Vector2d(2, 2); Vector2dPairVector segments; segments.push_back(segment); double angle_min = 0; double angle_max = M_PI_4; double angle_increment = M_PI / 16.0; double range_min = 0.0; double range_max = 10.0; Vector2dVector points; double field_of_view = angle_max - angle_min; int num_ranges = static_cast<int>(std::nearbyint(std::abs(field_of_view) / angle_increment)); std::cerr << "fov: " << field_of_view << std::endl; std::cerr << "num ranges: " << num_ranges << std::endl; points.resize(num_ranges); for (int i = 0; i < num_ranges; ++i) { const double angle = angle_min + i * angle_increment; std::cerr << "angle: " << angle << std::endl; const double m = std::tan(angle); Eigen::Vector2d point = Eigen::Vector2d::Zero(); bool found = false; for (const auto& segment : segments) { // compute segment coefficients (a*x + b*y = c) double a = segment.second.y() - segment.first.y(); double b = segment.first.x() - segment.second.x(); double c = a * segment.first.x() + b * segment.first.y(); // build A matrix and w vector to setup linear system of equation Eigen::Matrix2d A; A << -m, 1, a, b; Eigen::Vector2d w; w << 0, c; // solve linear system to find point of intersection Eigen::Vector2d p = A.colPivHouseholderQr().solve(w); // check that the point lies inside the segment double x_min = std::min(segment.first.x(), segment.second.x()); double x_max = std::max(segment.first.x(), segment.second.x()); double y_min = std::min(segment.first.y(), segment.second.y()); double y_max = std::max(segment.first.y(), segment.second.y()); // point is outside the segment if (p.x() < x_min || p.x() > x_max || p.y() < y_min || p.y() > y_max) { std::cerr << "p: " << p.transpose() << std::endl; std::cerr << "min: " << x_min << ", " << y_min << std::endl; std::cerr << "max: " << x_max << ", " << y_max << std::endl; std::cerr << (p.x() < x_min ? "true" : "false") << std::endl; std::cerr << (p.x() > x_max ? "true" : "false") << std::endl; std::cerr << (p.y() < y_min ? "true" : "false") << std::endl; std::cerr << (p.y() > y_max ? "true" : "false") << std::endl; continue; } std::cerr << "found" << std::endl; found = true; point = p; break; } if (!found) { throw std::runtime_error("wtf!!"); } std::cerr << std::endl; } return 0; } The weird thing is that at some point the final check is not behaving as expected. In particular, I get: p: 2 0.828427 min: 2, -2 max: 2, 2 true false false false That is the check p.x() < x_min is true when p.x() = 2 and x_min = 2. Can someone please tell me what is the problem? Thanks.
I found the problem. As usual, the computer is right!!! By doing: std::cerr.precision(60); I find that: p: 1.999999999999999555910790149937383830547332763671875000000000 0.828427124746190735038453567540273070335388183593750000000000 min: 2.000000000000000000000000000000000000000000000000000000000000, -2.000000000000000000000000000000000000000000000000000000000000 max: 2.000000000000000000000000000000000000000000000000000000000000, 2.000000000000000000000000000000000000000000000000000000000000 true false false false terminate called after throwing an instance of 'std::runtime_error' what(): wtf!!
Wrong multiplication of quaternions
I made my quaternion realization and to test it I used boost. I test it in cycle and I always have errors in multiplication and division. Then I used Wolfram to find out who was wrong and I find out that boost was wrong. I am not sure that the boost library was Wong, maybe some one can find out what is going on? Console output example: 0 Operation : [ * ] First vector = [ -41.4168 -92.2373 -33.0126 -42.2364 ] Second vector = [ -67.8087 -60.3523 58.8705 36.3265 ] My multiplication = [ 719.45 10041.3 5700.03 -6062.97 ] Boost multiplication = [ 719.45 10041.3 10041.3 5700.03 ] main.cpp #include "quaternion.h" #include <boost/math/quaternion.hpp> #include <random> #include <time.h> #include <QDebug> using boost::math::quaternion; double fRand(double fMin, double fMax) { double f = (double)rand() / RAND_MAX; return fMin + f * (fMax - fMin); } bool isEqual(double a, double b){ return std::abs(a-b) < std::numeric_limits<double>::epsilon(); } bool isEqual(Quaternion& quat1, quaternion<double> quat2){ if (!isEqual(quat2.R_component_1(), quat1.real)) return false; if (!isEqual(quat2.R_component_2(), quat1.imagine.data.at(0))) return false; if (!isEqual(quat2.R_component_3(), quat1.imagine.data.at(1))) return false; if (!isEqual(quat2.R_component_4(), quat1.imagine.data.at(2))) return false; return true; } int main () { std::srand(std::time(nullptr)); Quaternion compR; quaternion<double> boost; double a1, a2, a3, a4, b1, b2, b3, b4; try { for (unsigned int i=0; i<10000000; ++i){ a1 = fRand(-100, 100); a2 = fRand(-100, 100); a3 = fRand(-100, 100); a4 = fRand(-100, 100); b1 = fRand(-100, 100); b2 = fRand(-100, 100); b3 = fRand(-100, 100); b4 = fRand(-100, 100); Quaternion comp1{a1,a2,a3,a4}; Quaternion comp2{b1,b2,b3,b4}; quaternion<double> a(a1,a2,a3,a4); quaternion<double> b(b1,b2,b3,b4); //if (i%50000==0) qDebug() << i; compR = comp1+comp2; boost = a+b; if (!isEqual(compR, boost)) throw std::runtime_error("+"); compR = comp1-comp2; boost = a-b; if (!isEqual(compR, boost)) throw std::runtime_error("-"); compR = comp1*comp2; boost = a*b; if (!isEqual(compR, boost)) throw std::runtime_error("*"); compR = comp1/comp2; boost = a/b; if (!isEqual(compR, boost)) throw std::runtime_error("/"); } } catch (const std::runtime_error& error) { qDebug() << "Operation : [" << error.what() << "]"; qDebug() << " First vector = [" << a1 << " " << a2 << " " << " " << a3 << " " << a4 << "]\n" << "Second vector = [" << b1 << " " << b2 << " " << " " << b3 << " " << b4 << "]"; qDebug() << " My multiplication = [" << compR.real << " " << compR.imagine.data.at(0) << " " << compR.imagine.data.at(1)<< " " << compR.imagine.data.at(2) << "]\n" << "Boost multiplication = [" << boost.R_component_1() << " " << boost.R_component_2() << " " << boost.R_component_2() << " " << boost.R_component_3() << "]"; } return 0; } Full project:https://github.com/Yegres5/quaternion
You have two issues: you have a bug printing the boost quaternion (you print the index 2 twice) use a larger epsilon. Using epsilon as it is can only be suitable, if your numbers around 1. Your result is around 10,000, so you need to use at least 10,000x larger epsilon. At this still may not be large enough, as quaternion multiplication uses several operations, each one can add some additional error. So, to be safe, multiply epsilon further by ~10 or so.
Calculating the minimum translation to seperate two lines
Given two intersecting line segments(AB and CD) in R2 find the translation with the smallest magnitude to apply to CD so that it is no longer intersecting AB. What I've tried. I calculate the distance from each point on each line to the opposite line. Then I pick the smallest of the 4 values and apply that to the perpendicular of the line. However, the translation I calculate is often in the wrong direction. How do I fix this? #include <iostream> #include <cmath> #include <algorithm> #include <vector> #include <iterator> class Vector2 { public: double x; double y; Vector2() : x(x), y(y) {} Vector2(double x, double y) : x(x), y(y) {} Vector2 operator*(double val) { return Vector2(x*val, y*val);} Vector2 operator/(double val) { return Vector2(x/val, y/val);} Vector2 operator+(Vector2 &v) { return Vector2(x+v.x, y+v.y);} Vector2 operator-(Vector2 &v) { return Vector2(x-v.x, y-v.y);} Vector2 Perpendicular() { return Vector2(y, -x); } double Dot( Vector2 &v) {return (x * v.x) + (y * v.y); } double Magnitude() { return std::sqrt(Dot(*this)); } Vector2 Normal() { return *this / Magnitude(); } double GetDistance( Vector2 &v) { Vector2 d = *this - v; return d.Magnitude(); } }; class Line { public: Line() : a(Vector2()), b(Vector2()) {} Line( Vector2 a, Vector2 b) : a(a), b(b) {}; double DistanceFromPoint( Vector2 &p) ; Vector2 GetTranslation( Line &l) ; Vector2& GetPoint(unsigned i) {if (i==0) return a; else return b;} double GetLength() { return GetPoint(0).GetDistance(GetPoint(1)); } Vector2 a; Vector2 b; }; double Line::DistanceFromPoint( Vector2 &p) { double l2 = GetLength() * GetLength(); Vector2 pv = p - GetPoint(0); Vector2 wv = GetPoint(1) - GetPoint(0); double t = std::max(0.d, std::min(1.d, pv.Dot(wv) / l2)); Vector2 projection = (wv * t) + GetPoint(0); return p.GetDistance(projection); } Vector2 Line::GetTranslation( Line &l) { // Calculate Distances from each point to rthe opposite line std::vector<double> dist(4); dist[0] = DistanceFromPoint(l.GetPoint(0)); dist[1] = DistanceFromPoint(l.GetPoint(1)); dist[2] = l.DistanceFromPoint(GetPoint(0)); dist[3] = l.DistanceFromPoint(GetPoint(1)); //Get the smallest distance auto it = std::min_element(std::begin(dist), std::end(dist)); double min = *it; unsigned pos = std::distance(std::begin(dist), it); // Get the normalized perpendicular of line Vector2 axis; if (pos == 2 || pos == 3) axis = (GetPoint(1) - GetPoint(0)).Perpendicular().Normal(); else axis = (l.GetPoint(1) - l.GetPoint(0)).Perpendicular().Normal(); std::cout << "min: " << min << std::endl; std::cout << "axis: (" << axis.x << "," << axis.y << ")" << std::endl; //Apply that min to the perpendicular return axis * min; } int main() { Line A; Line B; Vector2 t; std::cout << "Left" << std::endl; A = Line(Vector2(0, 4), Vector2(8, 4)); B = Line(Vector2(2, 0), Vector2(2, 6)); t = A.GetTranslation(B); std::cout << "Expected: (-2, 0)" << std::endl; std::cout << "Got: (" << t.x << "," << t.y << ")" << std::endl << std::endl; std::cout << "Right" << std::endl; B = Line(Vector2(6, 0), Vector2(6, 6)); t = A.GetTranslation(B); std::cout << "Expected: (2, 0)" << std::endl; std::cout << "translation: (" << t.x << "," << t.y << ")" << std::endl << std::endl; std::cout << "Top" << std::endl; B = Line(Vector2(4, 0), Vector2(4, 6)); t = A.GetTranslation(B); std::cout << "Expected: (0, -2)" << std::endl; std::cout << "translation: (" << t.x << "," << t.y << ")" << std::endl << std::endl; std::cout << "Bottom" << std::endl; B = Line(Vector2(4, 6), Vector2(4, 8)); t = A.GetTranslation(B); std::cout << "Expected: (0, -2)" << std::endl; std::cout << "translation: (" << t.x << "," << t.y << ")" << std::endl; }
Your idea will work. You only need to have a signed distance function like #Tommy mentioned. Also, note that you will have to get the right perpendicular direction as well, depending on your sign conventions in the distance function. Here is a demo
You've got the right approach. From reasoning about the Minkowski sum, the shortest vector of separation will be perpendicular to one of the lines and will be just long enough to push one of the endpoints to the surface of the other line. However your DistanceFromPoint returns an unsigned distance: it's always zero or positive. You then use a single perpendicular per line to multiply that by. So you should end up going the wrong way 50% of the time because your distance has no sign. You might prefer to rearrange, not least because it'll save some of your square roots: get the two axes; for each line, project the vector from any point on the line to each endpoint of the other line onto that line's axis. This produces a signed result; pick the shortest of those results by magnitude; calculate the separation axis as the negative of the shortest number times the axis it was calculated on. ... because if you're embedded by n units along axis q then the way to resolve that is to move -n units along q; so pick the least n.
get vertices index to save Triangulation
I want to save a triangulation_2D to the next format : v x1 y1 v x2 y2 ... f v11 v12 v13 f v21 v22 v23 ... where v11 v12 ... are indexes of the points already saved, here what I'm using : (CDT is a Triangulation_2 and _Point is a point_2) std::vector<_Point> meshpoints; int find_index(_Point p) { bool trouv = false; unsigned i = 0; while(i < meshpoints.size() && !trouv) { if(p.x() == meshpoints.at(i).x() && p.y() == meshpoints.at(i).y()) trouv = true; else i++; } if(trouv) return i; return -1; } void save_triangulation(CDT triangulation, std::string link = "unkown.txt") { std::cout << "Saving IVGE . . ." ; std::ofstream triangulation_file(link); for(CDT::Vertex_iterator vertex = triangulation.vertices_begin() ; vertex != triangulation.vertices_end() ; vertex++) meshpoints.push_back(vertex->point()); for(unsigned i = 0 ; i < meshpoints.size() ; i++) triangulation_file << "v " << std::setprecision(15) << meshpoints.at(i) << std::endl; for(CDT::Face_iterator c = triangulation.faces_begin(); c != triangulation.faces_end(); c++) { triangulation_file << "f " << find_indice(c->vertex(0)->point()) << " " << find_indice(c->vertex(1)->point()) << " " << find_indice(c->vertex(2)->point()) <<std::endl; } std::cout << " Done\n" ; } my question is : is there any method to optimize find_index, cause I have a triangulation of more then 2M points. I changed the format of out put file, here is the new format: v x1 y1 v x2 y2 ... f v11 v12 v13 a11 a12 a13 f v21 v22 v23 a21 a22 a23 ... where a12 ... are triangle neighbors. the function becomes : std::ofstream Ivge_file(link); unsigned Vertx_index = 0; Ivge_file << std::setprecision(15); for(CDT::Vertex_iterator vertex = IVGE.vertices_begin() ; vertex != IVGE.vertices_end() ; vertex++) { Triangulation::Vertex_handle vertx_h = vertex; vertx_h->info() = Vertx_index; Vertx_index++; Ivge_file << "v " << vertex->point() << std::endl; } int id=0; for(CDT::Face_iterator c = IVGE.faces_begin(); c != IVGE.faces_end(); c++,id++) c->info()._Id = id; for(CDT::Face_iterator c = IVGE.faces_begin(); c != IVGE.faces_end(); c++) Ivge_file << "f " << c->vertex(0)->info() << " " << c->vertex(1)->info() << " " << c->vertex(2)->info() << " " << c->neighbor(0)->info()._Id << " " << c->neighbor(1)->info()._Id << " " << c->neighbor(2)->info()._Id << std::endl;
I changed the format of out put file, here is the new format: v x1 y1 v x2 y2 ... f v11 v12 v13 a11 a12 a13 f v21 v22 v23 a21 a22 a23 ... where a12 ... are triangle neighbors. the function becomes : std::ofstream Ivge_file(link); unsigned Vertx_index = 0; Ivge_file << std::setprecision(15); for(CDT::Vertex_iterator vertex = IVGE.vertices_begin() ; vertex != IVGE.vertices_end() ; vertex++) { Triangulation::Vertex_handle vertx_h = vertex; vertx_h->info() = Vertx_index; Vertx_index++; Ivge_file << "v " << vertex->point() << std::endl; } int id=0; for(CDT::Face_iterator c = IVGE.faces_begin(); c != IVGE.faces_end(); c++,id++) c->info()._Id = id; for(CDT::Face_iterator c = IVGE.faces_begin(); c != IVGE.faces_end(); c++) Ivge_file << "f " << c->vertex(0)->info() << " " << c->vertex(1)->info() << " " << c->vertex(2)->info() << " " << c->neighbor(0)->info()._Id << " " << c->neighbor(1)->info()._Id << " " << c->neighbor(2)->info()._Id << std::endl;