I want there to be 12 ellipses so they represent 5 minute intervals around the clock. However every time I change the linApproxLen for the path iterator, it always either draws too many ellipses or 9 ellipses. How can I make these ellipses represent the 5 minute mark?
private Ellipse2D ellipse = new Ellipse2D.Double();
public void setClockContour(int w, int h)
{
size = (w > h) ? h/6f : w/6f;
ellipse.setFrame(w/2-size*2-4.5f,h/2-size*2-4.5f,size*4,size*4);
double linApproxLen = 0.75 * size * 0.258819; // sin(15 degree)
PathIterator pi = ellipse.getPathIterator(null, linApproxLen);
Point2D[] points = new Point2D[100];
int num_pts = 0;
while ( !pi.isDone() )
{
float[] pt = new float[6];
switch ( pi.currentSegment(pt) ) {
case FlatteningPathIterator.SEG_MOVETO:
case FlatteningPathIterator.SEG_LINETO:
points[num_pts] = new Point2D.Float(pt[0], pt[1]);
num_pts++;
}
pi.next();
}
pts = new Point2D[num_pts];
System.arraycopy(points, 0, pts, 0, num_pts);
}
I think that approach will not work regardless of the linApproxLen parameter. This parameter only specifies the "flatness" of the line. While this will certainly influence the number of line segments that are returned, the actual number will at least depend on the radius of the ellipse that you are painting. So even when you manage to return exactly 12 points in one case, changing the radius (that is, the size given as w and h) will affect the number of returned points.
Computing the positions of 12 points in a circular formation is particularly easy. You can compute the position on the circle with the sine and cosine of the respective angle. The resulting points may then be scaled or moved in order to obtain the desired shape.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ClockCirclesTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new GridLayout(1, 0));
frame.getContentPane().add(new ClockCirclesPanel());
frame.setSize(400,400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class ClockCirclesPanel extends JPanel
{
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int w = getWidth() / 2;
int h = getHeight() / 2;
Point2D points[] = computePoints(w, h, 12);
int cx = getWidth() / 2;
int cy = getHeight() / 2;
g.translate(cx, cy);
for (Point2D p : points)
{
g.draw(new Ellipse2D.Double(
p.getX() - 5, p.getY() - 5,
10, 10));
}
}
private Point2D[] computePoints(double w, double h, int n)
{
Point2D points[] = new Point2D[n];
double angleDeltaRad = Math.PI * 2 / n;
for (int i=0; i<n; i++)
{
double angleRad = i * angleDeltaRad;
double ca = Math.cos(angleRad);
double sa = Math.sin(angleRad);
double x = sa * w/2;
double y = ca * h/2;
points[i] = new Point2D.Double(x,y);
}
return points;
}
}
Related
I need to be able to draw very crowded points for a graph, via Swing. E.g: let's look at the following points:
p1=(35.19589389346247,32.10152879327731),
p2 = (35.20319591121872,32.10318254621849),
p3 = (35.20752617756255,32.1025646605042),
p4 = (35.21007339305892,32.10107446554622),
p5 = (35.21310882485876,32.104636394957986),
etc...
I want to draw them, however, as can be seen, their coordinates are very dense to each other. In addition, expanding the scale also didn't work, but just moved the points inside the frame.
Here is a glance at my attempt:
private void Oval(Graphics2D g2, String id, int locationX, int locationY) {
AffineTransform old = g2.getTransform();
g2.scale(4,4);
g2.setPaint(Color.blue);
g2.fillOval(locationX - Radius, locationY - Radius, Radius * 2, Radius * 2);
g2.setPaint(Color.black);
g2.drawString(id, locationX + Radius, locationY - Radius);
g2.setTransform(old);
}
This is the code for the point drawing, considering the panel dimension is (1000,1000).
Here is the output of the points' drawing:
As you can see, they override each other, and it's clearly nothing like I was intended to do. My goal is to separate them so we can see the points clearly.
So, the basic concept is to have some kind of "translation" for the "virtual" coordinates (your points) to the "physical" coordinates (of the screen)
You did try and do this by scaling the graphics context, but the problem with this is it will also scale the size of the balls as well, which isn't really what you want to do.
The following is a really basic example of one possible solution.
It calculates the min/max range of the area represented by the points and then uses the component size to translate the points so that they will fit within the visible space
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.Point2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GraphPoint {
private String id;
private Point2D point;
public GraphPoint(String id, Point2D point) {
this.id = id;
this.point = point;
}
public String getId() {
return id;
}
public Point2D getPoint() {
return point;
}
}
public class TestPane extends JPanel {
private List<GraphPoint> points;
private int radius = 10;
private double virtualScale = 1.0;
private Point2D minRange;
private Point2D maxRange;
public TestPane() {
points = new ArrayList<>(16);
points.add(new GraphPoint("1", new Point2D.Double(35.19589389346247, 32.10152879327731)));
points.add(new GraphPoint("2", new Point2D.Double(35.20319591121872, 32.10318254621849)));
points.add(new GraphPoint("3", new Point2D.Double(35.20752617756255, 32.1025646605042)));
points.add(new GraphPoint("4", new Point2D.Double(35.21007339305892, 32.10107446554622)));
points.add(new GraphPoint("5", new Point2D.Double(35.21310882485876, 32.104636394957986)));
double minX = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double minY = Double.MAX_VALUE;
double maxY = Double.MIN_VALUE;
for (GraphPoint gp : points) {
minX = Math.min(minX, gp.getPoint().getX());
maxX = Math.max(maxX, gp.getPoint().getX());
minY = Math.min(minY, gp.getPoint().getY());
maxY = Math.max(maxY, gp.getPoint().getY());
}
minRange = new Point2D.Double(minX, minY);
maxRange = new Point2D.Double(maxX, maxY);
double xRange = maxRange.getX() - minRange.getX();
double yRange = maxRange.getY() - minRange.getY();
System.out.println(minRange.getX() + " - " + minRange.getY());
System.out.println(maxRange.getX() + " - " + maxRange.getY());
System.out.println(xRange + " - " + yRange);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (GraphPoint gp : points) {
paintPoint(g2d, gp);
}
g2d.dispose();
}
private void paintPoint(Graphics2D g2d, GraphPoint gp) {
Graphics2D g2 = (Graphics2D) g2d.create();
Point2D translated = translate(gp);
double xPos = translated.getX();
double yPos = translated.getY();
double offset = radius;
g2.translate(xPos - offset, yPos - offset);
g2.setPaint(Color.blue);
g2.fill(new Ellipse2D.Double(0, 0, offset * 2, offset * 2));
g2.dispose();
}
protected Point2D translate(GraphPoint gp) {
double xRange = maxRange.getX() - minRange.getX();
double yRange = maxRange.getY() - minRange.getY();
double offset = radius;
double width = getWidth() - (offset * 2);
double height = getHeight() - (offset * 2);
double xScale = width / xRange;
double yScale = height / yRange;
Point2D original = gp.getPoint();
double x = offset + ((original.getX() - minRange.getX()) * xScale);
double y = offset + ((original.getY() - minRange.getY()) * yScale);
System.out.println(gp.getId() + " " + x + " x " + y);
return new Point2D.Double(x, y);
}
}
}
I have to stress that this is one possible solution and that your actual requirements might differ, but this should give you starting point from which you can define your own algorithm, for example, you could define your own min/max range (ie 34x30 to 36x33)
the String I have tried now for 1 hour and I didn't get it I did edit your code already you help us a lot. the string above the points the id I mean point "0" this string above the point if u can help us or show us it will be appreciated a lot!
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GraphPoint {
private String id;
private Point2D point;
public GraphPoint(String id, Point2D point) {
this.id = id;
this.point = point;
}
public String getId() {
return id;
}
public Point2D getPoint() {
return point;
}
}
public class TestPane extends JPanel {
private List<GraphPoint> points;
private int radius = 10;
private double virtualScale = 1.0;
private Point2D minRange;
private Point2D maxRange;
public TestPane() {
points = new ArrayList<>(16);
points.add(new GraphPoint("1", new Point2D.Double(35.19589389346247, 32.10152879327731)));
points.add(new GraphPoint("2", new Point2D.Double(35.20319591121872, 32.10318254621849)));
points.add(new GraphPoint("3", new Point2D.Double(35.20752617756255, 32.1025646605042)));
points.add(new GraphPoint("4", new Point2D.Double(35.21007339305892, 32.10107446554622)));
points.add(new GraphPoint("5", new Point2D.Double(35.21310882485876, 32.104636394957986)));
double minX = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double minY = Double.MAX_VALUE;
double maxY = Double.MIN_VALUE;
for (GraphPoint gp : points) {
minX = Math.min(minX, gp.getPoint().getX());
maxX = Math.max(maxX, gp.getPoint().getX());
minY = Math.min(minY, gp.getPoint().getY());
maxY = Math.max(maxY, gp.getPoint().getY());
}
minRange = new Point2D.Double(minX, minY);
maxRange = new Point2D.Double(maxX, maxY);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
double insets = fm.getHeight() + radius;
// I'm lazy, so I'm drawing the lines first, then painting
// the points over the top as I can't be bothered to workout
// a clever way to ensure the lines are always painted under
// the dots
GraphPoint lastPoint = null;
for (GraphPoint gp : points) {
if (lastPoint != null) {
paintLine(g2d, lastPoint, gp, insets);
}
lastPoint = gp;
}
for (GraphPoint gp : points) {
paintPoint(g2d, gp, insets);
}
g2d.dispose();
}
private void paintLine(Graphics2D g2d, GraphPoint from, GraphPoint to, double insets) {
Point2D fromPoint = translate(from, insets);
Point2D toPoint = translate(to, insets);
g2d.setColor(Color.RED);
g2d.draw(new Line2D.Double(fromPoint, toPoint));
}
private void paintPoint(Graphics2D g2d, GraphPoint gp, double insets) {
Graphics2D g2 = (Graphics2D) g2d.create();
Point2D translated = translate(gp, insets);
double xPos = translated.getX();
double yPos = translated.getY();
double offset = radius;
g2.translate(xPos - offset, yPos - offset);
g2.setPaint(Color.blue);
g2.fill(new Ellipse2D.Double(0, 0, offset * 2, offset * 2));
FontMetrics fm = g2d.getFontMetrics();
String text = gp.getId();
double x = xPos - (fm.stringWidth(text) / 2);
double y = (yPos - radius - fm.getHeight()) + fm.getAscent();
g2d.drawString(text, (float)x, (float)y);
g2.dispose();
}
protected Point2D translate(GraphPoint gp, double insets) {
double xRange = maxRange.getX() - minRange.getX();
double yRange = maxRange.getY() - minRange.getY();
double offset = insets;
double width = getWidth() - (offset * 2);
double height = getHeight() - (offset * 2);
double xScale = width / xRange;
double yScale = height / yRange;
Point2D original = gp.getPoint();
double x = offset + ((original.getX() - minRange.getX()) * xScale);
double y = offset + ((original.getY() - minRange.getY()) * yScale);
System.out.println(gp.getId() + " " + x + " x " + y);
return new Point2D.Double(x, y);
}
}
}
The trick with the text is in understanding that I've already translated the origin point of the context, so the 0x0 position is actually the middle of the circle, so you need to subtract the text offset from it.
I've also made a slight update to allow the workflow to pass in a insets value which will decrease the available visible area to "help" compensate for the text
another question can make a line with an arrow in the tip
See...
Java make a directed line and make it move
How to draw 2d arrows with Java Swing?
for some ideas. Just beware, this becomes increasingly more complex, as the arrows need to be rotated towards the point they are looking, something like Connect two circles with a line might help
I have to make a program that generates stars in random locations of random size. My code already plots the stars in random locations, but I can't manage to randomly change their sizes. I tried assigning a size factor to each point to alter the distance between them but the stars came out all messed up. Is there a scaling method I can use?
Here is what I have so far, it plots the stars in random locations.
final int MID = WIDTH / 2;
final int TOP = 50;
//sky
Color skyColor = new Color(0, 0, 0);
page.fillRect(0,0,getWidth(),getHeight());
//ground
Color groundColor = new Color(95,95,95);
page.setColor(groundColor);
page.fillRect(0,HEIGHT-20,getWidth(),getHeight());
//star
for (int i = 1; i <= starCount; i++)
{
int ranLocX = gen.nextInt(700 - 100) + 100;
int ranLocY = gen.nextInt(300 - 75) + 75;
int ranSize = gen.nextInt(8 - 1) + 1;
int sizeXA = (-10 * ranSize);
int sizeXB = (10 * ranSize);
int sizeXC = (-5 * ranSize);
int sizeXD = (-10 * ranSize);
int sizeXE = (-10 * ranSize);
int sizeXF = (-10 * ranSize);
int sizeYC = (10 * ranSize);
int sizeYD = (-10 * ranSize);
int sizeYE = (10 * ranSize);
page.drawPolygon(new int[] {xa + ranLocX, xb + ranLocX, xc + ranLocX, xd + ranLocX, xe + ranLocX, xf + ranLocX}, new int[] {ya + ranLocY, yb + ranLocY, yc + ranLocY, yd + ranLocY, ye + ranLocY, yf + ranLocY}, 6);
}
Here is a simple method you can use to create a Shape with any given number of points and radius:
public static Shape radiusShape(int points, int... radii)
{
Polygon polygon = new Polygon();
for (int i = 0; i < points; i++)
{
double radians = Math.toRadians(i * 360 / points);
int radius = radii[i % radii.length];
double x = Math.cos(radians) * radius;
double y = Math.sin(radians) * radius;
polygon.addPoint((int)x, (int)y);
}
Rectangle bounds = polygon.getBounds();
polygon.translate(-bounds.x, -bounds.y);
return polygon;
}
To create your 5 point star you would use code like:
Shape star = ShapeUtils.radiusShape(10, 30, 12);
It will create a star with 5 outer points and 5 inner points to give the star shape.
So to randomize the size of the star you would randomize the radius.
Check out Playing With Shapes for more examples of the types of Shapes you can create using this method. The above radiusShape(...) method was taken from the ShapeUtils class found in the above link.
I would then suggest you create a custom class with the properties 1) Shape 2) Point so you can paint the Star at different locations on the panel. Then you create an ArrayList to hold instances of the class. In your painting method you iterate through this ArrayList to paint each Shape. The above link will also provide basic code for this concept.
Here's an example of how to change the size of a Polygon. I drew squares but any Shape will work.
just create a scale instance of an AffineTransform and use that to scale the Shape.
I used ThreadLocalRandom to randomly choose the scale to be applied. I always copy the original polygon(template) and then scale that.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Polygons extends JPanel {
static int WIDTH = 500;
static int HEIGHT = 500;
JFrame f = new JFrame();
Polygon b =new Polygon();
ThreadLocalRandom r = ThreadLocalRandom.current();
List<Shape> polys = new ArrayList<>();
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> new Polygons().start());
}
public void start() {
f.add(this);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
Polygon template = new Polygon();
template.addPoint(0,0);
template.addPoint(0,100);
template.addPoint(100,100);
template.addPoint(100,0);
// AffineTransform rotate = AffineTransform.getRotateInstance(Math.toRadians(72.), )
for (int i = 0; i < 20; i++) {
Polygon p = new Polygon(template.xpoints,template.ypoints, template.npoints);
p.translate(r.nextInt(WIDTH), r.nextInt(HEIGHT));
double scale = r.nextInt(10,90)/100.;
AffineTransform scaleIt = AffineTransform.getScaleInstance(scale,scale);
polys.add(scaleIt.createTransformedShape(p));
}
}
public Dimension getPreferredSize() {
return new Dimension(WIDTH,HEIGHT);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Shape shape : polys) {
g2d.draw(shape);
}
}
}
Here is something I wrote a long time ago to create a 5 point star. It draws a single arm and then rotates 72 degrees and draws another, repeating the process.
Because it was written to allow the base to be changed, hence the star size, this might be as better option for scaling the size of your stars rather than using the AffineTransform mentioned above:
for (int i = 0; i < 50; i++) {
// get the base of the next star between 5 and 29 inclusive
int base = r.nextInt(5,30);
Polygon star = createStar(base);
// now randomly position it.
star.translate(r.nextInt(0,400),r.nextInt(0,400));
// and add to the list
polys.add(star);
}
Creating a 5 point star
int startx = 250; // arbitrary starting points
int starty = 250;
public Polygon createStar(int armBase) {
Polygon star = new Polygon();
// The armBase is equal to one side of the inner
// pentagon of the star
// The height of the arm is the distance from the middle of the
// base to the tip of the stars arm. Since the tangent computes
// ratio of the sides of a right triangle, multiplying by half
// the base gives the other side, hence the height.
int armHeight =
(int) (armBase / 2 * Math.tan(Math.toRadians(72)));
// The center offset is the distance from the middle of a given
// base to the center of the inner pentagon.
int centerOffset =
(int) (armBase / 2 * Math.tan(Math.toRadians(54)));
// this works by creating the first arm, rotating 72 degrees
// and then adding the other two coodinates of succeeding arms.
star.addPoint(startx, starty);
star.addPoint(startx + armBase / 2, starty - armHeight);
star.addPoint(startx + armBase, starty);
for (int j = 0; j < 4; j++) {
rotatePolygon(-Math.PI / 5 * 2, startx + armBase / 2,
starty + centerOffset, star);
star.addPoint(startx + armBase / 2, starty - armHeight);
star.addPoint(startx + armBase, starty);
}
star.npoints--;
star.translate(-star.getBounds().x,-star.getBounds().y);
return star;
}
// This is general purpose rotation that rotates about a center
// point. This can be derived using the double angle identities of
// for sin and cosine.
private void rotatePolygon(double ang, double sx, double sy,
Polygon poly) {
for (int j = 0; j < poly.npoints; j++) {
double x = poly.xpoints[j];
double y = poly.ypoints[j];
double xx = sx + (x - sx) * Math.cos(ang)
- (y - sy) * Math.sin(ang);
double yy = sy + (x - sx) * Math.sin(ang)
+ (y - sy) * Math.cos(ang);
poly.xpoints[j] = (int) xx;
poly.ypoints[j] = (int) yy;
}
}
Here's a GUI to draw one five-pointed star.
Here's the complete runnable code. I used polar coordinates to calculate the 10 points I needed to draw a star. I guessed the fraction to get the intermediate points correct.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class StarryNight2GUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new StarryNight2GUI());
}
#Override
public void run() {
JFrame frame = new JFrame("Starry Night");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setBackground(Color.BLACK);
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Color groundColor = new Color(95, 95, 95);
g.setColor(groundColor);
g.fillRect(0, getHeight() - 30, getWidth(), 30);
Polygon polygon = createStar(new Point(320, 240), 80);
g.setColor(Color.YELLOW);
g.fillPolygon(polygon);
}
private Polygon createStar(Point centerPoint, int radius) {
Polygon polygon = new Polygon();
// 72, 144, 216, 288, 360
// 36, 108, 180, 252, 324
// 54, 126, 198, 270, 342
// 18, 54, 90, 126, 162, 198, 234, 270, 306, 342
for (int angle = 18; angle < 360; angle += 72) {
double r = 0.42 * radius;
Point point = toCartesian(centerPoint, angle, r);
polygon.addPoint(point.x, point.y);
point = toCartesian(centerPoint, angle + 36, radius);
polygon.addPoint(point.x, point.y);
}
return polygon;
}
private Point toCartesian(Point centerPoint, int angle, double radius) {
double theta = Math.toRadians(angle);
int x = centerPoint.x + (int) Math.round(Math.cos(theta) * radius);
int y = centerPoint.y + (int) Math.round(Math.sin(theta) * radius);
return new Point(x, y);
}
}
}
I have 3 different functions superimposed on each other and I'm trying to add radio buttons so that when button1 is selected, the function associated with it will draw, and the others will be invisible.
If there is a better way to do this, I'll do that.
import java.awt.*;
import java.awt.geom.Path2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.*;
public class DrawingStuff extends JComponent {
DrawingStuff(){
JRadioButton line1 = new JRadioButton("F(x)");
JRadioButton line2 = new JRadioButton("G(x)");
JRadioButton line3 = new JRadioButton("Cos(2x)");
ButtonGroup bG = new ButtonGroup();
bG.add(line1);
bG.add(line2);
bG.add(line3);
this.setLayout( new FlowLayout());
this.add(line1);
this.add(line2);
this.add(line3);
line1.setSelected(true);
this.setVisible(true);
}
public void paintComponent(Graphics g)
{
//w is x, and h is y (as in x/y values in a graph)
int w = this.getWidth()/2;
int h = this.getHeight()/2;
Graphics2D g1 = (Graphics2D) g;
g1.setStroke(new BasicStroke(2));
g1.setColor(Color.black);
g1.drawLine(0,h,w*2,h);
g1.drawLine(w,0,w,h*2);
g1.drawString("0", w - 7, h + 13);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
g2.setColor(Color.red);
int scale = 4;
//Path2D path1 = new Path2D.Double();
//path1.moveTo(w, h);
Polygon p = new Polygon();
for (int x = 0; x <= 4; x++) {
//path1.lineTo(w+scale*x, h - scale*((x*x*x) + x - 3));
p.addPoint(w+scale*x, h - scale*((x*x*x) + x - 3));
}
//g2.draw(path1);
g2.drawPolyline(p.xpoints, p.ypoints, p.npoints);
Polygon p1 = new Polygon();
//Path2D path2 = new Path2D.Double();
//path2.moveTo(w, h);
for (int x = -10; x <= 10; x++) {
//path2.lineTo(w+scale*x, h - scale * ((x*x*x)/100) - x + 10);
p1.addPoint(w + scale * x, h - scale * ((x*x*x)/100) - x + 10);
}
//g2.draw(path2);
g2.drawPolyline(p1.xpoints, p1.ypoints, p1.npoints);
Path2D path = new Path2D.Double();
for (int i = 0; i < 100; i++) {
double theta = i * 2 * Math.PI / 100;
double r = Math.cos(2 * theta);
double dX = w * r * Math.cos(theta) + w;
double dY = h * r * Math.sin(theta) + h;
if (i == 0) {
path.moveTo(dX, dY);
} else {
path.lineTo(dX, dY);
}
}
path.closePath();
g2.draw(path);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(800, 600);
frame.setTitle("Graphs");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
DrawingStuff draw = new DrawingStuff();
frame.add(draw);
frame.setVisible(true);
}
}
I'm not sure how to call the buttons from the constructor, and when I attempted to set a function aside for the buttons, they would replicate whenever I would maximize or restore down the window.
I also don't know how to erase a a line once plotted.
Thanks.
I also don't know how to erase a a line once plotted.
The secret is to draw only visible lines in your paintComponent() implementation. To do this, wrap your function in a class that maintains a notional visible field and give that class methods to get and set the attribute.
public void paintComponent(Graphics g) {
…
if (x. getFunctionVisible()) {
g2.drawPolyline(…);
}
}
In this example, VisibleAction uses getSeriesVisible() and setSeriesVisible() to effect a change; you implementation might declare getSeriesVisible() and setSeriesVisible() to effect a change; in contrast, your implementation might define getFunctionVisible() and set getFunctionVisible sVisible().
A relate example is seen here.
Really having trouble trying to draw a four leaf rose: This is the exercise:
Draw a picture of the “fourleaved rose” whose equation in polar coordinates is
r =cos(2θ) . Let θ go from 0 to 2*pi in 100 steps. Each time, compute r and then compute the (x, y) coordinates from the polar coordinates by using the formula
x = r ⋅ cos( θ) , y = r ⋅ sin(θ )
My Code:
public Rose(double awidth, double aheight)
{
width = awidth;
height = aheight;
theta = 0;
}
public void drawRose(Graphics2D g2)
{
Ellipse2D.Double test ;
double r = 0;
for(int i = 0; i <= 100; i++)
{
r = Math.cos(Math.toRadians(2*theta) );
x = r *( Math.cos( Math.toRadians(theta) ) * width ) + 300;
y = r * ( Math.sin( Math.toRadians(theta) ) * height ) + 300 ;
test = new Ellipse2D.Double(x, y, width, height);
theta += 3.6;
g2.draw(test);
}
}
}
Any help will be greatly appreciately.
Your biggest mistake is here:
test = new Ellipse2D.Double(x, y, width, height);
You're creating 100 Ellipses with the points that are on the rose, but that with heights and widths of the desired rose. You really don't want 100 ellipses, but rather you want to connect lines between the x and y points you've created, that is connect the current x, y with the previous ones created (as long as there is a previous x and y).
One way is via these suggestions, but there are other ways to do this:
Use a Path2D object, the concrete implementation would be a Path2D.Double, to hold your data points. Create this before creating data points.
Use a for loop that goes from 0 to 100, and do this in the class's constructor
set your double theta in the loop
set your double r variable in the loop
Calculate your x and y double points
Scale your x and y points by multiplying them with a scale factor so that the drawing has some size. I used 150.0
Translate your x and y values by adding a translation constant. I used 200 and it worked nicely in a 400 x 400 JPanel. Else the center of the rose will be at 0, 0 and only a fourth of it will be visible.
In the first iteration of the for loop call the Path2D's moveTo(...) method to add a starting point
In all other iterations call the lineTo(...) method. This will draw a line between neighboring points.
After the for loop, close the path by calling closePath() on it.
Draw the path in your JPanel's paintComponent method by casting your Graphics parameter into a Graphics2D object (actually you don't need this since your draw method gets a Grahpics2D object), and calling draw(path) with the Graphics2D object, passing in your Path2D object.
For example, this is created:
with this code:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class RosePanel extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int MAX = 100;
private static final double SCALE = 150.0;
private static final double DELTA_X = 200;
private static final double DELTA_Y = DELTA_X;
private static final Color ROSE_COLOR = Color.red;
private static final Stroke ROSE_STROKE = new BasicStroke(8f);
private Path2D path = new Path2D.Double();
public RosePanel() {
for (int i = 0; i < MAX; i++) {
double theta = i * 2 * Math.PI / MAX;
double r = Math.cos(2 * theta);
double dX = SCALE * r * Math.cos(theta) + DELTA_X;
double dY = SCALE * r * Math.sin(theta) + DELTA_Y;
if (i == 0) {
path.moveTo(dX, dY);
} else {
path.lineTo(dX, dY);
}
}
path.closePath();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(ROSE_COLOR);
g2.setStroke(ROSE_STROKE);
g2.draw(path);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
RosePanel mainPanel = new RosePanel();
JFrame frame = new JFrame("RosePanel");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note that the key difference between my code and yours, other than the translations and the scaling is that I'm connecting the line between points created.
I have been trying to figure this one out for some time now, I am making a program that uses a triangle as an arrow and been trying to figure out how to make an arrow with two points, meaning that that the first point would be at the midpoint of the base of the triangle, while the second point would be at the tip facing the direction away from the first point.
This crude paint drawing should help figure out what I am talking about
http://i.stack.imgur.com/f3ktz.png (Would put direct images but don't have enough rep)
Now, I went through and tried figuring out how to calculate those other two endpoints of the triangle so I could make the polygon, but I am not doing it correctly because I am getting a triangle that isn't isosceles and the endpoints don't create a line perpendicular to the original line.
What I am currently getting (With some drawing over it to show the points)
http://i.stack.imgur.com/dljsn.png
My current code
public class Triangle extends Shape{
private boolean assigned = false;
private int[] x;
private int[] y;
public Triangle(Point startPoint, Point endPoint){
this.startPoint = startPoint;
this.endPoint = endPoint;
}
#Override
public void draw(Graphics g) {
g.setColor(Color.white);
if(!assigned) {
x = new int[3];
y = new int[3];
double distance = startPoint.distance(endPoint);
double halfDistance = distance/2;
double angle = getAngle(startPoint,endPoint)- Math.PI/2.0;
x[0] = (int)endPoint.getX();
y[0] = (int)endPoint.getY();
x[1] = (int)((Math.sin(angle)*halfDistance) + startPoint.getX());
y[1] = (int)((Math.cos(angle)*halfDistance) + startPoint.getY());
x[2] = (int)(startPoint.getX() - (Math.sin(angle)*halfDistance));
y[2] = (int)(startPoint.getY() - (Math.cos(angle)*halfDistance));
assigned = true;
if(endPoint.distance(x[1],y[1]) == (Math.sqrt(5)*halfDistance))
System.out.println("DEBUG: Confirm Correct 1");
if(endPoint.distance(x[1],y[1]) == endPoint.distance(x[2],y[2]))
System.out.println("DEBUG: Confirm Correct 2");
}
g.fillPolygon(x,y,3);
g.setColor(Color.blue);
}
private double getAngle(Point pointOne, Point pointTwo){
double angle = Math.atan2(pointTwo.getY()- pointOne.getY(),pointTwo.getX()-pointOne.getX());
while(angle < 0){
angle += (2.0*Math.PI);
}
return angle;
}
}
I have working at this for hours and can't seem to figure it out, someone please help.
So, I ended up replacing double angle = getAngle(startPoint,endPoint)- Math.PI/2.0; with something more like double angle = -Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x);
I wrote this little test program, which allows you to move to points around a circle and which generates the resulting triangle...
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.GridLayout;
import java.awt.Point;
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();
}
TestPane tp = new TestPane();
JPanel control = new JPanel(new BorderLayout());
control.add(tp);
final JSlider startAngel = new JSlider(0, 359);
final JSlider endAngel = new JSlider(0, 359);
JPanel sliders = new JPanel(new GridLayout(1, 2));
sliders.add(startAngel);
sliders.add(endAngel);
startAngel.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
tp.setStartAngle(startAngel.getValue());
}
});
endAngel.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
tp.setEndAngle(endAngel.getValue());
}
});
startAngel.setValue(0);
endAngel.setValue(180);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(control);
frame.add(sliders, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Point startPoint, endPoint;
private float startAngle = 0;
private float endAngle = 180;
public TestPane() {
}
#Override
public void invalidate() {
super.invalidate();
recalculate();
}
protected void recalculate() {
int dim = Math.min(getWidth(), getHeight());
dim -= 50;
float radius = dim / 2f;
startPoint = getPointOnCircle(startAngle, radius);
endPoint = getPointOnCircle(endAngle, radius);
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected Point getPointOnCircle(float degress, float radius) {
int x = Math.round(getWidth() / 2);
int y = Math.round(getHeight() / 2);
double rads = Math.toRadians(degress - 90); // 0 becomes the top
// Calculate the outter point of the line
int xPosy = Math.round((float) (x + Math.cos(rads) * radius));
int yPosy = Math.round((float) (y + Math.sin(rads) * radius));
return new Point(xPosy, yPosy);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int[] x = new int[3];
int[] y = new int[3];
double distance = startPoint.distance(endPoint);
double halfDistance = distance / 2;
double angle = -Math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x);
System.out.println(angle);
x[0] = (int) endPoint.getX();
y[0] = (int) endPoint.getY();
x[1] = (int) ((Math.sin(angle) * halfDistance) + startPoint.getX());
y[1] = (int) ((Math.cos(angle) * halfDistance) + startPoint.getY());
x[2] = (int) (startPoint.getX() - (Math.sin(angle) * halfDistance));
y[2] = (int) (startPoint.getY() - (Math.cos(angle) * halfDistance));
g2d.setColor(Color.RED);
g2d.fillPolygon(x, y, 3);
g2d.setColor(Color.BLUE);
g2d.fillOval(startPoint.x - 5, startPoint.y - 5, 10, 10);
g2d.setColor(Color.GREEN);
g2d.fillOval(endPoint.x - 5, endPoint.y - 5, 10, 10);
g2d.dispose();
}
public void setStartAngle(float value) {
startAngle = value;
recalculate();
}
public void setEndAngle(float value) {
endAngle = value;
recalculate();
}
}
}
If that still gives you some weird results, apart from sharing some test data, I might consider using something like Math.atan2(Math.abs(endPoint.y - startPoint.y), Math.abs(endPoint.x - startPoint.x)) or simular
You don't need to calculate angles at all.
double startX = 40;
double startY = 120;
double endX = 110;
double endY = 15;
double deltaX = ( startY - endY ) / 2;
double deltaY = ( endX - startX ) / 2;
double[] polygonX = new double[3];
double[] polygonY = new double[3];
polygonX[0] = endX;
polygonY[0] = endY;
polygonX[1] = startX - deltaX;
polygonY[1] = startY - deltaY;
polygonX[2] = startX + deltaX;
polygonY[2] = startY + deltaY;
The drawing is VERY bad :D, but the point is that:
cos(ang) = 'distance' / ( startY - endY )
and
cod(ang) = ('distance'/2) / deltaX
so
deltaX = ( startY - endY ) / 2
The same aplies to deltaY = ( endX - startX ) / 2
So the other 2 point of the triangle, will be the startPoint minus and plus those deltas.