I am learning Java following class CS106A and am having issues that GCompound object is not interacting with mouse events.
I noticed the issue while doing programming exercises in "the Art and Science of Java" and couldn't figure out the reason. So i tried to copy the sample code of the book and tried to run it in eclipse, and they didn't work either.
Below is GFace class defined in the book that extends GCompound.
import acm.graphics.*;
public class GFace extends GCompound{
/** Constants specifying feature size as a fraction of the head size*/
private static final double EYE_WIDTH = 0.15;
private static final double EYE_HEIGHT = 0.15;
private static final double NOSE_WIDTH = 0.15;
private static final double NOSE_HEIGHT = 0.10;
private static final double MOUTH_WIDTH = 0.50;
private static final double MOUTH_HEIGHT = 0.03;
/** Construct a new GFace object with the specified dimensions. */
public GFace (double width, double height){
head = new GOval (width, height);
leftEye = new GOval (EYE_WIDTH * width, EYE_HEIGHT * height);
rightEye = new GOval (EYE_WIDTH * width, EYE_HEIGHT * height);
nose = createNose (NOSE_WIDTH * width, NOSE_HEIGHT * height);
mouth = new GRect(MOUTH_WIDTH * width, MOUTH_HEIGHT * height);
add(head, 0, 0);
add(leftEye, 0.25 * width - EYE_WIDTH * width / 2,
0.25 * height - EYE_HEIGHT * height / 2);
add(rightEye, 0.75 * width - EYE_WIDTH * width / 2,
0.25 * height - EYE_HEIGHT * height / 2);
add(nose, 0.50 * width, 0.50 * height);
add(mouth, 0.50 * width - MOUTH_WIDTH * width / 2,
0.75 * height - MOUTH_HEIGHT * height / 2);
}
/** creates a triangle for the nose*/
private GPolygon createNose(double width, double height){
GPolygon poly = new GPolygon();
poly.addVertex(0, -height/2);
poly.addVertex(width / 2, height / 2);
poly.addVertex(-width / 2, height / 2);
return poly;
}
/*instance variables*/
private GOval head;
private GOval leftEye, rightEye;
private GPolygon nose;
private GRect mouth;
}
and then below program is supposed to create a GFace object on Canvas so that user can drag it around with mouse. But, when i ran the program, the GFace object on canvas does not respond to my mouse, while GRect object can ne dragged on canvas. I've also added a rectangle to see if by clicking on the GFace, the program returns null, as suggested in the comment(thanks for the advice!), and it does. It just seems the GCompound object cannot be selected.
import acm.graphics.*;
import acm.program.*;
import java.awt.event.*;
import java.awt.*;
import acm.util.*;
public class DragFace extends GraphicsProgram{
/** Width of the face */
private static final double FACE_WIDTH = 200;
/** Height of the face */
private static final double FACE_HEIGHT = 300;
/** Runs the program */
public void run(){
/*added a filled rectangle to the screen.
* if no object selected and returns null,
* rectangle changes color randomly*/
add(temp);
temp.setFillColor(Color.RED);
temp.setFilled(true);
GFace face = new GFace(FACE_WIDTH, FACE_HEIGHT);
double x1 = (getWidth() - FACE_WIDTH) / 2;
double y1 = (getHeight() - FACE_HEIGHT) / 2;
add(face, x1, y1);
GObject rect = new GRect(FACE_WIDTH, FACE_HEIGHT);
double x = (getWidth() - FACE_WIDTH) / 2;
double y = (getHeight() - FACE_HEIGHT) / 2;
add(rect, x, y);
rect.setColor(Color.RED);
addMouseListeners();
}
/** called on mouse press to record the coordinates of the click */
public void mousePressed(MouseEvent e){
lastX = e.getX();
lastY = e.getY();
gobj = getElementAt(lastX, lastY);
/* if no object selected and returns null,
* rectangle changes color randomly each time */
if(gobj == null){
temp.setFillColor(rgen.nextColor());
}
}
/** called on mouse drag to reposition the object*/
public void mouseDragged(MouseEvent e){
if(gobj != null){
gobj.move(e.getX() - lastX, e.getY() - lastY);
lastX = e.getX();
lastY = e.getY();
}
}
/** called on mouse click to move this object to the front*/
public void mouseClicked(MouseEvent e){
if(gobj != null) gobj.sendToFront();
}
/*private state*/
private GObject gobj; /*the object being dragged*/
private double lastX; /*the last mouse X position*/
private double lastY; /*the last mouse Y position*/
private GRect temp = new GRect(0,0,100,100);/* private rectangle*/
private RandomGenerator rgen = new RandomGenerator();
}
MY question is
does GCompound object works as a unit like a GRect? If so, can can anyone point out the cause of the issue in the code above? I don't know if it's my software that's causing the issue, or if I am missing something with the use of GCompound class.
the Gface is supposed to be centered on Canvas, but somehow it shows up on the button right corner. The x- and y- coordinate of the Gface is always twice of the coordinate specified in the add call. Any idea how this might have happened?
Any help will be greatly appreciated.
Related
Create a Rectangle class that is almost identical to the Circle class. Use Rect as the class name. The easiest way to write this class will be to copy all the code from Circle.java into Rect.java, then change the class name, instance variables, and statements. My rect code only opens a blank window when I use the shapemover. Please help me identify what I am doing wrong.
Here's the circle class code:
package lab08;
import java.awt.Graphics;
import java.awt.Color;
/**
* Circle objects represent a circle shape
* drawn to the screen at a particular position
* with some size and color.
*
* #author Peter Jensen, Donat Mouele
* #version Fall 2022
*/
public class Circle extends Shape
{
// Instance variables.
private int x, y;
private Color color;
private int radius;
/**
* Constructor - initializes the position, diameter, and
* color of this circle object.
*
* #param x
* the x coordinate of this object's position
*
* #param y
* the x coordinate of this object's position
*
* #param diameter
* the diameter of this circle
*
* #param color
* the color of this circle
*/
public Circle (int x, int y, int diameter, Color color)
{
this.x = x;
this.y = y;
this.color = color;
this.radius = diameter / 2;
}
/**
* Changes the position of this shape by
* the specified amount. Note that this does
* not set the position absolutely, the deltas
* specify how far to move the shape from its
* current position.
*
* #param deltaX
* how far to move the shape horizontally
*
* #param deltaY
* how far to move the shape vertically
*/
public void move (int deltaX, int deltaY)
{
x = x + deltaX;
y = y + deltaY;
}
/**
* Draws the circle at it's current position and color
* to the specified graphics object.
*
* #param g
* the graphics object (where to draw to)
*/
public void draw (Graphics g)
{
g.setColor (color);
g.fillOval (x, y, radius * 2, radius * 2);
}
/**
* Returns true if the coordinates are within the circle.
*
* #param targetX
* an x coordinate
*
* #param targetY
* a y coordinate
*
* #return
* true if the coordinates are within the shape
*/
public boolean isInside (int targetX, int targetY)
{
int cx = x + radius; // Calculate the center point
int cy = y + radius;
int deltaX = cx - targetX; // Calculate the deltas to the click
int deltaY = cy - targetY;
// Make sure the distance from the click to the center is less
// than the radius. (Notice how I avoid a square root.)
return (deltaX * deltaX + deltaY * deltaY) <= radius * radius;
}
}
Here's the Rectangle Class Code that I have
package lab08;
import java.awt.Graphics;
import java.awt.Color;
/**
* Circle objects represent a circle shape
* drawn to the screen at a particular position
* with some size and color.
*
* #author Peter Jensen, Donat Mouele
* #version Fall 2022
*/
public class Rect extends Shape
{
// Instance variables.
private int x, y;
private Color color;
private int height;
private int width;
/**
* Constructor - initializes the position, diameter, and
* color of this circle object.
*
* #param x
* the x coordinate of this object's position
*
* #param y
* the x coordinate of this object's position
*
* #param diameter
* the diameter of this circle
*
* #param color
* the color of this circle
*/
public Rect (int x, int y, int perimeter, Color color)
{
this.x = x;
this.y = y;
this.color = color;
this.height = perimeter / 2 - width;
this.width = perimeter/2-height;
}
/**
* Changes the position of this shape by
* the specified amount. Note that this does
* not set the position absolutely, the deltas
* specify how far to move the shape from its
* current position.
*
* #param deltaX
* how far to move the shape horizontally
*
* #param deltaY
* how far to move the shape vertically
*/
public void move (int deltaX, int deltaY)
{
x = x + deltaX;
y = y + deltaY;
}
/**
* Draws the circle at it's current position and color
* to the specified graphics object.
*
* #param g
* the graphics object (where to draw to)
*/
public void draw (Graphics g)
{
g.setColor (color);
g.fillOval (x, y, width, height);
}
/**
* Returns true if the coordinates are within the circle.
*
* #param targetX
* an x coordinate
*
* #param targetY
* a y coordinate
*
* #return
* true if the coordinates are within the shape
*/
public boolean isInside (int targetX, int targetY)
{
int cx = x + width; // Calculate the center point
int cy = y + height;
int deltaX = cx - targetX; // Calculate the deltas to the click
int deltaY = cy - targetY;
return targetX >= x &&
targetX < x + width &&
targetY >= y &&
targetY < y + height;
}
}
Here's the shape mover
package lab08;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* This class represents and application/JPanel that
* draws a few shapes for the user and allows the
* user to drag the shapes around with the mouse.
*
* #author Peter Jensen
* #version Fall 2022
*/
public class ShapeMover extends JPanel implements MouseListener,
MouseMotionListener,
Runnable
{
// Instance variables.
Rect[] shapes;
int lastX, lastY;
Rect current;
/**
* Initialize this (our JPanel). Create the shapes, and
* register this object as a listener to mouse events
* produced by this object.
*/
public ShapeMover ()
{
// Make the shapes.
shapes = new Rect[]
{
new Rect (130, 40, 60, Color.RED),
new Rect (230, 40, 20, Color.BLUE),
new Rect (330, 40, 80, Color.GREEN.darker()),
new Rect (130, 140, 100, new Color (0.8f, 0.6f, 0.2f)),
new Rect ( 30, 140, 40, Color.YELLOW)
};
// Set the size of this panel.
Dimension d = new Dimension (600, 600);
this.setMinimumSize(d);
this.setPreferredSize(d);
this.setMaximumSize(d);
// Register this object as a listener to its own events.
this.addMouseListener (this);
this.addMouseMotionListener (this);
}
/**
* Draws the shapes at their current locations
* and colors.
*
* #param g
* the Graphics object to draw to
*/
public void paint (Graphics g)
{
// Clear the background to a nice light blue color.
g.setColor(new Color (0.8f, 0.8f, 1.0f));
g.fillRect(0, 0, this.getWidth(), this.getHeight());
// Draw all of the shapes.
for (Rect s : shapes)
s.draw(g);
}
/**
* This method is part of the MouseListener interface.
* Because we registered this application object as a listener
* to its own mouse events, this method will be automatically
* called whenever the mouse button is pressed down.
*
* In this method, we determine if the mouse click occurred
* in any of our shapes. If so, we record that shape object
* as the current shape. This has the effect of selecting
* a shape to drag around.
*
* #param e
* the mouse event
*/
public void mousePressed (MouseEvent e)
{
// Get the location of the mouse click within this window.
int x = e.getX ();
int y = e.getY ();
// Save it for later use.
lastX = x;
lastY = y;
// Determine if the mouse click is within any shape.
// If so, save the shape as the current shape.
for (Rect s : shapes)
if (s.isInside (x, y))
current = s;
}
/**
* This method is part of the MouseListener interface.
* Because we registered this applet object as a listener
* to its own mouse events, this method will be automatically
* called whenever the mouse button is let down.
*
* In this method, we mark the current shape as null. This
* has the effect of dropping whatever shape we are dragging
* around.
*
* #param e
* the mouse event
*/
public void mouseReleased (MouseEvent e)
{
current = null;
}
/**
* This method is part of the MouseMotionListener interface.
* Because we registered this applet object as a listener
* to its own mouse events, this method will be automatically
* called whenever the mouse is moved with the button pressed down.
*
* In this method, we adjust the position of the shape the user
* is dragging around.
*
* #param e
* the mouse event
*/
public void mouseDragged (MouseEvent e)
{
// Compute how far the mouse moved since the last event.
int x = e.getX ();
int y = e.getY ();
int deltaX = x - lastX;
int deltaY = y - lastY;
// Save the current mouse position.
lastX = x;
lastY = y;
// If the user is dragging around a shape, move it by
// the same amount that the mouse moved.
if (current != null)
{
current.move (deltaX, deltaY);
repaint ();
}
}
// Unused event methods (required by the interfaces).
public void mouseClicked (MouseEvent e) { }
public void mouseEntered (MouseEvent e) { }
public void mouseExited (MouseEvent e) { }
public void mouseMoved (MouseEvent e) { }
/* Above this point are the methods and variables that we use in the JPanel */
/* Below this point are the methods and variables that launch the application */
/* I violated separation of concerns. The JPanel and Application classes
* are merged, and 'main' is below. This works for simple code (like this
* lab), but it is not a good idea for larger projects.
*/
/**
* The application entry point.
*
* #param args unused
*/
public static void main (String[] args)
{
// Main runs in the 'main' execution thread, and the GUI
// needs to be built by the GUI execution thread.
// Ask the GUI thread to run our 'run' method (at some
// later time).
SwingUtilities.invokeLater(new ShapeMover());
// Done. Let the main thread of execution finish. All the
// remaining work will be done by the GUI thread.
}
/**
* Builds the GUI for this application. This method must
* only be called/executed by the GUI thread.
*/
public void run ()
{
JFrame f = new JFrame("Shape Mover 2021");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 'this' is our Runnable object, but it is also our JPanel.
f.setContentPane(this);
f.pack();
f.setLocationRelativeTo(null); // Centers window
f.setVisible(true);
}
}
I'm programming a simple diagram, with what you can display some points on x, y axes.
public class GraphPlotter extends JPanel {
private static final long serialVersionUID = 1L;
/** Default frame size X for frame in pixels */
private final int DEFAULT_FRAME_SIZE_X = 800;
/** Default frame size Y for frame in pixels */
private final int DEFAULT_FRAME_SIZE_Y = 600;
/** Padding to Frame */
private final int PAD = 30;
/** Radius of dot */
private final int DOT_RADIUS = 3;
/** Padding of label */
private final int LABEL_PAD = 10;
/** Height of label */
private final int LABEL_HEIGHT = 10;
/** Width of label */
private final int LABEL_WIDTH = 100;
/** Max value for x to print */
private int maxValueForX;
/** Scale factor depending to y*/
private int maxValueForY;
/** Label for the x axis */
private String labelForX = "time";
/** Label for the y axis */
private String labelForY;
/**
* List with points to draw. It holds the y coordinates of points. x
* coordinates are spaced
*/
private List<Integer> dataPoints = new ArrayList<>();
/**
*
* Constructor of this class
*
*/
public GraphPlotter(ArrayList<Integer> dataPoints, String labelForY) {
this.dataPoints = dataPoints;
this.maxValueForX = dataPoints.size();
this.maxValueForY = Collections.max(dataPoints);
this.labelForY = labelForY;
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(this);
f.setSize(this.DEFAULT_FRAME_SIZE_X + PAD, this.DEFAULT_FRAME_SIZE_Y + PAD);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
/** method that draws the points and lines between the points */
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int w = getWidth();
int h = getHeight();
// add labels
JLabel jLabelY = new JLabel(labelForY);
JLabel jLabelX = new JLabel(labelForX);
jLabelY.setSize(LABEL_WIDTH, LABEL_HEIGHT);
jLabelY.setLocation(LABEL_PAD, LABEL_PAD);
add(jLabelY);
jLabelX.setSize(LABEL_WIDTH, LABEL_HEIGHT);
jLabelX.setLocation(w - LABEL_WIDTH - LABEL_PAD, h - LABEL_HEIGHT - LABEL_PAD);
jLabelX.setHorizontalAlignment(SwingConstants.RIGHT);
add(jLabelX);
// add axis
g2.drawLine(PAD, PAD, PAD, h - PAD);
g2.drawLine(PAD, h - PAD, w - PAD, h - PAD);
double xScale = (w - 2 * PAD) / (dataPoints.size() + 1);
double yScale = (h - 2 * PAD) / this.maxValueForY;
int x0 = PAD;
int y0 = h - PAD;
// draw the points as small circles
g2.setPaint(Color.blue);
for (int i = 0; i < dataPoints.size(); i++) {
int x = x0 + (int) (xScale * (i + 1));
int y = y0 - (int) (yScale * dataPoints.get(i));
g2.fillOval(x - DOT_RADIUS, y - DOT_RADIUS, 2 * DOT_RADIUS,
2 * DOT_RADIUS);
}
}
/** Size of List */
private int getSizeOfDataPoints() {
return dataPoints.size();
}
/** Deletes last DataPoint in List */
private void deleteLastDataPoint() {
dataPoints.remove(0);
}
/**
* Ad point and repaint graph.
*
* #param point to draw (y-coord)
*/
public void add(int point) {
if (getSizeOfDataPoints() > this.maxValueForX) {
deleteLastDataPoint();
}
dataPoints.add(point);
this.repaint();
}
public static void main(String[] args) {
ArrayList<Integer> scores = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 50; i++) {
scores.add(random.nextInt(10));
}
String labelForY = "throughput";
GraphPlotter graphPlotter = new GraphPlotter(scores, labelForY);
graphPlotter.add(5);
}
The labels are displayed at start of the application. If I resize the window of the application, the labels will be displayed all over the window. (see screenshots)
before resizing
after resizing
How can I avoid the dublication of the labels?
I assume, that the program is adding the same labels in the panel, after the window is repainted. If I write
add(jLabelY);
repaint();
in the paintComponent() method, this error occures at startup of the application.
I also tried to put the panel into a FlowLayout, but didn't make any changes.
paintComponent() can be called at almost arbitrary times by Swing (whenever the component needs to be redrawn; e.g. on resize). Therefore, it should usually be side-effect free. However, you use paintComponent to add() labels as children of your panel, which you never remove. Therefore, each time your panel gets redrawn, two labels get added to its children. super.paintComponent() will then paint them all.
One solution to this would be to keep the two labels as fields of your panel and only update their location in paintComponent (before calling super.paintComponent())
I am new learner of public sources of stanford cs106a.
I got stucked when I read handout BouncingBall,here below are the complete codes
I got from cs106a handout.
The question is, for checkForCollision method code that is bottom of this page,
why should the ball do the code ball.move(0, -2 * diff), especially
execute the -2 * diff, I cannot understand this math code. Is it Ok for
ball do the code ball.move(0, -diff)? what is the difference?
what is the bounce logic? anyone here could help me to understand this
code, I am not good at math. Thank you so much
/*
* File: BouncingBall.java
*--------------------------
* This program graphically simulates a bouncing ball
*/
import acm.program.*;
import acm.graphics.*;
public class BouncingBall extends GraphicsProgram {
/** Size (diameter) of the ball */
private static final int DIAM_BALL = 30;
/** Amount Y velocity is increased each cycle
* as result of gravity
*/
private static final double GRAVITY = 3;
/** Animation delay or pause time between call moves */
private static final int DELAY = 50;
/** Initial X and Y location of ball */
private static final double X_START = DIAM_BALL;
private static final double Y_START = 100;
/** X Velocity */
private static final double X_VEL = 5;
/** Amount Y Velocity is reduced when it bounces */
private static final double BOUNCE_REDUCE = 0.9;
/** Starting X and Y Velocities */
private doublexVel = X_VEL;
private doubleyVel = 0.0;
/* private instance variable */
private GOval ball;
public void run() {
setup();
// Simulation ends when ball goes off right hand
// end of screen
while (ball.getX() < getWidth()) {
moveBall();
checkForCollision();
pause(DELAY);
}
}
/** Create and place a ball */
private void setup() {
ball = new GOval (X_START, Y_START, DIAM_BALL, DIAM_BALL);
ball.setFilled(true);
add(ball);
}
/** Update and move ball */
private void moveBall() {
// increase yVelocity due to gravity
yVel += GRAVITY;
ball.move(xVel, yVel);
}
/** Determine if collision with floor, update velocities
* and location as appropriate
*/
private void checkForCollision() {
if (ball.getY() > getHeight() - DIAM_BALL) {
// change ball's Y velocity to now bounce upwards.
yVel = -yVel * BOUNCE_REDUCE;
// Assume bounce will move ball an amount above
// the floor equal to the amount it would have dropped
// below the floor
double diff = ball.getY() - (getHeight() - DIAM_BALL);
ball.move(0, -2 * diff);
}
}
}
When the ball.move is called, the ball has moved beyond the surface it's bouncing against. So -diff would move it back to the surface, -2 * diff makes it bounce up.
I'm trying to draw a circle with a random center inside a big bigger circular surface. (I'm actually trying to simulate a human and his eyesight inside a room!) I need to draw a random line (call it line1) passing through its center which will intersect with the surface. line1 does not necessarily pass the center of circular surface. I also need to draw two lines forming 60 degree, facing on one side of line1. Can anyone help me with that?
I created an example of what I need to draw.
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Random;
import javax.swing.JFrame;
public class ShapeTest extends JFrame{
int width=500;
int height=500;
public ShapeTest(){
setSize(width,height);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String a[]){
new ShapeTest();
}
public void paint(Graphics g){
// Circular Surface
drawCircleByCenter(g, width/2, height/2, width/2);
Random r = new Random();
Point center = new Point();
center.x=r.nextInt(width/2);
center.y=r.nextInt(width/2);
drawCircleByCenter(g, center.x, center.y, width/15);
}
void drawCircleByCenter(Graphics g, int x, int y, int radius){
//g.setColor(Color.LIGHT_GRAY);
g.drawOval(x-radius, y-radius, 2*radius, 2*radius);
}
}
Start by changing your method to draw a circle based on its center and radius to a method which returns a Ellipse2D object representing the circle. This will allow us to do some clipping and other things with the shape besides just draw it.
Setting the clip to be the shape of your large circle prevents stray marks from being made where you don't want them (think "color inside the lines"). This is important because when we draw the circles and lines inside the big circle, some of them will be too big and would otherwise mark outside the bounds of the big circle.
Once we set the clip, we use the method Line2D getVector(Point2D, double, length) with an origin at the center of the large circle, a random angle and a random length (capped to keep the small blue circle inside the big circle). Think of this a random polar coordinate with the center of the large circle as the origin. The end point of this vector is used to mark the center of the small circle.
Using the center of the small circle as a starting point, we can generate two vectors in opposite directions (just negate the length of one to get it going the other direction) by using a random direction angle. We use a length equal to the diameter of the big circle to make certain that the lines will always go all the way up to the edge of the big circle (but not past, thanks to our clip).
We simply add 60 and 120 degrees to the angle of our blue dashed line and draw two green lines calculating the vectors the same way we did for the two blue dashed lines, except we don't need to create ones with negated lengths. We can also add a normal vector in for good measure simply by adding 90 degrees to the angle of the blue dashed line.
Lastly, we pick some random polar coordinates (just like we did for the small blue circle) to represent some people, and using the intersection of the people with the areas created by the various lines, we can see where they are at and draw them up with color coded values.
Now that we have all the people, we eliminate the clip and draw the big circle and voila!
Check out Draw a line at a specific angle in Java for details on how I calculated the vectors for the lines.
But enough talk, here's the code:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ShapeTest extends JFrame {
private static final long serialVersionUID = 1L;
private int width = 500;
private int height = 500;
private int padding = 50;
private BufferedImage graphicsContext;
private JPanel contentPanel = new JPanel();
private JLabel contextRender;
private Stroke dashedStroke = new BasicStroke(3.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 2f, new float[] {3f, 3f}, 0f);
private Stroke solidStroke = new BasicStroke(3.0f);
private RenderingHints antialiasing;
private Random random = new Random();
public static void main(String[] args) {
//you should always use the SwingUtilities.invodeLater() method
//to perform actions on swing elements to make certain everything
//is happening on the correct swing thread
Runnable swingStarter = new Runnable()
{
#Override
public void run(){
new ShapeTest();
}
};
SwingUtilities.invokeLater(swingStarter);
}
public ShapeTest(){
antialiasing = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphicsContext = new BufferedImage(width + (2 * padding), width + (2 * padding), BufferedImage.TYPE_INT_RGB);
contextRender = new JLabel(new ImageIcon(graphicsContext));
contentPanel.add(contextRender);
contentPanel.setSize(width + padding * 2, height + padding * 2);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.setContentPane(contentPanel);
//take advantage of auto-sizing the window based on the size of its contents
this.pack();
this.setLocationRelativeTo(null);
this.paint();
setVisible(true);
}
public void paint() {
Graphics2D g2d = graphicsContext.createGraphics();
g2d.setRenderingHints(antialiasing);
//Set up the font to print on the circles
Font font = g2d.getFont();
font = font.deriveFont(Font.BOLD, 14f);
g2d.setFont(font);
FontMetrics fontMetrics = g2d.getFontMetrics();
//clear the background
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, graphicsContext.getWidth(), graphicsContext.getHeight());
//set up the large circle
Point2D largeCircleCenter = new Point2D.Double((double)width / 2 + padding, (double)height / 2 + padding);
double largeCircleRadius = (double)width / 2;
Ellipse2D largeCircle = getCircleByCenter(largeCircleCenter, largeCircleRadius);
//here we build the small circle
Point2D smallCircleCenter = new Point2D.Double();
double smallCircleRadius = 15;
//we need to make certain it is confined inside the larger circle
//so we choose the following values carefully
//we want to go a random direction from the circle, so chose an
//angle randomly in any direction
double smallCenterVectorAngle = random.nextDouble() * 360.0d;
//and we want to be a random distance from the center of the large circle, but
//we limit the distance based on the radius of the small circle to prevent it
//from appearing outside the large circle
double smallCenterVectorLength = random.nextDouble() * (largeCircleRadius - smallCircleRadius);
Line2D vectorToSmallCenter = getVector(largeCircleCenter, smallCenterVectorAngle, smallCenterVectorLength);
//the resulting end point of the vector is a random distance from the center of the large circle
//in a random direction, and guaranteed to not place the small circle outside the large
smallCircleCenter.setLocation(vectorToSmallCenter.getP2());
Ellipse2D smallCircle = getCircleByCenter(smallCircleCenter, smallCircleRadius);
//before we draw any of the circles or lines, set the clip to the large circle
//to prevent drawing outside our boundaries
g2d.setClip(largeCircle);
//chose a random angle for the line through the center of the small circle
double angle = random.nextDouble() * 360.0d;
//we create two lines that start at the center and go out at the angle in
//opposite directions. We use 2*largeCircleRadius to make certain they
//will be large enough to fill the circle, and the clip we set prevent stray
//marks outside the big circle
Line2D centerLine1 = getVector(smallCircleCenter, angle, largeCircleRadius * 2);
Line2D centerLine2 = getVector(smallCircleCenter, angle, -largeCircleRadius * 2);
//now we just add 20 and 120 to our angle for the center-line, start at the center
//and again, use largeCircleRadius*2 to make certain the lines are big enough
Line2D sightVector1 = getVector(smallCircleCenter, angle + 60, largeCircleRadius * 2);
Line2D sightVector2 = getVector(smallCircleCenter, angle + 120, largeCircleRadius * 2);
Path2D visible = new Path2D.Double();
visible.moveTo(sightVector1.getX2(), sightVector1.getY2());
visible.lineTo(smallCircleCenter.getX(), smallCircleCenter.getY());
visible.lineTo(sightVector2.getX2(), sightVector2.getY2());
visible.closePath();
Path2D greenSide = new Path2D.Double();
greenSide.moveTo(centerLine1.getX2(), centerLine1.getY2());
greenSide.lineTo(smallCircleCenter.getX(), smallCircleCenter.getY());
greenSide.lineTo(centerLine2.getX2(), centerLine2.getY2());
greenSide.lineTo(sightVector1.getX2(), sightVector1.getY2());
greenSide.closePath();
int personCount = 5;
Area visibleArea = new Area(visible);
visibleArea.intersect(new Area(largeCircle));
Area greenSideArea = new Area(greenSide);
greenSideArea.intersect(new Area(largeCircle));
//we create a list of the people in the circle to
//prevent overlap
ArrayList<Shape> people = new ArrayList<Shape>();
people.add(smallCircle);
int i = 0;
personLoop: while (i < personCount){
double personCenterVectorAngle = random.nextDouble() * 360.0d;
double personCenterVectorLength = random.nextDouble() * (largeCircleRadius - smallCircleRadius);
Line2D vectorToPersonCenter = getVector(largeCircleCenter, personCenterVectorAngle, personCenterVectorLength);
Point2D personCircleCenter = vectorToPersonCenter.getP2();
Ellipse2D personCircle = getCircleByCenter(personCircleCenter, smallCircleRadius);
//this little loop lets us skip a person if they have overlap
//with another person, since people don't generally overlap
Area personArea = new Area(personCircle);
for (Shape person : people)
{
Area overlapArea = new Area(person);
overlapArea.intersect(personArea);
//this means that we have found a conflicting
//person, so should skip them
if (!overlapArea.isEmpty()){
continue personLoop;
}
}
people.add(personCircle);
personArea.intersect(visibleArea);
Area greenSideAreaTest = new Area(personCircle);
greenSideAreaTest.intersect(greenSideArea);
if (personArea.isEmpty()){
if (greenSideAreaTest.isEmpty()){
g2d.setColor(Color.orange);
System.out.println("Person " + i + " is behind the blue line");
}
else {
System.out.println("Person " + i + " is in front of the blue line");
g2d.setColor(Color.cyan);
}
}
else
{
System.out.println("Person " + i + " is between the green lines");
g2d.setColor(Color.magenta);
}
//alternatively to circles intersecting the area of interest, we can check whether the center
//is in the area of interest which may make more intuitive sense visually
// if (visibleArea.contains(personCircleCenter)){
// System.out.println("Person " + i + " is between the green lines");
// g2d.setColor(Color.magenta);
// }
// else {
// if (greenSideArea.contains(personCircleCenter)) {
// System.out.println("Person " + i + " is in front of the blue line");
// g2d.setColor(Color.cyan);
// }
// else{
// g2d.setColor(Color.orange);
// System.out.println("Person " + i + " is behind the blue line");
// }
// }
g2d.fill(personCircle);
g2d.setColor(Color.black);
String itemString = "" + i;
Rectangle2D itemStringBounds = fontMetrics.getStringBounds(itemString, g2d);
double textX = personCircleCenter.getX() - (itemStringBounds.getWidth() / 2);
double textY = personCircleCenter.getY() + (itemStringBounds.getHeight()/ 2);
g2d.drawString("" + i, (float)textX, (float)textY);
i++;
}
//fill the small circle with blue
g2d.setColor(Color.BLUE);
g2d.fill(smallCircle);
//draw the two center lines lines
g2d.setStroke(dashedStroke);
g2d.draw(centerLine1);
g2d.draw(centerLine2);
//create and draw the black offset vector
Line2D normalVector = getVector(smallCircleCenter, angle + 90, largeCircleRadius * 2);
g2d.setColor(Color.black);
g2d.draw(normalVector);
//draw the offset vectors
g2d.setColor(new Color(0, 200, 0));
g2d.draw(sightVector1);
g2d.draw(sightVector2);
//we save the big circle for last, to cover up any stray marks under the stroke
//of its perimeter. We also set the clip back to null to prevent the large circle
//itselft from accidentally getting clipped
g2d.setClip(null);
g2d.setStroke(solidStroke);
g2d.setColor(Color.BLACK);
g2d.draw(largeCircle);
g2d.dispose();
//force the container for the context to re-paint itself
contextRender.repaint();
}
private static Line2D getVector(Point2D start, double degrees, double length){
//we just multiply the unit vector in the direction we want by the length
//we want to get a vector of correct direction and magnitute
double endX = start.getX() + (length * Math.sin(Math.PI * degrees/ 180.0d));
double endY = start.getY() + (length * Math.cos(Math.PI * degrees/ 180.0d));
Point2D end = new Point2D.Double(endX, endY);
Line2D vector = new Line2D.Double(start, end);
return vector;
}
private static Ellipse2D getCircleByCenter(Point2D center, double radius)
{
Ellipse2D.Double myCircle = new Ellipse2D.Double(center.getX() - radius, center.getY() - radius, 2 * radius, 2 * radius);
return myCircle;
}
}
The logic of the geometry turned out to be more tricky than I'd presumed, but this is what I think you are after.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.*;
class HumanEyesightLines {
int rad = 150;
int radSmall = 15;
int pad = 10;
JPanel gui = new JPanel(new BorderLayout());
BufferedImage img = new BufferedImage(
2 * (rad + pad),
2 * (rad + pad),
BufferedImage.TYPE_INT_RGB);
Timer timer;
JLabel imgDisplay;
Random rnd = new Random();
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
HumanEyesightLines() {
imgDisplay = new JLabel(new ImageIcon(img));
gui.add(imgDisplay);
File f = new File(System.getProperty("user.home"));
final File f0 = new File("HumanEyesiteLines");
f0.mkdirs();
try {
Desktop.getDesktop().open(f0);
} catch (IOException ex) {
ex.printStackTrace();
}
ActionListener animationListener = new ActionListener() {
int ii = 0;
#Override
public void actionPerformed(ActionEvent e) {
paintImage();
ii++;
if (ii < 100) {
System.out.println(ii);
File f1 = new File(f0, "eg" + ii + ".png");
try {
ImageIO.write(img, "png", f1);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
};
timer = new Timer(100, animationListener);
paintImage();
}
float[] dash = {3f, 3f};
float phase = 0f;
private final void paintImage() {
Graphics2D g = img.createGraphics();
g.setRenderingHints(rh);
g.setStroke(new BasicStroke(2f));
// fill the BG
g.setColor(Color.WHITE);
g.fillRect(0, 0, 2 * (rad + pad), 2 * (rad + pad));
// draw the big circle
Point center = new Point(rad + pad, rad + pad);
Shape bigCircle = new Ellipse2D.Double(pad, pad, 2 * rad, 2 * rad);
g.setColor(Color.MAGENTA.darker());
g.fill(bigCircle);
// set the clip to that of the big circle
g.setClip(bigCircle);
// draw the small circle
int xOff = rnd.nextInt(rad) - rad / 2;
int yOff = rnd.nextInt(rad) - rad / 2;
int x = center.x - xOff;
int y = center.y - yOff;
Shape smallCircle = new Ellipse2D.Double(
x - radSmall, y - radSmall,
2 * radSmall, 2 * radSmall);
g.setColor(Color.YELLOW);
g.fill(smallCircle);
g.setColor(Color.ORANGE);
g.draw(smallCircle);
g.setStroke(new BasicStroke(
1.5f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_ROUND,
2f,
dash,
phase));
// I don't know what the rule is for where the blue line goes, so
// will use the top left corner of the image as a 2nd anchor point.
int x0 = 0;
int y0 = 0;
double grad = (double) (y - y0) / (double) (x - x0);
// now calculate the RHS point from y = mx + b
// where b = 0 and m is the gradient
int x1 = 2 * (pad + rad);
int y1 = (int) (grad * x1);
Line2D.Double line1 = new Line2D.Double(x0, y0, x1, y1);
g.setColor(Color.BLUE);
g.draw(line1);
//find the perpendicular gradient.
double perpGrad = -1d / grad;
double perpTheta = Math.atan(perpGrad);
// angle from perp
double diffTheta = Math.PI / 6d;
g.setColor(Color.GREEN);
double viewLine1Theta = perpTheta + diffTheta;
Line2D.Double viewLine1 = getLine(x, y, viewLine1Theta);
double viewLine2Theta = perpTheta - diffTheta;
Line2D.Double viewLine2 = getLine(x, y, viewLine2Theta);
g.draw(viewLine1);
g.draw(viewLine2);
g.setColor(Color.BLACK);
Line2D.Double viewPerp = getLine(x, y, perpTheta);
g.draw(viewPerp);
g.setColor(Color.RED);
g.draw(bigCircle);
g.dispose();
imgDisplay.repaint();
}
/**
* Returns a Line2D starting at the point x1,y1 at angle theta.
*/
private final Line2D.Double getLine(double x1, double y1, double theta) {
double m;
double b;
double x2;
double y2;
if (theta < (-Math.PI / 2d)) {
System.out.println("CHANGE IT! " + theta);
m = Math.tan(theta);
b = y1 - (m * x1);
x2 = 0;
y2 = (m * x2) + b;
} else {
m = Math.tan(theta);
b = y1 - (m * x1);
x2 = 2 * (rad + pad);
y2 = (m * x2) + b;
}
/*
* System.out.println("Perp theta: " + theta); System.out.println("Line
* grad: " + m); System.out.println("Line off: " + b);
* System.out.println("x1,y1: " + x1 + "," + y1);
* System.out.println("x2,y2: " + x2 + "," + y2);
*
*/
return new Line2D.Double(x1, y1, x2, y2);
}
public JComponent getGui() {
return gui;
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
HumanEyesightLines hel = new HumanEyesightLines();
hel.start();
JOptionPane.showMessageDialog(null, hel.getGui());
hel.stop();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
i am trying to figure out how Java know where to start according to this Graphics code when i run this code it showing ball moving from top to down but i can't understand why from top and why from this place and i know that java use math.random() to set the value but how it set the x and y
while when i trying to put any random number by my self it gives number that is bigger than the width it self (i am taking exactly about pos.x and pos.y)
this the first class
package movingball;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
/**
*
* #author isslam
*/
public class Ball {
private final int RADIUS = 10;
private final Point pos;
private final Color ballColor = Color.red;
private final int CHANGE = 3;
private final int height,
width;
public Ball(int frameWidth, int frameHight) {
width = frameWidth;
hight = frameHeight;
pos = new Point();
pos.x = (int)(Math.random() * (width - RADIUS)) + RADIUS;
pos.y = (int)(Math.random() * (height / 2 - RADIUS)) + RADIUS;
}
public void paint(Graphics g) {
g.setColor(ballColor);
g.fillOval(pos.x - RADIUS, pos.y - RADIUS, 2 * RADIUS, 2 * RADIUS);
}
public void move() {
if (pos.y < height - RADIUS) {
pos.translate(0, CHANGE);
}
}
}
this is the second class
package movingball;
import java.awt. * ;
import javax.swing. * ;
/**
*
* #author isslam
*/
public class ClassOfMoving extends JFrame {
protected final int FRAME_WIDTH = 240;
protected final int FRAME_HIGHT = 320;
private final Ball myBall = new Ball(FRAME_WIDTH, FRAME_HEIGHT);
public ClassOfMoving(String title) {
super(title);
setSize(FRAME_WIDTH, FRAME_HEIGHT);
setDefaultCloseOperation(ClassOfMoving.EXIT_ON_CLOSE);
}
public void paint(Graphics g) {
super.paint(g);
myBall.paint(g);
}
public void move() {
while (true) {
myBall.move();
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
System.exit(0);
}
}
}
}
the main class
package movingball;
/**
*
* #author isslam
*/
public class MovingBall {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
ClassOfMoving is = new ClassOfMoving("isslam");
is.setVisible(true);
is.move();
}
I'm not entirely sure what the problem is, You seem a bit confused.
This line does the actual moving.
pos.translate(0, CHANGE);
The Ball position pos is translated with CHANGE pixels in the y-dimension. CHANGE is defined as 3 somewhere else. Top-Left position is normally (0,0) on a computer screen, so the pos.y will increase with 3 and the Ball will move downwards by three pixels*.
Math.random() returns a number between 0 and 1. The following line positions the ball on a random position between 0+r and width-r.
pos.x = (int)(Math.random() * (width - RADIUS)) + RADIUS;
If you want to specify a specific position, set pos.x directly instead of replacing Math.random().
The y-position is defined as the top half half of the window because the max value is defined as height/2.
pos.y = (int)(Math.random() * (height / 2- RADIUS)) + RADIUS;
*:The coordinate system of a computer display is flipped upside down compared to the conventional coordinate system where positive is up. The reason is perhaps to be found in how old cathode ray tubes paints (from top left) and also how line numbers increase downwards on line printers, and also later in character based display systems.
Independently these two classes when compiled and executed in an app won't do anything as it doesnt have either main method or static block. but if you are using it in conjuction with any framework , jars or any additional code, that might be helping execute it by means of creating their objects. Hope it answers.