Draw a Circle with a Radius and Points Around the Edge

Draw a circle with a radius and points around the edge

Points on a circle may be specified as a function of the angle θ:

x = a + r cos(θ)
y = b + r sin(θ)

Here, increments of 2π/8 are shown.

Addendum: As suggested in a comment by @Christoffer Hammarström, this revised example reduces the number of magic numbers in the original. The desired number of points becomes a parameter to the constructor. It also adapts the rendering to the container's size.

alt text

/** @see https://stackoverflow.com/questions/2508704 */
public class CircleTest extends JPanel {

private static final int SIZE = 256;
private int a = SIZE / 2;
private int b = a;
private int r = 4 * SIZE / 5;
private int n;

/** @param n the desired number of circles. */
public CircleTest(int n) {
super(true);
this.setPreferredSize(new Dimension(SIZE, SIZE));
this.n = n;
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.black);
a = getWidth() / 2;
b = getHeight() / 2;
int m = Math.min(a, b);
r = 4 * m / 5;
int r2 = Math.abs(m - r) / 2;
g2d.drawOval(a - r, b - r, 2 * r, 2 * r);
g2d.setColor(Color.blue);
for (int i = 0; i < n; i++) {
double t = 2 * Math.PI * i / n;
int x = (int) Math.round(a + r * Math.cos(t));
int y = (int) Math.round(b + r * Math.sin(t));
g2d.fillOval(x - r2, y - r2, 2 * r2, 2 * r2);
}
}

private static void create() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new CircleTest(9));
f.pack();
f.setVisible(true);
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {

@Override
public void run() {
create();
}
});
}
}

JS drawing a line from the edge of a circle to another circle edge

Here's a solution that will achieve what you've asked for.

I've declared 3 'classes' to make things clearer to read. First, I define a generic shape class. Next, I define a basic circle class. Finally, I define a vec2 class. You could easily extend this as I have done, and add other shapes that inherit from the shape class - i.e square triangle, etc.

I create 10 circles at random positions and radii. I then draw a line between each circle and the one following it. I didn't bother with the 'wrap-around' case, so I draw 10 circles and 9 lines (I dont draw from circle 9 to circle 0)

I've used some of the code Tamura left, hence the familiar dimensions and id of the canvas.

<!doctype html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e)}
window.addEventListener('load', onDocLoaded, false);

var shapeList = [];

function onDocLoaded()
{
var i, n=10;
var canvas = byId('myCanvas');

for (i=0; i<n; i++)
{
shapeList[i] = new circle_t(Math.random()*578, Math.random()*400, Math.random()*30 + 20);
shapeList[i].draw(canvas);
}

for (i=0; i<n-1; i++)
draw_line2(shapeList[i].origX, shapeList[i].origY, shapeList[i].radius, shapeList[i+1].origX, shapeList[i+1].origY, shapeList[i+1].radius);
}

var shape_t = function(x,y)
{
this.origX = (x==undefined ? 0 : x);
this.origY = (y==undefined ? 0 : y);
}
shape_t.prototype =
{
origX:0, origY:0, typeString:'shape',
setPos: function(x,y){this.x=x;this.y=y;},
setType: function(typeString){this.typeString = typeString;},
toString: function(){return this.typeString + " - " + this.origX + "," + this.origY;},
draw: function(canElem){},
};

function circle_t(x,y,radius)
{
this.origX = (x==undefined ? 0 : x);
this.origY = (y==undefined ? 0 : y);
this.radius = (radius==undefined ? 10 : radius);
this.setType("circle");
}
circle_t.prototype = new shape_t();
circle_t.prototype.constructor = circle_t;
circle_t.prototype.draw = function(canElem, color)
{
var ctx = canElem.getContext('2d');
var col = 'black';
if (color != undefined)
col = color;
drawCircle(this.origX, this.origY, this.radius, ctx, col);
}

circle_t.prototype.setRadius = function(radius)
{
if (radius != undefined)
this.radius = radius;
}

function drawCircle(x, y, radius, ctx, col)
{
ctx.save();
if (col == undefined)
col = 'black';
ctx.strokeStyle = col;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.arc(x,y,radius,(Math.PI/180)*0, (Math.PI/180)*360, false);
ctx.stroke();
ctx.closePath();
ctx.restore();
}

// define a vec2 class to make vector maths easier (simpler to read)
function vec2(x,y)
{
this.length = function()
{
return Math.sqrt((this.x * this.x) + (this.y*this.y));
}
this.normalize = function()
{
var scale = this.length();
this.x /= scale;
this.y /= scale;
}
this.x = x;
this.y = y;
}

function draw_line2(center1_x, center1_y, radius1, center2_x, center2_y, radius2)
{
var betweenVec = new vec2(center2_x - center1_x, center2_y - center1_y);
betweenVec.normalize();

var p1x = center1_x + (radius1 * betweenVec.x);
var p1y = center1_y + (radius1 * betweenVec.y);

var p2x = center2_x - (radius2 * betweenVec.x);
var p2y = center2_y - (radius2 * betweenVec.y);

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(p1x,p1y);
context.lineTo(p2x,p2y);
context.stroke();
}
</script>
</head>
<body>
<canvas id="myCanvas" width="578" height="400"></canvas>
</body>
</html>

See here for a live demo: http://jsfiddle.net/YYjYL/

How to draw points images on edge of circle of image

Well; drawing a circle is a very straightforward, inside your onDraw() method add this line

canvas.drawCircle(cX, cY, radius, paint); 

Simply provide the center point's x and y values and radius and paint object as well.

And for the pins around the corner you can go like this,
e.g you want a pin at 30 degrees; with a simple trigonometric calculation, your pin's x and y values can be these;

pX = mX + radius * Math.cos(Math.toRadians(30));
pY = mY + radius * Math.sin(Math.toRadians(30));

So you can draw your pin at these x and y values respectively, also the degree can be changed.

Draw points on a circle's edge

Displace points around a circle edge equidistantly

Using HTML Canvas and a bit of trigonometry
Create a reusable circles() function and pass the desired arguments for Number of circles, Size, Radius, Color:

const ctx = document.getElementById("canvas").getContext("2d");
const cvsSize = 400;

ctx.canvas.width = cvsSize;
ctx.canvas.height = cvsSize;

function circles(tot, rad, dist, color) {
const arc = Math.PI * 2 / tot; // Arc in Radians
let ang = 0; // Start at angle 0 (East)

for (let i = 0; i < tot; i++) {
const x = dist * Math.cos(ang) + (cvsSize / 2);
const y = dist * Math.sin(ang) + (cvsSize / 2);
ctx.beginPath();
ctx.arc(x, y, rad, 0, Math.PI * 2, false);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
ang += arc;
}
}

// Circles, Radius, Distance, Color
circles(3, 5, 10, "#f0b");
circles(10, 8, 50, "#0bf");
circles(17, 10, 90, "#bf0");
circles(21, 15, 140, "#b0f");
<canvas id="canvas"></canvas>

Draw a circle based on two points

your radius computations is wrong ... it should be:

double radius = sqrt(((c->p2.x - c->p1.x)*(c->p2.x - c->p1.x))
+((c->p2.y - c->p1.y)*(c->p2.y - c->p1.y)));

drawing a circle of radius R around a point

If you don't want to graph a circle, you can use the set object circle command. You use it like this, for example:

set object X circle at axis 0,0 size scr 0.1 fc rgb "navy"

This will draw a navy blue circle at the origin with a radius of 0.1 of the screen (canvas) size. Note that when you specify a position/radius for the circle you have to specify which coordinate system you are using: first corresponds to the first x-y coordinate system, scr (short for screen) is for screen coordinates. You can learn more by looking in the documentation for drawing circles.

Trying to plot coordinates around the edge of a circle

Assuming that (x0, y0) is the center of your circle, and r is the radius:

var items = 4;
for(var i = 0; i < items; i++) {

var x = x0 + r * Math.cos(2 * Math.PI * i / items);
var y = y0 + r * Math.sin(2 * Math.PI * i / items);
$("#center").append("<div class='point' style='left:"+ x +"px;top:"+ y +"px'></div>");

}

Draw A line around the circle based on angle

Although there have been some hiccups in the question, and the code provided so far looks questionable, the core of the question as it stands now is quite interesting...

There are different options for solving this. From the images that you provided so far, it looks like the circles always have the same size, which makes things far simpler. For circles with different sizes, you'd really have to compute the tangents of the circles, in the desired direction, mutually considering the radius of the other circle. Of course, this is possible, but a bit less trivial.

For the case that you have equally-sized circles, you can

  • Compute the difference of the centers of two circles
  • Divide this by the distance, to obtain the (normalized) direction
  • Rotate this direction by 90°
  • Scale the rotated direction vector by the radius
  • Add the scaled and rotated vector to the circle center

This will yield one endpoint of such a line. The rotation about 90° can be done once in clockwise and once in counterclockwise direction, to obtain the "upper" and "lower" endpoint for the line, respectively.

LinesAtCircle

Image was updated with the EDIT, see below

The actual computation is done in the computeLine method of the MCVE below. Note that this example uses the "simple" approach, although it uses circles of slightly different sizes. The effect is that, when the difference between the sizes of two circles is too large (compared to the distance between the circles, basically), then the lines may slightly intersect the circles. But the solution should be a reasonable trade-off between simplicity and general applicability. Particularly, for equally-sized circles, there will be no intersections at all.

Code was updated with the EDIT, see below

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class LinesAtCirclesTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{

@Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel linesAtCirclesTestPanel = new LinesAtCirclesTestPanel();
f.getContentPane().add(linesAtCirclesTestPanel);
f.setSize(400,400);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}

class LinesAtCirclesTestPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Point2D draggedCenter;
private List<Point2D> centers = new ArrayList<Point2D>();
private List<Double> radii = new ArrayList<Double>();

public LinesAtCirclesTestPanel()
{
addMouseListener(this);
addMouseMotionListener(this);

addCircle(100, 100, 30);
addCircle(200, 300, 50);
addCircle(300, 200, 40);
}

private void addCircle(double x, double y, double radius)
{
centers.add(new Point2D.Double(x,y));
radii.add(radius);
}

@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);

for (int i=0; i<centers.size(); i++)
{
Point2D center0 = centers.get(i);
double radius0 = radii.get(i);

Shape ellipse = new Ellipse2D.Double(
center0.getX() - radius0, center0.getY() - radius0,
radius0 + radius0, radius0 + radius0);

g.setColor(Color.LIGHT_GRAY);
g.fill(ellipse);
g.setColor(Color.BLACK);
g.draw(ellipse);
}

g.setColor(Color.RED);
for (int i=0; i<centers.size() - 1; i++)
{
Point2D center0 = centers.get(i);
double radius0 = radii.get(i);
Point2D center1 = centers.get(i+1);
double radius1 = radii.get(i+1);

g.draw(createArrow(computeLine(center0, radius0, center1, radius1, true)));
g.draw(createArrow(computeLine(center0, radius0, center1, radius1, false)));
}
}

private static Shape createArrow(Line2D line)
{
double dx = line.getX2() - line.getX1();
double dy = line.getY2() - line.getY1();
double angleToX = Math.atan2(dy, dx);
final double angleRad = Math.toRadians(30);
final double headLength = 20.0f;
double dxL = Math.cos(Math.PI + angleToX + angleRad) * headLength;
double dyL = Math.sin(Math.PI + angleToX + angleRad) * headLength;
double dxR = Math.cos(Math.PI + angleToX - angleRad) * headLength;
double dyR = Math.sin(Math.PI + angleToX - angleRad) * headLength;
Path2D arrow = new Path2D.Double();
arrow.moveTo(line.getX1(), line.getY1());
arrow.lineTo(line.getX2(), line.getY2());
arrow.lineTo(line.getX2() + dxL, line.getY2() + dyL);
arrow.moveTo(line.getX2(), line.getY2());
arrow.lineTo(line.getX2() + dxR, line.getY2() + dyR);
return arrow;
}

private static Line2D computeLine(
Point2D center0, double radius0,
Point2D center1, double radius1,
boolean upper)
{
double dx = center1.getX() - center0.getX();
double dy = center1.getY() - center0.getY();
double invLength = 1.0 / Math.hypot(dx, dy);
double dirX = dx * invLength;
double dirY = dy * invLength;

double rotDirX = dirY;
double rotDirY = -dirX;
if (upper)
{
rotDirX = -dirY;
rotDirY = dirX;
}

double x0 = center0.getX() + rotDirX * radius0;
double y0 = center0.getY() + rotDirY * radius0;

double x1 = center1.getX() + rotDirX * radius1;
double y1 = center1.getY() + rotDirY * radius1;

if (upper)
{
return new Line2D.Double(x1, y1, x0, y0);
}
return new Line2D.Double(x0, y0, x1, y1);
}

@Override
public void mousePressed(MouseEvent e)
{
draggedCenter = null;
for (int i=0; i<centers.size(); i++)
{
Point2D center = centers.get(i);
double radius = radii.get(i);
if (e.getPoint().distance(center) < radius)
{
draggedCenter = center;
}
}
}

@Override
public void mouseReleased(MouseEvent e)
{
draggedCenter = null;
}

@Override
public void mouseDragged(MouseEvent e)
{
if (draggedCenter == null)
{
return;
}
draggedCenter.setLocation(e.getPoint());
repaint();
}

@Override
public void mouseMoved(MouseEvent e)
{
// Not used
}

@Override
public void mouseClicked(MouseEvent e)
{
// Not used
}

@Override
public void mouseEntered(MouseEvent e)
{
// Not used
}

@Override
public void mouseExited(MouseEvent e)
{
// Not used
}
}

EDIT in response to the comment:

The original code computed Line2D objects. Creating an arrow from a line is, in the simplest case, basically done with a bit of trigonometry, and many resources exist for this on the web.

In response to the comment, I extended the example to show simple arrows, as depicted in the above image.


However, when taking a closer look at this, one may notice several degrees of freedom for such an arrow:

  • Should the head length be absolute or relative to the arrow?
  • Should the head width be absolute or relative to the arrow?
  • (Or: What should be the angle of the arrow head?)
  • Should the arrow head be filled, or consist of lines?
  • Should the "trunk" of the arrow be a single line, or an outline shape?
  • What should be the width of the trunk?
  • ...

In order to cover some of these degrees of freedom, I created an ArrowCreator class a while ago, and there's also a sample showing how it may be used.

drawing a circle around many points with different radius

It looks like you may want to use the with circles option. If you have a data file with three columns (x y radius), the following command will plot circles with radii from the file at each point:

 plot 'datafile' u 1:2:3 with circles

http://gnuplot.sourceforge.net/demo/circles.html



Related Topics



Leave a reply



Submit