Consider I have 2 points.
QPoint(100,100)
QPoint(200,200)
Now, I need to draw a pointed arrow at the end of line QPoint(200,200).
How can I get the points of the arrow coordinates since the line is in inclined angle? The arrow should be like given below.
As this is more a general question, Im tagging on pyqt5 and css.
You need to calculate the slope of your line, which will let you find a point on the line a given distance from your endpoint. You can then build a a new line perpendicular to the original line that goes through the point. The ends of the arrows should lie on that line a given distance from the original. It's easier to show than explain:
function draw(point1, point2, distance, length) {
// slope is dx/dy
let dx = point2[0] - point1[0]
let dy = point2[1] - point1[1]
let v_norm = Math.sqrt(dx ** 2 + dy ** 2)
// point on line at distance
let point_on_line = [point2[0] - distance * dx / v_norm, point2[1] - distance * dy / v_norm]
// endpoints of arrows at length above point (the distance from the original line
let point_below = [point_on_line[0] - length * -dy / v_norm, point_on_line[1] - length * dx / v_norm, ]
let point_above = [point_on_line[0] + length * -dy / v_norm, point_on_line[1] + length * dx / v_norm, ]
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(...point1);
ctx.lineTo(...point2);
ctx.moveTo(...point_above)
ctx.lineTo(...point2)
ctx.lineTo(...point_below)
ctx.stroke();
}
draw([100, 100], [200, 150], 20, 10)
draw([100, 100], [300, 150], 20, 10)
draw([100, 100], [150, 10], 20, 10)
draw([100, 100], [90, 150], 20, 10)
draw([100, 100], [200, 100], 20, 10)
draw([100, 100], [5, 10], 20, 10)
<canvas id="canvas" width='500' height='500'></canvas>
You can alter the shape of the arrows using distance and length
If we have a look at the points we need (U and D needed, S and E given):
U
/|
/ |
S---------------------E
\ |
\|
D
We actually have to go back from S to E, then we turn by 90 degrees and walk up the same length. If we take the direction between S and E (represented as Vectors):
d = E - S
Then we can get the distance to walk back:
b = -d / 4
And then to walk up in a 90 degree angle, we just swap x and y, to go down we just take the negative one:
u = [b.y, b.x]
d = -u
So we can finally get our points as:
U = S + b + u
D = S + b + d
(The code above is pseudocode as you wanted a general solution)
Related
I am trying to draw a railway in the way it is implemented in railroad tycoon 3. I have understood some of the patterns by which this is done, but I cannot find a formula for their application. I am trying to build an arc using only two points and some initial direction. For simplicity, let's assume that I can only plot a path in the range from 0 to 90 degrees. The arc should always start in the direction of "Initial direction". I need a method that would implement this. I tried to depict the idea in the drawings. Any pseudocode would do I guess.
general idea
borderline case at alpha = 0
borderline case at alpha = 90 degrees
Center C lies at middle perpendicular to SE segment, so we should find vectors
se = (E.x - S.x, E.y - S.y)
perp = (-se.y, se.x) = (S.y - E.y, E.x - S.x) //MC direction
and middle point
M = ((E.x + S.x)/2, (E.y - S.y)/2)
and express C coordinates using parametric equation
C = M + perp*t
Note that initial arc direction d is perpendicular to radius CS, so their dot product is zero:
(C - S).dot.d = 0
(M.x + perp.x * t - S.x)*d.x + (M.y + perp.y * t - S.y)*d.y = 0
This is linear equation for unknown parameter t, after solving it we know center C and can build the arc.
Python code. Perhaps examples are too simple to reveal bugs, but the first with half-circle gives R=L/2, center at M, and the second one shows correct C position and R.
from math import hypot, atan2
def arcfromptsdir(sx, sy, ex, ey, dx, dy):
sex = ex - sx
sey = ey - sy
perpx = -sey
perpy = sex
mx = (ex + sx) / 2
my = (ey + sy) / 2
#equation
#(sex/2 + perpx * t )*dx + (sey/2 + perp.y * t )*dy = 0
p = perpx * dx + perpy * dy
if p:
t = -0.5*(sex*dx + sey*dx) / p
else:
return None
#centerx, centery, radius
cx = mx + perpx * t
cy = my + perpy * t
radius = hypot(cx-sx, cy-sy)
arcangle = atan2((sx-cx) * (ey-cy) - (sy-cy) * (ex-cx),
(sx-cx) * (ex-cx) + (sy-cy) * (ey-cy))
return (cx, cy, radius, arcangle)
print(arcfromptsdir(0, 0, 2, 0, 0, 1))
print(arcfromptsdir(0, 0, 2, 0, 0.7071, 0.7071))
(1.0, 0.0, 1.0, -3.141592653589793)
(1.0, -1.0, 1.4142135623730951, -1.5707963267948966)
I have a side of a irregular polygon (x1,y1) (x2,y2) at a angle A and the midpoint of the side (mx, my).
I need to find two points (x3,y3) and (x4,y4) on a perpendicular from A passing through (mx, my), with some offset. So that I could check which point is the inside/outside of the polygon.
I'll use the outside one to show the measurement text of the side of the polygon, e.g 2cms
Click to see Visuals
You dont specify the language, but in almost all of them we have atan2 function for that.
canvas.width = window.innerWidth - 10;
canvas.height = window.innerHeight - 10;
const ctx = canvas.getContext('2d');
const line = [canvas.width/2, canvas.height/2, 10, 10];
const pLen = 100; // Length of perpendicular
function drawOrto(line) {
// line vector
const dx = line[2] - line[0];
const dy = line[3] - line[1];
// center point
const mx = line[0] + dx / 2;
const my = line[1] + dy / 2;
const atan = Math.atan2(dy, dx);
// perpendicular vector
const pdx = - pLen * Math.sin(atan);
const pdy = pLen * Math.cos(atan);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Line
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.moveTo(line[0], line[1]);
ctx.lineTo(line[2], line[3]);
ctx.stroke();
// Perpendicular (draw vector both sides from center)
ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.moveTo(mx - pdx / 2, my - pdy / 2);
ctx.lineTo(mx + pdx / 2, my + pdy / 2);
ctx.stroke();
// Dots
ctx.fillStyle = 'red';
ctx.fillRect(line[0] - 1, line[1] - 1, 3, 3);
ctx.fillRect(line[2] - 1, line[3] - 1, 3, 3);
ctx.fillStyle = 'yellow';
ctx.fillRect(mx - 1, my - 1, 3, 3);
ctx.fillStyle = 'green';
ctx.fillRect(mx - pdx / 2 - 1, my - pdy / 2 - 1, 3, 3);
ctx.fillRect(mx + pdx / 2 - 1, my + pdy / 2 - 1, 3, 3);
}
drawOrto(line);
canvas.onmousemove = e => {
line[2] = e.offsetX;
line[3] = e.offsetY;
drawOrto(line);
}
<canvas id=canvas></canvas>
Rotation by a quarter-turn is easy: if you have a vector with coordinates (x, y) and rotate it by a quarter-turn anti-clockwise, the coordinates of the new vector are (-y, x).
To find a point (x5,y5) on the perpendicular, you can rotate point (x2,y2) by a quarter-turn anti-clockwise centered on (mx,my), so that:
x5 - mx = - (y2 - my)
y5 - my = x2 - mx
Then you can choose your two points (x3, y3) and (x4, y4) by renormalizing vector (x5-mx, y5-my) to have the length you want:
x4 - mx = (x5 - mx) * (2 cm) / ((x5-mx)**2 + (y5-my)**2)
y4 - my = (y5 - my) * (2 cm) / ((x5-mx)**2 + (y5-my)**2)
x3 - mx = (x5 - mx) * (-2 cm) / ((x5-mx)**2 + (y5-my)**2)
y3 - my = (y5 - my) * (-2 cm) / ((x5-mx)**2 + (y5-my)**2)
First you have to find the side equation:
y - y1 = (y2-y1)/(x2-x1)*(x-x1)
y = (y2-y1)/(x2-x1) * x1 - (y2-y1)/(x2-x1) * x + y1
Once you have an equation like this y=mx+q (y and x are costants, so you only care about finding m and q) you can find the middle point
M((x1+x2)/2), (y1+y2)/2)
When you have both the equation and the middle point you can choose your offset. Now just find the points that correspond to that offset on the segment
P1(mx + offset, my + (offset * m)) and P2(mx - offset, my - (offset * m))
where m is the one you found in the first equation
Now you have the 2 points
To find out which is inside and which is outside we would need to have more data. You could try for example to bisect the angles to find the middle point of the polygon M1 and check which between P1 and P2 has a distance to M1 so that:
if distance(P1, M1) < distance(M, M1) then P1 is inside
"if the distance between the point and the middle of the polygon is less than the distance between the middle of the polygon and the middle of the side then P1 is inside the polygon"
There are other solutions, but with the data available this is all i can do. Now you just have to turn this explanation into code
LEGEND:
y -> equation constant
x -> equation constant
m -> line inclination
q -> intersection with axis (you really don't need it for this)
M -> middle point of the side mx, my -> coordinates of middle point of the side
P1 -> point on the line with positive offset
P2 -> point on the line with negative offset
M1 -> middle point of the polygon
I'm trying to align multiple line objects along a human body circumference depending on the orientation of the triangles from the mesh. I would like to put the lines parallel to the mesh. I correctly assign the position for the lines along the circumference, but I also need to add the rotation of the lines such that to be parallel with the body.
The body is a mesh formed by multiple triangles and every line is "linked" with a triangle.
All I have is:
3 points for the closest triangle from the mesh for every line
The normal of the triangle
The positions for the instantiated lines (2 points, start and end)
I need to calculate the angle for every X, Y, Z axes for the line such that the normal of the triangle is perpendicular with the line mesh. I don't know how to get the desired angle. I really appreciate if someone would like to help me.
input:
FVector TrianglePoints[3];
FVector Triangle_Normal; //Calculated as (B-A)^(C-A), where A,B,C are the points of the triangle
FVector linePosition; //I also have the start line and the endLine position if that helps
ouput:
//FRotator rotation(x,y,z), such that the triangle normal and the line object to be perpendicular.
An overview of the circumference line construction. Now the rotation is calculated using the Start position and End position for each line. When we cross some irregular parts of the mesh we want to rotate the lines correctly. Now the rotation is fixed, depending just on the line start and end position.
If I have understood correctly your goal, here is some related vector geometry:
A,B,C are the vertices of the triangle:
A = [xA, yA, zA],
B = [xB, yB, zB]
C = [xC, yC, zC]
K,L are the endpoints of the line-segment:
K = [xK, yK, zK]
L = [xL, yL, zL]
vectors are interpreted as row-vectors
by . I denote matrix multiplication
by x I denote cross product of 3D vectors
by t() I denote the transpose of a matrix
by | | I denote the norm (magnitude) of a vector
Goal: find the rotation matrix and rotation transformation of segment KL
around its midpoint, so that after rotation KL is parallel to the plane ABC
also, the rotation is the "minimal" angle rotation by witch we need to
rotate KL in order to make it parallel to ABC
AB = B - A
AC = C - A
KL = L - K
n = AB x AC
n = n / |n|
u = KL x n
u = u / |u|
v = n x u
cos = ( KL . t(v) ) / |KL|
sin = ( KL . t(n) ) / |KL|
U = [[ u[0], u[1], u[2] ],
[ v[0], v[1], v[2] ],
[ n[0], n[1], n[2] ],
R = [[1, 0, 0],
[0, cos, sin],
[0, -sin, cos]]
ROT = t(U).R.U
then, one can rotate the segment KL around its midpoint
M = (K + L)/2
Y = M + ROT (X - M)
Here is a python script version
A = np.array([0,0,0])
B = np.array([3,0,0])
C = np.array([2,3,0])
K = np.array([ -1,0,1])
L = np.array([ 2,2,2])
KL = L-K
U = np.empty((3,3), dtype=float)
U[2,:] = np.cross(B-A, C-A)
U[2,:] = U[2,:] / np.linalg.norm(U[2,:])
U[0,:] = np.cross(KL, U[2,:])
U[0,:] = U[0,:] / np.linalg.norm(U[0,:])
U[1,:] = np.cross(U[2,:], U[0,:])
norm_KL = np.linalg.norm(KL)
cos_ = KL.dot(U[1,:]) / norm_KL
sin_ = KL.dot(U[2,:]) / norm_KL
R = np.array([[1, 0, 0],
[0, cos_, sin_],
[0,-sin_, cos_]])
ROT = (U.T).dot(R.dot(U))
M = (K+L) / 2
K_rot = M + ROT.dot( K - M )
L_rot = M + ROT.dot( L - M )
print(L_rot)
print(K_rot)
print(L_rot-K_rot)
print((L_rot-K_rot).dot(U[2,:]))
A more inspired solution was to use a procedural mesh, generated at runtime, that have all the requirements that I need:
Continuously along multiple vertices
Easy to apply a UV map for texture tiling
Can be updated at runtime
Isn't hard to compute/work with it
I have this jsbin that shows my working.
In the jsbin, I am trying to draw a line through the altitude through point A (1, 1) that is perpendicular to Line BC which has points B (6, 18) and C (14, 6).
The way I have worked this out is to try and get 2 equations into the form y = mx + c and then rearrange them to y -mx = c and then solve them through simultaneous equations using matrices.
I have this altitude function that does the work:
function altitude(vertex, a, b) {
var slope = gradient(a, b),
x1 = - slope,
y1 = 1,
c1 = getYIntercept(a, slope),
perpendicularSlope = perpendicularGradient(a, b),
x2 = - perpendicularSlope,
y2 = 1,
c2 = getYIntercept(vertex, perpendicularSlope);
var matrix = [
[x1, y1],
[x2, y2]
];
var result = solve(matrix, [c1, c2]);
var g = svg.append('g');
g.append('line')
.style('stroke', 'red')
.attr('class', 'line')
.attr('x1', xScale(vertex.x))
.attr('y1', yScale(vertex.y))
.attr('x2', xScale(result.x))
.attr('y2', yScale(result.y));
}
I first of all get the gradient of BC using this function
var gradient = function(a, b) {
return (b.y - a.y) / (b.x - a.x);
};
Which is -1.5 and from that I can get the perpendicular gradient using this function:
var perpendicularGradient = function (a, b) {
return -1 / gradient(a, b);
};
I make that to be 0.66666 or (2/3).
I get the 2 equations to look like this:
y + 1.5 = 27
y -0.6666666666666666 = 0.33333333333333337
I have some functions in the jsbin that will solve these simultaneously using matrices and cramer's rule, the main one being solve:
function solve(matrix, r) {
var determinant = det(matrix);
var x = det([
[r[0], matrix[0][1]],
[r[1], matrix[1][1]]
]) / determinant;
var y = det([
[matrix[0][0], r[0]],
[matrix[1][0], r[1]]
]) / determinant;
return {x: Math.approx(x), y: Math.approx(y)};
}
function det(matrix) {
return (matrix[0][0]*matrix[1][1])-(matrix[0][1]*matrix[1][0]);
}
I get the coordinates of the intercept to be roughly (12.31, 8.54).
The problem is, it does not look right on the diagram.
Have I taken a wrong step somewhere? I think my calculations are right but I would not rule out them being wrong. It might be down to scale perhaps.
You want to find projection of point A onto line BC.
Let's make vectors
Q = C - B
P = A - B
normalized (unit length):
uQ = Q/ |Q|
Needed projection point D is
D = B + uQ * DotProduct(P, uQ)
For your example A(1,1), B(6,18), C(14,6)
Q = (8, -12)
|Q| = Sqrt(8*8+12*12)~14.4
uQ= (0.55, -0.83)
P=(-5,-17)
DotProduct(P, uQ)=0.55*(-5) -(0.83*-17)=11.39
D = (6+0.55*11.39, 18-0.83*11.39) = (12.26, 8,54)
So your calculation gives right result (though approach is not very efficient), but picture is not exact - different scales of X and Y axes deform angles.
P.S: Second line width = 660 - margin.left - margin.right, makes the picture more reliable
There's a line A-B and C at the center between A and B. It forms a circle as in the figure. If we assume A-B line as a diameter of the circle and then C is it's center. My problem is I have no idea how to draw another three lines (in blue) each 45 degree away from AC or AB. No, this is not a homework, it's part of my complex geometry in a rendering.
alt text http://www.freeimagehosting.net/uploads/befcd84d8c.png
There are a few ways to solve this problem, one of which is to find the angle of the line and then add 45 degrees to this a few times. Here's an example, it's in Python, but translating the math should be easy (and I've tried to write the Python in a simplistic way).
Here's the output for a few lines:
The main function is calc_points, the rest is just to give it A and B that intersect the circle, and make the plots.
from math import atan2, sin, cos, sqrt, pi
from matplotlib import pyplot
def calc_points(A, B, C):
dx = C[0]-A[0]
dy = C[1]-A[1]
line_angle = atan2(dy, dx)
radius = sqrt(dy*dy + dx*dx)
new_points = []
# now go around the circle and find the points
for i in range(3):
angle = line_angle + (i+1)*45*(pi/180) # new angle in radians
x = radius*cos(angle) + C[0]
y = radius*sin(angle) + C[1]
new_points.append([x, y])
return new_points
# test this with some reasonable values
pyplot.figure()
for i, a in enumerate((-20, 20, 190)):
radius = 5
C = [2, 2]
# find an A and B on the circle and plot them
angle = a*(pi/180)
A = [radius*cos(pi+angle)+C[0], radius*sin(pi+angle)+C[1]]
B = [radius*cos(angle)+C[0], radius*sin(angle)+C[1]]
pyplot.subplot(1,3,i+1)
pyplot.plot([A[0], C[0]], [A[1], C[1]], 'r')
pyplot.plot([B[0], C[0]], [B[1], C[1]], 'r')
# now run these through the calc_points function and the new lines
new_points = calc_points(A, B, C)
for np in new_points:
pyplot.plot([np[0], C[0]], [np[1], C[1]], 'b')
pyplot.xlim(-8, 8)
pyplot.ylim(-8, 8)
for x, X in (("A", A), ("B", B), ("C", C)):
pyplot.text(X[0], X[1], x)
pyplot.show()
If you want to find coordinates of blue lines, may be you will find helpful some information about tranformations (rotations):
http://en.wikipedia.org/wiki/Rotation_matrix
You need to rotate for example vector AC and then you can find coordinate of end point of blue line.
start with this and add a button with code:
private void btnCircleLined_Click(object sender, System.EventArgs e)
{
Graphics graph = Graphics.FromImage(DrawArea);
int x = 100, y = 100, diameter = 50;
myPen.Color = Color.Green;
myPen.Width = 10;
graph.DrawEllipse(myPen, x, y, diameter, diameter);
myPen.Color = Color.Red;
double radian = 45 * Math.PI / 180;
int xOffSet = (int)(Math.Cos(radian) * diameter / 2);
int yOffSet = (int)(Math.Sin(radian) * diameter / 2);
graph.DrawLine(myPen, x, y + yOffSet + myPen.Width + diameter / 2, x + xOffSet + myPen.Width + diameter / 2, y);
graph.DrawLine(myPen, x, y, x + xOffSet + myPen.Width + diameter / 2, y + yOffSet + myPen.Width + diameter / 2);
graph.Dispose();
this.Invalidate();
}
edit: could not see your picture so I misinterpeted your question, but this should get you started.
Translate A with C at the origin (i.e. A-C), rotate CW 45°, then translate back. Repeat three more times.
If I were doing this I'd use polar co-ordinates (apologies for including the link if you are already well aware what they are) as an easy way of figuring out the co-ordinates of the points on the circumference that you need. Then draw lines to there from the centre of the circle.