I'm looking for an algorithm that can find intersecting rectangles that overlap each other.
The catch is that my data structure is similar to a quadtree with bounding boxes instead of points.
I'm doing a basic rectangle intersection check but the problem is as I zoom into the tree the child nodes get detected and the parents, I would like to exclude the parent if its fully occluded by a child for the given camera rectangle.
zoom animation
As you can see from the image above as the camera rectangle (black box) sits inside the green node the purple node is still highlighted (filled), consequently as I zoom more and more the parents are always highlighted, even though the camera rectangle can be fully filled with child nodes only.
This makes sense since the camera rectangle is still inside the parent but I have searched and thought about the problem for a while and can't seem to figure out an elegant solution. There seems to be several ways of doing this for 3D spaces but I can't find anything simple for 2D AABB rectangles.
A few solutions that I thought of:
Subtract the child nodes from the parents resulting in concave polygons and then perform polygon intersection.
Use the fill color to check which rectangles are visible, therefore occluding the ones behind.
Perform raycasting or sub-division and check which is the smallest node for a given section.
Is there a better way to do this?
Thank you
Update 1
I have solved the problem by subdividing the camera into smaller sections and for each section the smallest intersecting node is found. This works but there must be a more efficient and cleaner way of doing this.
Update 2
Thank you Trentium for your answer. I can clearly see that an algorithm like that would be a lot more performant than what I'm currently doing.
Eventually I will implement it as splitting a rectangle into smaller rectangles and not polygons sounds like a fun challenge.
Also, I did some very non scientific benchmarks on the current approach and it takes 0.5ms-1ms to both filter and draw everything, so for now performance is still not a concern.
Suggest considering a variation of your first solution proposed "Subtract the child nodes from the parents resulting in concave polygons and then perform polygon intersection."
Specifically, if the immediate children are wholly contained within the parent, then suggest that for each rectangle, that an associated array of visible residual rectangles also be maintained. And then use this array of residual rectangles as the means of determining if the camera / viewport rectangle includes the parent or not.
For example, let's say the parent rectangle is (0,0) - (100,100) and there is an initial child rectangle added at (75,75)-(100,100). The data structure will appear as...
parent.rectangle = {0,0,100,100}
parent.visible = [ {0,0,100,75}, {0,75,75,100} ]
child1.rectangle = {75,75,100,100}
child1.visible = [ {75,75,100,100} ]
...and then if a second child comes along, say at {50,50,75,90}, then this new child is checked against each residual rectangle in the parent.visible array, subdividing the parent visible rectangles further as necessary...
parent.rectangle = {0,0,100,100}
parent.visible = [ {0,0,100,50}, {0,50,50,75}, {75,50,100,75}, {0,75,50,100}, {50,90,75,100} ]
child1.rectangle = {75,75,100,100}
child1.visible = [ {75,75,100,100} ]
child2.rectangle = {50,50,75,90}
child2.visible = [ {50,50,75,90} ]
This method will add a bit of work up front adjusting the immediate parent's visible rectangles as children are added, but should greatly reduce the amount of rectangle intersection tests relative to the current algorithm that involves subdivision of the camera / viewport. Plus this proposed algorithm only makes use of rectangle-to-rectangle intersection tests (both when adding a child to a parent, and when testing the camera / viewport intersections!) rather than the suggested rectangle-to-polygon tests...
I've been using easelJS within the createJS framework for a project and have enjoyed it a lot until recently hitting a roadblock. I have multiple objects that I'd like to move simultaneously when one of the group is dragged. Here is my current situation:
What I'd like to do is when the red circle is moved, the red crosshairs would also move so that they appear to be "locked" to the circle. The same with the green circle.
I have been able to accomplish something very close to this by adding the circles and crosshairs to a container, as mentioned in the answers to this question:
Easeljs Scrollable Container
But the issue I encounter is that the container is actually a rectangle, such that I can click anywhere between the circle and crosshairs to move the various objects contained within the container. Instead I would like for the objects to be moved only when I click on a circle.
Does anyone have any idea how to accomplish this? Am I correct in thinking this can be accomplished somehow with easelJS containers?
Containers should be fine. You can turn off mouseEnabled on the cross-hair in order to make it ignore the mouse.
You could also just store the offset for each cross-hair/circle, and just set the cross-hair position when the circle moves.
Here is a quick demo:
http://jsfiddle.net/lannymcnie/kah9of6e/
// Set the offset when the circle is pressed
circle.on("mousedown", function(e) {
circle.offset = new createjs.Point(crosshair.x-circle.x, crosshair.y-circle.y);
});
// Add drag and drop to each shape
circle.on("pressmove", handleDrag);
crosshair.on("pressmove", handleDrag);
function handleDrag(e) {
// Move the target to the mouse
e.target.x = e.stageX; e.target.y = e.stageY;
// If the target is the circle, also move the cross-hair
if (e.target == circle) {
// Move the cross-hair
crosshair.x = circle.x + circle.offset.x;
x.y = circle.y + circle.offset.y;
}
}
I basically have a rectangle with a PointText item overlayed on top of it. I currently have a working sample where I can move items around on the canvas by using sample code from the paperjs-v0.9.23\examples\Paperjs.org\HitTesting.html which has been really useful. However, I want to treat the rectangle and the text as one logical grouping to be moved around.
Please see the link below to see what I mean:
http://jsfiddle.net/svt9wa9f/6/
In the HitTesting.html example it has this within the onMouseDown event: `
if (hitResult)
{
path = hitResult.item;
if (hitResult.type == 'segment')
{
segment = hitResult.segment;
} else if (hitResult.type == 'stroke')
{
var location = hitResult.location;
segment = path.insert(location.index + 1, event.point);
path.smooth();
}
}
`
I was hoping I could modify this to work with the groups, but it seems this type of hitesting doesn't work. Everytime I click within any object I just get the 'fill' type. So I was then thinking I would have to do a linear search through the array of groups performing a hittest just to see which item is within the group. Or a hashmap keyd on items, and the value as the group. But there must be an easier way?
Within the fiddle example, the group code is commented out because. When you first execute it you can't see the items, you have to hover the mouse pointer of the canvas to get them to appear. Any help on this would be appreciated.
Ideally, I just need to be able to move these groups around through drag events, and extend the code to be able to determine whilst dragging if I am over another grouping. But that is further down the line.
Thank you for your time.
I have a plot in D3 where I draw some circles and then some ellipses afterwards to show the median. I have a 1.5 second delay on my median, to try and draw it after the circles have appeared, but I still run into problems.
Here is a screenshot of an example: http://puu.sh/8csEK.png
The circle to the far right are behind it's median, the rest of the circles are all in front. When areas are crowded you cannot see the median anymore.
I have even tried using the following on transitions of my circles, but it's no use:
.each("end", <call function to draw ellipses>)
So my question is, how do i make sure that my ellipses are drawn on top of my circles?
I have a function that draws my ellipses and a function that draws my circles right now.
I'm assuming that you're using SVG to render your elements. In SVG, the display order is the order of drawing/appending to the DOM. That is, the element you append first is drawn at the back, the element you append last at the front. Child elements (e.g. something underneath a g) are drawn when their parent elements are drawn.
To make sure that groups of elements have the right order, it's usually easiest to add them to different SVG groups that are drawn in the right order. In code, this looks something like this.
var circles = svg.append("g");
var ellipses = svg.append("g");
// ...
ellipses.append(...); // this element appears in the front although it is drawn
// earlier because it is appended to the group appended last
circles.append(...); // this element appears behind the one appended to ellipses
I have a little tool that draws up a grid of circles(representing holes) that allows the user to add text and lines to these circles. Right now I have it set up so if the user clicks on any of the holes then wherever the hole is moved so is every other element on the Paper object. What I am trying to implement next is the ability to rotate everything as one object. I realize that for this to work that I need to know the central point of all the objects, which I can easily get.
What I want to know is should I draw everything on another object. This object will act as another Paper object of sorts, but will only serve for movement and rotation. Any click events on the holes drawn on the object will be passed on to the parent (i.e. the pseudo-paper object everything is drawn on). Is this possible? If so how would I draw everything onto say, a rectangle? And if not what would be the best way to go implementing it?
What you need is a Set. You create it, push objects to it, and then treat it as an entire group, in your case by applying transformations.
Example:
var elements = paper.set();
if (!view.text) {
view.text = App.R.text(0, 0, this.value);
view.text.attr({
'font-size': font_size,
});
elements.push(view.text);
}
elements.transform('something');
Note that you can also bind events to this entire set.