Midpoint Ellipse Algorithm in Java - java

I am trying to write a program to allow a user to draw an ellipse by using clicks. The user left-clicks at first to select the radius, then right-clicks to select the horizontal radius, then right-clicks again to select the vertical radius. Nothing is drawn after clicking. I don't understand where the error is.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Ellipse extends JPanel implements MouseListener{
Graphics P;
public Ellipse()
{
addMouseListener(this);
}
static int Radius = 0;
int CenterX, CenterY, RadiusX, RadiusY;
public void paintComponent(Graphics g)
{
P=g;
EllipseMidpoint(CenterX, CenterY, RadiusX, RadiusY);
}
public void EllipseMidpoint(int Cx, int Cy, int Rx, int Ry)
{
int Rx2 = Rx * Rx;
int Ry2 = Ry * Ry;
int twoRx2 = 2 * Rx2;
int twoRy2 = 2 * Ry2;
int x = 0;
int y = Ry;
int p;
int px= 0;
int py = twoRx2 * y;
PlotEllipsePoint(Cx, Cy, x, y);
//Region 1
p = (int)(Ry2 - (Rx2 * Ry) + (0.25 + Rx2));
while (px < py)
{
x = x + 1;
px = twoRy2 + px;
if (p < 0)
{
p = Ry2 + px + p;
}
else
{
y = y - 1;
py = twoRx2 - py;
p = Ry2 + px - py + p;
}
PlotEllipsePoint(Cx, Cy, x, y);
}
//Region2
p = (int)(Ry2 * (x + 0.5) * (x + 0.5) + Rx2 * (y - 1) * (y - 1) - Rx2 * Ry2);
while (y > 0)
{
y = y - 1;
py = twoRx2 - py;
if (p > 0)
{
p = Rx2 - py + p;
}
else
{
x = x + 1;
px = twoRy2 + px;
p = Rx2 + px - py + p;
}
PlotEllipsePoint(Cx, Cy, x, y);
}
}
public void PlotEllipsePoint(int CX, int CY, int X, int Y)
{
drawPixel(CX + X, CY + Y);
drawPixel(CX - X, CY + Y);
drawPixel(CX + X, CY - Y);
drawPixel(CX - X, CY - Y);
}
public void drawPixel(int x, int y)
{
P.fillOval(x, y, 5, 5);
}
public void mousePressed(MouseEvent e)
{
if (e.getButton() == MouseEvent.BUTTON1)
{
CenterX = e.getX();
CenterY = e.getY();
}
else if (e.getButton() == MouseEvent.BUTTON3)
{
Radius = Radius + 1;
if (Radius == 1)
{
RadiusX = (int) Math.pow((Math.pow((e.getX() - CenterX), 2) + Math.pow((e.getY() - CenterY), 2)), 0.5);
}
else if (Radius == 2)
{
RadiusY = (int) Math.pow((Math.pow((e.getX() - CenterX), 2) + Math.pow((e.getY() - CenterY), 2)), 0.5);
}
PlotEllipsePoint(CenterX, CenterY, RadiusX, RadiusY);
}
}
public static void main(String[] args)
{
JFrame JF = new JFrame("Ellipse");
JF.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JF.setSize(500,500);
Ellipse E = new Ellipse();
JF.getContentPane().add(E);
JF.setVisible(true);
}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}

Three things immediately jump out...
You should be calling super.paintComponent before doing any custom painting (and there is no need for paintComponent to be public), see Performing Custom Painting and Painting in AWT and Swing for more details
NEVER, EVER maintain a reference to the graphics context, you want something painted, you make a request to the repaint manager and you wait until one of your paint methods is called. Painting is controlled by the repaint manager an repaints may occur at any time, most of the time without yout knowledge or interaction.
You never call repaint to ask the repaint manager to repaint your component...At the end of your mousePressed method, call repaint()...

Related

Java - Objects follow each other, when given diffrent tasks

I have been working on this java application.
So far it has no meaning, just a randomly colored ball bouncing around.
But now, when i wanted to add another ball to the bouncing app, the balls followed each other.
This is my code so far.
import java.awt.*;
import javax.swing.*;
public class MainFrame extends JPanel implements Runnable {
Color color = Color.red;
int dia = 60;
Diameter of the objects.
long delay = 20;
Delay time.
private int x = (int)Math.floor(Math.random() * 580);
private int y = (int)Math.floor(Math.random() * 900);
private int xx = (int)Math.floor(Math.random() * 580);
private int yy = (int)Math.floor(Math.random() * 900);
Above is the objects position.
private int dx = (int)Math.floor(Math.random() * 7);
private int dy = (int)Math.floor(Math.random() * 7);
private int dxx = (int)Math.floor(Math.random() * 7);
private int dyy = (int)Math.floor(Math.random() * 7);
Above is object speed.
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.fillOval(x,y,60,60);
g.setColor(color);
g.fillOval(xx,yy,60,60);
}
The graphics.
And below is just calculations, thread.sleep and the JFrame.
public void run() {
while(isVisible()) {
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
System.out.println("interrupted");
}
move();
repaint();
}
}
public void move() {
if(x + dx < 0 || x + dia + dx > getWidth()) {
dx *= -1;
color = getColor();
}
if(y + dy < 0 || y + dia + dy > getHeight()) {
dy *= -1;
color = getColor();
}
if(xx + dxx < 0 || xx + dia + dxx > getWidth()) {
dxx *= -1;
color = getColor();
}
if(yy + dyy < 0 || yy + dia + dyy > getHeight()) {
dyy *= -1;
color = getColor();
}
x += dx;
y += dy;
xx += dx;
yy += dy;
}
private Color getColor() {
int rval = (int)Math.floor(Math.random() * 256);
int gval = (int)Math.floor(Math.random() * 256);
int bval = (int)Math.floor(Math.random() * 256);
return new Color(rval, gval, bval);
}
private void start() {
while(!isVisible()) {
try {
Thread.sleep(25);
} catch(InterruptedException e) {
System.exit(1);
}
}
Thread thread = new Thread(this);
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
public static void main(String[] args) {
MainFrame test = new MainFrame();
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(test);
f.setSize(640, 960);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
f.setLocation(dim.width/2-f.getSize().width/2, dim.height/2-f.getSize().height/2);
f.setVisible(true);
test.start();
}
}
I just can not figure it out.
I know the answer is going to be simple.
You should define a single class Ball and create two instances of it rather than repeating the variables and have an x,y co-ordinate and velocity dx and dy inside that class.
The two follow each other ebcause you add the same velocity to both balls all the time:
x += dx;
y += dy;
xx += dx;
yy += dy;

Java Animations

I've started to take interest with making animations(slideshows, backgrounds etc) in Java. I know that JavaFX is much better for doing this, but I'm just to stubborn to bother switching over.
Here is what I got so far.
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BlurredLightCells extends JPanel {
private static final long serialVersionUID = 4610174943257637060L;
private Random random = new Random();
private ArrayList<LightCell> lightcells;
private float[] blurData = new float[500];
public static void main(String[] args) {
JFrame frame = new JFrame("Swing animated bubbles");
frame.setSize(1000, 750);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BlurredLightCells(60));
frame.setVisible(true);
}
public BlurredLightCells(int amtOfBCells) {
setSize(1000, 750);
/**
* Below we initiate all the cells that are going to be drawn on screen
*/
Arrays.fill(blurData, 1f / 20f);
lightcells = new ArrayList<LightCell>(amtOfBCells);
for (int i = 0; i < amtOfBCells; i++) {
/**
* Below we generate all the values for each cell(SHOULD be random for each one)
*/
int baseSpeed = random(0, 3);
int xSpeed = (int) Math.floor((Math.random() * (baseSpeed - -baseSpeed + baseSpeed)) + -baseSpeed);
int ySpeed = (int) Math.round((Math.random() * baseSpeed) + 0.5);
int radius = random(25, 100);
int x = (int) Math.floor(Math.random() * getWidth());
int y = (int) Math.floor(Math.random() * getHeight());
int blurrAmount = (int) (Math.floor(Math.random() * 10) + 5);
int alpha = (int) ((Math.random() * 15) + 3);
/**
* Now we draw a image, and apply transparency and a slight blur to it
*/
Kernel kernel = new Kernel(blurrAmount, blurrAmount, blurData);
BufferedImageOp op = new ConvolveOp(kernel);
BufferedImage circle = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);
Graphics2D circlegfx = circle.createGraphics();
circlegfx.setColor(new Color(255, 255, 255, alpha));
circlegfx.fillOval(20, 20, radius, radius);
circle = op.filter(circle, null);
LightCell bubble = new LightCell(x, y, xSpeed, ySpeed, radius, getDirection(random.nextInt(3)), circle);
lightcells.add(bubble);
}
}
public int random(int min, int max) {
final int n = Math.abs(max - min);
return Math.min(min, max) + (n == 0 ? 0 : random.nextInt(n));
}
#Override
public void paint(Graphics g) {
int w = getWidth();
int h = getHeight();
final Graphics2D g2 = (Graphics2D) g;
GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
g2.setPaint(gp);
g2.fillRect(0, 0, w, h);
long start = System.currentTimeMillis();
for (int i = 0; i < lightcells.size(); i++) {
LightCell cell = lightcells.get(i);
cell.process(g2);
}
System.out.println("Took " + (System.currentTimeMillis() - start) + " milliseconds to draw ALL cells.");
repaint();
}
public String getDirection(int i) {
switch (i) {
case 0:
return "right";
case 1:
return "left";
case 2:
return "up";
case 3:
return "down";
}
return "";
}
private class LightCell {
private int x, y, xSpeed, ySpeed, radius;
private String direction;
private BufferedImage image;
public LightCell(int x, int y, int xSpeed, int ySpeed, int radius, String direction, BufferedImage image) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.radius = radius;
this.direction = direction;
this.image = image;
}
public void process(Graphics g) {
switch (direction) {
case "right":
moveRight();
break;
case "left":
moveLeft();
break;
case "up":
moveUp();
break;
case "down":
moveDown();
break;
}
g.drawImage(image, x, y, null);
}
private void moveUp() {
x += xSpeed;
y -= ySpeed;
if (y + (radius / 2) < 0) {
y = getHeight() + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
y = radius + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveDown() {
x += xSpeed;
y += ySpeed;
if (y - (radius / 2) > getHeight()) {
y = 0 - (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
y = getHeight() + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveRight() {
x += ySpeed;
y += xSpeed;
if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
x = 0 - (radius / 2);
y = (int) Math.floor(Math.random() * getHeight());
}
if ((x - radius / 2) > getWidth()) {
x = 0 - (radius / 2);
y = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveLeft() {
x -= ySpeed;
y -= xSpeed;
if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
x = getWidth() + (radius / 2);
y = (int) Math.floor(Math.random() * getHeight());
}
if ((x + radius / 2) < 0) {
x = getWidth() + (radius / 2);
y = (int) Math.floor(Math.random() * getWidth());
}
}
}
}
If you run that, you will see the cells move at a very high speed, and if you look through the code, you see I call repaint() in the paint method in which I override. I know thats not good to do. But my question is, is their any other way in which I could draw each cell outside of the repaint() loop I have right now, because that causes other components to flash/flicker when I use this in a JFrame with other components.
FYI: Aventually I'd like to achieve something similar to this: Click Here
Thanks!
The issue of flicker is to do with the fact that top level containers are not double buffered. Instead of extending from JFrame (or other top level containers), you should consider using something more like JPanel and override it's paintComponent.
nb- Had it in my head that the OP was extending from JFrame...
Two issues could be causing the flickering. The first is overriding paint, the second is not calling super.paint(g) and the time between the updates. A better solution would be to override paintComponent and make sure you are calling super.paintComponent. Also using something like a javax.swing.Timer to schedule updates and regular intervals would also help...
Only call repaint when you want to encourage the RepaintManager to update you component. Don't call repaint from within any paintXxx method, this will cause a never ending loop of paint requests to schedule onto the event queue, eventually consuiming your CPU
I would avoid doing anything in your paintXxx methods that might take time to perform, this will slow down the rendering process. Instead, I would use a javax.swing.Timer for simple updates or for more complicated processing, a Thread which could be used to update the model before it is rendered to the screen.
This is an example of some simple optimisation process I did to take animation of 500 objects to 4500 with only a slight degration in the overall performance.
Updated
I changed you code slight and it works fine...
I changed your paint method to paintComponent and added super.paintComponent(g)
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
final Graphics2D g2 = (Graphics2D) g;
GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
g2.setPaint(gp);
g2.fillRect(0, 0, w, h);
for (int i = 0; i < lightcells.size(); i++) {
LightCell cell = lightcells.get(i);
cell.process(g2);
}
}
And at the end of your constructor I added...
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
To update the UI on a regular bases...

Java Draw Arc Between 2 Points

I'm having trouble drawing the smallest arc described by 3 points: the arc center, an "anchored" end point, and a second point that gives the other end of the arc by determining a radius. I used the law of cosines to determine the length of the arc and tried using atan for the starting degree, but the starting position for the arc is off.
I managed to get the arc to lock onto the anchor point (x1,y1) when it's in Quadrant 2, but that will only work when it is in Quadrant 2.
Solutions I can see all have a bunch of if-statements to determine the location of the 2 points relative to each other, but I'm curious if I'm overlooking something simple. Any help would be greatly appreciated.
SSCCE:
import javax.swing.JComponent;
import javax.swing.JFrame;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.*;
import java.awt.*;
import java.util.*;
class Canvas extends JComponent {
float circleX, circleY, x1, y1, x2, y2, dx, dy, dx2, dy2, radius, radius2;
Random random = new Random();
public Canvas() {
//Setup.
x1 = random.nextInt(250);
y1 = random.nextInt(250);
//Cant have x2 == circleX
while (x1 == 150 || y1 == 150)
{
x1 = random.nextInt(250);
y1 = random.nextInt(250);
}
circleX = 150; //circle center is always dead center.
circleY = 150;
//Radius between the 2 points must be equal.
dx = Math.abs(circleX-x1);
dy = Math.abs(circleY-y1);
//c^2 = a^2 + b^2 to solve for the radius
radius = (float) Math.sqrt((float)Math.pow(dx, 2) + (float)Math.pow(dy, 2));
//2nd random point
x2 = random.nextInt(250);
y2 = random.nextInt(250);
//I need to push it out to radius length, because the radius is equal for both points.
dx2 = Math.abs(circleX-x2);
dy2 = Math.abs(circleY-y2);
radius2 = (float) Math.sqrt((float)Math.pow(dx2, 2) + (float)Math.pow(dy2, 2));
dx2 *= radius/radius2;
dy2 *= radius/radius2;
y2 = circleY+dy2;
x2 = circleX+dx2;
//Radius now equal for both points.
}
public void paintComponent(Graphics g2) {
Graphics2D g = (Graphics2D) g2;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL));
Arc2D.Float centerPoint = new Arc2D.Float(150-2,150-2,4,4, 0, 360, Arc2D.OPEN);
Arc2D.Float point1 = new Arc2D.Float(x1-2, y1-2, 4, 4, 0, 360, Arc2D.OPEN);
Arc2D.Float point2 = new Arc2D.Float(x2-2, y2-2, 4, 4, 0, 360, Arc2D.OPEN);
//3 points drawn in black
g.setColor(Color.BLACK);
g.draw(centerPoint);
g.draw(point1);
g.draw(point2);
float start = 0;
float distance;
//Form a right triangle to find the length of the hypotenuse.
distance = (float) Math.sqrt(Math.pow(Math.abs(x2-x1),2) + Math.pow(Math.abs(y2-y1), 2));
//Law of cosines to determine the internal angle between the 2 points.
distance = (float) (Math.acos(((radius*radius) + (radius*radius) - (distance*distance)) / (2*radius*radius)) * 180/Math.PI);
float deltaY = circleY - y1;
float deltaX = circleX - x1;
float deltaY2 = circleY - y2;
float deltaX2 = circleX - x2;
float angleInDegrees = (float) ((float) Math.atan((float) (deltaY / deltaX)) * 180 / Math.PI);
float angleInDegrees2 = (float) ((float) Math.atan((float) (deltaY2 / deltaX2)) * 180 / Math.PI);
start = angleInDegrees;
//Q2 works.
if (x1 < circleX)
{
if (y1 < circleY)
{
start*=-1;
start+=180;
} else if (y2 > circleX) {
start+=180;
start+=distance;
}
}
//System.out.println("Start: " + start);
//Arc drawn in blue
g.setColor(Color.BLUE);
Arc2D.Float arc = new Arc2D.Float(circleX-radius, //Center x
circleY-radius, //Center y Rotates around this point.
radius*2,
radius*2,
start, //start degree
distance, //distance to travel
Arc2D.OPEN); //Type of arc.
g.draw(arc);
}
}
public class Angle implements MouseListener {
Canvas view;
JFrame window;
public Angle() {
window = new JFrame();
view = new Canvas();
view.addMouseListener(this);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(30, 30, 400, 400);
window.getContentPane().add(view);
window.setVisible(true);
}
public static void main(String[] a) {
new Angle();
}
#Override
public void mouseClicked(MouseEvent arg0) {
window.getContentPane().remove(view);
view = new Canvas();
window.getContentPane().add(view);
view.addMouseListener(this);
view.revalidate();
view.repaint();
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
Perhaps this will help. It tests with click and drag to set the two points rather than random numbers. It's considerably simpler than what you were attempting and other solutions posted so far.
Notes:
Math.atan2() is a friend in problems like this.
Little helper functions make it easier to reason about your code.
It's best practice to use instance variables for independent values only and compute the dependent values in local variables.
My code fixes some Swing usage problems like calling Swing functions from the main thread.
Code follows:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
class TestCanvas extends JComponent {
float x0 = 150f, y0 = 150f; // Arc center. Subscript 0 used for center throughout.
float xa = 200f, ya = 150f; // Arc anchor point. Subscript a for anchor.
float xd = 150f, yd = 50f; // Point determining arc angle. Subscript d for determiner.
// Return the distance from any point to the arc center.
float dist0(float x, float y) {
return (float)Math.sqrt(sqr(x - x0) + sqr(y - y0));
}
// Return polar angle of any point relative to arc center.
float angle0(float x, float y) {
return (float)Math.toDegrees(Math.atan2(y0 - y, x - x0));
}
#Override
protected void paintComponent(Graphics g0) {
Graphics2D g = (Graphics2D) g0;
// Can always draw the center point.
dot(g, x0, y0);
// Get radii of anchor and det point.
float ra = dist0(xa, ya);
float rd = dist0(xd, yd);
// If either is zero there's nothing else to draw.
if (ra == 0 || rd == 0) { return; }
// Get the angles from center to points.
float aa = angle0(xa, ya);
float ad = angle0(xd, yd); // (xb, yb) would work fine, too.
// Draw the arc and other dots.
g.draw(new Arc2D.Float(x0 - ra, y0 - ra, // box upper left
2 * ra, 2 * ra, // box width and height
aa, angleDiff(aa, ad), // angle start, extent
Arc2D.OPEN));
dot(g, xa, ya);
// Use similar triangles to get the second dot location.
float xb = x0 + (xd - x0) * ra / rd;
float yb = y0 + (yd - y0) * ra / rd;
dot(g, xb, yb);
}
// Some helper functions.
// Draw a small dot with the current color.
static void dot(Graphics2D g, float x, float y) {
final int rad = 2;
g.fill(new Ellipse2D.Float(x - rad, y - rad, 2 * rad, 2 * rad));
}
// Return the square of a float.
static float sqr(float x) { return x * x; }
// Find the angular difference between a and b, -180 <= diff < 180.
static float angleDiff(float a, float b) {
float d = b - a;
while (d >= 180f) { d -= 360f; }
while (d < -180f) { d += 360f; }
return d;
}
// Construct a test canvas with mouse handling.
TestCanvas() {
addMouseListener(mouseListener);
addMouseMotionListener(mouseListener);
}
// Listener changes arc parameters with click and drag.
MouseInputAdapter mouseListener = new MouseInputAdapter() {
boolean mouseDown = false; // Is left mouse button down?
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
mouseDown = true;
xa = xd = e.getX();
ya = yd = e.getY();
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
mouseDown = false;
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (mouseDown) {
xd = e.getX();
yd = e.getY();
repaint();
}
}
};
}
public class Test extends JFrame {
public Test() {
setSize(400, 400);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().add(new TestCanvas());
}
public static void main(String[] args) {
// Swing code must run in the UI thread, so
// must invoke setVisible rather than just calling it.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test().setVisible(true);
}
});
}
}
package curve;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
public class Main
{
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException
{
PointF pFrom = new PointF(-10f, 30.0f);
PointF pTo = new PointF(-100f, 0.0f);
List<PointF> points = generateCurve(pFrom, pTo, 100f, 7f, true, true);
System.out.println(points);
// Calculate the bounds of the curve
Rectangle2D.Float bounds = new Rectangle2D.Float(points.get(0).x, points.get(0).y, 0, 0);
for (int i = 1; i < points.size(); ++i) {
bounds.add(points.get(i).x, points.get(i).y);
}
bounds.add(pFrom.x, pFrom.y);
bounds.add(pTo.x, pTo.y);
BufferedImage img = new BufferedImage((int) (bounds.width - bounds.x + 50), (int) (bounds.height - bounds.y + 50), BufferedImage.TYPE_4BYTE_ABGR_PRE);
Graphics2D g = img.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.translate(25.0f - bounds.getX(), 25.0f - bounds.getY());
g.setStroke(new BasicStroke(1.0f));
g.setColor(Color.DARK_GRAY);
g.drawLine(-1000, 0, 1000, 0);
g.drawLine(0, -1000, 0, 1000);
g.setColor(Color.RED);
for (int i = 0; i < points.size(); ++i) {
if (i > 0) {
Line2D.Float f = new Line2D.Float(points.get(i - 1).x, points.get(i - 1).y, points.get(i).x, points.get(i).y);
System.out.println("Dist : " + f.getP1().distance(f.getP2()));
// g.draw(f);
}
g.fill(new Ellipse2D.Float(points.get(i).x - 0.8f, points.get(i).y - 0.8f, 1.6f, 1.6f));
}
g.setColor(Color.BLUE);
g.fill(new Ellipse2D.Float(pFrom.x - 1, pFrom.y - 1, 3, 3));
g.fill(new Ellipse2D.Float(pTo.x - 1, pTo.y - 1, 3, 3));
g.dispose();
ImageIO.write(img, "PNG", new File("result.png"));
}
static class PointF
{
public float x, y;
public PointF(float x, float y)
{
this.x = x;
this.y = y;
}
#Override
public String toString()
{
return "(" + x + "," + y + ")";
}
}
private static List<PointF> generateCurve(PointF pFrom, PointF pTo, float pRadius, float pMinDistance, boolean shortest, boolean side)
{
List<PointF> pOutPut = new ArrayList<PointF>();
// Calculate the middle of the two given points.
PointF mPoint = new PointF(pFrom.x + pTo.x, pFrom.y + pTo.y);
mPoint.x /= 2.0f;
mPoint.y /= 2.0f;
System.out.println("Middle Between From and To = " + mPoint);
// Calculate the distance between the two points
float xDiff = pTo.x - pFrom.x;
float yDiff = pTo.y - pFrom.y;
float distance = (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff);
System.out.println("Distance between From and To = " + distance);
if (pRadius * 2.0f < distance) {
throw new IllegalArgumentException("The radius is too small! The given points wont fall on the circle.");
}
// Calculate the middle of the expected curve.
float factor = (float) Math.sqrt((pRadius * pRadius) / ((pTo.x - pFrom.x) * (pTo.x - pFrom.x) + (pTo.y - pFrom.y) * (pTo.y - pFrom.y)) - 0.25f);
PointF circleMiddlePoint = new PointF(0, 0);
if (side) {
circleMiddlePoint.x = 0.5f * (pFrom.x + pTo.x) + factor * (pTo.y - pFrom.y);
circleMiddlePoint.y = 0.5f * (pFrom.y + pTo.y) + factor * (pFrom.x - pTo.x);
} else {
circleMiddlePoint.x = 0.5f * (pFrom.x + pTo.x) - factor * (pTo.y - pFrom.y);
circleMiddlePoint.y = 0.5f * (pFrom.y + pTo.y) - factor * (pFrom.x - pTo.x);
}
System.out.println("Middle = " + circleMiddlePoint);
// Calculate the two reference angles
float angle1 = (float) Math.atan2(pFrom.y - circleMiddlePoint.y, pFrom.x - circleMiddlePoint.x);
float angle2 = (float) Math.atan2(pTo.y - circleMiddlePoint.y, pTo.x - circleMiddlePoint.x);
// Calculate the step.
float step = pMinDistance / pRadius;
System.out.println("Step = " + step);
// Swap them if needed
if (angle1 > angle2) {
float temp = angle1;
angle1 = angle2;
angle2 = temp;
}
boolean flipped = false;
if (!shortest) {
if (angle2 - angle1 < Math.PI) {
float temp = angle1;
angle1 = angle2;
angle2 = temp;
angle2 += Math.PI * 2.0f;
flipped = true;
}
}
for (float f = angle1; f < angle2; f += step) {
PointF p = new PointF((float) Math.cos(f) * pRadius + circleMiddlePoint.x, (float) Math.sin(f) * pRadius + circleMiddlePoint.y);
pOutPut.add(p);
}
if (flipped ^ side) {
pOutPut.add(pFrom);
} else {
pOutPut.add(pTo);
}
return pOutPut;
}
}
and the use the generateCurve method like this to have a curve between the from and to points..
generateCurve(pFrom, pTo, 100f, 7f, true, false);
Okay, here it is, testing and working. The problems were based on the fact that I don't use graphics much, so I have to remind myself that the coordinate systems are backward, and on the fact that the Javadoc description of the Arc2D constructor is atrocious.
In addition to these, I found that your point creation (for the two points to be connected) was extremely inefficient given the requirements. I had assumed you actually had to receive two arbitrary points and then calculate their angles, etc., but based on what you put on Pastebin, we can define the two points however we please. This benefits us.
Anyway, here's a working version, with none of that gobbledegook from before. Simplified code is simplified:
import javax.swing.JComponent;
import java.awt.geom.*;
import java.awt.*;
import java.util.*;
public class Canvas extends JComponent {
double circleX, circleY, x1, y1, x2, y2, dx, dy, dx2, dy2, radius, radius2;
Random random = new Random();
double distance;
private static double theta1;
private static double theta2;
private static double theta;
// private static double radius;
private Point2D point1;
private Point2D point2;
private Point2D center;
private static int direction;
private static final int CW = -1;
private static final int CCW = 1;
public Canvas() {
/*
* You want two random points on a circle, so let's start correctly,
* by setting a random *radius*, and then two random *angles*.
*
* This has the added benefit of giving us the angles without having to calculate them
*/
radius = random.nextInt(175); //your maximum radius is higher, but we only have 200 pixels in each cardinal direction
theta1 = random.nextInt(360); //angle to first point (absolute measurement)
theta2 = random.nextInt(360); //angle to second point
//build the points
center = new Point2D.Double(200, 200); //your frame is actually 400 pixels on a side
point1 = new Point2D.Double(radius * Math.cos(toRadians(theta1)) + center.getX(), center.getY() - radius * Math.sin(toRadians(theta1)));
point2 = new Point2D.Double(radius * Math.cos(toRadians(theta2)) + center.getX(), center.getY() - radius * Math.sin(toRadians(theta2)));
theta = Math.abs(theta1 - theta2) <= 180 ? Math.abs(theta1 - theta2) : 360 - (Math.abs(theta1 - theta2));
if ((theta1 + theta) % 360 == theta2) {
direction = CCW;
} else {
direction = CW;
}
System.out.println("theta1: " + theta1 + "; theta2: " + theta2 + "; theta: " + theta + "; direction: " + (direction == CCW ? "CCW" : "CW"));
System.out.println("point1: (" + (point1.getX() - center.getX()) + ", " + (center.getY() - point1.getY()) + ")");
System.out.println("point2: (" + (point2.getX() - center.getX()) + ", " + (center.getY() - point2.getY()) + ")");
// Radius now equal for both points.
}
public double toRadians(double angle) {
return angle * Math.PI / 180;
}
public double toDegrees(double angle) {
return angle * 180 / Math.PI;
}
public void paintComponent(Graphics g2) {
Graphics2D g = (Graphics2D) g2;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setStroke(new BasicStroke(2.0f, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL));
//centerpoint should be based on the actual center point
Arc2D.Double centerPoint = new Arc2D.Double(center.getX() - 2, center.getY() - 2, 4, 4, 0,
360, Arc2D.OPEN);
//likewise these points
Arc2D.Double point11 = new Arc2D.Double(point1.getX() - 2, point1.getY() - 2, 4, 4, 0, 360,
Arc2D.OPEN);
Arc2D.Double point22 = new Arc2D.Double(point2.getX() - 2, point2.getY() - 2, 4, 4, 0, 360,
Arc2D.OPEN);
// 3 points drawn in black
g.setColor(Color.BLACK);
g.draw(centerPoint);
g.draw(point11);
g.draw(point22);
// Arc drawn in blue
g.setColor(Color.BLUE);
g.draw(new Arc2D.Double(center.getX() - radius, center.getY() - radius, 2 * radius, 2 * radius, theta1, theta * direction, Arc2D.OPEN));
}
}

Java double-recursive call.

I have an assignment that requires us to paint a tree of Pythagoras using recursion. The tree is started with the square ABCD and the points A and B are defined by mouse clicks. Everything seems to work until I get to the recursion where I can get either the left or right part of the tree to paint, but not both. I placed a comment where I believe I am running into problems.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class PythagorasTree extends JFrame
{
public static void main(String[] args)
{
new PythagorasTree();
}
PythagorasTree()
{
super("Pythagoras Tree");
setSize(800,800);
add("Center", new DrawingPanel());
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
class DrawingPanel extends JPanel
{
Random random = new Random();
int centerX;
int centerY;
int clickCount = 0;
float pixelSize;
float rWidth = 10.0F;
float rHeight = 7.5F;
float red, green, blue;
Point a = new Point();
Point b = new Point();
Point c = new Point();
Point d = new Point();
Point e = new Point();
Point u = new Point();
DrawingPanel()
{
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent click)
{
clickCount++;
if (clickCount == 1)
{
a.x = fx(click.getX());
a.y = fy(click.getY());
repaint();
}
if (clickCount == 2)
{
b.x = fx(click.getX());
b.y = fy(click.getY());
repaint();
}
}
});
}
void initgr()
{
Dimension d = getSize();
int maxX = d.width - 1;
int maxY = d.height - 1;
pixelSize = Math.max(rWidth/maxX, rHeight/maxY);
centerX = maxX/2;
centerY = maxY/2;
}
int iX(float x){return Math.round(centerX + x/pixelSize);}
int iY(float y){return Math.round(centerY - y/pixelSize);}
float fx(int x){return (x - centerX) * pixelSize;}
float fy(int y){return (centerY - y) * pixelSize;}
public void paintComponent(Graphics g)
{
initgr();
super.paintComponent(g);
setBackground(Color.white);
g.setColor(Color.red);
if (clickCount == 1)
g.drawLine(iX(a.x), iY(a.y), iX(a.x), iY(a.y));
if (clickCount > 1)
drawTree(g,a,b);
}
public void drawTree(Graphics g, Point first, Point second)
{
float xSquared = (float) Math.pow((second.x-first.x),2);
float ySquared = (float) Math.pow((second.y-first.y),2);
float length = (float) Math.sqrt(xSquared + ySquared);
if ( length > .001)
{
u.x = second.x - first.x;
u.y = second.y - first.y;
a.x = first.x;
a.y = first.y;
b.x = second.x;
b.y = second.y;
d.x = first.x + (u.y * -1);
d.y = first.y + u.x;
c.x = d.x + u.x;
c.y = d.y + u.y;
e.x = d.x + .5F * (u.x + (u.y*-1));
e.y = d.y + .5F * (u.y + u.x);
Polygon square = new Polygon();
Polygon triangle = new Polygon();
square.addPoint(iX(a.x), iY(a.y));
square.addPoint(iX(b.x), iY(b.y));
square.addPoint(iX(c.x), iY(c.y));
square.addPoint(iX(d.x), iY(d.y));
red = random.nextFloat();
green = random.nextFloat();
blue = random.nextFloat();
g.setColor(new Color(red, green, blue));
g.fillPolygon(square);
triangle.addPoint(iX(c.x), iY(c.y));
triangle.addPoint(iX(d.x), iY(d.y));
triangle.addPoint(iX(e.x), iY(e.y));
red = random.nextFloat();
green = random.nextFloat();
blue = random.nextFloat();
g.setColor(new Color(red, green, blue));
g.fillPolygon(triangle);
/* Problem code is here, tree will draw Left or Right depending on which recursive call
* is first in the code, but will never reach the 2nd call.
*/
drawTree(g,d,e); //Draw tree left
drawTree(g,e,c); //Draw tree right
}
}
}
class Point
{
public float x;
public float y;
public Point()
{
}
}
It's time this question had an answer. The problem is with these member variable declarations:
Point c = new Point();
Point e = new Point();
And this code in drawTree():
drawTree(g,d,e); //Draw tree left
drawTree(g,e,c); //Draw tree right
Since c and e are a member variables, not local, they get modified by the first recursive call to drawTree(g, d, e) so by the time we make the second call to drawTree(g, e, c), it's no longer the same c and e we thought we had. The following rework of the code makes these local (clearly not as efficient GC-wise, but also not as buggy) as well as several other small modifications:
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
class Point
{
public float x;
public float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public Point() {
}
}
class DrawingPanel extends JPanel
{
Random random = new Random();
int clickCount = 0;
float pixelSize;
float rWidth = 10.0F;
float rHeight = 7.5F;
float red, green, blue;
Point a = new Point();
Point b = new Point();
Point center = new Point();
DrawingPanel()
{
addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent click)
{
clickCount++;
if (clickCount == 1)
{
a.x = fx(click.getX());
a.y = fy(click.getY());
repaint();
}
else if (clickCount == 2)
{
b.x = fx(click.getX());
b.y = fy(click.getY());
repaint();
}
}
});
}
void initgr()
{
Dimension d = getSize();
int maxX = d.width - 1;
int maxY = d.height - 1;
pixelSize = Math.max(rWidth / maxX, rHeight / maxY);
center.x = maxX / 2;
center.y = maxY / 2;
}
int iX(float x){ return Math.round(center.x + x / pixelSize); }
int iY(float y){ return Math.round(center.y - y / pixelSize); }
float fx(int x){ return (x - center.x) * pixelSize; }
float fy(int y){ return (center.y - y) * pixelSize; }
public void paintComponent(Graphics g)
{
super.paintComponent(g);
initgr();
setBackground(Color.white);
if (clickCount == 1)
{
g.setColor(Color.red);
g.drawLine(iX(a.x), iY(a.y), iX(a.x), iY(a.y));
}
else if (clickCount > 1) {
drawTree(g, a, b);
}
}
public void drawTree(Graphics g, Point first, Point second)
{
double xSquared = Math.pow(second.x - first.x, 2);
double ySquared = Math.pow(second.y - first.y, 2);
if (Math.sqrt(xSquared + ySquared) < 0.01) {
return;
}
Point u = new Point(second.x - first.x, second.y - first.y);
Point d = new Point(first.x - u.y, first.y + u.x);
Point c = new Point(d.x + u.x, d.y + u.y);
Point e = new Point(d.x + 0.5F * (u.x - u.y), d.y + 0.5F * (u.y + u.x));
Polygon square = new Polygon();
square.addPoint(iX(first.x), iY(first.y));
square.addPoint(iX(second.x), iY(second.y));
square.addPoint(iX(c.x), iY(c.y));
square.addPoint(iX(d.x), iY(d.y));
red = random.nextFloat();
green = random.nextFloat();
blue = random.nextFloat();
g.setColor(new Color(red, green, blue));
g.fillPolygon(square);
Polygon triangle = new Polygon();
triangle.addPoint(iX(c.x), iY(c.y));
triangle.addPoint(iX(d.x), iY(d.y));
triangle.addPoint(iX(e.x), iY(e.y));
red = random.nextFloat();
green = random.nextFloat();
blue = random.nextFloat();
g.setColor(new Color(red, green, blue));
g.fillPolygon(triangle);
drawTree(g, d, e); // Draw tree left
drawTree(g, e, c); // Draw tree right
}
}
public class PythagorasTree extends JFrame
{
PythagorasTree()
{
super("Pythagoras Tree");
setSize(800, 800);
add("Center", new DrawingPanel());
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args)
{
new PythagorasTree();
}
}
OUTPUT

Render only the segment/area of a circle that intersects the main circle

I absolutely love maths (or 'math' as most of you would say!) but I haven't done it to a level where I know the answer to this problem. I have a main circle which could have a centre point at any x and y on a display. Other circles will move around the display at will but at any given call to a render method I want to render not only those circles that intersect the main circle, but also only render the segment of that circle that is visible inside the main circle. An analogy would be a shadow cast on a real life object, and I only want to draw the part of that object that is 'illuminated'.
I want to do this preferably in Java, but if you have a raw formula that would be appreciated. I wonder how one might draw the shape and fill it in Java, I'm sure there must be some variation on a polyline with arcs or something?
Many thanks
Let A and B be the 2 intersection points (you can ignore it when there is no, or 1 intercetion point).
Then calculate the length of the circular line segment between A and B.
With this information, you should be able to draw the arc using Graphics' drawArc(...) method (if I'm not mistaken...).
EDIT
Well, you don't even need the length of the circular line segment. I had the line-intersection code laying around, so I built a small GUI around it how you could paint/view the ARC of such intersecting circles (there are a bit of comments in the code):
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Arc2D;
/**
* #author: Bart Kiers
*/
public class GUI extends JFrame {
private GUI() {
super("Circle Intersection Demo");
initGUI();
}
private void initGUI() {
super.setSize(600, 640);
super.setDefaultCloseOperation(EXIT_ON_CLOSE);
super.setLayout(new BorderLayout(5, 5));
final Grid grid = new Grid();
grid.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
Point p = new Point(e.getX(), e.getY()).toCartesianPoint(grid.getWidth(), grid.getHeight());
grid.showDraggedCircle(p);
}
});
grid.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
Point p = new Point(e.getX(), e.getY()).toCartesianPoint(grid.getWidth(), grid.getHeight());
grid.released(p);
}
#Override
public void mousePressed(MouseEvent e) {
Point p = new Point(e.getX(), e.getY()).toCartesianPoint(grid.getWidth(), grid.getHeight());
grid.pressed(p);
}
});
super.add(grid, BorderLayout.CENTER);
super.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new GUI();
}
});
}
private static class Grid extends JPanel {
private Circle c1 = null;
private Circle c2 = null;
private Point screenClick = null;
private Point currentPosition = null;
public void released(Point p) {
if (c1 == null || c2 != null) {
c1 = new Circle(screenClick, screenClick.distance(p));
c2 = null;
} else {
c2 = new Circle(screenClick, screenClick.distance(p));
}
screenClick = null;
repaint();
}
public void pressed(Point p) {
if(c1 != null && c2 != null) {
c1 = null;
c2 = null;
}
screenClick = p;
repaint();
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, super.getWidth(), super.getHeight());
final int W = super.getWidth();
final int H = super.getHeight();
g2d.setColor(Color.LIGHT_GRAY);
g2d.drawLine(0, H / 2, W, H / 2); // x-axis
g2d.drawLine(W / 2, 0, W / 2, H); // y-axis
if (c1 != null) {
g2d.setColor(Color.RED);
c1.drawOn(g2d, W, H);
}
if (c2 != null) {
g2d.setColor(Color.ORANGE);
c2.drawOn(g2d, W, H);
}
if (screenClick != null && currentPosition != null) {
g2d.setColor(Color.DARK_GRAY);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
Circle temp = new Circle(screenClick, screenClick.distance(currentPosition));
temp.drawOn(g2d, W, H);
currentPosition = null;
}
if (c1 != null && c2 != null) {
g2d.setColor(Color.BLUE);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
Point[] ips = c1.intersections(c2);
for (Point ip : ips) {
ip.drawOn(g, W, H);
}
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.2f));
if (ips.length == 2) {
g2d.setStroke(new BasicStroke(10.0f));
c1.highlightArc(g2d, ips[0], ips[1], W, H);
}
}
g2d.dispose();
}
public void showDraggedCircle(Point p) {
currentPosition = p;
repaint();
}
}
private static class Circle {
public final Point center;
public final double radius;
public Circle(Point center, double radius) {
this.center = center;
this.radius = radius;
}
public void drawOn(Graphics g, int width, int height) {
// translate Cartesian(x,y) to Screen(x,y)
Point screenP = center.toScreenPoint(width, height);
int r = (int) Math.rint(radius);
g.drawOval((int) screenP.x - r, (int) screenP.y - r, r + r, r + r);
// draw the center
Point screenCenter = center.toScreenPoint(width, height);
r = 4;
g.drawOval((int) screenCenter.x - r, (int) screenCenter.y - r, r + r, r + r);
}
public void highlightArc(Graphics2D g2d, Point p1, Point p2, int width, int height) {
double a = center.degrees(p1);
double b = center.degrees(p2);
// translate Cartesian(x,y) to Screen(x,y)
Point screenP = center.toScreenPoint(width, height);
int r = (int) Math.rint(radius);
// find the point to start drawing our arc
double start = Math.abs(a - b) < 180 ? Math.min(a, b) : Math.max(a, b);
// find the minimum angle to go from `start`-angle to the other angle
double extent = Math.abs(a - b) < 180 ? Math.abs(a - b) : 360 - Math.abs(a - b);
// draw the arc
g2d.draw(new Arc2D.Double((int) screenP.x - r, (int) screenP.y - r, r + r, r + r, start, extent, Arc2D.OPEN));
}
public Point[] intersections(Circle that) {
// see: http://mathworld.wolfram.com/Circle-CircleIntersection.html
double d = this.center.distance(that.center);
double d1 = ((this.radius * this.radius) - (that.radius * that.radius) + (d * d)) / (2 * d);
double h = Math.sqrt((this.radius * this.radius) - (d1 * d1));
double x3 = this.center.x + (d1 * (that.center.x - this.center.x)) / d;
double y3 = this.center.y + (d1 * (that.center.y - this.center.y)) / d;
double x4_i = x3 + (h * (that.center.y - this.center.y)) / d;
double y4_i = y3 - (h * (that.center.x - this.center.x)) / d;
double x4_ii = x3 - (h * (that.center.y - this.center.y)) / d;
double y4_ii = y3 + (h * (that.center.x - this.center.x)) / d;
if (Double.isNaN(x4_i)) {
// no intersections
return new Point[0];
}
// create the intersection points
Point i1 = new Point(x4_i, y4_i);
Point i2 = new Point(x4_ii, y4_ii);
if (i1.distance(i2) < 0.0000000001) {
// i1 and i2 are (more or less) the same: a single intersection
return new Point[]{i1};
}
// two unique intersections
return new Point[]{i1, i2};
}
#Override
public String toString() {
return String.format("{center=%s, radius=%.2f}", center, radius);
}
}
private static class Point {
public final double x;
public final double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double degrees(Point that) {
double deg = Math.toDegrees(Math.atan2(that.y - this.y, that.x - this.x));
return deg < 0.0 ? deg + 360 : deg;
}
public double distance(Point that) {
double dX = this.x - that.x;
double dY = this.y - that.y;
return Math.sqrt(dX * dX + dY * dY);
}
public void drawOn(Graphics g, int width, int height) {
// translate Cartesian(x,y) to Screen(x,y)
Point screenP = toScreenPoint(width, height);
int r = 7;
g.fillOval((int) screenP.x - r, (int) screenP.y - r, r + r, r + r);
}
public Point toCartesianPoint(int width, int height) {
double xCart = x - (width / 2);
double yCart = -(y - (height / 2));
return new Point(xCart, yCart);
}
public Point toScreenPoint(int width, int height) {
double screenX = x + (width / 2);
double screenY = -(y - (height / 2));
return new Point(screenX, screenY);
}
#Override
public String toString() {
return String.format("(%.2f,%.2f)", x, y);
}
}
}
If you start the GUI above and then type 100 0 130 -80 55 180 in the text box and hit return, you'll see the following: ...
Changed the code so that circles can be drawn by pressing- and dragging the mouse. Screenshot:
Assuming you know the center point and the radius of the two circles:
Calculate the points where the circles intersect. This can easily be done with trigonometry. There may be no intersection (distance between the center points is longer than the sum of the radiuses, ignorable in your case), one point (distance between center points is equal to the sum of the radiuses, ignorable), or two. Special cases: the circles are identical, or the moving circle ist smaller and completely inside the main circle.
If there are two intersection points: take the center point from the moving circle and draw an arc between those points.
(I have no code for you, but since you love maths... ;-)

Categories

Resources