What's the best or an efficient method for subtracting a bounding box from another bounding box (I.e. Creating n bounding boxes from a bounding box Boolean subtraction)?
Ideally the resulting bounding boxes are as square as possible so that there are limited 'shard' (I.e. 1width, 1height, 100depth).
This question is quite old, but since I did it, I'll answer it.
Think of two extents (bounding boxes) first as concentric squares. We are subtracting the inner one from the outer.
A----------------------------------B
| |
| A----------------------B |
| | | |
| | | |
| D----------------------C |
| |
D----------------------------------C
Then you are going to end up with 8 boxes.
A----------------------------------B
| 1 | 2 | 3 |
|----------------------------------|
| | | 4 |
| 8 | | |
|----------------------------------|
| 7 | 6 | 5 |
D----------------------------------C
So, the trick is to make the squares. Most graphics libraries have code to deal with 'extents'. I'll use OpenLayers in Javascript for an example. The idea is that you make extents (bounding boxes) by drawing a diagonal from each pair of points and get its bounding box, then cascade down using the points of some previously made bounding boxes. The code below should be self explanatory. We are subtracting extent e2, which is pictured as the inner extent, from extent e1, which is pictured as the outer extent:
var b1 = ol.extent.boundingExtent([ol.extent.getTopLeft(e1), ol.extent.getTopLeft(e2)]);
var b2 = ol.extent.boundingExtent([ol.extent.getBottomLeft(b1), ol.extent.getBottomLeft(e2)]);
var b3 = ol.extent.boundingExtent([ol.extent.getBottomLeft(e1), ol.extent.getBottomLeft(e2)]);
var b4 = ol.extent.boundingExtent([ol.extent.getBottomLeft(b3), ol.extent.getBottomRight(e2)]);
var b5 = ol.extent.boundingExtent([ol.extent.getBottomRight(e1), ol.extent.getBottomRight(e2)]);
var b6 = ol.extent.boundingExtent([ol.extent.getBottomRight(b5), ol.extent.getTopRight(e2)]);
var b7 = ol.extent.boundingExtent([ol.extent.getTopRight(e1), ol.extent.getTopRight(e2)]);
var b8 = ol.extent.boundingExtent([ol.extent.getTopLeft(e1), ol.extent.getTopLeft(b7)]);
For instance, notice how we use a coordinate from bounding box b1 to make b2, b3 to make b4, etc.
Now we know that the extents may not be not concentric. Some boxes may be outside of our answer. However, one cool condition to notice is that if a corner of the subtracting extent (e2) is inside the base extent (e1) then we need the three of its connected boxes. So, if the top left of e2 is in e1, then we need b1, b2, and b8. Similarly, if the bottom left corner of e2 is in e1, then we need b6, b7, and b8. You might notice some duplicates there such as b8, but we'll filter those out later. So, let's collect our results.
var results = [];
if (ol.extent.containsCoordinate(e1, ol.extent.getTopLeft(e2))) {
results.push(b1, b2, b3);
}
if (ol.extent.containsCoordinate(e1, ol.extent.getTopRight(e2))) {
results.push(b8, b7, b6);
}
if (ol.extent.containsCoordinate(e1, ol.extent.getBottomRight(e2))) {
results.push(b6, b5, b4);
}
if (ol.extent.containsCoordinate(e1, ol.extent.getBottomLeft(e2))) {
results.push(b2, b3, b4);
}
Eliminate the duplicates, but also pay attention to the fact that, especially in the case where the extents share borders, some of those boxes will be "empty". Usually, you can figure that out with an extent related getArea() function. Below is a clever javascript way to filter out duplicate objects in an array since Array.indexOf uses '===' and returns only its first match. And, we only collect boxes that actually have an area.
results = results.filter(function(a,i,arr) {
return arr.indexOf(a) === i && ol.extent.getArea(a) > 0;
});
Caveats of use:
Notice that if the two initial bounding boxes do not intersect, an empty list will result. So, this method only works on extents that intersect. Graphics libraries usually have a boolean function for intersection of two extents.
For example, if you are looking to load a potential extent (e1) minus one you've already loaded (e2) and they do not intersect, you probably just want to load e1 straight up. Depends on your application.
Hope this answer is useful to someone!
Related
I am very new to graph visualizations and software like graph-tool (gt). My main field is mathematics, but I am somewhat familiar with Python and programming in general. However, I'm not a programmer, so my code may be less than elegant; any suggestions of improvement will be gratefully appreciated.
Description:
I am very fond of the circular layout with edge bundling used to visualize very large graphs. As an example, I am trying to plot the C.Elegans connectome using the wonderful Python module graph-tool by Tiago Peixoto. For this I use the following:
import graph_tool.all as gt
g_celegans = gt.load_graph("c.elegans_neural.male_1.graphml")
v_prop = g_celegans.vertex_properties
celegans_state = gt.minimize_nested_blockmodel_dl(g_celegans)
celegans_state.draw(vertex_text = v_prop['name'], bg_color = 'w')
which produces:
Questions:
How could I (in gt) place the vertex labels on a line drawn from the center? Like this
Can I plot a similar layout, i.e. circular and edge bundling, but using a clustering (partition of the vertices) of my own, instead of the stochastic block model? In a sense, I guess I would just like to be able to use the circular + edge bundling feature on its own. Or am I missing the whole point somewhat?
(Would anyone recommend a good introductory treatment on graph visualizations including this type (connectogram)?)
Attempt at Question 1:
So, I managed to at least get somewhere with this:
def getAngle(vec):
norm_vec = vec / np.linalg.norm(vec)
one_vec = np.array([1,0])
dot_product = np.dot(norm_vec, one_vec)
return np.arccos(dot_product)
text_rot = [0]*len(list(text_pos))
for i, p in enumerate(text_pos):
if p[0]>=0 and p[1]>=0:
text_rot[i] = getAngle(p)
elif p[0]>=0 and p[1]<0:
text_rot[i] = -getAngle(p)
elif p[0]<0 and p[1]>=0:
text_rot[i] = getAngle(p)-np.pi
elif p[0]<0 and p[1]<0:
text_rot[i] = -getAngle(p)+np.pi
text_rot = np.asarray(text_rot)
t_rot = g_celegans.new_property('v','float', vals = text_rot)
options = {'pos': pos,
'vertex_text': v_prop['name'],
'vertex_text_rotation':t_rot,
'bg_color': 'w',
'vertex_shape': 'none',
'vertex_font_size': 5,
'edge_end_marker': 'none'
}
celegans_state.draw(**options)
which produces:
So, the rotation is fine, but I would like to offset the labels a bit further out. Now they're in the center of an invisible vertex. There are two vertex properties called 'text_position' and 'text_offset', which you may read about here.
Now, any value for 'vertex_text_position', such as -1 or 'centered' or if I pass a VertexPropertyMap object like for 'vertex_text_rotation' above, generates an IndexError:
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-197-d529fcf5647e> in <module>
9 }
10
---> 11 celegans_state.draw(**options)
~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/inference/nested_blockmodel.py in draw(self, **kwargs)
986 draws the hierarchical state."""
987 import graph_tool.draw
--> 988 return graph_tool.draw.draw_hierarchy(self, **kwargs)
989
990
~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/draw/cairo_draw.py in draw_hierarchy(state, pos, layout, beta, node_weight, vprops, eprops, hvprops, heprops, subsample_edges, rel_order, deg_size, vsize_scale, hsize_scale, hshortcuts, hide, bip_aspect, empty_branches, **kwargs)
2121 kwargs[k] = u.own_property(v.copy())
2122
-> 2123 pos = graph_draw(u, pos, vprops=t_vprops, eprops=t_eprops, vorder=tvorder,
2124 **kwargs)
2125
~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/draw/cairo_draw.py in graph_draw(g, pos, vprops, eprops, vorder, eorder, nodesfirst, output_size, fit_view, fit_view_ink, adjust_aspect, ink_scale, inline, inline_scale, mplfig, output, fmt, bg_color, **kwargs)
1074 vprops.get("fill_color", _vdefaults["fill_color"]),
1075 vcmap)
-> 1076 vprops["text_color"] = auto_colors(g, bg,
1077 vprops.get("text_position",
1078 _vdefaults["text_position"]),
~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/draw/cairo_draw.py in auto_colors(g, bg, pos, back)
724 return color_contrast(back)
725 c = g.new_vertex_property("vector<double>")
--> 726 map_property_values(bgc_pos, c, conv)
727 return c
728
~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/__init__.py in map_property_values(src_prop, tgt_prop, map_func)
1189 u = GraphView(g, directed=True, reversed=g.is_reversed(),
1190 skip_properties=True)
-> 1191 libcore.property_map_values(u._Graph__graph,
1192 _prop(k, g, src_prop),
1193 _prop(k, g, tgt_prop),
~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/draw/cairo_draw.py in conv(x)
722 return color_contrast(bgc)
723 else:
--> 724 return color_contrast(back)
725 c = g.new_vertex_property("vector<double>")
726 map_property_values(bgc_pos, c, conv)
~/anaconda3/envs/gt/lib/python3.9/site-packages/graph_tool/draw/cairo_draw.py in color_contrast(color)
694 def color_contrast(color):
695 c = np.asarray(color)
--> 696 y = c[0] * .299 + c[1] * .587 + c[2] * .114
697 if y < .5:
698 c[:3] = 1
IndexError: too many indices for array: array is 0-dimensional, but 1 were indexed
If I do 'vertex_text_offset = pos', this would mean that I offset each vertex by its own coordinates, and I could then just scale by say 0.1 to get them appropriately far out, which actually DID work great without rotation. Then I rotated the text, which yielded this (without scaling):
The problem seems to be that the center for rotation is the center of the vertex, which is not ideal if the text is moved out from the vertex. So, even if 'vertex_text_position' above would have worked, I'm guessing the rotation would have messed that up as well.
Weirdly enough, If I rotate the vertices using 'vertex_rotation' instead, the labels are rotated along with them (great!), but when I offset the text with vertex position (which should "push" outwards), I get the same faulty plot as above.
Next I tried 'vertex_shape = circle', and filling the vertices with white using 'vertex_fill_color = 'w''. Thus I would push the text out a bit from the edge by increasing the size of the vertex. For some reason this made all the edges of the graph white as well; so no colors at all in the plot. I guess the edges are thus colored based on the vertex colors.
What I ended up doing is to use the vertex properties 'text_out_color' and 'text_out_width', with a width of 0.003. This gives a nice bold style to the text, which makes it more readable against the colored background.
But, now I'm pretty much out of ideas.
Do anyone know a solution to my problem? i.e. placing the labels like I have them, but moving them further out (in the direction outwards from the center) or framing them in white so that they're more readable, but so that the plot still looks nice; as well as to question 2 above.
This is a couple months late so hopefully you figured it out but I'll leave this here for future reference:
To get those labels to look the way you want them to in this case is when you call the draw function you'll want to specify the position of the vertex text like so:
celegans_state.draw(vertex_text = v_prop['name'], bg_color = 'w', vertex_text_position='centered')
(as can be seen in the list of properties in the documentation 'centered' gives this exact effect: https://graph-tool.skewed.de/static/doc/draw.html#graph_tool.draw.graph_draw)
To get a circular graph I figure you want to use the radial tree layout (https://graph-tool.skewed.de/static/doc/draw.html?highlight=radial_tree_layout#graph_tool.draw.radial_tree_layout)
Hopefully this helps!
How can I modify this code to make the space between second and third levels on the box plot. I mean, I want to spit the box plot into two section, a and b on right side and c on the left side.
library(ggplot2)
df <- data.frame(group=sample(c("a","b","c"),100,replace=T),x=rnorm(100),y=rnorm(100)*rnorm(100))
xlabs <- paste(levels(df$group),"\n(N=",table(df$group),")",sep="")
ggplot(df,aes(x=group,y=x,color=group))+geom_boxplot()+scale_x_discrete(labels=xlabs)
Try to add new variable for facetting in ggplot.
df$facets <- ifelse(df$group == "a" | df$group == "b", "ab", "c")
Now plot with facet_grid and modified scale_x_dsicrete.
ggplot(df,aes(x=group,y=x,color=group)) +
geom_boxplot()+
facet_grid(~ facets, scales = "free_x", space = "free_x") +
scale_x_discrete(breaks = levels(df$group), labels = xlabs)
If your question has been answered, please make sure to accept an answer for further references.
In PostGIS you can intersect two geometries using:
geometry ST_Intersection (geometry geomA, geometry geomB);
In my case both geomA and geomB are LINESTRING so ST_Intersection() returns a POINT geometry.
I want to know if the intersection occurs in a begin/end node (the geometries touch) or in the middle (the geometries intersect).
I can compare the (Point.X, Point.Y) with each ending node:
geomA.nodes(0) - geomA.nodes(len-1)
geomB.nodes(0) - geomB.nodes(len-1)
But is very complex. And I would like a simple solution.
There are 3 intersect cases.
Example 1: Two lines in a "L" shape intersect in an end node on both lines on the bottom left.
Example 2: Two lines in a "T" shape where the vertical line intersects in the middle of the horizontal line. In this case the vertical line end node touches a non-end node of the horizontal line.
Example 3: Two lines in a "X" shape. Intersection point isn't an end node for either line.
For my problem I'm only interested in finding the touching scenario like Example 2.
NOTE
This is the pseudo code I use now.
geomM, geomN Linestring
a, b, c, d, z Points.
(a,b) begin/end node for geomM ST_StartPoint(geom) and ST_EndPoint(geom)
(c,d) begin/end node for geomN
z = ST_Intersect(geomM, geomN)
SELECT geomM, geomN, z
FROM Table
WHERE
(A and not ( B or C or D))
OR (B and not ( A or C or D))
OR (C and not ( A or B or D))
OR (D and not ( A or B or C))
A, B, C, D replace ( a=z ) ( b=z ) ( c=z ) ( d=z )
This mean one node {a,b,c,d} is equal to intersection z. But only one
This return all "T" shape intersections.
You need the PostGIS function ST_Touches() here. The function returns true if the geometries touch on their boundaries, but false if they intersect. In terms of your examples, Example 1 and 2 return true, Example 3 returns false.
Relaxed solution
To select the IDs of all pairs of touching geometry(LINESTRING, xxx) records from a single table use this:
SELECT x.id AS idA, y.id AS idB
FROM my_table x
JOIN my_table y ON ST_Touches(y.the_geom, x.the_geom)
WHERE x.id < y.id;
(The WHERE clause avoids duplicate results like (132, 254) and (254, 132).)
Note that the linestrings can also touch on any of their non-node vertices. If you want to strictly follow Example 2 then you have to compare every point on every linestring against every point on all other linestrings, which is obviously going to be a very intensive operation. Example 2 is basically only feasible when you know that the linestrings are very short, preferably just straight lines.
Strict solution, straight lines only
If all LINESTRINGs are straight, i.e. composed of a starting and an ending node only, then this is your solution:
SELECT h.id AS touched, v.id AS touching, ST_Intersection(h.the_geom, v.the_geom) AS touch_point
FROM my_table h -- "horizontal" T bar, being touched
JOIN my_table v ON -- "vertical" T bar, touching
(
-- The "vertical" start node touches, but not on either of the "horizonal" nodes
ST_Equals(ST_Intersection(h.the_geom, v.the_geom), ST_StartPoint(v.the_geom))
AND NOT ST_Equals(ST_StartPoint(h.the_geom), ST_StartPoint(v.the_geom))
AND NOT ST_Equals(ST_EndPoint(h.the_geom), ST_StartPoint(v.the_geom))
) OR (
-- The "vertical" end node touches, but not on either of the "horizonal" nodes
ST_Equals(ST_Intersection(h.the_geom, v.the_geom), ST_EndPoint(v.the_geom))
AND NOT ST_Equals(ST_StartPoint(h.the_geom), ST_EndPoint(v.the_geom))
AND NOT ST_Equals(ST_EndPoint(h.the_geom), ST_EndPoint(v.the_geom))
);
All the requirements are checked in the JOIN ON clause. This will also return the location where the "vertical" bar of the T touches the "horizontal" bar. Note that the conditions are short-circuited when being evaluated and repeated calls to a function with the same input data are optimized to a single call.
I'm trying to implement mouse selection for my game. When I QueryAABB it looks like it's treating objects much larger than they really are.
Here's what's going on in the image
The blue box is an actor containing a body that I'd like to select
The outline on the blue box is drawn by Box2DDebugRenderer
The mouse selects a region on the screen (white box), this is entirely graphical
The AABB is converted to meters and passed to QueryAABB
The callback was called for the blue box and turned it red
The green outline left behind is a separate body to check if my conversions were correct, this is not used for the actual selection process
It seems to be connected to my meter size, the larger it is, the more inaccurate the result is. At 1 meter = 1 pixel it works perfectly.
Meter conversions
val MetersToPixels = 160f
val PixelsToMeters = 1/MetersToPixels
def toMeters(n: Float) = n * PixelsToMeters
def toPixels(n: Float) = n * MetersToPixels
In the image I'm using MetersToPixels = 160f so the inaccuracy is more visible, but I really want MetersToPixels = 16f.
Relevant selection code
val x1 = selectPos.x
val y1 = selectPos.y
val x2 = getX
val y2 = getY + getHeight
val (l,r) =
if (x2 < x1)
(x2,x1)
else
(x1,x2)
val (b,t) =
if (y2 < y1)
(y2,y1)
else
(y1,y2)
world.QueryAABB(selectCallback, toMeters(l),toMeters(b), toMeters(r),toMeters(t))
This code is inside the act method of my CursorActor class. And selectPos represents the initial point where the use pressed down the left mouse button and getX and getY are Actor methods giving the current position. The next bit sorts them because they might be out of order. Then they are converted to meters because they are all in pixel units.
selectCallback: QueryCallback
override def reportFixture(fixture: Fixture): Boolean = {
fixture.getBody.getUserData match {
case selectable: Selectable =>
selected += selectable
true
case _ => true
}
}
Selectable is a trait that sets a boolean flag internally after the query which helps determines the color of the blue box. And selected is a mutable.HashSet[Selectable] defined inside of CursorActor.
Other things possibly worth noting
I'm new to libgdx and box2d.
The camera is scaled x2
My Box2DDebugRenderer uses the camera's combined matrix multiplied by MetersToPixels
From what I was able to gather, QueryAABB is naturally inaccurate for optimization. However, I've hit a roadblock with libgdx because it doesn't have any publicly visible function like b2testOverlap and from what I understand, there's no plan for there to be one any time soon.
I think my best solution would probably be to use jbox2d and pretend that libgdx's physics implementation doesn't exist.
Or as noone suggested I could add it to libgdx myself.
UPDATE
I decided to go with a simple solution of gathering the vertices from the fixture's shape and using com.badlogic.gdx.math.Intersector against the vertices of the selection. It works I guess. I may stop using QueryAABB all together if I decide to switch to using a sensor for the select box.
I'm working on my first video game for the Android platform as a bit of a nights and weekends project.
It is coming along nicely, but I am very unhappy with the control sensativity.
In this game, you move an object left and right on the screen. On the bottom of the screen is a "touchpad" of sorts, which is where your finger should rest.
/-------------------------\
| |
| |
| |
| Game Area |
| |
| |
| |
| |
| |
/-------------------------\
| |
| Touch Area |
| |
\-------------------------/
I am currently using a state variable to hold "MOVING_LEFT, MOVING_RIGHT, NOT_MOVING" and am updating the location of the player object each frame based on that variable.
However, my code that reads the touchscreen input and sets this state variable is either too sensative, or too laggy, depending on how I tweak it:
public void doTouch (MotionEvent e) {
int action = e.getAction();
if (action == MotionEvent.ACTION_DOWN) {
this.mTouchX = (int)e.getX();
this.mTouchY = (int)e.getY();
}
else if (action == MotionEvent.ACTION_MOVE) {
if ((int)e.getX() >= this.mTouchX) {
this.mTouchX = (int)e.getX();
this.mTouchY = (int)e.getY();
if (this.TouchRect.contains(this.mTouchX, this.mTouchY)) {
this.mTouchDirection = MOVING_RIGHT;
}
}
else if ((int)e.getX() <= this.mTouchX) {
this.mTouchX = (int)e.getX();
this.mTouchY = (int)e.getY();
if (this.TouchRect.contains(this.mTouchX, this.mTouchY)) {
this.mTouchDirection = MOVING_LEFT;
}
}
else {
this.mTouchDirection = NOT_MOVING;
}
}
else if (action == MotionEvent.ACTION_UP) {
this.mTouchDirection = NOT_MOVING;
}
}
The idea is that when there is any movement, I check the previous location of the users finger and then figure out what direction to move the player.
This doesn't work very well, I figure there are some IPhone/Android developers on here who have figured out how to do good controls via a touchscreen and can give some advice.
You could try something similar to "drag rectangles" on Windows. When you hold down the mouse button on something, you don't start a drag operation until the mouse moves outside a small region around the mouse-down location. Reason being that it's very hard to keep the cursor on the same pixel while clicking.
So a first attempt could be (int)e.getX() >= this.mTouchX + DEAD_ZONE and similar for the other case, where DEAD_ZONE is a small integer.
This does not deal with turning around in the same stroke, however. You could deal with that by only turning left when the current position is at least DEAD_ZONE pixels to the left of the last position after turning right, and vice versa.
One obvious problem is that nowhere do you take account of the time since the last touch.
I would suggest you treat the players touch as an expression of desired movement on an analogue range from -x/+x and -y/+y, then perform the actual movement elsewhere based on time.
E.g.
objectPos += objectPos + (joyPos * timeDelta * maxSpeed);
So if the max-speed of your object is 10ms-1 and the players finger is at 0.5 on the control pad then the object would be moving 5 meters every second. If your game is running at 10fps then each frame the object would move 0.5 meters.
These figures are fictional, but hopefully demonstrate the need to separate control from movement and then factor in time.