I'm having a bit of a mind blank on this at the moment.
I've got a problem where I need to calculate the position of points around a central point, assuming they're all equidistant from the center and from each other.
The number of points is variable so it's DrawCirclePoints(int x)
I'm sure there's a simple solution, but for the life of me, I just can't see it :)
Given a radius length r and an angle t in radians and a circle's center (h,k), you can calculate the coordinates of a point on the circumference as follows (this is pseudo-code, you'll have to adapt it to your language):
float x = r*cos(t) + h;
float y = r*sin(t) + k;
A point at angle theta on the circle whose centre is (x0,y0) and whose radius is r is (x0 + r cos theta, y0 + r sin theta). Now choose theta values evenly spaced between 0 and 2pi.
Here's a solution using C#:
void DrawCirclePoints(int points, double radius, Point center)
{
double slice = 2 * Math.PI / points;
for (int i = 0; i < points; i++)
{
double angle = slice * i;
int newX = (int)(center.X + radius * Math.Cos(angle));
int newY = (int)(center.Y + radius * Math.Sin(angle));
Point p = new Point(newX, newY);
Console.WriteLine(p);
}
}
Sample output from DrawCirclePoints(8, 10, new Point(0,0));:
{X=10,Y=0}
{X=7,Y=7}
{X=0,Y=10}
{X=-7,Y=7}
{X=-10,Y=0}
{X=-7,Y=-7}
{X=0,Y=-10}
{X=7,Y=-7}
Good luck!
Placing a number in a circular path
// variable
let number = 12; // how many number to be placed
let size = 260; // size of circle i.e. w = h = 260
let cx= size/2; // center of x(in a circle)
let cy = size/2; // center of y(in a circle)
let r = size/2; // radius of a circle
for(let i=1; i<=number; i++) {
let ang = i*(Math.PI/(number/2));
let left = cx + (r*Math.cos(ang));
let top = cy + (r*Math.sin(ang));
console.log("top: ", top, ", left: ", left);
}
Using one of the above answers as a base, here's the Java/Android example:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF bounds = new RectF(canvas.getClipBounds());
float centerX = bounds.centerX();
float centerY = bounds.centerY();
float angleDeg = 90f;
float radius = 20f
float xPos = radius * (float)Math.cos(Math.toRadians(angleDeg)) + centerX;
float yPos = radius * (float)Math.sin(Math.toRadians(angleDeg)) + centerY;
//draw my point at xPos/yPos
}
For the sake of completion, what you describe as "position of points around a central point(assuming they're all equidistant from the center)" is nothing but "Polar Coordinates". And you are asking for way to Convert between polar and Cartesian coordinates which is given as x = r*cos(t), y = r*sin(t).
PHP Solution:
class point{
private $x = 0;
private $y = 0;
public function setX($xpos){
$this->x = $xpos;
}
public function setY($ypos){
$this->y = $ypos;
}
public function getX(){
return $this->x;
}
public function getY(){
return $this->y;
}
public function printX(){
echo $this->x;
}
public function printY(){
echo $this->y;
}
}
function drawCirclePoints($points, $radius, &$center){
$pointarray = array();
$slice = (2*pi())/$points;
for($i=0;$i<$points;$i++){
$angle = $slice*$i;
$newx = (int)($center->getX() + ($radius * cos($angle)));
$newy = (int)($center->getY() + ($radius * sin($angle)));
$point = new point();
$point->setX($newx);
$point->setY($newy);
array_push($pointarray,$point);
}
return $pointarray;
}
Here is how I found out a point on a circle with javascript, calculating the angle (degree) from the top of the circle.
const centreX = 50; // centre x of circle
const centreY = 50; // centre y of circle
const r = 20; // radius
const angleDeg = 45; // degree in angle from top
const radians = angleDeg * (Math.PI/180);
const pointY = centreY - (Math.cos(radians) * r); // specific point y on the circle for the angle
const pointX = centreX + (Math.sin(radians) * r); // specific point x on the circle for the angle
I had to do this on the web, so here's a coffeescript version of #scottyab's answer above:
points = 8
radius = 10
center = {x: 0, y: 0}
drawCirclePoints = (points, radius, center) ->
slice = 2 * Math.PI / points
for i in [0...points]
angle = slice * i
newX = center.x + radius * Math.cos(angle)
newY = center.y + radius * Math.sin(angle)
point = {x: newX, y: newY}
console.log point
drawCirclePoints(points, radius, center)
Here is an R version based on the #Pirijan answer above.
points <- 8
radius <- 10
center_x <- 5
center_y <- 5
drawCirclePoints <- function(points, radius, center_x, center_y) {
slice <- 2 * pi / points
angle <- slice * seq(0, points, by = 1)
newX <- center_x + radius * cos(angle)
newY <- center_y + radius * sin(angle)
plot(newX, newY)
}
drawCirclePoints(points, radius, center_x, center_y)
The angle between each of your points is going to be 2Pi/x so you can say that for points n= 0 to x-1 the angle from a defined 0 point is 2nPi/x.
Assuming your first point is at (r,0) (where r is the distance from the centre point) then the positions relative to the central point will be:
rCos(2nPi/x),rSin(2nPi/x)
Working Solution in Java:
import java.awt.event.*;
import java.awt.Robot;
public class CircleMouse {
/* circle stuff */
final static int RADIUS = 100;
final static int XSTART = 500;
final static int YSTART = 500;
final static int DELAYMS = 1;
final static int ROUNDS = 5;
public static void main(String args[]) {
long startT = System.currentTimeMillis();
Robot bot = null;
try {
bot = new Robot();
} catch (Exception failed) {
System.err.println("Failed instantiating Robot: " + failed);
}
int mask = InputEvent.BUTTON1_DOWN_MASK;
int howMany = 360 * ROUNDS;
while (howMany > 0) {
int x = getX(howMany);
int y = getY(howMany);
bot.mouseMove(x, y);
bot.delay(DELAYMS);
System.out.println("x:" + x + " y:" + y);
howMany--;
}
long endT = System.currentTimeMillis();
System.out.println("Duration: " + (endT - startT));
}
/**
*
* #param angle
* in degree
* #return
*/
private static int getX(int angle) {
double radians = Math.toRadians(angle);
Double x = RADIUS * Math.cos(radians) + XSTART;
int result = x.intValue();
return result;
}
/**
*
* #param angle
* in degree
* #return
*/
private static int getY(int angle) {
double radians = Math.toRadians(angle);
Double y = RADIUS * Math.sin(radians) + YSTART;
int result = y.intValue();
return result;
}
}
Based on the answer above from Daniel, here's my take using Python3.
import numpy
def circlepoints(points,radius,center):
shape = []
slice = 2 * 3.14 / points
for i in range(points):
angle = slice * i
new_x = center[0] + radius*numpy.cos(angle)
new_y = center[1] + radius*numpy.sin(angle)
p = (new_x,new_y)
shape.append(p)
return shape
print(circlepoints(100,20,[0,0]))
Related
I have a Big Circle and several small circles around it as seen in picture
First I'm drawing middle small circle like this:
cxSmallMiddle = cxBig + radiusBig + hDist + radiusSmall;
sySmallMiddle = radiusBig;
cxBig is center of Big circle. hDist is the distance I want every small circle to be from big circle.
So this way now middle small circle's middle point is parallel to big circle's.
Now I want to draw next small circle with hDist from big circle and vDist (vertical distance) from middle small circle.
So this way hDist and vDist will control the distance small circles are separated from big circle and gap between small circles accordingly.
how can I find cx and cy for other buttons?
This is a hand drawn finished version
Edit: added a code suggested by #Gene
#Override
public void onDraw(Canvas canvas) {
float radiusBig = 110f * singleDp;
float cxBig = screenWidth / 2f;
//float cyBig = screenHeight / 2f;
float cyBig = radiusBig + strokeWidth + (20*singleDp);
canvas.drawCircle(cxBig, cyBig, radiusBig, paint);
float radiusSmall = 20 * singleDp;
float vDist = 0 * singleDp;
float hDist = 0 * singleDp;
float acPoint = radiusBig;
float bcPoint = radiusSmall + vDist;
float theta = (float) Math.acos(bcPoint / acPoint);
int i = 0;
double x_i = acPoint * Math.cos(i * theta) + cxBig;
double y_i = acPoint * Math.sin(i * theta) + cyBig;
canvas.drawCircle((float) x_i, (float) y_i, radiusSmall, paint);
i = 1;
x_i = acPoint * Math.cos(i * theta) + cxBig;
y_i = acPoint * Math.sin(i * theta) + cyBig;
canvas.drawCircle((float) x_i, (float) y_i, radiusSmall, paint);
}
I experimented a lot with this code and this is what I got. When I draw i=0 is almost 45 degree distance from i=0. While experimenting, I discovered if I specify vDist = 80; then it looks okay. The bigger the vDist the closer it gets to i=0.
This is high school trigonometry. There's a right triangle formed by the big circle center (A), the small circle center (C), and the point (B) on the horizontal radius directly below the small circle center.
The length of edge BC is vDist + 2 * radiusSmall. The length of AC is radiusBig
Let \theta be the angle BAC. Then
sin(\theta) = BC / AC = (vDist + 2 * radiusSmall) / radiusBig.
So you can determine \theta:
\theta = arcsin((vDist + radiusSmall) / radiusBig)
Once you have \theta, the locations of the circles wrt the origin are
x_i = radiusBig * cos(i * \theta)
y_i = radiusBig * sin(i * \theta)
For i = 0, +1, -1, +2, -2, ...
Edit
Okay here is a quick hack in Java Swing. Sorry in the original post I said arccos when I meant arcsin.
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Circles extends JPanel {
public static void main(String[] a) {
JFrame f = new JFrame();
f.setSize(800, 800);
f.add(new Circles());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
#Override
public void paint(Graphics g) {
int cx = 400, cy = 400, rBig = 200, rSmall = 40, hDist = 20, vDist = 10;
drawCircle(g, cx, cy, rBig); // Big circle.
int rSmallCircleCenters = rBig + hDist + rSmall;
double theta = Math.asin(((double) vDist + 2 * rSmall) / rSmallCircleCenters);
int nPairs = 3;
for (int i = 1 - nPairs; i < nPairs; ++i) {
int dx = (int) (rSmallCircleCenters * Math.cos(i * theta));
int dy = (int) (rSmallCircleCenters * Math.sin(i * theta));
drawCircle(g, cx + dx, cy + dy, rSmall);
drawCircle(g, cx - dx, cy - dy, rSmall);
}
}
private void drawCircle(Graphics g, int cx, int cy, int r) {
g.drawOval(cx - r, cy - r, 2 * r, 2 * r);
}
}
Here's what it draws:
I'm trying to create a program that receives a photograph of a surface from a certain angle and position, and generates an image of what an isometric projection of the plane would look like. For example, given a photo of a checkerboard
and information about the positioning and properties of the camera, it could reconstruct a section of the undistorted pattern
My approach has been divided into two parts. The first part is to create four rays, coming from the camera, following the four corners of its field of view. I compute where these rays intersect with the plane, to form the quadrangle of the area of the plane that the camera can see, like this:
The second part is to render an isomorphic projection of the plane with the textured quadrangle. I divide the quadrangle into two triangles, then for each pixel on the rendering, I convert the cartesian coordinates into barymetric coordinates relative to each triangle, then convert it back into cartesian coordinates relative to a corresponding triangle that consumes half of the photograph, so that I can sample a color.
(I am aware that this could be done more efficiently with OpenGL, but I would like to not use it for logistical reasons. I am also aware that the quality will be affected by lack of interpolation, that does not matter for this task.)
I am testing the program with some data, but the rendering does not occur as intended. Here is the photograph:
And here is the program output:
I believe that the problem is occurring in the quadrangle rendering, because I have graphed the projected vertices, and they appear to be correct:
I am by no means an expert in computer graphics, so I would very much appreciate if someone had any idea what would cause this problem. Here is the relevant code:
public class ImageProjector {
private static final EquationSystem ground = new EquationSystem(0, 1, 0, 0);
private double fov;
private double aspectRatio;
private vec3d position;
private double xAngle;
private double yAngle;
private double zAngle;
public ImageProjector(double fov, double aspectRatio, vec3d position, double xAngle, double yAngle, double zAngle) {
this.fov = fov;
this.aspectRatio = aspectRatio;
this.position = position;
this.xAngle = xAngle;
this.yAngle = yAngle;
this.zAngle = zAngle;
}
public vec3d[] computeVertices() {
return new vec3d[] {
computeVertex(1, 1),
computeVertex(1, -1),
computeVertex(-1, -1),
computeVertex(-1, 1)
};
}
private vec3d computeVertex(int horizCoef, int vertCoef) {
vec3d p2 = new vec3d(tan(fov / 2) * horizCoef, tan((fov / 2) / aspectRatio) * vertCoef, 1);
p2 = p2.rotateXAxis(xAngle);
p2 = p2.rotateYAxis(yAngle);
p2 = p2.rotateZAxis(zAngle);
if (p2.y > 0) {
throw new RuntimeException("sky is visible to camera: " + p2);
}
p2 = p2.plus(position);
//System.out.println("passing through " + p2);
EquationSystem line = new LineBuilder(position, p2).build();
return new vec3d(line.add(ground).solveVariables());
}
}
public class barypoint {
public barypoint(double u, double v, double w) {
this.u = u;
this.v = v;
this.w = w;
}
public final double u;
public final double v;
public final double w;
public barypoint(vec2d p, vec2d a, vec2d b, vec2d c) {
vec2d v0 = b.minus(a);
vec2d v1 = c.minus(a);
vec2d v2 = p.minus(a);
double d00 = v0.dotProduct(v0);
double d01 = v0.dotProduct(v1);
double d11 = v1.dotProduct(v1);
double d20 = v2.dotProduct(v0);
double d21 = v2.dotProduct(v1);
double denom = d00 * d11 - d01 * d01;
v = (d11 * d20 - d01 * d21) / denom;
w = (d00 * d21 - d01 * d20) / denom;
u = 1.0 - v - w;
}
public barypoint(vec2d p, Triangle triangle) {
this(p, triangle.a, triangle.b, triangle.c);
}
public vec2d toCartesian(vec2d a, vec2d b, vec2d c) {
return new vec2d(
u * a.x + v * b.x + w * c.x,
u * a.y + v * b.y + w * c.y
);
}
public vec2d toCartesian(Triangle triangle) {
return toCartesian(triangle.a, triangle.b, triangle.c);
}
}
public class ImageTransposer {
private BufferedImage source;
private BufferedImage receiver;
public ImageTransposer(BufferedImage source, BufferedImage receiver) {
this.source = source;
this.receiver = receiver;
}
public void transpose(Triangle sourceCoords, Triangle receiverCoords) {
int xMin = (int) Double.min(Double.min(receiverCoords.a.x, receiverCoords.b.x), receiverCoords.c.x);
int xMax = (int) Double.max(Double.max(receiverCoords.a.x, receiverCoords.b.x), receiverCoords.c.x);
int yMin = (int) Double.min(Double.min(receiverCoords.a.y, receiverCoords.b.y), receiverCoords.c.y);
int yMax = (int) Double.max(Double.max(receiverCoords.a.y, receiverCoords.b.y), receiverCoords.c.y);
for (int x = xMin; x <= xMax; x++) {
for (int y = yMin; y <= yMax; y++) {
vec2d p = new vec2d(x, y);
if (receiverCoords.contains(p) && p.x >= 0 && p.y >= 0 && p.x < receiver.getWidth() && y < receiver.getHeight()) {
barypoint bary = new barypoint(p, receiverCoords);
vec2d sp = bary.toCartesian(sourceCoords);
if (sp.x >= 0 && sp.y >= 0 && sp.x < source.getWidth() && sp.y < source.getHeight()) {
receiver.setRGB(x, y, source.getRGB((int) sp.x, (int) sp.y));
}
}
}
}
}
}
public class ProjectionRenderer {
private String imagePath;
private BufferedImage mat;
private vec3d[] vertices;
private vec2d pos;
private double scale;
private int width;
private int height;
public boolean error = false;
public ProjectionRenderer(String image, BufferedImage mat, vec3d[] vertices, vec3d pos, double scale, int width, int height) {
this.imagePath = image;
this.mat = mat;
this.vertices = vertices;
this.pos = new vec2d(pos.x, pos.z);
this.scale = scale;
this.width = width;
this.height = height;
}
public void run() {
try {
BufferedImage image = ImageIO.read(new File(imagePath));
vec2d[] transVerts = Arrays.stream(vertices)
.map(v -> new vec2d(v.x, v.z))
.map(v -> v.minus(pos))
.map(v -> v.multiply(scale))
.map(v -> v.plus(new vec2d(mat.getWidth() / 2, mat.getHeight() / 2)))
// this fixes the image being upside down
.map(v -> new vec2d(v.x, mat.getHeight() / 2 + (mat.getHeight() / 2 - v.y)))
.toArray(vec2d[]::new);
System.out.println(Arrays.toString(transVerts));
Triangle sourceTri1 = new Triangle(
new vec2d(0, 0),
new vec2d(image.getWidth(), 0),
new vec2d(0, image.getHeight())
);
Triangle sourceTri2 = new Triangle(
new vec2d(image.getWidth(), image.getHeight()),
new vec2d(0, image.getHeight()),
new vec2d(image.getWidth(), 0)
);
Triangle destTri1 = new Triangle(
transVerts[3],
transVerts[0],
transVerts[2]
);
Triangle destTri2 = new Triangle(
transVerts[1],
transVerts[2],
transVerts[0]
);
ImageTransposer transposer = new ImageTransposer(image, mat);
System.out.println("transposing " + sourceTri1 + " -> " + destTri1);
transposer.transpose(sourceTri1, destTri1);
System.out.println("transposing " + sourceTri2 + " -> " + destTri2);
transposer.transpose(sourceTri2, destTri2);
} catch (IOException e) {
e.printStackTrace();
error = true;
}
}
}
The reason it's not working is because your transpose function works entirely with 2D co-ordinates, therefore it cannot compensate for the image distortion resulting from 3D perspective. You have effectively implemented a 2D affine transformation. Parallel lines remain parallel, which they do not under a 3D perspective transform. If you draw a straight line between two points on your triangle, you can linearly interpolate between them by linearly interpolating the barycentric co-ordinates, and vice versa.
To take Z into account, you can keep the barycentric co-ordinate approach, but provide a Z co-ordinate for each point in sourceCoords. The trick is to interpolate between 1/Z values (which can be linearly interpolated in a perspective image) instead of interpolating Z itself. So instead of interpolating what are effectively the texture co-ordinates for each point, interpolate the texture co-ordinate divided by Z, along with inverse Z, and interpolate all of those using your barycentric system. Then divide by inverse Z before doing your texture lookup to get texture co-ordinates back.
You could do that like this (assume a b c contain an extra z co-ordinate giving distance from camera):
public vec3d toCartesianInvZ(vec3d a, vec3d b, vec3d c) {
// put some asserts in to check for z = 0 to avoid div by zero
return new vec3d(
u * a.x/a.z + v * b.x/b.z + w * c.x/c.z,
u * a.y/a.z + v * b.y/b.z + w * c.y/c.z,
u * 1/a.z + v * 1/b.z + w * 1/c.z
);
}
(You could obviously speed up/simplify this by pre-computing all those divides and storing in sourceCoords, and just doing regular barycentric interpolation in 3D)
Then after you call it in transpose, divide by inv Z to get the texture co-ords back:
vec3d spInvZ = bary.toCartesianInvZ(sourceCoords);
vec2d sp = new vec2d(spInvZ.x / spInvZ.z, spInvZ.y / spInvZ.z);
etc. The Z co-ordinate that you need is the distance of the point in 3D space from the camera position, in the direction the camera is pointing. You can compute it with a dot product if you aren't getting it some other way:
float z = point.subtract(camera_pos).dot(camera_direction);
etc
The issue is i got an array of PVectors placed around my main PVector which is in the middle. I want my array of PVectors to rotate around my main PVector based on a rotation variable. Is there any way to do this?
Right now I have this code but it does not rotate the PVectors, just places them farther away based on the rotation var.
class Box {
PVector location;
PVector[] points;
float rotation = random(360);
Box() {
location = new PVector(random(width), random(height));
points = new PVector[4];
for(a = 0; a < points.length; a ++) {
points[a] = new PVector(0,0);
}
}
void update() {
points[0].x = location.x + 10 * sin(rotation);
points[0].y = location.y + 10 * sin(rotation);
points[1].x = location.x + 10 * sin(rotation);
points[1].y = location.y - 10 * sin(rotation);
points[2].x = location.x - 10 * sin(rotation);
points[2].y = location.y + 10 * sin(rotation);
points[3].x = location.x - 10 * sin(rotation);
points[3].y = location.y - 10 * sin(rotation);
}
To rotate the vectors, you do need to use trig functions like sin and cos like you have in your code. However, your approach isn't really the best. Adding onto the existing (x,y) coordinates on each update isn't really feasible, since the number you have to add on is changing every time. It's easier just to overwrite and calculate new values for each update. The x and y coordinates for a given angle are given by the unit circle:
So, the x of a given PVector varies with cos(theta) and the y varies with sin(theta). Check the following code:
Box b;
void setup(){
size(300,300);
b = new Box();
}
void draw(){
background(255);
b.update(mouseX, mouseY);
b.display();
}
class Box {
PVector location;
PVector[] points;
float rotation;
float radius;
Box() {
location = new PVector(width/2,height/2);
points = new PVector[7];
rotation = 0;
radius = 50;
for(int i = 0; i < points.length; i ++) {
//this centers the points around (0,0), so you need to add in
//the box coordinates later on.
points[i] = new PVector(radius*cos(rotation + i*TWO_PI/points.length),
radius*sin(rotation + i*TWO_PI/points.length));
}
}
void update(int x, int y) {
location.set(x,y);
rotation += 0.08; // change for different rotation speeds.
for(int i = 0; i < points.length; i++){
points[i].set(radius*cos(rotation + i*TWO_PI/points.length),
radius*sin(rotation + i*TWO_PI/points.length));
}
}
void display(){
stroke(0);
for(int i = 0; i < points.length; i++){
//points are treated as offsets from the center point:
line(location.x,location.y,location.x+points[i].x,location.y+points[i].y);
ellipse(location.x+points[i].x,location.y+points[i].y,10,10);
}
}
}
For every update() call, it increments the rotation variable and calculates the new x and y values for each point in the array. You can change the speed and direction of rotation by changing 0.08 to bigger/smaller/positive/negative numbers.
To rotate a point around location:
double x = cos(rotation) * (point.x-location.x) - sin(rotation) * (point.y-location.y) + location.x;
double y = sin(rotation) * (point.x-location.x) + cos(rotation) * (point.y-location.y) + location.y;
point.x = x;
point.y = y;
See Rotate a point by an angle
I have a line that is based on two (x,y) coordinates I know. This line has a starting and an end point. Now I want to add an arrowhead at the end point of the line.
I know that the arrow is an equilateral triangle, and therefore each angle has 60 degrees. Additionally, I know the length of one side, which will be 20. I also no one edge of the triangle (that is the end point of the line).
How can I calculate the other two points of the triangle? I know I should use some trigonometry but how?
P.s. The endpoint of the line should be the arrowhead's tip.
You don't need trig., just some vector arithmetic...
Say the line goes from A to B, with the front vertex of the arrowhead at B. The length of the arrowhead is h = 10(√3) and its half-width is w = 10. We'll denote the unit vector from A to B as U = (B - A)/|B - A| (i.e., the difference divided by the length of the difference), and the unit vector perpendicular to this as V = [-Uy, Ux].
From these quantities, you can calculate the two rear vertices of the arrowhead as B - hU ± wV.
In C++:
struct vec { float x, y; /* … */ };
void arrowhead(vec A, vec B, vec& v1, vec& v2) {
float h = 10*sqrtf(3), w = 10;
vec U = (B - A)/(B - A).length();
vec V = vec(-U.y, U.x);
v1 = B - h*U + w*V;
v2 = B - h*U - w*V;
}
If you want to specify different angles, then you will need some trig. to calculate different values of h and w. Assuming you want an arrowhead of length h and tip-angle θ, then w = h tan(θ/2). In practice, however, it's simplest to specify h and w directly.
Here's a sample LINQPad program that shows how to do that:
void Main()
{
const int imageWidth = 512;
Bitmap b = new Bitmap(imageWidth , imageWidth , PixelFormat.Format24bppRgb);
Random r = new Random();
for (int index = 0; index < 10; index++)
{
Point fromPoint = new Point(0, 0);
Point toPoint = new Point(0, 0);
// Ensure we actually have a line
while (fromPoint == toPoint)
{
fromPoint = new Point(r.Next(imageWidth ), r.Next(imageWidth ));
toPoint = new Point(r.Next(imageWidth ), r.Next(imageWidth ));
}
// dx,dy = arrow line vector
var dx = toPoint.X - fromPoint.X;
var dy = toPoint.Y - fromPoint.Y;
// normalize
var length = Math.Sqrt(dx * dx + dy * dy);
var unitDx = dx / length;
var unitDy = dy / length;
// increase this to get a larger arrow head
const int arrowHeadBoxSize = 10;
var arrowPoint1 = new Point(
Convert.ToInt32(toPoint.X - unitDx * arrowHeadBoxSize - unitDy * arrowHeadBoxSize),
Convert.ToInt32(toPoint.Y - unitDy * arrowHeadBoxSize + unitDx * arrowHeadBoxSize));
var arrowPoint2 = new Point(
Convert.ToInt32(toPoint.X - unitDx * arrowHeadBoxSize + unitDy * arrowHeadBoxSize),
Convert.ToInt32(toPoint.Y - unitDy * arrowHeadBoxSize - unitDx * arrowHeadBoxSize));
using (Graphics g = Graphics.FromImage(b))
{
if (index == 0)
g.Clear(Color.White);
g.DrawLine(Pens.Black, fromPoint, toPoint);
g.DrawLine(Pens.Black, toPoint, arrowPoint1);
g.DrawLine(Pens.Black, toPoint, arrowPoint2);
}
}
using (var stream = new MemoryStream())
{
b.Save(stream, ImageFormat.Png);
Util.Image(stream.ToArray()).Dump();
}
}
Basically, you:
Calculate the vector of the arrow line
Normalize the vector, ie. making its length 1
Calculate the ends of the arrow heads by going:
First back from the head a certain distance
Then perpendicular out from the line a certain distance
Note that if you want the arrow head lines to have a different angle than 45 degrees, you'll have to use a different method.
The program above will draw 10 random arrows each time, here's an example:
Let's your line is (x0,y0)-(x1,y1)
Backward direction vector (dx, dy) = (x0-x1, y0-y1)
It's norm Norm = Sqrt(dx*dx+dy*dy)
Normalize it: (udx, udy) = (dx/Norm, dy/Norm)
Rotate by angles Pi/6 and -Pi/6
ax = udx * Sqrt(3)/2 - udy * 1/2
ay = udx * 1/2 + udy * Sqrt(3)/2
bx = udx * Sqrt(3)/2 + udy * 1/2
by = - udx * 1/2 + udy * Sqrt(3)/2
Your points: (x1 + 20 * ax, y1 + 20 * ay) and (x1 + 20 * bx, y1 + 20 * by)
I want to contribute my answer in C# based on Marcelo Cantos' answer since the algorithm works really well. I wrote a program to calculate the centroid of a laser beam projected on the CCD array. After the centroid is found, the direction angle line is drawn and I need the arrow head pointing at that direction. Since the angle is calculated, the arrow head would have to follow the angle in any of the direction.
This code gives you the flexibility of changing the arrow head size as shown in the pictures.
First you need the vector struct with all the necessary operators overloading.
private struct vec
{
public float x;
public float y;
public vec(float x, float y)
{
this.x = x;
this.y = y;
}
public static vec operator -(vec v1, vec v2)
{
return new vec(v1.x - v2.x, v1.y - v2.y);
}
public static vec operator +(vec v1, vec v2)
{
return new vec(v1.x + v2.x, v1.y + v2.y);
}
public static vec operator /(vec v1, float number)
{
return new vec(v1.x / number, v1.y / number);
}
public static vec operator *(vec v1, float number)
{
return new vec(v1.x * number, v1.y * number);
}
public static vec operator *(float number, vec v1)
{
return new vec(v1.x * number, v1.y * number);
}
public float length()
{
double distance;
distance = (this.x * this.x) + (this.y * this.y);
return (float)Math.Sqrt(distance);
}
}
Then you can use the same code given by Marcelo Cantos, but I made the length and half_width of the arrow head variables so that you can define that when calling the function.
private void arrowhead(float length, float half_width,
vec A, vec B, ref vec v1, ref vec v2)
{
float h = length * (float)Math.Sqrt(3);
float w = half_width;
vec U = (B - A) / (B - A).length();
vec V = new vec(-U.y, U.x);
v1 = B - h * U + w * V;
v2 = B - h * U - w * V;
}
Now you can call the function like this:
vec leftArrowHead = new vec();
vec rightArrowHead = new vec();
arrowhead(20, 10, new vec(circle_center_x, circle_center_y),
new vec(x_centroid_pixel, y_centroid_pixel),
ref leftArrowHead, ref rightArrowHead);
In my code, the circle center is the first vector location (arrow butt), and the centroid_pixel is the second vector location (arrow head).
I draw the arrow head by storing the vector values in the points for graphics.DrawPolygon() function in the System.Drawings. Code is shown below:
Point[] ppts = new Point[3];
ppts[0] = new Point((int)leftArrowHead.x, (int)leftArrowHead.y);
ppts[1] = new Point(x_cm_pixel,y_cm_pixel);
ppts[2] = new Point((int)rightArrowHead.x, (int)rightArrowHead.y);
g2.DrawPolygon(p, ppts);
You can find angle of line.
Vector ox = Vector(1,0);
Vector line_direction = Vector(line_begin.x - line_end.x, line_begin.y - line_end.y);
line_direction.normalize();
float angle = acos(ox.x * line_direction.x + line_direction.y * ox.y);
Then use this function to all 3 points using found angle.
Point rotate(Point point, float angle)
{
Point rotated_point;
rotated_point.x = point.x * cos(angle) - point.y * sin(angle);
rotated_point.y = point.x * sin(angle) + point.y * cos(angle);
return rotated_point;
}
Assuming that upper point of arrow's head is line's end it will perfectly rotated and fit to line.
Didn't test it =(
For anyone that is interested, #TomP was wondering about a js version, so here is a javascript version that I made. It is based off of #Patratacus and #Marcelo Cantos answers. Javascript doesn't support operator overloading, so it isn't as clean looking as C++ or other languages. Feel free to offer improvements.
I am using Class.js to create classes.
Vector = Class.extend({
NAME: "Vector",
init: function(x, y)
{
this.x = x;
this.y = y;
},
subtract: function(v1)
{
return new Vector(this.x - v1.x, this.y - v1.y);
},
add: function(v1)
{
return new Vector(this.x + v1.x, this.y + v1.y);
},
divide: function(number)
{
return new Vector(this.x / number, this.y / number);
},
multiply: function(number)
{
return new Vector(this.x * number, this.y * number);
},
length: function()
{
var distance;
distance = (this.x * this.x) + (this.y * this.y);
return Math.sqrt(distance);
}
});
And then a function to do the logic:
var getArrowhead = function(A, B)
{
var h = 10 * Math.sqrt(3);
var w = 5;
var v1 = B.subtract(A);
var length = v1.length();
var U = v1.divide(length);
var V = new Vector(-U.y, U.x);
var r1 = B.subtract(U.multiply(h)).add(V.multiply(w));
var r2 = B.subtract(U.multiply(h)).subtract(V.multiply(w));
return [r1,r2];
}
And call the function like this:
var A = new Vector(start.x,start.y);
var B = new Vector(end.x,end.y);
var vec = getArrowhead(A,B);
console.log(vec[0]);
console.log(vec[1]);
I know the OP didn't ask for any specific language, but I came across this looking for a JS implementation, so I thought I would post the result.
I am trying to make some objects, say 12, to rotate in an ellipse path continuously in Processing. I got a sketch which does rotation in a circle and I want to make it to rotate in a ellipse. I have some pointer from processing forum but the code from the pointer is different from the code that I posted and I couldn't understand yet (weak in trigonometry).
I googled a bit and found a post trying to achieve this with this algorithm:
You need to define your ellipse with a few parameters:
x, y: center of the ellipse
a, b: semimajor and semiminor axes
If you want to move on the elipses this means that you change the
angle between the major axes and your position on the ellipse. Lets
call this angle alpha.
Your position (X,Y) is:
X = x + (a * Math.cos(alpha));
Y = y + (b * Math.sin(alpha));
In order to move left or right you need to increase/decrease alpha and
then recalculate your position. Source:
http://answers.unity3d.com/questions/27620/move-object-allong-an-ellipsoid-path.html
How do I integrate it into my sketch? Thank you.
Here's my sketch:
void setup()
{
size(1024, 768);
textFont(createFont("Arial", 30));
}
void draw()
{
background(0);
stroke(255);
int cx = 500;
int cy = 350;
int r = 300; //radius of the circle
float t = millis()/4000.0f; //increase to slow down the movement
ellipse(cx, cy, 5, 5);
for (int i = 1 ; i <= 12; i++) {
t = t + 100;
int x = (int)(cx + r * cos(t));
int y = (int)(cy + r * sin(t));
line(cx, cy, x, y);
textSize(30);
text(i, x, y);
if (i == 10) {
textSize(15);
text("x: " + x + " y: " + y, x - 50, y - 20);
}
}
}
Replace
int r = 300; //radius of the circle
with
int a = 350; // major axis of ellipse
int b = 250; // minor axis of ellipse
and replace
int x = (int)(cx + r * cos(t));
int y = (int)(cy + r * sin(t));
with
int x = (int)(cx + a * cos(t));
int y = (int)(cy + b * sin(t));