0

For one of my current javafx projects I'm working with Venn Diagrams. I'd like to take advantage of Shape.intersect to ease the amount of math calculations I'd have to personally work through (and avoid working with Path objects). Shape.intersect produces a perfect shape, but it is translated / moved a bit and I can't figure out how much to put it back. Ideally, I'd like to have the intersection area occur exactly where the two original shapes intersect - i.e. the new intersection would fit seamlessly with the original shapes.

At the moment, my code produces a triple Venn Diagram that scales with the window. I have access to the 3 center points of the circles as well as rough approximations of other important points of intersection. These other points are approximated because they rely on sin and cos values to make sure all three circles are the same size and symmetrical.

I mocked up the following image (the points/letters were added externally in paint but the underlying image is what my app produces now): enter image description here

As you can see, the intersection of circles A and B (shown in blue) is off the mark.

To produce this, my code scales the underlying canvas to the window size, calculates where to place the centers of the circles, and then draws all 3 with the same radius. Cutting out all the extra fluff, my code essentially looks like this:

// Constructor creates 3 circles and adds to canvas
Circle a = new Circle();
canvas.getChildren().add(a);
// etc. for the other two circles

// And also makes a Shape object to hold the intersection
Shape ab = Shape.intersect(a,b); // initially empty
canvas.getChildren().add(ab);

Later on...

// Resizing the window calls an update function
// which recalculates the circles' centers and 
// radii and then updates them. Since circles are
// drawn from upper left corner, the relocate call 
// makes sure to subtract the radius to center it
a.setRadius(radius);
a.relocate(acx-radius,acy-radius);

And the intersection logic (also in the update function):

canvas.getChildren().remove(ab);
ab = Shape.intersect(a,b);

// Can relocate to point c, but this doesn't do the job either (see below)
ab.relocate(pcx,pcy);

// Add the new intersection back to the canvas
canvas.getChildren().add(ab);

I've tried doing a relocate on the resulting intersection shape to point C, but I'm not sure if my approximation of that point is just rounded too much because it still isn't properly aligned:

enter image description here

I'm guessing this offset might have something to do with layout bounds / visual bounds or something that gets altered on a resize and never properly updated, but I spent a long term looking at the docs and couldn't figure it out.

Ideally, I'd like to avoid having to calculate point C by hand since the circles themselves are generated through approximation / rounding and this makes C virtually impossible to get exact. I hope someone can point me to a solution that can use the original circles only, but I'll take anything that makes the intersection appear at the right location regardless of the size of the current canvas.

In summary: how can I make the shape returned by Shape.intersect appear at the center of the two shapes it is created from? Thanks for your time.

Prester
  • 47
  • 1
  • 7
  • 1
    I tried recreating your problem, but [it worked fine](http://imgur.com/a/42vf9) with me. I see you have a menu bar and a status bar, could it be because your intersection binds to a set of coordinates that doesn't include one of those two elements? Maybe if you shared the code that binds or resizes your shapes, it would be easier to fix? – MikaelF Jan 30 '17 at 06:08
  • Thanks for the response. I'm going to see if I can make a very concise example of my issue since my code is so long and messy right now it'd be counterproductive to upload it as is. I'll update in a couple of hours after I finish work today. I suspect the issue is with the resizing of the canvas object I'm using, so yeah I agree that'll definitely be important to share. – Prester Jan 30 '17 at 13:09

1 Answers1

0

I managed to figure out the issue. I was working with an auto-resizing canvas object inspired by this post I found here on StackOverflow. In case that link ever changes, the key bit of code I borrowed from there looked like the following:

// Update function when the window is resized
@Override
protected void layoutChildren() {
    final double top = snappedTopInset();
    final double right = snappedRightInset();
    final double bottom = snappedBottomInset();
    final double left = snappedLeftInset();

    width = getWidth() - left - right;
    height = getHeight() - top - bottom;

    // Update the layout
    canvas.setLayoutX(left);
    canvas.setLayoutY(top);

    if (width != canvas.getWidth() || height != canvas.getHeight()) {
        // Resize
        canvas.setWidth(width);
        canvas.setHeight(height);

        // Determine center
        cx = width / 2;
        cy = height / 2;

        // Determine radius of circles
        radius = 0.25 * Math.min(width, height);

        // Clear and redraw circles
        clear();
        redraw();
    }
}

where "canvas" was a private instance of a Canvas object, and this entire function was inside a class called CanvasPane that extended Pane.

What I found out The code above has an issue when you also have objects like Circles added to the children() of the CanvasPane. My redraw() method only moved the circle's centers and adjusted the radii. I never updated the .setLayoutX() and .setLayoutY() functions like I did for the actual canvas object for the actual circles. So resizing the CanvasPane would update the layout information inconsistently and all the circles' underlying layout positions would be out of sync.

So, the fix here is to change the Update layout section to the following:

// Update the layout
for (Node n : getChildren()) {
    n.setLayoutX(left);
    n.setLayoutY(top);
}

This updates the layout positions of the circles and any other object added to the CanvasPane (not just the canvas as I had originally), making the resulting intersection piece actually in the right position.

In hindsight, I definitely should have included more code in my original question so others could help out. Hopefully though someone someday will have the same issues as me and be able to find this answer.

Community
  • 1
  • 1
Prester
  • 47
  • 1
  • 7