How to move object along the polygons - javascript

Suppose (say triangle), I want to move an object from A to B then B to C and then C to A. How can I do this?
I googled a lot, but can't find an example of moving an object (say ball) around polygon. I know, i can done it with bezier curves, but for a simple triangle or rectangle how can I do without it? Please give some pseudo code or code in any language. (prefer JavaScript/Processing).

Interpolate
You can get the position by using interpolation:
x = x1 + (x2 - x1) * f;
y = y1 + (y2 - y1) * f;
where x1, y1 is your first point, x2, y2 your end point.
f is a value between 0.0 and 1.0 which determines where on the line you are (ie. 0 = beginning, 0.5 is half way, 1 = end).
When you f = 1 you just go to the next segment in the polygon and reset f to 0.
Fiddle (JS)
Pseudo:
//prepare first segment:
x1 = nextX1;
y1 = nextY1;
x2 = nextX2;
y2 = nextY2;
loop:
f += speed; /// f.ex. 0.01
x = x1 + (x2 - x1) * f;
y = y1 + (y2 - y1) * f;
if f=1 then
f = 0;
x1 = nextX1;
y1 = nextY1;
x2 = nextX2;
y2 = nextY2;
repeat loop;
Example in JavaScript (see demo link above for full working example)
function loop() {
/// leave a trace behind for demo
ctx.clearRect(x + 1, y + 1, 6, 6);
f += speed;
if (f >= 1) {
/// initialize next line in polygon (see demo)
getNextSegment();
}
/// Here: get x and y based on interpolation
x = x1 + (x2 - x1) * f;
y = y1 + (y2 - y1) * f;
/// draw something to show position
ctx.fillRect(x, y, 8, 8);
/// loop
requestAnimationFrame(loop);
}
For constant speed calculate distance between start end end point and divide a step value (predefined fixed value) on that distance which you use for speed.

Related

How to solve the lack of symmetry in this line rasterization algorithm?

I am working on an algorithm to snap 3D lines to a 3D tessellated space.
Here is a 2D example of the algorithm that works for positive and negative slopes between -1 and 1 inclusive.
Using the slope to calculate the value of y at each x results in slow and error-prone floating calculations. The solution is to simulate the division with a remainder variable.
When dx >= dy, start with an initial remainder variable ry = 0. Then, for each x increment, add dy to ry variable. When it surpasses dx, increment y, then set ry equal to ry - dx.
function line(x1, y1, x2, y2) {
let points = []
let dx = Math.abs(x2 - x1);
let dy = Math.abs(y2 - y1);
// The remainder variable for y axes.
// No rx is created because we are assuming dx >= dy.
let ry = 0;
// Current value of y for a given point
let y = 0;
// The slope could be positive or negative, so increments coordinates as they go down or up.
let pointIncrement;
if (x2 > x1) {
pointIncrement = 1;
} else if (x2 < x1) {
pointIncrement = -1;
y = y1
}
for (let x = x1; pointIncrement < 0 ? x >= x2 : x <= x2; x += pointIncrement) {
if (ry >= dx) {
ry -= dx;
y += pointIncrement;
}
// Add dy to ry until it surpasses dx. This simulates the division of dy/dx for slope.
ry += dy;
points.push([x, y])
}
return points
}
Now, if you call the function with a slope of 1/4th:
line(0,0,20,5)
You get the following results:
[[0,0],[1,0],[2,0],[3,0],[4,1],[5,1],[6,1],[7,1],[8,2],[9,2],[10,2],[11,2],[12,3],[13,3],[14,3],[15,3],[16,4],[17,4],[18,4],[19,4],[20,5]]
Now, if you call it again but in the negative direction, then reverse the coordinate order:
line(20,5,0,0).reverse()
You get the following results:
[[0,0],[1,1],[2,1],[3,1],[4,1],[5,2],[6,2],[7,2],[8,2],[9,3],[10,3],[11,3],[12,3],[13,4],[14,4],[15,4],[16,4],[17,5],[18,5],[19,5],[20,5]]
Why is this occurring?
Is anyone aware of a solution to this problem to make the negative slope symmetric to the positive slope?
In the upward direction, you are calculating y = y1 + floor((x-x1) * dy / dx). In the downward direction, you are calculating y = y1 + ceil((x-x1) * dy / dx)
In both cases, this produces "unbalanced" lines, with dy/dx points with y == y1, but only one point with y == y2.
What you want to calculate is y = y1 + round((x-x1) * dy / dx). That will make the line ends balanced. If you want the upward and downward directions to be exactly symmetric, then you need to take care to make sure that 0.5 rounds in the same direction in both cases.
One way to accomplish rounding in this kind of implementation is with an offset. If A and B are integers, and B is positive, then round(A/B) == floor((A+floor(B/2))/B). This floor(B/2) offset can be added to your remainder at the start.
Taking care to make sure 0.5 always rounds up, you can fix your asymmetry just by changing the initial value for ry to:
let ry = y2 >= y1 ? Math.floor(dx/2) : Math.floor((dx-1)/2);
There are a couple other bugs in your code, too, though. If I fix them all, it looks like this:
function line(x1, y1, x2, y2) {
let points = []
// increment direction for x axis
const incX = x2 >= x1 ? 1 : -1;
// increment direction for y axis
const incY = y2 >= y1 ? 1 : -1;
// absolute span in x and y
const dx = (x2 - x1) * incX;
const dy = (y2 - y1) * incY;
// The remainder variable for y axes.
let ry = y2 >= y1 ? Math.floor(dx/2) : Math.floor((dx-1)/2);
// Current value of y for a given point
let y = y1;
for (let x = x1; incX < 0 ? x >= x2 : x <= x2; x += incX) {
if (ry >= dx) {
ry -= dx;
y += incY;
}
// Add dy to ry until it surpasses dx. This simulates the division of dy/dx for slope.
ry += dy;
points.push([x, y])
}
return points
}

Javascript Midpoint Line Drawing Algorithm

I'm drawing a line in javascript using the Midpoint Algorithm Line Drawing.
When the line is in a descent trajectory, it computes the wrong values.
Can somebody help me?
data input
//midline(1,6,8,4);
function midline(x0, y0, x1, y1) {
/* Using midpoint algorithm for lines. */
const dx = x1 - x0;
const dy = y1 - y0;
var d = 2 * dy - dx;
const incrE = 2 * dy;
const incrNE = 2 * (dy - dx);
var x = x0;
var y = y0;
var output = [];
//listeners
console.log("dy = ", dy, "dx = ", dx);
console.log("d = ", d);
console.log("incrE = ", incrE);
console.log("incrNE = ", incrNE);
console.log("----------------------------------");
while (x <= x1) {
// if not the last x
console.log("x = ", x, " y = ", y);
output.push([x,y]);
if (d <= 0) {
console.log("E");
d = d + incrE;
x++;
} else {
console.log("NE");
d = d + incrNE;
x++;
y++;
}
}
console.table(output);
}
If per my comment the Mid Point Line Drawing algorithm is only suited for the first quadrant, that is...
x0 < x1 and y0 < y1
...must hold true, then adjustments are required if x1 < x0 or y1 < y0.
Taking the case presented (1,6) - (8,4), this is a downward slope because y1 < y0 (ie, 4 < 6 ). To make this a workable case for the Mid Point Line Drawing algorithm, you can simply negate the y values, in which case y0 < y1 will then hold true. Of course, when capturing the results, the values need to then be adjusted by multiplying by -1 again. So, suggest wedging in the following before putting x0, y0, x1, and y1 to use...
let xReflect = 1;
if ( x1 < x0 ) {
x0 = -x0;
x1 = -x1;
xReflect = -1;
}
let yReflect = 1;
if ( y1 < y0 ) {
y0 = -y0;
y1 = -y1;
yReflect = -1;
}
...and then, when pushing the output to the array, you will need to perform the following...
output.push( [ x * xReflect, y * yReflect ] );
Hope this helps.

Finding two points that intersect a rectangle on a line that is perpendicular to a line segment

I'm having the following problem:
Given:
A rectangle with a defined height(Y) and width(X)
The line segment given by the points A and B
A point inside the segment C
Find the points D and E that:
Intersect the rectangle
Forms a line segment that goes through C
Forms a line segment that is perpendicular to the segment AB
To solve this problem, I've tried first calculating the slope and creating a line function, but all answers that I've seen to get the intersection between a line and a polygon uses a line segment and not a line function. How can I solve this? Am I missing a better way to find a perpendicular line that doesn't require a function?
function getPerpendicular(ax,ay,bx,by,cx,cy,x,y){
let a=bx-ax;
let b=by-ay;
let slope;
let line;
// Because if a==0 the slope is infinite
if(a===0){
line=function(y){
return cx;
}
}else{
slope= (b)/(-a);
line=function(x){
return slope*x+cy-cx;
}
}
// Intersection with the line function?
}
Get direction vector for AB line (your a, b)
xx=bx-ax;
yy=by-ay;
Get perpendicular vector
dx = - yy
dy = xx
Now perpendicular line has parametric equation (it is for reference, no need to implement)
x = cx + dx * t
y = cy + dy * t
first check for horizontal/vertical lines
if dx = 0 then
return cx, 0, cx, Y
if dy = 0 then
return 0, cy, X, cy
prerequisites: potential border positions
if dx > 0 then
bx1 = X
bx2 = 0
else
bx1 = 0
bx2 = X
if dy > 0 then
by1 = Y
by2 = 0
else
by1 = 0
by2 = Y
in general case find parameters of intersection with horizontal and vertical edge
tx1 = (bx1 - cx) / dx
ty1 = (by1 - cy) / dy
tx2 = (bx2 - cx) / dx //should be negative
ty2 = (by2 - cy) / dy //should be negative
and get intersection for smaller parameter value for one end:
if tx1 <= ty1 then
ix1 = bx1
iy1 = cy + tx1 * dy
else
iy1 = by1
ix1 = cx + ty1 * dx
and for larger parameter value at another end:
if tx2 >= ty2 then
ix2 = bx2
iy2 = cy + tx2 * dy
else
iy2 = by2
ix2 = cx + ty2 * dx
now return both intersection points
return ix1, iy1, ix2, iy2

Find a tangent between line segment and circle

I have a line segment A (x1, y1), B (x2, y2) and a circle with the center C (x3, y3). Also I have radius of the circle. How do I get tangents?
These tangent lines should always be parallel to line segment.
P.S. Sorry if it is doesn't make sense to you, I am very very bad at math. Ask me any questions, I just don't know what else be needed to solve this task. Thanks.
Segment defines direction vector
D = (dx, dy) = (b.x - ax, b.y - a.y)
dlen = Length(D) = Sqrt(dx * dx + dy * dy)
Radius-vector is perpendicular to tangent and it's length is R. Let circle center is (0,0) (we will make correction after). So
x * x + y * y = R^2
x * dx + y * dy = 0 //scalar product
Solving this system for unknown x,y, we have
x = +- R * dy / dlen
y = -+ R * dx / dlen
And after correction two base points of tangents are
px1 = x3 + R * dy / dlen
py1 = y3 - R * dx / dlen
px2 = x3 - R * dy / dlen
py2 = y3 + R * dx / dlen
Direction vectors for these tangents are D, so you can define second points for tangents as
px1_2 = x3 + R * dy / dlen + dx
py1_2 = y3 - R * dx / dlen + dy
And the first tangent is line through (px1, py1) and (px1_2, py1_2)
Note that solution works for any direction of segment (including horizontal and vertical)
Write the equation of the line AB in the implicit form
a X + b Y + c = 0
where the coefficients are normalized so that a² + b² = 1. https://en.wikipedia.org/wiki/Linear_equation#Two-point_form
The two requested tangents are parallel to AB, with equation
a X + b Y + c' = 0
and it suffices to express that the distance of the lines to the center of the circle is R,
|a Xc + b Yc + c'| = R
giving two solutions in c'.
I assume you are looking for the tangent lines with slope == the slope of the line. I will detail the algorithm and I leave the actual code to you.
So first lineslope = (y2-y1)/(x2-x1).
Next a circle with radius r centered at (x3, y3) can be described by the parametric equations x = r*cos(t) + x3; y = r*sin(t) + y3.
The slope of the circle at a given t is (dy/dt)/(dx/dt) = -cos(t)/sin(t) = -1/tan(t).
Set -1/tan(t) = lineslope = (y2-y1)/(x2-x1). Solving for t yields t = (pi / 2) - arctan(-(y2-y1)/(x2-x1)). (This should produce two values for t on [0, 2pi] which will be the two places the slope of the tangent line equals the slope of your segment).
Plug the value for t you just calculated into the circle equations from (3), and you will have the point (x4, y4) on the circle where the tangent line has the same slope as your line segment.
The equation of the tangent line is then just y - y4 = lineslope*(x-x4)

How can I keep the edge pixels of lines from being semi-transparent?

I am working with an HTML canvas blown up 32x its pixel width and height. When I draw lines on it, however, I notice that the edge pixels of lines are drawn semi-transparently (exactly half transparent). Is there a way to stop this?
In this picture, the red line is a single line from one point to the other. I would like all of the blocks to be either black or #FF0000 red.
N.B. I am already using canvas.translate() to align pixels properly and am using the solution in this post to render the expanded pixels in discrete blocks.
Problem background
Canvas uses anti-aliasing to make drawings appear more smooth which is why it fills in semi-transparent pixels here and there (see this explanation for how that works).
Smoothing (aka interpolation) can be turned off, but only for images (ctx.imageSmoothingEnabled = false, as the name implies).
Solutions
For this a "line renderer" needs to be implemented. However, the typical line algorithms only supports lines of 1 pixel in width. This includes Bresenham as well as the EFLA (Extremely Fast Line Algorithm by Po-Han Lin), latter is faster than Bresenham.
For lines thicker than 1 pixel you would need to find tangent angle and then render each segment along the main line.
I provide both implementations below which I have optimized to some degree. None of them require access to the bitmap itself, just supply the context.
The only thing you need to remember is to use fillStyle (and fill()) instead of strokeStyle (and stroke()) to set its color. You can generate several lines before filling, which is generally faster than filling each line segment, provided they use the same color.
Optionally you could use image data and set pixels there directly, but that is slower and requires CORS in case you use images (use such a bitmap with a Uint32 view if this is preferred. There are also special tricks to speed up this approach, but they are not addressed here).
EFLA (Extremely Fast Line Algorithm)
This algorithm is intended where you want to draw continuous polygon lines, ie. the last point is not set. But in the following implementation we set it manually so it can be used for single line segments.
Visit the linked site above for a more in-depth explanation of it (as well as for license).
Just make sure input values are integer values:
function lineEFLA(ctx, x1, y1, x2, y2) {
var dlt, mul, yl = false, i,
sl = y2 - y1,
ll = x2 - x1,
lls = ll >> 31,
sls = sl >> 31;
if ((sl ^ sls) - sls > (ll ^ lls) - lls) {
sl ^= ll;
ll ^= sl;
sl ^= ll;
yl = true;
}
dlt = ll < 0 ? -1 : 1;
mul = (ll === 0) ? sl : sl / ll;
if (yl) {
x1 += 0.5; // preset for rounding
for (i = 0; i !== ll; i += dlt) setPixel((x1 + i * mul)|0, y1 + i);
}
else {
y1 += 0.5;
for (i = 0; i !== ll; i += dlt) setPixel(x1 + i, (y1 + i * mul)|0);
}
setPixel(x2, y2); // sets last pixel
function setPixel(x, y) {ctx.rect(x, y, 1, 1)}
}
Bresenham
This is a classic line algorithm used in many applications and computers in the old days where a simple line needed to be rendered.
The algorithm is explained more in detail here.
function lineBresenham(ctx, x1, y1, x2, y2) {
if (x1 === x2) { // special case, vertical line
ctx.rect(x1, Math.min(y1, y2), 1, Math.abs(y2 - y1) + 1);
return;
}
if (y1 === y2) { // special case, horizontal line
ctx.rect(Math.min(x1, x2), y1, Math.abs(x2 - x1) + 1, 1);
return;
}
var dx = Math.abs(x2 - x1), sx = x1 < x2 ? 1 : -1,
dy = Math.abs(y2 - y1), sy = y1 < y2 ? 1 : -1,
err = (dx > dy ? dx : -dy) * 0.5;
while(!0) {
ctx.rect(x1, y1, 1, 1);
if (x1 === x2 && y1 === y2) break;
var e2 = err;
if (e2 > -dx) { err -= dy; x1 += sx; }
if (e2 < dy) { err += dx; y1 += sy; }
}
}
Live demo including zoom
var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); // bg color
ctx.scale(20, 20); // scale
ctx.fillStyle = "#f00"; // color for line in this case
lineEFLA(ctx, 0, 0, 17, 20); // algo 1
lineBresenham(ctx, 3, 0, 20, 20); // algo 2
ctx.fill(); // fill the rects, use beginPath() for next
function lineEFLA(ctx, x1, y1, x2, y2) {
/* x1 |= 0; // make sure values are integer values
x2 |= 0;
y1 |= 0;
y2 |= 0;*/
var dlt,
mul,
sl = y2 - y1,
ll = x2 - x1,
yl = false,
lls = ll >> 31,
sls = sl >> 31,
i;
if ((sl ^ sls) - sls > (ll ^ lls) - lls) {
sl ^= ll;
ll ^= sl;
sl ^= ll;
yl = true;
}
dlt = ll < 0 ? -1 : 1;
mul = (ll === 0) ? sl : sl / ll;
if (yl) {
x1 += 0.5;
for (i = 0; i !== ll; i += dlt)
setPixel((x1 + i * mul) | 0, y1 + i);
} else {
y1 += 0.5;
for (i = 0; i !== ll; i += dlt)
setPixel(x1 + i, (y1 + i * mul) | 0);
}
setPixel(x2, y2); // sets last pixel
function setPixel(x, y) {
ctx.rect(x, y, 1, 1)
}
}
function lineBresenham(ctx, x1, y1, x2, y2) {
if (x1 === x2) { // special case, vertical line
ctx.rect(x1, Math.min(y1, y2), 1, Math.abs(y2 - y1) + 1);
return;
}
if (y1 === y2) { // special case, horizontal line
ctx.rect(Math.min(x1, x2), y1, Math.abs(x2 - x1) + 1, 1);
return;
}
var dx = Math.abs(x2 - x1),
sx = x1 < x2 ? 1 : -1,
dy = Math.abs(y2 - y1),
sy = y1 < y2 ? 1 : -1,
err = (dx > dy ? dx : -dy) * 0.5;
while (!0) {
ctx.rect(x1, y1, 1, 1);
if (x1 === x2 && y1 === y2) break;
var e2 = err;
if (e2 > -dx) {
err -= dy;
x1 += sx;
}
if (e2 < dy) {
err += dx;
y1 += sy;
}
}
}
<canvas width=400 height=400></canvas>
Tip: These implementations can be optimized further by using a single rect() for vertical and horizontal lines (shown for Bresenham, not for EFLA). The setPixel() is for flexibility (f.ex. it can be rewritten to set a bitmap pixel instead etc.).

Categories

Resources