I'm drawing a quadratic curve between two points, and then I locate a random point on that curve. I want to highlight the curved part between the random point and the end point.
So I'm thinking of finding another control point between the random point and the end point, and then draw another quadratic curve with different stroke color on top of the original curve.
Is it possible to find such point or not? Or is there any other way to accomplish this task?
Here is the piece of code that I'm working on:
var startpoint = {x: 50, y: 50}; // Red
var endpoint = {x: 50, y: 250}; // Green
var controlpoint = {x: 100, y: 150}; // Blue
var t = 0.75;
var randompoint = {
x: (1 - t) * (1 - t) * startpoint.x + 2 * (1 - t) * t * controlpoint.x + t * t * endpoint.x,
y: (1 - t) * (1 - t) * startpoint.y + 2 * (1 - t) * t * controlpoint.y + t * t * endpoint.y
}; // Orange
context.beginPath();
context.moveTo(startpoint.x, startpoint.y);
context.quadraticCurveTo(controlpoint.x, controlpoint.y, endpoint.x, endpoint.y);
context.stroke();
Here is the working code following answer by MBo
function lerp(a, b, t)
{
var _t = 1 - t;
return {
x: a.x * _t + b.x * t,
y: a.y * _t + b.y * t
};
}
var newpoint = lerp(controlpoint, endpoint, t);
context.beginPath();
context.moveTo(randompoint.x, randompoint.y);
context.quadraticCurveTo(newpoint.x, newpoint.y, endpoint.x, endpoint.y);
context.stroke();
Yes, you can make new control point for this new curve with simple approach:
P1' = P0 * (1-t) + P1 * t
where P0 is starting point, P1 is control point of old curve, P1' is control point for new curve
(This is particlular case for general Bezier curve subdivision problem)
A solution is moving a copy of the curve of between random point and endpoint in the direction of perpendicular to the cure. To find the direction, you can find the line of passing from the random point and endpoint and find the perpendicular line to that line. Hence, moving a copy of the curve in the perpendicular direction as a highlighter.
Related
I have point c, which represents the center of a circle. After I drag the circle, I want its y coordinate hmm to snap to a line that's drawn between point a and b. How do I solve for hmm?
So far I've been able to find the midway y value between points a and b. However, it's not taking into account point c's x value.:
const snapYToLine = (aY, bY) => {
const yDist = bY - aY;
return aY + (yDist * 0.5);
}
const a = { x: 10, y: 10 };
const b = { x: 50, y: 30 };
const c = { x: 20, y: 0 }; // not doing anything with this yet...
const hmm = snapYToLine(a.y, b.y); // will need to include c.x here...
console.log(hmm);
X coordinate of C depends on where you drag it, but Y coordinate should be so point stays on line between A and B?
The equation of line between 2 points (A and B):
(x - Xa) / (Xa - Xb) = (y - Ya) / (Ya - Yb).
So hmm = (x - Xa) * (Ya - Yb) / (Xa - Xb) + Ya.
Because it's equation of line you can drag point C over A/B X coordinates and still find corresponsive Y value.
Hope I've understood your question right.
I have a point defined like {x:(x value), y: (y value)}, and I have an axis with a slope and a y intercept. I am trying to project the point onto the line. I have searched 'project point onto line' and looked around for a long time, but i can't find anything that projects a point onto a slope and intercept line.
This code works for me (a slight change from OP's answer):
function project (x, y, slope, yint) {
var slope2 = -1 / slope;
var yint2 = y - slope2 * x;
var nx = (yint2 - yint) / (slope - slope2);
return {x: nx, y: (slope2 * nx) + yint};
}
The actual answer was simple. I just needed to make a perpendicular line to the other line that touches the point being projected. Then, the projection was just the point of intersection of both of the lines. So I implemented a function in javascript, the parameters being the point's x, the point's y, the line's slope, and the line's y intercept, and it returns the projection as {x, y}.
function project (x, y, slope, yint) {
var slope2 = -1 / slope;
var yint2 = y - slope2 * x;
var nx = (yint2 - yint) / (slope - slope2);
return {x: nx, y: (slope2 * nx) + yint}; //thanks to #igobivo for fixing that mistake
}
I'm building a simple app that places a marker on your screen where at the top of certain landmarks in the real world, going to overlay the markers over the camera's view.
I have the latitude/longitude/altitude for both the viewing device and the world landmarks, and I convert those to ECEF coordinates. But I am having trouble with the 3D projection math. The point always seems to get placed in the middle of the screen... maybe my scaling is wrong somewhere so it looks like it's hardly moving from the center?
Viewing device GPS coordinates:
GPS:
lat: 45.492132
lon: -122.721062
alt: 124 (meters)
ECEF:
x: -2421034.078421273
y: -3768100.560012433
z: 4525944.676268726
Landmark GPS coordinates:
GPS:
lat: 45.499278
lon: -122.708417
alt: 479 (meters)
ECEF:
x: -2420030.781624382
y: -3768367.5284123267
z: 4526754.604333807
I tried following the math from here to build a function to get me screen coordinates from 3D point coordinates.
When I put those ECEF points into my projection function, with a viewport of 1440x335 I get: x: 721, y: 167
Here is my function:
function projectionCoordinates(origin, destination) {
const relativeX = destination.x - origin.x;
const relativeY = destination.y - origin.y;
const relativeZ = destination.z - origin.z;
const xPerspective = relativeX / relativeZ;
const yPerspective = relativeY / relativeZ;
const xNormalized = (xPerspective + viewPort.width / 2) / viewPort.width;
const yNormalized = (yPerspective + viewPort.height / 2) / viewPort.height;
const xRaster = Math.floor(xNormalized * viewPort.width);
const yRaster = Math.floor((1 - yNormalized) * viewPort.height);
return { x: xRaster, y: yRaster };
}
I believe the point should be placed much higher on the screen. That article I linked mentions 3x4 matrices which I couldn't follow along with (not sure how to build the 3x4 matrices from the 3D points). Maybe those are important, especially since I will eventually have to take the device's tilt into consideration (looking up or down with phone).
If it's needed, here is my function to convert latitude/longitude/altitude coordinates to ECEF (copy/pasted from another SO answer):
function llaToCartesion({ lat, lon, alt }) {
const cosLat = Math.cos((lat * Math.PI) / 180.0);
const sinLat = Math.sin((lat * Math.PI) / 180.0);
const cosLon = Math.cos((lon * Math.PI) / 180.0);
const sinLon = Math.sin((lon * Math.PI) / 180.0);
const rad = 6378137.0;
const f = 1.0 / 298.257224;
const C =
1.0 / Math.sqrt(cosLat * cosLat + (1 - f) * (1 - f) * sinLat * sinLat);
const S = (1.0 - f) * (1.0 - f) * C;
const h = alt;
const x = (rad * C + h) * cosLat * cosLon;
const y = (rad * C + h) * cosLat * sinLon;
const z = (rad * S + h) * sinLat;
return { x, y, z };
}
Your normalise and raster steps are cancelling out the view port scaling you need. Multiplying out this:
const xNormalized = (xPerspective + viewPort.width / 2) / viewPort.width;
gives you:
const xNormalized = xPerspective / viewPort.width + 0.5;
And applying this line:
const xRaster = Math.floor(xNormalized * viewPort.width);
gives you:
const xRaster = Math.floor(xPerspective + viewPort.width * 0.5);
Your calculation of xPerspective is correct (but see comment below) - however the value is going to be around 1 looking at your numbers. Which is why the point is near the centre of the screen.
The correct way to do this is:
const xRaster = Math.floor(xPerspective * viewPort.width /2 + viewPort.width /2);
You can simplify that. The idea is that xPerspective is the tan of the angle that xRelative subtends at the eye. Multiplying the tan by half the width of the screen gives you the x distance from the centre of the screen. You then add the x position of the centre of the screen to get the screen coordinate.
Your maths uses an implicit camera view which is aligned with the x, y, z axes. To move the view around you need to calculate xRelative etc relative to the camera before doing the perspective divide step (division by zRelative). An easy way to do this is to represent your camera as 3 vectors which are the X,Y,Z of the camera view. You then calculate the projection of the your 3D point on your camera by taking the dot product of the vector [xRelative, yRelative, zRelative] with each of X,Y and Z. This gives you a new [xCamera, yCamera, zCamera] which will change as you move your camera. You can also do this with matrices.
I need to draw a line in the following manner:
For now, it will be only drawn in code, no user input.
My question is, how to draw perpendiculars to a line, if I draw it point by point? (Obviously, this will be the case, because drawing with bezier curves will not give me the possibility to somehow impact the drawing).
The closest answer I found was possibly this one, but I can't reverse the equations to derive C. Also there is no length of the decoration mentioned, so I think this will not work as I'd like it to.
Find the segment perpendicular to another one is quite easy.
Say we have points A, B.
Compute vector AB.
Normalize it to compute NAB (== the 'same' vector, but having a length of 1).
Then if a vector has (x,y) as coordinates, its normal vector has (-y,x) as coordinates, so
you can have PNAB easily (PNAB = perpendicular normal vector to AB).
// vector AB
var ABx = B.x - A.x ;
var ABy = B.y - A.y ;
var ABLength = Math.sqrt( ABx*ABx + ABy*ABy );
// normalized vector AB
var NABx = ABx / ABLength;
var NABy = ABy / ABLength;
// Perpendicular + normalized vector.
var PNABx = -NABy ;
var PNABy = NABx ;
last step is to compute D, the point that is at a distance l of A : just add l * PNAB to A :
// compute D = A + l * PNAB
var Dx = A.x + l* PNAB.x;
var Dy = A.y + l *PNAB.y;
Updated JSBIN :
http://jsbin.com/bojozibuvu/1/edit?js,output
Edit :
A second step is to draw the decorations at regular distance, since it's Christmas time, here's how i would do it :
http://jsbin.com/gavebucadu/1/edit?js,console,output
function drawDecoratedSegment(A, B, l, runningLength) {
// vector AB
var ABx = B.x - A.x;
var ABy = B.y - A.y;
var ABLength = Math.sqrt(ABx * ABx + ABy * ABy);
// normalized vector AB
var NABx = ABx / ABLength;
var NABy = ABy / ABLength;
// Perpendicular + normalized vector.
var PNAB = { x: -NABy, y: NABx };
//
var C = { x: 0, y: 0 };
var D = { x: 0, y: 0 };
//
drawSegment(A, B);
// end length of drawn segment
var endLength = runningLength + ABLength;
// while we can draw a decoration on this line
while (lastDecorationPos + decorationSpacing < endLength) {
// compute relative position of decoration.
var decRelPos = (lastDecorationPos + decorationSpacing) - runningLength;
// compute C, the start point of decoration
C.x = A.x + decRelPos * NABx;
C.y = A.y + decRelPos * NABy;
// compute D, the end point of decoration
D.x = C.x + l * PNAB.x;
D.y = C.y + l * PNAB.y;
// draw
drawSegment(C, D);
// iterate
lastDecorationPos += decorationSpacing;
}
return ABLength;
}
All you need is direction of curve (or polyline segment) in every point, where you want to draw perpendicular.
If direction vector in point P0 is (dx, dy), then perpendicular (left one) will have direction vector (-dy, dx). To draw perpendicular with length Len, use this pseudocode:
Norm = Sqrt(dx*dx + dy*dy) //use Math.Hypot if available
P1.X = P0.X - Len * dy / Norm
P1.Y = P0.Y + Len * dx / Norm
P.S. If you know direction angle A, then direction vector
(dx, dy) = (Cos(A), Sin(A))
and you don't need to calculate Norm, it is equal to 1.0
I'm trying to find a point that is equal distance away from the middle of a perpendicular line. I want to use this point to create a Bézier curve using the start and end points, and this other point I'm trying to find.
I've calculated the perpendicular line, and I can plot points on that line, but the problem is that depending on the angle of the line, the points get further away or closer to the original line, and I want to be able to calculate it so it's always X units away.
Take a look at this JSFiddle which shows the original line, with some points plotted along the perpendicular line:
http://jsfiddle.net/eLxcB/1/.
If you change the start and end points, you can see these plotted points getting closer together or further away.
How do I get them to be uniformly the same distance apart from each other no matter what the angle is?
Code snippit below:
// Start and end points
var startX = 120
var startY = 150
var endX = 180
var endY = 130
// Calculate how far above or below the control point should be
var centrePointX = ((startX + endX) / 2);
var centrePointY = ((startY + endY) / 2);
// Calculate slopes and Y intersects
var lineSlope = (endY - startY) / (endX - startX);
var perpendicularSlope = -1 / lineSlope;
var yIntersect = centrePointY - (centrePointX * perpendicularSlope);
// Draw a line between the two original points
R.path('M '+startX+' '+startY+', L '+endX+' '+endY);
Generally you can get the coordinates of a normal of a line like this:
P1 = {r * cos(a) + Cx, -r * sin(a) + Cy},
P2 = {-r * cos(a) + Cx, r * sin(a) + Cy}.
A demo applying this to your case at jsFiddle.