Skip to content
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

Merged
merged 8 commits into from
Apr 16, 2018
Merged

Relax intersection test. #111

merged 8 commits into from
Apr 16, 2018

Conversation

mbostock
Copy link
Member

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?

@robinhouston
Copy link
Contributor

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.
@robinhouston
Copy link
Contributor

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

@robinhouston
Copy link
Contributor

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.

@robinhouston
Copy link
Contributor

robinhouston commented Apr 15, 2018

Here is a simple example where place(a, b, c) results in c containing both a and b with a very comfortable margin:

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: x: -4499999.99, y: 0.

This is so wrong I wonder if there’s actually an error in the algorithm, rather than it being the result of numerical imprecision.

@robinhouston
Copy link
Contributor

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.

@mbostock
Copy link
Member Author

Related d3/d3@7c28977 d3/d3@9b3e780.

@robinhouston
Copy link
Contributor

robinhouston commented Apr 15, 2018

Here is a version of the place function that seems to be holding up to my random testing so far:

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;
  }
}

robinhouston added a commit to robinhouston/d3-hierarchy that referenced this pull request Apr 15, 2018
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.
robinhouston and others added 3 commits April 16, 2018 13:02
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.
@mbostock mbostock merged commit 2796cb5 into master Apr 16, 2018
mbostock pushed a commit that referenced this pull request Apr 16, 2018
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.
@mbostock mbostock deleted the place-precision branch April 16, 2018 14:43
@mbostock mbostock mentioned this pull request Apr 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

d3.packSiblings overlaps, hangs.
2 participants