Related
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()...
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
i'm trying to make a program that will connect two points that i click in my JPanel. i'm trying to connect the two points with a line. i displayed the values of my (x1, y1) and (x2,y2) coordinates whenever i click using the mouse and there is no error in the values. but when the line is displayed, it the line doesn't seem to follow the coordinates i specify but instead outputs in a different location and is distorted. most of the lines will appear to be cut by something, i think it's the rectangle created by the line because i used setBounds(). also, i add a System.out.println("") inside my paintComponent function and i noticed that it printed multiple times (increasing by 1 after every click) even though it should only print once. can anyone help me with this? thanks!
here are two of the classes which contribute to the error:
CLASS # 1:
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author Arch. Don Saborrido
*/
public class twixtBoard extends JPanel implements java.awt.event.MouseListener{
int x1 = 0, x2, y1 = 0, y2;
DrawLine line;
//DrawLine line;
public twixtBoard(){
//requestFocus();
setLayout(null);
setVisible(false);
setBounds(0,0,600,450);
setOpaque(false);
setFocusable(false);
addListener();
}
public void addListener(){
addMouseListener(this);
}
public void mouseClicked(MouseEvent e) {
if (x1 == 0 || y1 == 0){
x1 = e.getX();
y1 = e.getY();
System.out.println(x1 + " " + y1);
}
else{
x2 = e.getX();
y2 = e.getY();
//System.out.println(x2 + " " + y2);
line = new DrawLine(x1, y1, x2, y2);
line.setBounds(x1, y1, x2, y2);
System.out.println("" + line.getLocation());
//line.setOpaque(false);
add(line);
x1 = x2;
y1 = y2;
repaint();
}
}
public void mousePressed(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet.");
}
public void mouseReleased(MouseEvent e) {
///throw new UnsupportedOperationException("Not supported yet.");
}
public void mouseEntered(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet.");
}
public void mouseExited(MouseEvent e) {
//throw new UnsupportedOperationException("Not supported yet.");
}
}
CLASS # 2 (Paint Class):
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author Arch. Don Saborrido
*/
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.geom.GeneralPath;
//import java.awt.geom.Line2D;
import javax.swing.JPanel;
public class DrawLine extends JPanel{
int x1 = 0, x2 = 0, y1 = 0, y2 = 0;
//Line2D line;
Stroke[] s = new Stroke[] {new BasicStroke(10.0f, BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND)};
//new BasicStroke(25.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL),
//new BasicStroke(25.0f, BasicStroke.CAP_SQUARE,BasicStroke.JOIN_MITER)
GeneralPath path = new GeneralPath();
public DrawLine(int start_x, int start_y, int end_x, int end_y){
x1 = start_x;
y1 = start_y;
x2 = end_x;
y2 = end_y;
System.out.println(x1+ " " + y1+ " " + x2+ " " + y2+ " ");
}
#Override
protected void paintComponent(Graphics g) {
System.out.println("entered paint");
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
g2.setStroke(s[0]);
path.moveTo(x1,y1);
System.out.println("x1 = " + x1 + " y1 = " + y1);
path.lineTo(x2,y2);
System.out.println("x2 = " + x2 + " y2 = " + y2);
System.out.println("" + path.getBounds2D());
g2.draw(path);
//line = new Line2D.Float(x1, y1, x2, y2);
//if(x1 != x2 && y1 != y2)
//g2.draw(line);
}
}
This is a very complicated approach.
Try the following:
make a class (e.g. LineClass) with four variables: x1, x2, y1 and y2.
keep a ArrayList of your generated lines
draw each line in the ArrayList on your JPanel using g.drawline(line.x1, line.y1, line.x2, line.y2) where line is the element of the ArrayList
class MainClass extends JPanel implements MouseListener {
boolean clickedOnce = false;
ArrayList<Line> lines = new ArrayList<Line>();
int x, y;
MainClass () {
// init...
addMouseListener(this);
}
// more MouseEvent methods
public void mouseClicked(MouseEvent e) {
if (!clickedOnce){
x=e.getX();
y = e.getY();
clickedOnce = true;
}
else{
lines.add(new Line(x,y,e.getX(), e.getY());
clickedOnce = false;
repaint();
}
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
for (Line l: lines)
g.drawLine(l.x1, l.y2, l.x2, l.y2);
}
private class Line {
public int x1, y1, x2, y2;
Line (int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
}
}
}
line = new DrawLine(x1, y1, x2, y2);
ine.setBounds(x1, y1, x2, y2);
add(line);
The basic logic looks wrong to me. You can't just use the 4 values fo the size and location.
The location would be the minimum of the x1/x2 and y1/y2.
The size (width, height) would then be the absolute value of (x1 - x2) and (y1 - y2).
Then when you draw the line you would draw the line from (0, 0) to (width, height).
Maybe the code examples from Custom Painting Approaches will help with a slightly different solution.
One of the problems is that you are calculating the width and height wrong. They would be the difference between x1 and x2 and y1 and y2.
You could also probably do this easier using java's Graphics class. That has methods like drawLine(), which would probably make your life easier.
I am trying to paint a cube on a JFrame.
Sounds simple, but lags a lot. The 7th and 8th lines usually flash pretty bad.
here is the code:
http://pastebin.com/ncDasST6
if someone can give me a hint or two on how to stop this lag from occurring, that would be great :D.
Originally was for Applet, but i wanted it to execute through a .jar file.
Also, any way to add an Applet to a JFrame?
I tried doing: add(new Rotational()); //name of JApplet it is based off of.
Thanks, Fire
Does this variant work to your expectation? There are a number of changes which I did not bother to document (as I was 'just playing' with the code). Do a diff. to reveal the extent and nature of the changes.
It shows no lag or rendering artifacts here at 700x700.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.border.EmptyBorder;
public class Square extends JPanel implements MouseListener,
MouseMotionListener {
private static final long serialVersionUID = 1L;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
JFrame f = new JFrame("Cube Rotational");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Square square = new Square();
square.setBorder(new EmptyBorder(5,5,5,5));
f.setContentPane(square);
f.pack();
f.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Square() {
init();
setPreferredSize(new Dimension(700,700));
}
class Point3D {
public int x, y, z;
public Point3D(int X, int Y, int Z) {
x = X;
y = Y;
z = Z;
}
}
class Edge {
public int a, b;
public Edge(int A, int B) {
a = A;
b = B;
}
}
static int width, height;
static int mx, my;
static int azimuth = 45, elevation = 45;
static Point3D[] vertices;
static Edge[] edges;
public void init() {
width = 500;
height = 500;
vertices = new Point3D[8];
vertices[0] = new Point3D(-1, -1, -1);
vertices[1] = new Point3D(-1, -1, 1);
vertices[2] = new Point3D(-1, 1, -1);
vertices[3] = new Point3D(-1, 1, 1);
vertices[4] = new Point3D(1, 1, -1);
vertices[5] = new Point3D(1, 1, 1);
vertices[6] = new Point3D(1, -1, -1);
vertices[7] = new Point3D(1, -1, 1);
edges = new Edge[12];
edges[0] = new Edge(0, 1);
edges[1] = new Edge(0, 2);
edges[2] = new Edge(0, 6);
edges[3] = new Edge(1, 3);
edges[4] = new Edge(1, 7);
edges[5] = new Edge(2, 3);
edges[6] = new Edge(2, 4);
edges[7] = new Edge(3, 5);
edges[8] = new Edge(4, 5);
edges[9] = new Edge(4, 6);
edges[10] = new Edge(5, 7);
edges[11] = new Edge(6, 7);
setCursor(new Cursor(Cursor.HAND_CURSOR));
addMouseListener(this);
addMouseMotionListener(this);
setVisible(true);
}
void drawWireframe(Graphics g) {
double theta = Math.PI * azimuth / 180.0;
double phi = Math.PI * elevation / 180.0;
float cosT = (float) Math.cos(theta);
float sinT = (float) Math.sin(theta);
float cosP = (float) Math.cos(phi);
float sinP = (float) Math.sin(phi);
float cosTcosP = cosT * cosP;
float cosTsinP = cosT * sinP;
float sinTcosP = sinT * cosP;
float sinTsinP = sinT * sinP;
Point[] points;
points = new Point[vertices.length];
float scaleFactor = (getWidth() + getHeight()) / 8;
float near = (float) 6;
float nearToObj = 1.5f;
for (int j = 0; j < vertices.length; ++j) {
int x0 = vertices[j].x;
int y0 = vertices[j].y;
int z0 = vertices[j].z;
float x1 = cosT * x0 + sinT * z0;
float y1 = -sinTsinP * x0 + cosP * y0 + cosTsinP * z0;
float z1 = cosTcosP * z0 - sinTcosP * x0 - sinP * y0;
x1 = x1 * near / (z1 + near + nearToObj);
y1 = y1 * near / (z1 + near + nearToObj);
points[j] = new Point(
(int) (getWidth() / 2 + scaleFactor * x1 + 0.5),
(int) (getHeight() / 2 - scaleFactor * y1 + 0.5));
}
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.white);
for (int j = 0; j < edges.length; ++j) {
int x1 = points[edges[j].a].x;
int x2 = points[edges[j].b].x;
int y1 = points[edges[j].a].y;
int y2 = points[edges[j].b].y;
((Graphics2D) g).setStroke(new BasicStroke(5));
g.drawLine(x1, y1, x2, y2);
}
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
mx = e.getX();
my = e.getY();
e.consume();
}
public void mouseReleased(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
int new_mx = e.getX();
int new_my = e.getY();
azimuth -= new_mx - mx;
azimuth %= 360;
elevation += new_my - my;
elevation %= 360;
repaint();
mx = new_mx;
my = new_my;
repaint();
e.consume();
}
#Override
public void paintComponent(Graphics g) {
drawWireframe(g);
}
}
Originally was for Applet, but i wanted it to execute through a .jar file.
Good idea converting an applet to something more sensible, but note that an applet can (and usually should) be packed into a Jar.
Also, any way to add an Applet to a JFrame?
This is possible, relatively easy with this code (barring mixing Swing (JFrame) & AWT (Applet) components), but not the best way to go. It is better to create a hybrid like (for example) the subway applet/application.
By moving the custom rendering from the frame to a JPanel, the code has been partially transformed into a hybrid, since the panel can be added to a frame or applet (or window or dialog, or another panel or..).