I have triangle: a, b, c. Each vertex has a value: va, vb, vc. In my software the user drags point p around inside and outside of this triangle. I use barycentric coordinates to determine the value vp at p based on va, vb, and vc. So far, so good.
Now I want to limit p so that vp is within range min and max. If a user chooses p where vp is < min or > max, how can I find the point closest to p where vp is equal to min or max, respectively?
Edit: Here is an example where I test each point. Light gray is within min/max. How can I find the equations of the lines that make up the min/max boundary?
a = 200, 180
b = 300, 220
c = 300, 300
va = 1
vb = 1.4
vc = 3.2
min = 0.5
max = 3.5
Edit: FWIW, so far first I get the barycentric coordinates v,w for p using the triangle vertices a, b, c (standard stuff I think, but looks like this). Then to get vp:
u = 1 - w - v
vp = va * u + vb * w + vc * v
That is all fine. My trouble is that I need the line equations for min/max so I can choose a new position for p when vp is out of range. The new position for p is the point closest to p on the min or max line.
Note that p is an XY coordinate and vp is a value for that coordinate determined by the triangle and the values at each vertex. min and max are also values. The two line equations I need will give me XY coordinates for which the values determined by the triangle are min or max.
It doesn't matter if barycentric coordinates are used in the solution.
The trick is to use the ratio of value to cartesian distance to extend each triangle edge until it hits min or max. Easier to see with a pic:
The cyan lines show how the triangle edges are extended, the green Xs are points on the min or max lines. With just 2 of these points we know the slope if the line. The yellow lines show connecting the Xs aligns with the light gray.
The math works like this, first get the value distance between vb and vc:
valueDistBtoC = vc - vb
Then get the cartesian distance from b to c:
cartesianDistBtoC = b.distance(c)
Then get the value distance from b to max:
valueDistBtoMax = max - vb
Now we can cross multiply to get the cartesian distance from b to max:
cartesianDistBtoMax = (valueDistBtoMax * cartesianDistBtoC) / valueDistBtoC
Do the same for min and also for a,b and c,a. The 6 points are enough to restrict the position of p.
Consider your triangle to actually be a 3D triangle, with points (ax,ay,va), (bx,by,vb), and (cx,cy,vc). These three points define a plane, containing all the possible p,vp triplets obtainable through barycentric interpolation.
Now think of your constraints as two other planes, at z>=max and z<=min. Each of these planes intersects your triangle's plane along an infinite line; the infinite beam between them, projected back down onto the xy plane, represents the area of points which satisfy the constraints. Once you have the lines (projected down), you can just find which (if either) is violated by a particular point, and move it onto that constraint (along a vector which is perpendicular to the constraint).
Now I'm not sure about your hexagon, though. That's not the shape I would expect.
Mathematically speaking the problem is simply a change of coordinates. The more difficult part is finding a good notation for the quantities involved.
You have two systems of coordinates: (x,y) are the cartesian coordinates of your display and (v,w) are the baricentric coordinates with respect to the vectors (c-a),(b-a) which determine another (non orthogonal) system.
What you need is to find the equation of the two lines in the (x,y) system, then it will be easy to project the point p on these lines.
To achieve this you could explicitly find the matrix to pass from (x,y) coordinates to (v,w) coordinates and back. The function you are using toBaryCoords makes this computation to find the coordinates (v,w) from (x,y) and we can reuse that function.
We want to find the coefficients of the transformation from world coordinates (x,y) to barycentric coordinates (v,w). It must be in the form
v = O_v + x_v * x + y_v * y
w = O_w + x_w * x + y_w * y
i.e.
(v,w) = (O_v,O_w) + (x_v,y_y) * (x,y)
and you can determine (O_v,O_w) by computing toBaryCoord(0,0), then find (x_v,x_w) by computing the coordinates of (1,0) and find (y_v,y_w)=toBaryCoord(1,0) - (O_v,O_w) and then find (y_v,y_w) by computing (y_v,y_w) = toBaryCoord(0,1)-(O_v,O_w).
This computation requires calling toBaryCoord three times, but actually the coefficients are computed inside that routine every time, so you could modify it to compute at once all six values.
The value of your function vp can be computed as follows. I will use f instead of v because we are using v for a baricenter coordinate. Hence in the following I mean f(x,y) = vp, fa = va, fb = vb, fc = vc.
You have:
f(v,w) = fa + (fb-fa)*v + (fc-fa)*w
i.e.
f(x,y) = fa + (fb-fa) (O_v + x_v * x + y_v * y) + (fc-fa) (O_w + x_w * x + y_w * y)
where (x,y) are the coordinates of your point p. You can check the validity of this equation by inserting the coordinates of the three vertices a, b, c and verify that you obtain the three values fa, fb and fc. Remember that the barycenter coordinates of a are (0,0) hence O_v + x_v * a_x + y_v * a_y = 0 and so on... (a_x and a_y are the x,y coordinates of the point a).
If you let
q = fa + (fb_fa)*O_v + (fc-fa)*O_w
fx = (fb-fa)*x_v + (fc-fa) * x_w
fy = (fb-fa)*y_v + (fc-fa) * y_w
you get
f(x,y) = q + fx*x + fy * y
Notice that q, fx and fy can be computed once from a,b,c,fa,fb,fc and you can reuse them if you only change the coordinates (x,y) of the point p.
Now if f(x,y)>max, you can easily project (x,y) on the line where max is achieved. The coordinates of the projection are:
(x',y') = (x,y) - [(x,y) * (fx,fy) - max + q]/[(fx,fy) * (fx,fy)] (fx,fy)
Now. You would like to have the code. Well here is some pseudo-code:
toBarycoord(Vector2(0,0),a,b,c,O);
toBarycoord(Vector2(1,0),a,b,c,X);
toBarycoord(Vector2(0,1),a,b,c,Y);
X.sub(O); // X = X - O
Y.sub(O); // Y = Y - O
V = Vector2(fb-fa,fc-fa);
q = fa + V.dot(O); // q = fa + V*O
N = Vector2(V.dot(X),V.dot(Y)); // N = (V*X,V*Y)
// p is the point to be considered
f = q + N.dot(p); // f = q + N*p
if (f > max) {
Vector2 tmp;
tmp.set(N);
tmp.multiply((N.dot(p) - max + q)/(N.dot(N))); // scalar multiplication
p.sub(tmp);
}
if (f < min) {
Vector2 tmp;
tmp.set(N);
tmp.multiply((N.dot(p) - min + q)/(N.dot(N))); // scalar multiplication
p.sum(tmp);
}
We think of the problem as follows: The three points are interpreted as a triangle floating in 3D space with the value being the Z-axis and the cartesian coordinates mapped to the X- and Y- axes respectively.
Then the question is to find the gradient of the plane that is defined by the three points. The lines where the plane intersects with the z = min and z = max planes are the lines you want to restrict your points to.
If you have found a point p where v(p) > max or v(p) < min we need to go in the direction of the steepest slope (the gradient) until v(p + k * g) = max or min respectively. g is the direction of the gradient and k is the factor we need to find. The coordinates you are looking for (in the cartesian coordinates) are the corresponding components of p + k * g.
In order to determine g we calculate the orthonormal vector that is perpendicular to the plane that is determined by the three points using the cross product:
// input: px, py, pz,
// output: p2x, p2y
// local variables
var v1x, v1y, v1z, v2x, v2y, v2z, nx, ny, nz, tp, k,
// two vectors pointing from b to a and c respectively
v1x = ax - bx;
v1y = ay - by;
v1z = az - bz;
v2x = cx - bx;
v2y = cy - by;
v2z = cz - bz;
// the cross poduct
nx = v2y * v1z - v2z * v1y;
ny = v2z * v1x - v2x * v1z;
nz = v2x * v1y - v2y * v1x;
// using the right triangle altitude theorem
// we can calculate the vector that is perpendicular to n
// in our triangle we are looking for q where p is nz, and h is sqrt(nx*nx+ny*ny)
// the theorem says p*q = h^2 so p = h^2 / q - we use tp to disambiguate with the point p - we need to negate the value as it points into the opposite Z direction
tp = -(nx*nx + ny*ny) / nz;
// now our vector g = (nx, ny, tp) points into the direction of the steepest slope
// and thus is perpendicular to the bounding lines
// given a point p (px, py, pz) we can now calculate the nearest point p2 (p2x, p2y, p2z) where min <= v(p2z) <= max
if (pz > max){
// find k
k = (max - pz) / tp;
p2x = px + k * nx;
p2y = py + k * ny;
// proof: p2z = v = pz + k * tp = pz + ((max - pz) / tp) * tp = pz + max - pz = max
} else if (pz < min){
// find k
k = (min - pz) / tp;
p2x = px + k * nx;
p2y = py + k * ny;
} else {
// already fits
p2x = px;
p2y = py;
}
Note that obviously if the triangle is vertically oriented (in 2D it's not a triangle anymore actually), nz becomes zero and tp cannot be calculated. That's because there are no more two lines where the value is min or max respectively. For this case you will have to choose another value on the remaining line or point.
Related
How to calculate the third point of the isosceles triangle using JAVA, given its two points and the circumcenter. There will be two solutions for this, and it is sufficient for me if I get the shortest one from the points A and B.
If AB is the base of isosceles triangle (AC=BC), then solution is rather simple.
Given points A, B, CC (circumcenter)
Circumradius is
R = Length(CC-A) = Sqrt((CC.X - A.X)^2 + (CC.Y - A.Y)^2)
Edit: changed direction vector calculation to avoid ambiguity:
Middle point of AB
M = ((A.X + B.X)/2, (A.Y + B.Y)/2)
Direction vector from CC to vertice C
D = (CC.X - M.X, CC.Y - M.Y)
Normalized (unit) direction vector
uD = (D.X / Length(D), D.Y / Length(D))
Vertice C coordinates
C = (CC.X + R * uD.X, CC.Y + R * uD.Y)
I have an infinite grid of hexagons, defined by a cubic (x y z) coordinate system like so:
I also have a viewport -- a rectangular canvas where I will draw the hexagons.
My issue is this. Because the grid of hexagons is infinite in all directions, I can't feasibly draw all of them at once. Therefore, I need to draw all the hexagons that are in the viewport, and ONLY those hexagons.
This image summarizes what I want to do:
In this image, purple-colored hexagons are those I want to render, while white-colored hexagons are those I don't want to render. The black rectangle is hte viewport -- all the hexagons that intersect with it are to be drawn. How would I find which hexagons to render (IE their xyz coordinates)?
Some other info:
I have a function that can recall a hexagon tile and draw it centered at position(x,y) in the viewport, given its cubic xyz coordinates. Therefore, all I should need is the xyz coords of each rectangle to draw, and I can draw them. This might simplify the problem.
I have formulas to convert from cubic hexagon coordinates to x/y coordinates, and back. Given the above diagram, r/g/b being the axes for the cubic coords with the image above, x and y being the cartesian coordinates, and s being the length of a hexagon's edge...
y = 3/2 * s * b
b = 2/3 * y / s
x = sqrt(3) * s * ( b/2 + r)
x = - sqrt(3) * s * ( b/2 + g )
r = (sqrt(3)/3 * x - y/3 ) / s
g = -(sqrt(3)/3 * x + y/3 ) / s
r + b + g = 0
Let's X0, Y0 are coordinates of top left corner, RectWidth is rectangle width, HexWidth = s * Sqrt(3/2) is hexagon width.
Find center of the closest hexagon r0, g0, b0, HX0, HY0. (Rect corner lies in this hexagon, because hexagons are Voronoy diagram cells). Remember horizontal and vertical shift DX = X0 - HX0, DY = Y0 - HY0
Draw horizontal row of Ceil(RectWidth/HexWidth) hexagons, incrementing r coordinate, decrementing f, and keeping b the same, ROWINC=(1,-1,0).
Note that if DY > HexWidth/2, you need extra top row with initial coordinates shifted up (r0, g0-1, b0+1)
Shift starting point by L=(0, 1, -1) if the DX < 0, or by R=(1, 0, -1) otherwise. Draw another horizontal row with the same ROWINC
Shift row starting point by alternative way (L after R, R after L). Draw horizontal rows until bottom edge is reached.
Check whether extra row is needed in the bottom.
You can think of the rectangular box in terms of constraints on an axis.
In the diagram, the horizontal lines correspond to b and your constraints will be of the form somenumber ≤ b and b ≤ somenumber. For example the rectangle might be in the range 3 ≤ b ≤ 7.
The vertical lines are a little trickier, but they are a “diagonal” that corresponds to r-g. Your constraints will be of the form somenumber ≤ r-g and r-g ≤ somenumber. For example it might be the range -4 ≤ r-g ≤ 5.
Now you have two axes with constraints on them, and you can form a loop. The easiest thing will be to have the outer loop use b:
for (b = 3; b ≤ 7; b++) {
…
}
The inner loop is a little trickier, because that's the diagonal constraint. Since we know r+g+b=0, and we know the value of b from the outer loop, we can rewrite the two-variable constraint on r-g. Express r+g+b=0 as g=0-r-b. Now substitute into r-g and get r-(0-r-b). Simplify r-(0-r-b) to 2*r-b. Instead of -4 ≤ r-g we can say -4 ≤ 2*r-b or -4+b ≤ 2*r or (-4+b)/2 ≤ r. Similarly, we can rearrange r-g ≤ 5 to 2*r-b ≤ 5 to r ≤ (5+b)/2. This gives us our inner loop:
for (b = 3; b ≤ 7; b++) {
for (r = (-4+b)/2; r ≤ (5+b)/2; r++) {
g = 0-b-r;
…
}
}
The last bit is to generalize, replacing the constants 3,7,-4,5 with the actual bounds for your rectangle.
Suppose I want to approximate a half-cosine curve in SVG using bezier paths. The half cosine should look like this:
and runs from [x0,y0] (the left-hand control point) to [x1,y1] (the right-hand one).
How can I find an acceptable set of coefficients for a good approximation of this function?
Bonus question: how is it possible to generalize the formula for, for example, a quarter of cosine?
Please note that I don't want to approximate the cosine with a series of interconnected segments, I'd like to calculate a good approximation using a Bezier curve.
I tried the solution in comments, but, with those coefficients, the curve seems to end after the second point.
Let's assume you want to keep the tangent horizontal on both ends. So naturally the solution is going to be symmetric, and boils down to finding a first control point in horizontal direction.
I wrote a program to do this:
/*
* Find the best cubic Bézier curve approximation of a sine curve.
*
* We want a cubic Bézier curve made out of points (0,0), (0,K), (1-K,1), (1,1) that approximates
* the shifted sine curve (y = a⋅sin(bx + c) + d) which has its minimum at (0,0) and maximum at (1,1).
* This is useful for CSS animation functions.
*
* ↑ P2 P3
* 1 ו••••••***×
* | ***
* | **
* | *
* | **
* | ***
* ×***•••••••×------1-→
* P0 P1
*/
const sampleSize = 10000; // number of points to compare when determining the root-mean-square deviation
const iterations = 12; // each iteration gives one more digit
// f(x) = (sin(π⋅(x - 1/2)) + 1) / 2 = (1 - cos(πx)) / 2
const f = x => (1 - Math.cos(Math.PI * x)) / 2;
const sum = function (a, b, c) {
if (Array.isArray(c)) {
return [...arguments].reduce(sum);
}
return [a[0] + b[0], a[1] + b[1]];
};
const times = (c, [x0, x1]) => [c * x0, c * x1];
// starting points for our iteration
let [left, right] = [0, 1];
for (let digits = 1; digits <= iterations; digits++) {
// left and right are always integers (digits after 0), this keeps rounding errors low
// In each iteration, we divide them by a higher power of 10
let power = Math.pow(10, digits);
let min = [null, Infinity];
for (let K = 10 * left; K <= 10 * right; K+= 1) { // note that the candidates for K have one more digit than previous `left` and `right`
const P1 = [K / power, 0];
const P2 = [1 - K / power, 1];
const P3 = [1, 1];
let bezierPoint = t => sum(
times(3 * t * (1 - t) * (1 - t), P1),
times(3 * t * t * (1 - t), P2),
times(t * t * t, P3)
);
// determine the error (root-mean-square)
let squaredErrorSum = 0;
for (let i = 0; i < sampleSize; i++) {
let t = i / sampleSize / 2;
let P = bezierPoint(t);
let delta = P[1] - f(P[0]);
squaredErrorSum += delta * delta;
}
let deviation = Math.sqrt(squaredErrorSum); // no need to divide by sampleSize, since it is constant
if (deviation < min[1]) {
// this is the best K value with ${digits + 1} digits
min = [K, deviation];
}
}
left = min[0] - 1;
right = min[0] + 1;
console.log(`.${min[0]}`);
}
To simplify calculations, I use the normalized sine curve, which passes through (0,0) and (1,1) as its minimal / maximal points. This is also useful for CSS animations.
It returns (.3642124232,0)* as the point with the smallest root-mean-square deviation (about 0.00013).
I also created a Desmos graph that shows the accuracy:
(Click to try it out - you can drag the control point left and right)
* Note that there are rounding errors when doing math with JS, so the value is presumably accurate to no more than 5 digits or so.
Because a Bezier curve cannot exactly reconstruct a sinusoidal curve, there are many ways to create an approximation. I am going to assume that our curve starts at the point (0, 0) and ends at (1, 1).
Simple method
A simple way to approach this problem is to construct a Bezier curve B with the control points (K, 0) and ((1 - K), 1) because of the symmetry involved and the desire to keep a horizontal tangent at t=0 and t=1.
Then we just need to find a value of K such that the derivative of our Bezier curve matches that of the sinusoidal at t=0.5, i.e., .
Since the derivative of our Bezier curve is given by , this simplifies to at the point t=0.5.
Setting this equal to our desired derivative, we obtain the solution
Thus, our approximation results in:
cubic-bezier(0.3633802276324187, 0, 0.6366197723675813, 1)
and it comes very close with a root mean square deviation of about 0.000224528:
Advanced Method
For a better approximation, we may want to minimize the root mean square of their difference instead. This is more complicated to calculate, as we are now trying to find the value of K in the interval (0, 1) that minimizes the following expression:
where B is defined as follows:
cubic-bezier(0.364212423249, 0, 0.635787576751, 1)
After few tries/errors, I found that the correct ratio is K=0.37.
"M" + x1 + "," + y1
+ "C" + (x1 + K * (x2 - x1)) + "," + y1 + ","
+ (x2 - K * (x2 - x1)) + "," + y2 + ","
+ x2 + "," + y2
Look at this samples to see how Bezier matches with cosine: http://jsfiddle.net/6165Lxu6/
The green line is the real cosine, the black one is the Bezier. Scroll down to see 5 samples. Points are random at each refresh.
For the generalization, I suggest to use clipping.
I would recommend reading this article on the math of bezier curves and ellipses, as this is basicly what you want (draw a part of an ellipse):
http://www.spaceroots.org/documents/ellipse/elliptical-arc.pdf
it provides some of the insights required.
then look at this graphic:
http://www.svgopen.org/2003/papers/AnimatedMathematics/ellipse.svg
where an example is made for an ellipse
now that you get the math involved, please see this example in LUA ;)
http://commons.wikimedia.org/wiki/File:Harmonic_partials_on_strings.svg
tada...
I am building a tilt-based Arduino device that needs to detect the "fall-line" vector of the device once it is tilted in a particular orientation. By "fall-line" I'll use the following example:
Imagine a frictionless plane with a point mass in the the middle of it and a 3-axis accelerometer mounted in the plane so that the x and y axes of the accelerometer are parallel to the plane. At rest, the plane is flat and the point mass does not move. Once the plane is tilted, the point mass will move in a particular direction at a given acceleration due to gravity. I need to calculate the angle in the x-y plane that the mass will move toward and a magnitude measure corresponding to the acceleration in that direction.
I realise this is probably simple Newtonian mechanics, but I have no idea how to work this out.
The direction of the "fall-line" and the magnitude of the acceleration are both determined by the projection of the gravitational pull vector onto the plane. If the plane has a normal vector n, then the projector operator is P( n ) = 1 - nn, where 1 is the identity operator and nn is the outer (tensor) product of the normal vector with itself. The projection of the gravitational pull vector g is simply g' = P( n ).g = (1 - nn) g = g - (n . g) n, where the dot denotes inner (dot) product. Now you only have to choose a suitable orthonormal reference frame (ex, ey, ez), where ei is a unit vector along direction i. In this reference frame:
n = nx ex + ny ey + nz ez
g = gx ex + gy ey + gz ez
The dot product n . g is then:
n . g = nx * gx + ny * gy + nz * gz
A very suitable choice of a reference frame is one where ez is collinear with n. Then nx = 0 and ny = 0 and nz = ||n|| = 1, because normal vectors are of unit length. In this frame n . g is simply gz. The components of the projection of g are then:
g'x = gx
g'y = gy
g'z = 0
The direction of g' in the XY plane can be determined by the fact that for the dot product in orthonormal reference frames a . b = ||a|| ||b|| cos(a, b), where ||a|| denotes the norm (length) of a and cos(a, b) is the cosine of the angle between a and b. If you measure the angle from the X direction, then:
g' . ex = (gx ex + gy ey) . ex = gx = ||g'|| ||ex|| cos(g', ex) = g' cos(g', ex)
where g' = ||g'|| = sqrt(gx^2 + gy^2). The angle is simply arccos(gx/g'), i.e. arc-cosine of the ratio between the X component of the gravity pull vector and the magnitude of its projection onto the XY plane:
angle = arccos[gx / sqrt(gx^2 + gy^2)]
The magnitude of the acceleration is proportional to the magnitude of g', which is (once again):
g' = ||g'|| = sqrt(gx^2 + gy^2)
Now the nice thing is that all accelerometers measure the components of the gravity field in a reference frame that usually have ex aligned with the height (or the width) of the device, the ex aligned with the width (or the height) of the device and ez is perpendicular to the surface of the device, which matches exactly the reference frame, where ez is collinear with the plane normal. If this is not the case with your Arduino device, simply rotate the accelerometer and align it as needed.
Two points P and Q with coordinates (Px, Py) and (Qx, Qy) satisfy the following properties:
The coordinates Px, Py, Qx, and Qy are integers.
Px = −Qx.
The line PQ is tangent to a circle with center (0, 0) and radius r
0 < Px ≤ a for some integer limit a.
How do I find all such pairs of points P and Q?
For example, in the image below I have a circle with radius r=2 and the limit a = 6. The pair of points P = (6, 2) and Q = (−6, −7) are a solution, because:
The coordinates of P and Q are integers.
Px = −Qx.
The line PQ is tangent to the circle.
0 < Px ≤ 6.
But this is just one pair. I need to find all such pairs. There are a finite number of solutions.
So, is there a way to check if coordinates of points are tangent to the circle and are integers, then to list them all? I've looked at slope equations and shortest path from the center of the circle to the line equations, however, in the first case it requires coordinates to be known (which I could do by brute forcing every single digit, but I cannot see the pattern, because my guts tell me there should be some sort of equation I should apply), and in the second case I have to know the slope equation.
This is the algorithm I came up with but I don't think it is correct or good enough:
Find the slope equation y = mx + b for all 1 ≤ Px ≤ a and −a ≤ Qx ≤ −1.
For every y = mx + b check if it is tangent to the circle (how to do that???)
If true, return the pair
Line PQ has equation:
(x-Px)/(Qx-Px)=(y-Py)/(Qy-Py) or
(x-Px)*(Qy-Py)-(y-Py)*(Qx-Px)=0 or
x*(Qy-Py)+y*(Px-Qx)-Px*Qy+Px*Py+Py*Qx-Py*Px =
x*(Qy-Py)+y*2*Px-Px*(Qy+Py)=0
Distance from zero point to this line (circle radius) is
r=Px*(Qy+Py)/Sqrt((2*Px)^2+(Qy-Py)^2)
Note that radius and nominator are integers, so denominator must be integer too. It is possible when 2*Px and |Qy-Py| are members of some Pythagorean triple (for your example - 12^2+9^2 =15^2).So you can use "Generating a triple" method from the above link to significantly reduce the search and find all the possible point pairs (with radius checking)
Px = k * m * n (for m>=n, radius < k*m*n <= a)
|Qy-Py| = k * (m^2 - n^2)
With a=6 max value of n is 2, and your example corresponds to (k, m, n) set of (3, 2, 1)