Graphics2D circular table - java

I'm trying to build a User Interface for the RGBike POV:
http://www.instructables.com/id/RGBike-POV-Open-project/
The program will display a bike wheel in form of a grid. The user can click
onto the single squares and changes the colour of these.
I want to build this applet in java. I'm stuck at drawing the wheel in the right way.
I need to have a sort of array of every rectangle, to export the colour later.
The best thing would be to draw a sort of circular table. Drawing each shape
With graphics2D to have each as a single object would be an idea, too. But that would
be around 860 single shapes, little bit too much to update them every time by paint().
Spoke POV has done such a user Interface for their project already:
http://www.ladyada.net/make/spokepov/software.html
But only their old python script is open source.

Be VERY grateful that I have previously generate a "segment" shape in the past ;)
This basically generates each segment individually (does some funky translation into real space) and maintains a cache of shapes which can be checked to see if the mouse falls within there bounds.
This is rather inefficient, but I think you get the idea.
I should also be noted, that I didn't bother with a backing buffer. Not to say it could use one, I just got away without it...
public class TestSpoke {
public static void main(String[] args) {
new TestSpoke();
}
public TestSpoke() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public static final int CIRCLE_COUNT = 16;
public static final int SEGMENT_COUNT = 80;
private Map<Integer, List<Shape>> mapWheel;
private Map<Point, Color> mapColors;
public TestPane() {
mapColors = new HashMap<>(CIRCLE_COUNT * SEGMENT_COUNT);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Map<Integer, List<Shape>> mapWheel = getWheel();
for (Integer circle : mapWheel.keySet()) {
List<Shape> segments = mapWheel.get(circle);
for (int index = 0; index < segments.size(); index++) {
Shape segment = segments.get(index);
if (segment.contains(e.getPoint())) {
mapColors.put(new Point(circle, index), Color.RED);
repaint();
break;
}
}
}
}
});
}
#Override
public void invalidate() {
mapWheel = null;
super.invalidate();
}
protected float getRadius() {
return Math.min(getWidth(), getHeight());
}
/**
* This builds a wheel (if required) made of segments.
* #return
*/
protected Map<Integer, List<Shape>> getWheel() {
if (mapWheel == null) {
mapWheel = new HashMap<>(CIRCLE_COUNT);
// The current radius
float radius = getRadius();
// The radius of each individual circle...
float circleRadius = radius / CIRCLE_COUNT;
// The range of each segment
float extent = 360f / SEGMENT_COUNT;
for (int circle = 0; circle < CIRCLE_COUNT; circle++) {
float startAngle = 0;
List<Shape> segments = new ArrayList<>(SEGMENT_COUNT);
mapWheel.put(circle, segments);
// Calculate the "translation" to place each segement in the
// center of the screen
float innerRadius = circleRadius * circle;
float x = (getWidth() - innerRadius) / 2;
float y = (getHeight() - innerRadius) / 2;
for (int seg = 0; seg < SEGMENT_COUNT; seg++) {
// Generate a Segment shape
Segment segment = new Segment(circleRadius * circle, circleRadius, startAngle, extent);
startAngle += extent;
// We translate the segment to the screen space
// This will make it faster to paint and check for mouse clicks
PathIterator pi = segment.getPathIterator(AffineTransform.getTranslateInstance(x, y));
Path2D path = new Path2D.Float();
path.append(pi, true);
segments.add(path);
}
}
}
return mapWheel;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Map<Integer, List<Shape>> mapWheel = getWheel();
for (Integer circle : mapWheel.keySet()) {
List<Shape> segments = mapWheel.get(circle);
for (int index = 0; index < segments.size(); index++) {
Shape segment = segments.get(index);
Color color = mapColors.get(new Point(circle, index));
if (color != null) {
g2d.setColor(color);
g2d.fill(segment);
}
g2d.setColor(Color.BLACK);
g2d.draw(segment);
}
}
g2d.dispose();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public static class Segment extends Path2D.Float {
public Segment(float radius, float thickness, float extent) {
this(radius, thickness, 0f, extent);
}
public Segment(float radius, float thickness, float startAngle, float extent) {
// Basically, we want to draw the outter edge from a to b angle,
// draw the connecting line from the outter to the inner,
// draw the inner from b to a angel and
// draw the connecting line from the inner to out the outter
// We want to span about 30 degrees, with a small gap...
// I want the gap to be a factor of the radius
Arc2D.Float outter = new Arc2D.Float(0, 0, radius, radius, startAngle, extent, Arc2D.OPEN);
Arc2D.Float inner = new Arc2D.Float(thickness / 2f, thickness / 2f, radius - thickness, radius - thickness, startAngle + extent, -extent, Arc2D.OPEN);
append(outter, true);
float angel = startAngle + extent;
Point2D p1 = getPointOnEdge(angel, radius);
Point2D p2 = getPointOnEdge(angel, radius - thickness);
// We need to adjust in for the change in the radius
p2.setLocation(p2.getX() + (thickness / 2f), p2.getY() + (thickness / 2f));
lineTo(p2.getX(), p2.getY());
append(inner, true);
angel = startAngle;
p1 = getPointOnEdge(angel, radius);
p2 = getPointOnEdge(angel, radius - thickness);
p2.setLocation(p2.getX() + (thickness / 2f), p2.getY() + (thickness / 2f));
lineTo(p1.getX(), p1.getY());
closePath();
}
public Point2D getPointOnEdge(float angel, float radius) {
angel -= 90;
float x = radius / 2f;
float y = radius / 2f;
double rads = Math.toRadians((angel + 90));
// This determins the length of tick as calculate from the center of
// the circle. The original code from which this derived allowed
// for a varible length line from the center of the cirlce, we
// actually want the opposite, so we calculate the outter limit first
float fullLength = (radius / 2f);
// Calculate the outter point of the line
float xPosy = (float) (x + Math.cos(rads) * fullLength);
float yPosy = (float) (y - Math.sin(rads) * fullLength);
return new Point2D.Float(xPosy, yPosy);
}
}
}

Related

How to place the ball position properly in Wheel game?

I have a static number of slots for the number in the wheel game. Start from number 00 to number 1 (clockwise). The position count is 0 to 37 in my Collection array. Currently, I successful place the ball position in the middle of the number with 1 degree. But I unable to calculate a correct value for each of the position when I tried to pass in the number position.
I already tried messing with the calculation, it either the ball sit in the middle between the number or out of range!
public class WheelDisplay extends JPanel implements ConstantVariable {
private Image image;
private ImageObserver imageObserver;
private float degrees = 1;
private int post = 0;
public WheelDisplay() {
ImageIcon icon = new ImageIcon(IMAGE_LOCATION);
image = icon.getImage();
imageObserver = icon.getImageObserver();
}
/** Credit to stackoverflow forum : https://stackoverflow.com/questions/25923480/simple-circle-rotation-simulate-motion **/
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int circleDiameter = Math.min(getWidth(), getHeight());
double circleRadius = circleDiameter / 2;
int offSetX = (getWidth() - circleDiameter) / 2;
int offSetY = (getHeight() - circleDiameter) / 2;
g2d.drawImage(image, offSetX, offSetY, circleDiameter, circleDiameter, imageObserver);
g2d.setColor(Color.BLUE);
int ballDiameter = (int) (circleDiameter * 0.02);
int ballRadius = ballDiameter / 2;
Point p = getPointOnCircle(this.degrees, circleRadius * 0.9, circleRadius);
int valueX = offSetX + p.x - ballRadius;
int valueY = offSetY + p.y - ballRadius;
g2d.fillOval(valueX, valueY, ballDiameter, ballDiameter);
g2d.dispose();
}
private Point getPointOnCircle(float degress, double circleRadius, double innerCircleRadius) {
//The calculation that allow ball sit in the middle of each number when spin
double rads = ((Math.PI * degress) / 38);
// Calculate the outter point of the line
int xCordinate = Math.round((float) (innerCircleRadius + Math.cos(rads) * circleRadius));
int yCordinate = Math.round((float) (innerCircleRadius + Math.sin(rads) * circleRadius));
return new Point(xCordinate, yCordinate);
}
public void setDegree(int x) {
this.degrees += 2;
this.post = x; // The number of position coming from Collection (0-37)
}
}
The ball supposes to sit in the middle of each 38 numbers in the wheel image.
Hope that someone can point out which part I'm doing wrong!
The image that I'm using is this.

Trying to do a simple Jump

I'm trying to learn how to make a 2D Game without Game Engines, anyways I already created a background scrolling right now my goal is to make my character jump. But the thing is whenever I start my app the character is spinning up and down and it will just go away to the background.
Here's my character code
public class Deer extends GameCharacter {
private Bitmap spritesheet;
private double dya;
private boolean playing;
private long startTime;
private boolean Jump;
private Animate Animation = new Animate();
public Deer(Bitmap res, int w, int h, int numFrames) {
x = 20;
y = 400;
dy = 0;
height = h;
width = w;
Bitmap[] image = new Bitmap[numFrames];
spritesheet = res;
for (int i = 0; i < image.length; i++)
{
image[i] = Bitmap.createBitmap(spritesheet, i*width, 0, width, height);
}
Animation.setFrames(image);
Animation.setDelay(10);
startTime = System.nanoTime();
}
public void setJump(boolean b){
Jump = b;
}
public void update()
{
long elapsed = (System.nanoTime()-startTime)/1000000;
if(elapsed>100)
{
}
Animation.update();
if(Jump){
dy = (int)(dya+=5.5);
}
else{
dy = (int)(dya+=5.5);
}
if(dy>14)dy = 14;
if(dy>14)dy = -14;
y += dy*2;
dy = 0;
}
public void draw(Canvas canvas)
{
canvas.drawBitmap(Animation.getImage(),x,y,null);
}
public boolean getPlaying(){return playing;}
public void setPlaying(boolean b){playing = b;}
public void resetDYA(){dya = 0;}
}
x - character's horizontal position
y - character's vertical position
dx - character's horizontal acceleration
dy - character's vertical acceleration
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN) {
if(!deer.getPlaying()) {
deer.setPlaying(true);
}
deer.setJump(true);
return true;
}
return super.onTouchEvent(event);
}
I can't say for sure if this is the only problem because you have other suspicious code but it looks like you jump no matter what.
if(Jump){
dy = (int)(dya+=5.5);
} else {
dy = (int)(dya+=5.5);
}
If Jump is true you set the vertical acceleration. But you also set the vertical acceleration to the same value if Jump is false. You also don't show in your code where Jump is ever set to false.
Another odd bit of code is:
if(dy>14)dy = 14;
if(dy>14)dy = -14;
Here, if dy>14 you set it to 14. Then you check dy>14 immediately after. Of course, this time it's false. But because those two conditions are the same the second one will never pass since the one before it ensures it won't. The only other option is they both fail. IOW, you'll never be able to enter the second if.
All that aside, I'm not sure why you're taking this approach. You can simply rely on physics equations with constant acceleration, give an initial velocity, check for a collision with the ground (or at least the original height), and just let it run. For example:
// These are the variables you need.
int x = 200, y0 = 0, y = 0, velocity = 15;
double t = 0.0, gravity = -9.8;
// This is the statement that should run when you update the GUI.
// It is the fundamental equation for motion with constant acceleration.
// The acceleration is the gravitational constant.
y = (int) (y0 + velocity * t + .5 * gravity * t * t);
if (y < 0) {
y = y0 = 0;
//Stop jumping!
Jump = false;
} else {
// Swap the y values.
y0 = y;
// Increase the time with the frame rate.
t += frameRate;
}
// Draw the character using the y value
The best part about this is you don't need to worry about when you get to the maximum height because the equation will automatically bring you down. It also looks more natural as if the mechanics are real. Try it out.
A simple Swing example that you can play around with. Note that the values are different to deal with the way the components are drawn to the screen. Normally, you would deal with that with transformations but this will do for the task.
public class Main {
static Timer timer;
Main() {
JFrame frame = new JFrame("Hello sample");
frame.setSize(new Dimension(550, 550));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel panel = new MyPanel();
frame.add(panel);
frame.setVisible(true);
timer = new Timer(5, (e) -> panel.repaint());
timer.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(Main::new);
}
class MyPanel extends JPanel {
int x = 200, y0 = 300, y = 0, w = 200, h = 200, v = -8;
double t = 0.0, gravity = 9.8;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
y = (int) (y0 + v * t + .5 * gravity * t * t);
if (y > 300) {
y = y0 = 300;
// To prevent it from stopping comment the timer.stop() and
// uncomment the t = 0.0 statements.
//t = 0.0;
timer.stop();
} else {
y0 = y;
t += .025;
}
g.drawOval(x, y, w, h);
}
}
}

Having trouble displaying balls in the right position using Java and vector objects

I'm having issues with the balls that I display in my JFrame window. Here's the main idea of my application:
Point object (with coordinates x and y) --> Vector object (a class I wrote that has the x and y components of a vector, as well as a few methods... gets its location and head/tail points from the Point object) --> Ball object (the position, velocity, and acceleration vectors are collected in an ArrayList, also has radius and color attributes) --> ContainerBox object (contains the balls, defines the min/max x and y for collision detection purposes).
I'm working toward having one ball centered in the window and the other orbiting the first, but right now I'm just trying to get my objects to play nicely. I'm very new to OOP principles and this is the first time I've written a program using classes in this way.
Everything works perfectly, the JFrame comes up and displays the balls... the problem is that the balls won't show up in the right places. No matter what I put in for the x and y coordinates of the balls (either explicitly or using the objects), they always show up in the upper left-hand corner of the screen. Here's a pic of what I get: Picture Here
I don't know whether it's the vectors or the collision detection or what... any ideas? Thanks a lot for reading and replying!
EDIT: Here's some of the code that I'm using (sorry to put so much, I have no idea where the problem is):
EDIT AGAIN: Added the ball class that I forgot.
package chaneyBouncingBall;
import java.util.*;
public class Point
{
float x;
float y;
public Point(float x, float y)
{
this.x = x;
this.y = y;
}
}
package chaneyBouncingBall;
import java.util.ArrayList;
public class Chaney2DVector
{
float x;
float y;
Point fromLocation;
public Chaney2DVector(float x,
float y)
{
this.x = x;
this.y = y;
}
public Chaney2DVector(Point point1,
Point point2)
{
fromLocation = new Point(point1.x, point1.y);
this.x = point2.x - point1.x;
this.y = point2.y - point1.y;
}
}
package chaneyBouncingBall;
import java.awt.*;
import java.util.*;
public class Ball
{
float x, y;
float velX, velY;
float accelX, accelY;
float radius;
private Color color;
public Ball(float x, float y, float velX,
float velY, float accelX,
float accelY, float radius,
Color color)
{
Chaney2DVector position = new Chaney2DVector(x, y);
Chaney2DVector velocity = new Chaney2DVector(velX, velY);
Chaney2DVector acceleration = new Chaney2DVector(accelX, accelY);
ArrayList posVelAcc = new ArrayList();
posVelAcc.add(position);
posVelAcc.add(velocity);
posVelAcc.add(acceleration);
this.radius = radius;
this.color = color;
}
public void draw(Graphics g)
{
g.setColor(color);
g.fillOval((int)(x - radius), (int)(y - radius),
(int)(2 * radius), (int)(2 * radius));
}
public void moveOneStepWithCollisionDetection( ContainerBox box)
{
float ballMinX = box.minX + radius;
float ballMinY = box.minY + radius;
float ballMaxX = box.maxX - radius;
float ballMaxY = box.maxY - radius;
x = x + velX;
y = y + velY;
if (x < ballMinX)
{
velX = -velX;
x = ballMinX;
}
else if (x > ballMaxX)
{
velX = -velX;
x = ballMaxX;
}
if (y < ballMinY)
{
velY = -velY;
y = ballMinY;
}
else if (y > ballMaxY)
{
velY = -velY;
y = ballMaxY;
}
}
}
package chaneyBouncingBall;
import java.awt.*;
/**
* A rectangular container box, containing the bouncing ball.
*/
public class ContainerBox {
int minX, maxX, minY, maxY; // Box's bounds (package access)
private Color colorFilled; // Box's filled color (background)
private Color colorBorder; // Box's border color
private static final Color DEFAULT_COLOR_FILLED = Color.BLACK;
private static final Color DEFAULT_COLOR_BORDER = Color.YELLOW;
/** Constructors */
public ContainerBox(int x, int y, int width, int height, Color colorFilled, Color colorBorder) {
minX = x;
minY = y;
maxX = x + width - 1;
maxY = y + height - 1;
this.colorFilled = colorFilled;
this.colorBorder = colorBorder;
}
/** Constructor with the default color */
public ContainerBox(int x, int y, int width, int height) {
this(x, y, width, height, DEFAULT_COLOR_FILLED, DEFAULT_COLOR_BORDER);
}
/** Set or reset the boundaries of the box. */
public void set(int x, int y, int width, int height) {
minX = x;
minY = y;
maxX = x + width - 1;
maxY = y + height - 1;
}
/** Draw itself using the given graphic context. */
public void draw(Graphics g) {
g.setColor(colorFilled);
g.fillRect(minX, minY, maxX - minX - 1, maxY - minY - 1);
g.setColor(colorBorder);
g.drawRect(minX, minY, maxX - minX - 1, maxY - minY - 1);
}
}
package chaneyBouncingBall;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
/**
* The control logic and main display panel for game.
*/
public class BallWorld extends JPanel {
private static final int UPDATE_RATE = 50; // Frames per second (fps)
private Ball ball; // A single bouncing Ball's instance
private Ball ball2;
private ContainerBox box; // The container rectangular box
private DrawCanvas canvas; // Custom canvas for drawing the box/ball
private int canvasWidth;
private int canvasHeight;
/**
* Constructor to create the UI components and init the game objects.
* Set the drawing canvas to fill the screen (given its width and height).
*
* #param width : screen width
* #param height : screen height
*/
public BallWorld(int width, int height) {
canvasWidth = width;
canvasHeight = height;
boolean stationary = true;
Random rand = new Random();
int angleInDegree = rand.nextInt(360);
int radius = 50;
int radius2 = 25;
// double accelAngle;
float x1 = rand.nextInt(canvasWidth - radius * 2 - 20) + radius + 10;
float y1 = rand.nextInt(canvasHeight - radius * 2 - 20) + radius + 10;
float x2 = rand.nextInt(canvasWidth - radius * 2 - 20) + radius + 10;
float y2 = rand.nextInt(canvasHeight - radius * 2 - 20) + radius + 10;
// float x = 100;
// float y = 100;
float velX1 = 0;
float velY1 = 0;
float accelX1 = 0;
float accelY1 = 0;
float velX2 = 0;
float velY2 = 0;
float accelX2 = 0;
float accelY2 = 0;
ball = new Ball(canvasWidth / 2, canvasHeight / 2, velX1, velY1, accelX1, accelY1,
radius, Color.BLUE);
ball2 = new Ball(x2, y2, velX2, velY2, accelX2, accelY2, radius / 5, Color.YELLOW);
// Init the Container Box to fill the screen
box = new ContainerBox(0, 0, canvasWidth, canvasHeight, Color.BLACK, Color.WHITE);
// Init the custom drawing panel for drawing the game
canvas = new DrawCanvas();
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
// Handling window resize.
this.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
Component c = (Component)e.getSource();
Dimension dim = c.getSize();
canvasWidth = dim.width;
canvasHeight = dim.height;
// Adjust the bounds of the container to fill the window
box.set(0, 0, canvasWidth, canvasHeight);
}
});
// Start the ball bouncing
gameStart();
}
/** Start the ball bouncing. */
public void gameStart() {
// Run the game logic in its own thread.
Thread gameThread = new Thread() {
public void run() {
while (true) {
// Execute one time-step for the game
gameUpdate();
// Refresh the display
repaint();
// Delay and give other thread a chance
try {
Thread.sleep(1000 / UPDATE_RATE);
} catch (InterruptedException ex) {}
}
}
};
gameThread.start(); // Invoke GaemThread.run()
}
/**
* One game time-step.
* Update the game objects, with proper collision detection and response.
*/
public void gameUpdate() {
ball.moveOneStepWithCollisionDetection(box);
ball2.moveOneStepWithCollisionDetection(box);
}
/** The custom drawing panel for the bouncing ball (inner class). */
class DrawCanvas extends JPanel {
/** Custom drawing codes */
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // Paint background
// Draw the box and the ball
box.draw(g);
ball.draw(g);
ball2.draw(g);
// Display ball's information
g.setColor(Color.WHITE);
g.setFont(new Font("Courier New", Font.PLAIN, 12));
// g.drawString("Ball " + ball.toString(), 20, 30);
}
/** Called back to get the preferred size of the component. */
#Override
public Dimension getPreferredSize() {
return (new Dimension(canvasWidth, canvasHeight));
}
}
}
package chaneyBouncingBall;
import javax.swing.JFrame;
public class Main
{
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame("Matt Chaney's Gravity App");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new BallWorld(550, 450));
frame.pack();
frame.setVisible(true);
}
});
}
}
In the constructor of your Ball class, you don't assign all the instance variables to their parameters, these need to be added.
public Ball(float x, float y, float velX, float velY, float accelX, float accelY, float radius, Color color) {
...
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
this.accelX = accelX;
this.accelY = accelY;
}

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));
}
}

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