Related
Please help me how to color the triangle with the three colors. I have it as a problem for school,
Use Java programming language
Hello this is the code:
I am trying to fill the triangle with the three colors, but is hard to mix colors to get it. Please if you are to solve it(approximate it) as much as you can send it to me, i have it as a homework and 5-6 days deadline
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.geom.*;
public class NotFullVersion2 extends JApplet {
public static void main(String s[]) {
JFrame frame = new JFrame();
frame.setTitle("Colors");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JApplet applet = new NotFullVersion2();
applet.init();
applet.addMouseListener(
new MouseAdapter(){
public void mousePressed(MouseEvent e) {
System.out.println(e.getX() + " " + e.getY());
}});
frame.getContentPane().add(applet);
frame.pack();
frame.setVisible(true);
}
ColorPanel panel;
public void init() {
panel = new ColorPanel();
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
cp.add(panel, BorderLayout.CENTER);
JPanel p = new JPanel();
cp.add(p,BorderLayout.EAST);
}
}
class ColorPanel extends JPanel {
//int red = 100,green = 100, blue = 100;
public ColorPanel() {
setPreferredSize(new Dimension(500, 500));
setBackground(Color.black);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for(int i = 0; i < 256; i++) {
int start = 399;
g2.setColor(new Color(0,i,255-i));
for(int j = 0; j < 200; j ++) {
Rectangle rec = new Rectangle(150+j,start - i,1,1);
g2.fill(rec);
}
}
for(int j = 0; j < 100; j++) {
int start = 100;
for(int i = 0; i < 300; i++) {
if(i < 22) {
g2.setColor(new Color(255,0,0));
Rectangle rec = new Rectangle(100 + i,start + j,1,1);
g2.fill(rec);
} else if(i > 21 && i < 278) {
g2.setColor(new Color(255-(i-22),(i-22),0));
Rectangle rec = new Rectangle(100 + i,start + j,1,1);
g2.fill(rec);
} else if(i < 300) {
g2.setColor(new Color(0,255,0));
Rectangle rec = new Rectangle(100 + i,start + j,1,1);
g2.fill(rec);
}
}
}
GeneralPath closePath1a = new GeneralPath();
g2.setColor(new Color(0,0,0));
closePath1a.moveTo(100,100);
closePath1a.lineTo(100,400);
closePath1a.lineTo(250,400);
closePath1a.closePath();
g2.fill(closePath1a);
GeneralPath closePath2a = new GeneralPath();
g2.setColor(new Color(0,0,0));
closePath2a.moveTo(400,100);
closePath2a.lineTo(400,400);
closePath2a.lineTo(250,400);
closePath2a.closePath();
g2.fill(closePath2a);
GeneralPath closePath3a = new GeneralPath();
g2.setColor(new Color(0,0,0));
closePath3a.moveTo(100,100);
closePath3a.lineTo(100,50);
closePath3a.lineTo(400,50);
closePath3a.lineTo(400,100);
closePath3a.closePath();
g2.fill(closePath3a);
}
}
Coloring a triangle like this is equivalent to calculating the barycentric coordinates of each pixel within the triangle. The following untested code calculates the barycentric coordinates for each pixel within the triangle ABC, and then uses it to color that pixel:
private float area(float Ax, float Ay, float Bx, float By, float Cx, float Cy) {
return 0.5*((Ax - Cx)*(By - Ay) - (Ax - Bx)*(Cy - Ay));
}
private void paintTriangle(Graphics g, float Ax, float Ay, float Bx, float By, float Cx, float Cy) {
// calculate the bounding box of the triangle:
int minX = Math.round(Math.min(Ax, Math.min(Bx, Cx)));
int minY = Math.round(Math.min(Ay, Math.min(By, Cy)));
int maxX = Math.round(Math.max(Ax, Math.max(Bx, Cx)));
int maxY = Math.round(Math.max(Ay, Math.max(By, Cy)));
// loop for each pixel in the bounding box of the triangle
for(int y = minY; y < maxY; ++y) {
for(int x = minX; x < maxX; ++x) {
// center of the pixel (x,y)
float Px = x + 0.5, Py = y + 0.5;
// barycentric coordinates of P
float denom = area(Ax, Ay, Bx, By, Cx, Cy);
float b0 = area(Px, Py, Bx, By, Cx, Cy)/denom;
float b1 = area(Ax, Ay, Px, Py, Cx, Cy)/denom;
float b2 = area(Ax, Ay, Bx, By, Px, Py)/denom;
// discard pixels outside the triangle
if(b0 < 0 || b1 < 0 || b2 < 0)
continue;
// paint a pixel of color (b0,b1,b2) at (x,y)
g.setColor(new Color(b0,b1,b2));
g.fillRect(x,y,1,1));
}
}
}
I'll leave it to you to test and integrate this code.
You can read more about barycentric coordinates on wikipedia;
I drew a line that is at an angle based on a slider.
I am trying to make the line's end Y coordinate a certain number (let's say 300), even if it is at an angle.
Any ideas on how to do this? Here is the work on my line so far:
double angle = intAngle;
angle = angle * Math.PI / 180;
double length = 300;
graphics.setColor(Color.RED);
double startX = 300;
double startY = 100;
double endX = startX + length * Math.cos(angle);
double endY = startY + length * Math.sin(angle);
double end2X;
double end2Y;
double dblAngle;
double angle2;
int intAngle2;
double start2X = endX;
double start2Y = endY;
intAngle2 = 180 - intAngle;
angle2 = intAngle2;
angle2 = (angle2 * Math.PI / 180);
end2X = (start2X - length * Math.cos(angle2));
end2Y = (start2Y - length * Math.sin(angle2));
int intEndX = (int)endX;
int intEndY = (int)endY;
if(blnButton == true){
graphics.draw(new Line2D.Double(startX, startY, endX, endY));
graphics.draw(new Line2D.Double(start2X, start2Y, end2X, end2Y));
}
There's probably a simpler way, but basically, you can calculate two points on a circle based on the angle and the inverse of the angle (angle - 360)
With a circle with a radius of 150, this will give you a line of 300, for example
The red line is the line from the center of the circle to point on the circle represented by the given angel. The blue is the inverse. Each line is 150 pixels line, meaning together, they are 300 pixels in length.
This examples draws the separately, but realistically, they could be draw as a single line
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
DrawPane drawPane = new DrawPane();
add(drawPane);
JSlider slider = new JSlider(0, 100);
add(slider, BorderLayout.SOUTH);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
drawPane.setAngleInDegrees(360d * (slider.getValue() / 100d));
}
});
slider.setValue(0);
}
}
public class DrawPane extends JPanel {
private double angle;
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// Radius of the circle
double r = 150;
// Degrees to radians...
double radians = Math.toRadians(angle);
// The end point on the circle...
int endX = (int) Math.round(r * Math.cos(radians));
int endY = (int) Math.round(r * Math.sin(radians));
// The start point on the circle, 360 degress from the
// start angle
radians = Math.toRadians(angle - 360);
int startX = (int) Math.round(r * Math.cos(radians));
int startY = (int) Math.round(r * Math.sin(radians));
// Offset for the ellipse (center of the screen)
double x = (getWidth() / 2d) - r;
double y = (getWidth() / 2d) - r;
g2d.setColor(Color.LIGHT_GRAY);
g2d.draw(new Ellipse2D.Double(x, y, r * 2, r * 2));
// Center of the circle...
x = (getWidth() / 2d);
y = (getWidth() / 2d);
// One single line
//g2d.setColor(Color.BLACK);
//g2d.draw(new Line2D.Double(x - startX, y - startY, x + endX, y + endY));
g2d.setColor(Color.RED);
g2d.draw(new Line2D.Double(x, y, x - startX, y - startY));
g2d.setColor(Color.BLUE);
g2d.draw(new Line2D.Double(x, y, x + endX, y + endY));
g2d.dispose();
}
public void setAngleInDegrees(double value) {
if (angle != value) {
angle = Math.min(Math.max(value, 0), 360);
repaint();
}
}
}
}
or something along those lines...
I've got a problem couse I need to move my ball and it just stand still. I've succed moving it with a simple function (x=x+1) but when it comes to radians it just doesnt work. I've read some posts here and I thought I'm making it in the right way, but its obvious I've missed something :)
Here is my Ball class:
public class Ball {
int x = 0;
int y = 0;
int rightleft = 1;
int updown = 1;
private static final int sizeBall = 30;
float angle = 120;
float angleInRadians = (float) (angle*Math.PI/180);
private Main main;
public Ball(Main main){
this.main=main;
}
// That function should move my ball
void move() {
x = (int) (x + Math.cos(angleInRadians));
y= (int) (x+Math.sin(angleInRadians));
}
void paint(Graphics2D g) {
g.setColor(Color.red);
g.fillOval(x, y, sizeBall, sizeBall);
}
public Rectangle getSize(){
return new Rectangle(x,y,sizeBall,sizeBall);
}
}
And here is my Main class:
public class Main extends JPanel {
Ball ball = new Ball(this);
private void moveBall() throws InterruptedException{
ball.move();
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ball.paint(g2d);
}
public static void main(String[] args) throws InterruptedException {
// TODO code application logic here
JFrame okno = new JFrame("TEST");
Main main = new Main();
okno.add(main);
okno.setSize(500,500);
okno.setVisible(true);
okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while(true){
main.moveBall();
main.repaint();
Thread.sleep(10);
}
}
}
Do you know where is my mistake?
x and y will always = 0. Understand that they are 0 to begin with, and then you add a sine or cose to them which is guaranteed to be < 1.
x = (int) (x + Math.cos(angleInRadians));
y = (int) (x+Math.sin(angleInRadians));
So 0 + a number < 1 will be < 1.
Then when you cast to int the number < 1 will become 0.
Also
use a Swing Timer not a while (true) loop.
override JPanel's paintComponent method, not its paint method for smoother animation.
I would use double numbers to represent my x and y values, and only cast or round when using them to draw.
I'm not sure what trajectory that you're aiming for, but your current code (if it worked) does not move in a polar way, but rather always at 45% angle from the current point.
For example, this GUI is created by the code below:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyPolar extends JPanel {
private static final int BI_W = 400;
private static final int BI_H = BI_W;
private static final int CTR_X = BI_W / 2;
private static final int CTR_Y = BI_H / 2;
private static final Color AXIS_COLOR = Color.black;
private static final Color GRID_LINE_COLOR = Color.LIGHT_GRAY;
private static final Color DRAWING_COLOR = Color.RED;
private static final float AXIS_LINE_WIDTH = 4f;
private static final double SCALE = BI_W / (2 * 1.25);
private static final float GRID_LINE_WIDTH = 2f;
private static final float DRAWING_WIDTH = 2f;
private static final double DELTA_THETA = Math.PI / (2 * 360);
private static final int TIMER_DELAY = 20;
private BufferedImage axiImg;
private List<Point> ptList = new ArrayList<>();
private double theta = 0;
public MyPolar() {
axiImg = createAxiImg();
int x = xEquation(theta);
int y = yEquation(theta);
ptList.add(new Point(x, y));
new Timer(TIMER_DELAY, new TimerListener()).start();
}
private int xEquation(double theta) {
double r = 2 * Math.sin(4 * theta);
return (int) (SCALE * 0.5 * r * Math.cos(theta)) + CTR_X;
}
private int yEquation(double theta) {
double r = 2 * Math.sin(4 * theta);
return (int) (SCALE * 0.5 * r * Math.sin(theta)) + CTR_Y;
}
private BufferedImage createAxiImg() {
BufferedImage img = new BufferedImage(BI_W, BI_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(AXIS_COLOR);
g2.setStroke(new BasicStroke(AXIS_LINE_WIDTH));
int x1 = 0;
int y1 = CTR_Y;
int x2 = BI_W;
int y2 = y1;
g2.drawLine(x1, y1, x2, y2);
x1 = CTR_X;
y1 = 0;
x2 = x1;
y2 = BI_H;
g2.drawLine(x1, y1, x2, y2);
g2.setColor(GRID_LINE_COLOR);
g2.setStroke(new BasicStroke(GRID_LINE_WIDTH));
x1 = (int) (CTR_X - BI_H * 0.5 * Math.tan(Math.PI / 6));
y1 = BI_H;
x2 = (int) (CTR_X + BI_H * 0.5 * Math.tan(Math.PI / 6));
y2 = 0;
g2.drawLine(x1, y1, x2, y2);
x1 = BI_W - x1;
x2 = BI_W - x2;
g2.drawLine(x1, y1, x2, y2);
x1 = (int) (CTR_X - BI_H * 0.5 * Math.tan(Math.PI / 3));
y1 = BI_H;
x2 = (int) (CTR_X + BI_H * 0.5 * Math.tan(Math.PI / 3));
y2 = 0;
g2.drawLine(x1, y1, x2, y2);
x1 = BI_W - x1;
x2 = BI_W - x2;
g2.drawLine(x1, y1, x2, y2);
for (int i = 1; i < 4; i++) {
int x = (int) (CTR_X - i * SCALE / 2.0);
int y = x;
int width = (int) (i * SCALE);
int height = width;
g2.drawOval(x, y, width, height);
}
g2.dispose();
return img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (axiImg != null) {
g2.drawImage(axiImg, 0, 0, null);
}
g2.setColor(DRAWING_COLOR);
g2.setStroke(new BasicStroke(DRAWING_WIDTH));
Point prev = null;
for (Point point : ptList) {
if (prev != null) {
int x1 = prev.x;
int y1 = prev.y;
int x2 = point.x;
int y2 = point.y;
g2.drawLine(x1, y1, x2, y2);
}
prev = point;
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(BI_W, BI_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
theta += DELTA_THETA;
if (theta > 2 * Math.PI) {
((Timer) e.getSource()).stop();
} else {
int x = xEquation(theta);
int y = yEquation(theta);
ptList.add(new Point(x, y));
}
repaint();
}
}
private static void createAndShowGui() {
MyPolar mainPanel = new MyPolar();
JFrame frame = new JFrame("MyPolar");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
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));
}
}
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