How to a get an array of 3D cameras to follow a Bezier curve in unity? - visual-studio

I need to implement a manual Bezier curve script to a camera in my scene. I CANNOT use any methods provided by unity to implement the Bezier curve. I got the formula right but I don't know how to get a camera to follow the curve let alone 4. I could not do an array and just apply the script to each camera if that's an option. I got a lineRenderer script that show the curve:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bezier : MonoBehaviour
{
public LineRenderer lineRenderer;
public Transform point0, point1, point2, point3;
private int cPoints = 50;
private Vector3[] positions = new Vector3[50];
// Start is called before the first frame update
void Start()
{
lineRenderer.positionCount = cPoints;
DrawCubicCurve();
}
// Update is called once per frame
void Update()
{
DrawCubicCurve();
}
private void DrawCubicCurve()
{
for (int i = 1; i < cPoints - 1; i++)
{
float t = i / (float)cPoints;
positions[i - 1] = CalculateCubicBezierCurve(t, point0.position, point1.position, point2.position, point3.position);
}
lineRenderer.SetPositions(positions);
}
private Vector3 CalculateCubicBezierCurve(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
float u = 1 - t;
float t2 = t * t;
float uu = u * u;
float uuu = uu * u;
float t3 = t2 * t;
Vector3 p = uuu * p0;
p += 3 * uu * t * p1;
p += 3 * u * t2 * p2;
p += t3 * p3;
return p;
}
}
Then I have the script I am trying to make that camera move along such a curve:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CamBezier : MonoBehaviour
{
public GameObject[] cam;
public Transform point0, point1, point2, point3;
private float cPoints = 50.0f;
private Vector3[] positions = new Vector3[50];
// Start is called before the first frame update
void Start()
{
for (int i = 1; i < cam.Length - 1; i++)
{
float x = cam[i].transform.position.x;
float y = cam[i].transform.position.y;
float z = cam[i].transform.position.z;
}
DrawCubicCurve();
}
// Update is called once per frame
void Update()
{
DrawCubicCurve();
}
private void DrawCubicCurve()
{
for (int i = 1; i < cam.Length - 1; i++)
{
float t = i / (float)cPoints;
positions[i - 1] = CalculateCubicBezierCurve(t, point0.position, point1.position, point2.position, point3.position);
cam.transform.position = positions;
}
}
private Vector3 CalculateCubicBezierCurve(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
float u = 1 - t;
float t2 = t * t;
float uu = u * u;
float uuu = uu * u;
float t3 = t2 * t;
Vector3 p = uuu * p0;
p += 3 * uu * t * p1;
p += 3 * u * t2 * p2;
p += t3 * p3;
return p;
}
}
Again I CANNOT use any methods provided by unity to implement the Bezier curve. The goal is just to get a camera to move along the Bezier curve. Doing an array of them is extra.

Related

Having trouble drawing Sierpinski's Triangle in Processing

I was trying to draw Sierpinski's Triangle in Processing 3, and managed to get it to run the first two layers. However, when it tries to draw the third and any later layers, it only draws more triangles in some of the triangles.
Here's the code
ArrayList<PVector> initPoints;
int level;
void setup() {
size(400, 400);
noFill();
initPoints = new ArrayList<PVector>();
initPoints.add(new PVector(width/2, height/4));
initPoints.add(new PVector(width/4, 3 * height/4));
initPoints.add(new PVector(3 * width/4, 3 * height/4));
}
void draw() {
triangle(initPoints.get(0).x, initPoints.get(0).y, initPoints.get(1).x, initPoints.get(1).y, initPoints.get(2).x, initPoints.get(2).y);
for (int i = 0; i < 3; i++) {
level = 1;
drawTri(i, initPoints, level);
}
}
PVector findMid(PVector a, PVector b) {
int midX = floor((a.x + b.x)/2);
int midY = floor((a.y + b.y)/2);
return new PVector(midX, midY);
}
void drawTri(int vertex, ArrayList<PVector> basePoints, int layer) {
level = layer + 1;
ArrayList<PVector> points = new ArrayList<PVector>();
points.add(basePoints.get(vertex % 3));
points.add(findMid(basePoints.get(vertex % 3), basePoints.get((vertex + 1) % 3)));
points.add(findMid(basePoints.get(vertex % 3), basePoints.get((vertex + 2) % 3)));
triangle(points.get(0).x, points.get(0).y, points.get(1).x, points.get(1).y, points.get(2).x, points.get(2).y);
if (level < 4) {
for (int i = 0; i < 3; i++) {
drawTri(i, points, level);
}
}
}
Any tips? I think it's something to do with how I'm running the for loops but I'm not sure.
Like I said in my comment, please try to debug your program before you post a question. You need to isolate the problem and understand exactly what the code is doing, and you can do that using print statements and the debugger that comes with the Processing editor.
But just looking at your code, I'm suspicious of the fact that you have a sketch-level level variable as well as a level variable that you're passing into the drawTri() function.
Think about the value of that sketch-level level variable as your code executes. Add print statements to see exactly what it's doing.
If I get rid of the sketch-level level variable, I get this, which I'm guessing is closer to what you wanted:
float x = int(random(0,1024));
float y = int(random(0,600));
float ax=512;
float ay=0;
float bx=0;
float by=600;
float cx=1024;
float cy=600;
void setup()
{
size(1024, 600);
background(0);
}
void nextPoint()
{
int r = int(random(1,7));
float X;
float Y;
if(r==1||r==2)
{
X = lerp(x, ax, 0.5) ;
Y= lerp(y, ay, 0.5);
}
else if(r==3||r==4)
{
X = lerp(x, bx, 0.5) ;
Y= lerp(y, by, 0.5);
}
else
{
X = lerp(x, cx, 0.5) ;
Y= lerp(y, cy, 0.5);
}
x=X;
y=Y;
}
void drawPoint()
{
colorMode(HSB,255,255,255);
stroke(map(y, 0, 15000,100,4000),200,255,10);
strokeWeight(1);
point(ax,ay);
point(bx,by);
point(cx,cy);
point(x, y);
}
void draw()
{
for(int i=0;i<100;++i)
{
drawPoint();
nextPoint();
}
}

Incorrect behavior of image undistortion algorithm

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

Processing PVector rotations

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

How to calculate the coordinates of a arrowhead based on the arrow?

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.

Calculating the position of points in a circle

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]))

Resources