I'm well aware of the marching / DDA algorithms, but instead I would like to be able to do a per voxel-ray pair check, in constant time, without having to "march" through the voxel space. How would I go about this?
To be clear, I am not trying to find the first voxel a ray intersects, but rather, given a ray and a voxel, determine if that voxel's cell even lies within the ray's path.
A ray is P = O + t D, where P, O, D are vectors and t a positive real. There is an intersection with the voxel [x,y,z]x[x+1,y+1,z+1] if the system below has a solution:
x < Ox + t Dx < x + 1
y < Oy + t Dy < y + 1
z < Oz + t Dz < z + 1
0 < t
which we rewrite for brevity (with x' = (x - Ox) / Dx ...)
x' < t < x"
y' < t < y"
z' < t < z"
0 < t
If Dx < 0, the inequalities must be reversed. If Dx == 0, the inequation degenerates in x < Ox < x + 1, which can be directly decided.
Repeat the same discussion for all three axis and check if all bracketings are compatible.
For instance, for Dx, Dy, Dz > 0, you must have
max(0, x', y', z') < min(x", y", z").
There are 27 sign combinations to be considered, which are separated in three cascaded three-way comparisons.
As a micro-optimization, you can rescale the last inequality by Dx Dy Dz and simplify, to trade divisions for (faster) multiplications.
If many rays miss the voxel, you can speed-up the process a little by using the bounding sphere. Assuming that the voxel center is C, voxel radius R and the vector D is normalized, the pre-test is
(OC x D)² < R²
You can use any Ray Box (AABB) Intersection algorithm.
Arbitrary one
If you need intersection coordinates, then choose 3D line clipping algorithm
Related
How to calculate the width / height of the largest aligned rectangular with fixed aspect ratio in an arbitrary convex polygon?
The examples of such rectangles (red) in the different convex polygons (black) are presented on this image:
.
I found different papers on subject, but they are not fit to set of my limitations. That's weird, because they should significally simplify the algorithm, but unfortunately I didn't found any clue of it.
Fixed ratio does simplify the problem since now there's a linear program.
You want to find x1, y1, x2, y2 to maximize x2 − x1 subject to the constraints that (x2 − x1) h = w (y2 − y1) where the aspect ratio is w:h, and for each linear inequality defining the convex polygon, each of the points (x1, y1), (x1, y2), (x2, y1), (x2, y2) satisfy it.
For example, consider this convex polygon:
The linear program for the rectangle with four points (x_1, y_1), (x_1, y_2), (x_2, y_2), (x_2, y_1), aspect ratio r = w / h and inscribed to the polygon above will be following:
In theory, there are specialized algorithms for low-dimensional linear programming that run in linear time. In practice, you can throw a solver at it. If you want to code your own, then you could do the simplex method, but gradient descent is even simpler.
First let's get rid of the equality constraint and a variable by maximizing z over variables x, y, z subject to the point-in-polygon constraints for x1 = (x − w z), y1 = (y − h z), x2 = (x + w z), y2 = (y + h z). Second let's trade in those constraints for an objective term. Normally a point-in-polygon constraint will look like (signed distance to half-plane) ≤ 0. Instead we're going to apply a penalty term to the objective. Let α > 0 be a parameter. The new term is −exp(α (signed distance to half plane)). If the signed distance is negative (the point is inside half-plane), then the penalty will go to zero as α goes to infinity. If the signed distance is positive, then the penalty goes to minus infinity. If we make α large enough, then the optimal solution of the transformed problem will be approximately feasible.
This is what it looks like in Python. I'm not a continuous optimization expert, so caveat emptor.
# Aspect ratio.
w = 3
h = 2
# Represented as intersection of half-spaces a*x + b*y - c <= 0 given below as
# (a, b, c). For robustness, these should be scaled so that a**2 + b**2 == 1,
# but it's not strictly necessary.
polygon = [(1, 1, 20), (1, -2, 30), (-2, 1, 40)]
# Initial solution -- take the centroid of three hull points. Cheat by just
# using (0, 0) here.
x, y, z = (0, 0, 0)
from math import exp
# Play with these.
alpha = 10
rate = 0.02
for epoch in range(5):
for iteration in range(10 ** epoch, 10 ** (epoch + 1)):
# Compute the gradient of the objective function. Absent penalties, we
# only care about how big the rectangle is, not where.
dx, dy, dz = (0, 0, 1)
# Loop through the polygon boundaries, applying penalties.
for a, b, c in polygon:
for u in [-w, w]:
for v in [-h, h]:
term = -exp(alpha * (a * (x + u * z) + b * (y + v * z) - c))
dx += alpha * a * term
dy += alpha * b * term
dz += alpha * (a * u + b * v) * term
x += rate * dx
y += rate * dy
z += rate * dz
print(x, y, z)
Hint:
WLOG the rectangle can be a square (stretch space if necessary).
Then the solution is given by the highest point of the distance map to the polygon, in the Chebyshev metric (L∞). It can be determined from the medial axis transform, itself obtained from the Voronoi diagram.
Suppose I have a NxN matrix, where each cell is a 1x1 white square.
Suppose I have a position P and a radius R. I want to paint all the cells of the circle of radius R centered in P.
Of course I could do this:
for(int i = P.x - R; i < P.x + R; i++)
for(int j = P.y - R; j < P.y + R; j++)
if (distance from P to (i,j) < R)
Paint(i,j)
But as I will run this code on a shader that will execute every frame, I'd like to know a faster way to find the correct cells instead of asking the distance for each cell, which is slow.
Is there a smarter way?
You could for each given height of the circle calculate its segment width and fill it out completely.
You'd go from y = P - R to P + R filling all points in the chord (circular segment).
For the length of the chord just use formula (9) from here.
How can one efficiently test if an axis-aligned rectangle R
intersects a nice quadrilateral Q?
Nice means: Q is convex (not a chevron) and non-selfintersecting (not a bowtie, not degenerate).
Just 2D.
Just yes/no. I don't need the actual region of intersection.
Edit: Q and R may be open or closed, whatever's convenient.
Obviously one could test, for each edge of Q, whether it intersects R.
That reduces the problem to
How to test if a line segment intersects an axis-aligned rectange in 2D?.
But just like R's axis-alignedness is exploited by Liang-Barsky
to be faster than Cohen-Sutherland,
Q's properties might be exploited to get something faster than running Liang-Barsky multiple times.
Thus, the polygon-rectangle intersection algorithms
Sutherland–Hodgman, Vatti, and Greiner-Hormann, all of which let Q be nonconvex, are unlikely to be optimal.
Area of rectangle-rectangle intersection looks promising, even though it doesn't exploit R's axis-alignedness.
Be careful not to neglect the case where Q entirely covers R, or vice versa, as the edge test then would give a false negative.
One approach, conceptually:
Regard R as the intersection of two axis-aligned infinite bands, a vertical band [x0,x1] and a horizontal band [y0,y1].
Let xmin and xmax represent the extent of the intersection of Q with the horizontal band [y0,y1]; if [xmin,xmax] ∩ [x0,x1] is non-empty, then Q intersects R.
In terms of implementation:
Initialise xmin := +inf; xmax := -inf
For each edge pq of Q, p=(px,py) q=(qx,qy), with py ≥ qy:
a. If qy > y1 or y0 > py, ignore this edge, and examine the next one.
b. If py > y1, let (x,y1) be the intersection of pq with the horizontal line y = y1; otherwise let x be px.
c. Update xmin = min(xmin,x); xmax = max(xmax,x).
d. If y0 > qy, let (x,y0) be the intersection of pq with the horizontal line y = y0; otherwise let x be qx.
e. Update xmin = min(xmin,x); xmax = max(xmax,x).
Q intersects R if xmin < x1 and xmax > x0.
1) Construct Q's axis-aligned bounding box. Test that it does not intersect R.
2) For every edge E of Q, test that all four vertices of R lie on the outer side of E's supporting line. (Use the implicit equation of the edge, A.x + B.y + c <> 0.)
If and only if either of these tests succeeds, there is no intersection.
How to count efficiently the number of right isosceles triangles in a nxm rectangular grid?
The 3 corners of the triangles must have integer coordinates.
The right sides of the triangles are not necessarily parallel to grid lines.
Let A be the triangle's corner where the right angle is located. Let ab be the difference vector to the first corner and ac the difference vector to the other corner. ac should always be left of ab. So for a given ab, we can calculate ac as:
ac = (-ab.y, ab.x)
So there are only two integer degrees of freedom for a certain triangle, given A. We can calculate the possible intervals for ab as:
A.x + ab.x >= 0 --> ab.x >= -A.x
A.x + ab.x <= m --> ab.x <= m - A.x
A.y + ab.x >= 0 --> ab.x >= -A.y
A.y + ab.x <= n --> ab.x <= n - A.y
A.y + ab.y >= 0 --> ab.y >= -A.y
A.y + ab.y <= n --> ab.y <= n - A.y
A.x - ab.y >= 0 --> ab.y <= A.x
A.x - ab.y <= m --> ab.y >= A.x - m
So the possible intervals are
xmin = max(-A.x, -A.y)
xmax = min(m - A.x, n - A.y)
ymin = max(-A.y, A.x - m)
ymax = min(n - A.y, A.x)
The number of possible vectors that fulfil these conditions (except the 0-vector) are:
c = (xmax - xmin + 1) * (ymax - ymin + 1) - 1
The desired result is the sum of c for all possible A within the grid. With the naive approach you get an O(grid cells) runtime. Depending on your grid, this could already be fast enough.
Update
In order to split the sum of c (over all grid cells) we must determine where the results of the max and min operations change. E.g. ymax changes as follows:
Note that I have offset the line a bit to the right, so there is a unique mapping from points to the regions the points belong to.
When we do this for all interval boundaries, we get the following picture (for m > n):
Now we can calculate the sum of c for each region independently. I'll show that exemplarily for the left most triangle region.
For that region the following conditions hold:
xmin = -A.y
xmax = n - A.y
ymin = -A.y
ymax = A.x
c(x, y) = (n - y + x) * (x + y) - 1
= (nx + ny - y^2 + x^2 - 1)
Now we sum that for all xs and ys in that region. x runs from 0 through n/2. y runs from x to n-x.
So we get:
n/2 n-x
sumLeftRegion = Sum Sum (nx + ny - y² + x² - 1)
x=0 y=x
We can solve this (e.g. using your favorite algebra program and get:
sumLeftRegion = 1/48 * (2 + n)*(-24 - 14n + 6n^2 + 5n^3)
This can be done for all other regions. You would need some few cases (i.e. m > n, m = n, m < n, probably even and odd n and m). The case n > m can be converted to m > n by rotating the grid (which does not change the number of possible triangles). It'll be some work, but in the end you get the result in O(1) because there will be no iterations.
Consider RAIT ABC. If we maintain a corner (A) of the triangle at (0,0), then we can say that for a every positive point (B) along y=1 (so (1,1),(2,1),etc a RAIT exists whose other corner (C) is at (n,n), where Cn = Bx-1. It should be obvious that must also be true for y=2 (starting from B(2,2), y=3 (starting from B(3,3), etc. - you're just doubling, tripling, etc. the cooordinates. Does that help? (I'm no mathematician - in case that's not obvious)
Consider any triangle in the box. It has a unique bounding box (in the sense that it only has one, not that it's the only triangle with that bounding box). So, if you consider every possible bounding box side lengths, (i,j), and count the number of RAIT triangles with this bounding box, (denote this CP(i,j)) then your answer is sum(i=1 to n, sum(j=1 to m,CP(i,j)*(m-j+1))*(n-i+1)). The (m-j+1)*(n-i+1) is counting how many of that size box can fit in the n*m box. So the hard part now is to find CP(i,j). Again, CP(i,j) is the number of RAIT triangles with points on the perimeter of the box with lengths i and j. We can use symmetry to our advantage here. Assume with no loss of generality that the right angle is ABC. Imagine the triangle being aligned with the positive axes.
A
|\
| \
B__C
Now consider rotating it clockwise until it lines back up with itself. There are two possibilities through that process: BC is in quadrants 1 and 2 or in quadrants 3 and 4.
2|1
---
3|4
What we notice here is that in the first case B lies along the bottom or right of the bounding box, and in the second case B lies along the top or left of the bounding box. So wlog we assume that B lies along the bottom or right, excluding the very bottom left corner.
+-----*
| *
| *
+******
Once we calculate how many RAIT triangles have this property we can just double it!
If B is at the top right corner, then there is just one triangle to test for RAIT-ness, same if it's in the bottom right corner. If it's along the right wall, then there are i triangles to check (placing C at each point along the top, and calculating the third point to see if it forms the given bounding box). Similarly, if it's along the bottom wall, then there are j triangles to check.
This is definitely worse than O(m*n), it's probably somewhere around O(m^2*n^2), but hopefully this helps somehow.
EDIT
We can actually do a little better in our testing triangles. With a little geometric intuition, we can see that if B is along the bottom, and closer to the bottom right corner, then C is in the top right corner. Similarly if it's closer to the bottom left corner, then A is in the top right corner. So we can just consider half of B's positions, and for each position we only need to consider one possible triangle. (The argument when B is on the right is similar).
Given a set of points with unequal x-coordinates, I want to calculate the value v > 0 such that the shear transformation (x, y) -> (x + v*y, y) doesn't change the order in the x-direction.
This isn't difficult. Order the points by their x-axis. Because of the continuity of the shear transformation, it's enough for you to find a maximum v that two consecutive points (in x-order) do not change order. Let (x,y) and (x',y') be two consecutive points in your ordering. with v>0, the x coordinates change as x -> x + vy and x' -> x' + vy'. Now as x'>x, you want to find maximum v such that x' + vy' >= x + vy. By linearity, it's enough to solve
x' + vy' = x + vy
i.e.
x' - x = vy - vy' = v(y - y')
thus
v = (x' - x)/(y - y')
If the result is negative, then any value of v goes (the points are moving farther away); if the result is positive, that's the maximum value that the pair (x,y), (x',y') can tolerate. Now calculate this maximum for all consecutive pairs and take their minimum.
Note that if y = y', v becomes undefined. In this case the points lie at the same point on y-axis and the shear transformation doesn't change their distance.
Convert each point (x, y) into a ray {(x + yv, v) | v ≥ 0} in the xv-halfplane with v ≥ 0. Use a segment intersection algorithm to find the one with minimum v.