-
Notifications
You must be signed in to change notification settings - Fork 315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Relax intersection test. #111
Conversation
Okay, let me try and break it! :-) |
We’re looking for cases where place(a, b, c) will place c so it intersects with a or b.
No, I’m afraid it still breaks rather easily. I’ve pushed some test code in PR #112. Here’s a sample failure: const a = { r: 0.8733723828537281, x: 104.30717184715218, y: 0 },
b = { r: 104.30717184715218, x: -0.8733723828537281, y: 0 },
c = { r: 15053206.77362454 };
place(a, b, c);
console.log(intersects(a, c)); // true
console.log(intersects(b, c)); // true |
Suppose we were to make the intersection test very relaxed indeed: function intersects(a, b) {
var dr = a.r + b.r - 0.1, dx = b.x - a.x, dy = b.y - a.y;
return dr > 0 && dr * dr * 0.9 > dx * dx + dy * dy;
} it still finds plenty of errors, e.g. a = {x: 271644298.81561947, y: 0, r: 1.0899431836479527},
b = {x: -1.0899431836479527, y: 0, r: 271644298.81561947},
c = {r: 0.7603609799366486} Which suggests to me we need to think of something cleverer than relaxing the intersection test. |
Here is a simple example where a = {x: 0.01, y: 0, r: 0.03},
b = {x: -0.03, y: 0, r: 0.01},
c = {r: 9e6}; In fact the placement is obviously wrong: This is so wrong I wonder if there’s actually an error in the algorithm, rather than it being the result of numerical imprecision. |
Here’s a thing. I tried a different implementation of the place function: function place(a, b, c) {
var dx = b.x - a.x,
dy = b.y - a.y,
dc = dx * dx + dy * dy;
if (dc) {
var scale = Math.sqrt(dc);
var ar = a.r / scale, br = b.r / scale, cr = c.r / scale;
var x = (1 - (br - ar) * (ar + br + 2*cr)) / 2;
var y = Math.sqrt(Math.max(0, (ar + cr) * (ar + cr) - x * x));
c.x = a.x + x * dx + y * dy;
c.y = a.y + x * dy - y * dx;
} else {
c.x = a.x + a.r + c.r;
c.y = a.y;
}
} This implementation works okay in the cases where yours fails, i.e. where c is much larger than a and b. But it seems to fail in a different class of cases, where a is large but b and c are small. So perhaps we can use a hybrid approach, where we switch algorithms depending on the inputs. |
Related d3/d3@7c28977 d3/d3@9b3e780. |
Here is a version of the function place(a, b, c) {
var dx = b.x - a.x,
dy = b.y - a.y,
dc = dx * dx + dy * dy;
if (dc) {
var scale = Math.sqrt(dc);
var ar = a.r / scale, br = b.r / scale, cr = c.r / scale;
var x, y;
if (a.r > b.r) {
x = (1 - (ar - br) * (ar + br + 2*cr)) / 2;
y = Math.sqrt(Math.max(0, (br + cr) * (br + cr) - x * x));
c.x = b.x - x * dx - y * dy;
c.y = b.y - x * dy + y * dx;
} else {
x = (1 - (br - ar) * (ar + br + 2*cr)) / 2;
y = Math.sqrt(Math.max(0, (ar + cr) * (ar + cr) - x * x));
c.x = a.x + x * dx - y * dy;
c.y = a.y + x * dy + y * dx;
}
} else {
c.x = a.x + a.r + c.r;
c.y = a.y;
}
} |
The old place(a, b, c) function would sometimes place c so it overlapped with a and/or b, if c had a much larger radius than a and b. This version doesn’t seem to. As per discussion on d3#111. This commit also updates the tests in find-place-bugs.js to use the new function, and updates the intersection test in siblings-test.js to match Note that flare-test.js fails. I haven’t investigated why: I hope it is simply a result of the changes in numerical precision causing slightly different circle placements.
The old place(a, b, c) function would sometimes place c so it overlapped with a and/or b, if c had a much larger radius than a and b. This version doesn’t seem to. As per discussion on #111. This commit also updates the tests in find-place-bugs.js to use the new function, and updates the intersection test in siblings-test.js to match Note that flare-test.js fails. I haven’t investigated why: I hope it is simply a result of the changes in numerical precision causing slightly different circle placements.
Since the values that had been divided by scale = sqrt(dc) are only being used in pairs, we can avoid the square root and just divide by dc instead. This should help with the precision, marginally, I hope.
The old place(a, b, c) function would sometimes place c so it overlapped with a and/or b, if c had a much larger radius than a and b. This version doesn’t seem to. As per discussion on #111. This commit also updates the tests in find-place-bugs.js to use the new function, and updates the intersection test in siblings-test.js to match Note that flare-test.js fails. I haven’t investigated why: I hope it is simply a result of the changes in numerical precision causing slightly different circle placements.
Fixes #109. Applies a similar strategy to enclosesWeak. I also tweaked the arithmetic a tiny bit to maybe offer a tiny bit more precision. Look good, @robinhouston?