What I made:
I drew X and Y Axises and my segments;
I wrote a code, that returns true if the segments intersect.
I wrote a code, that returns the coordinate of an intersection point.
But how I can calculate a ratio does the intersection point divide line segments?
Thanks a lot for your help. ^^
My code is:
package com.staaankey;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Cartesian {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
CartesianFrame frame = new CartesianFrame();
frame.showUI();
}
});
}
}
class CartesianFrame extends JFrame {
CartesianPanel panel;
public CartesianFrame() {
panel = new CartesianPanel();
add(panel);
}
public void showUI() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Main");
setSize(700, 700);
setVisible(true);
}
}
class CartesianPanel extends JPanel {
// x-axis coord constants
public static final int X_AXIS_FIRST_X_COORD = 50;
public static final int X_AXIS_SECOND_X_COORD = 600;
public static final int X_AXIS_Y_COORD = 600;
// y-axis coord constants
public static final int Y_AXIS_FIRST_Y_COORD = 50;
public static final int Y_AXIS_SECOND_Y_COORD = 600;
public static final int Y_AXIS_X_COORD = 50;
//arrows of axis are represented with "hipotenuse" of
//triangle
// now we are define length of cathetas of that triangle
public static final int FIRST_LENGHT = 10;
public static final int SECOND_LENGHT = 5;
// size of start coordinate lenght
public static final int ORIGIN_COORDINATE_LENGHT = 6;
// distance of coordinate strings from axis
public static final int AXIS_STRING_DISTANCE = 20;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// x-axis
g2.drawLine(X_AXIS_FIRST_X_COORD, X_AXIS_Y_COORD,
X_AXIS_SECOND_X_COORD, X_AXIS_Y_COORD);
// y-axis
g2.drawLine(Y_AXIS_X_COORD, Y_AXIS_FIRST_Y_COORD,
Y_AXIS_X_COORD, Y_AXIS_SECOND_Y_COORD);
// x-axis arrow
g2.drawLine(X_AXIS_SECOND_X_COORD - FIRST_LENGHT,
X_AXIS_Y_COORD - SECOND_LENGHT,
X_AXIS_SECOND_X_COORD, X_AXIS_Y_COORD);
g2.drawLine(X_AXIS_SECOND_X_COORD - FIRST_LENGHT,
X_AXIS_Y_COORD + SECOND_LENGHT,
X_AXIS_SECOND_X_COORD, X_AXIS_Y_COORD);
// y-axis arrow
g2.drawLine(Y_AXIS_X_COORD - SECOND_LENGHT,
Y_AXIS_FIRST_Y_COORD + FIRST_LENGHT,
Y_AXIS_X_COORD, Y_AXIS_FIRST_Y_COORD);
g2.drawLine(Y_AXIS_X_COORD + SECOND_LENGHT,
Y_AXIS_FIRST_Y_COORD + FIRST_LENGHT,
Y_AXIS_X_COORD, Y_AXIS_FIRST_Y_COORD);
// draw origin Point
g2.fillOval(
X_AXIS_FIRST_X_COORD - (ORIGIN_COORDINATE_LENGHT / 2),
Y_AXIS_SECOND_Y_COORD - (ORIGIN_COORDINATE_LENGHT / 2),
ORIGIN_COORDINATE_LENGHT, ORIGIN_COORDINATE_LENGHT);
// draw text "X" and draw text "Y"
g2.drawString("X", X_AXIS_SECOND_X_COORD - AXIS_STRING_DISTANCE / 2,
X_AXIS_Y_COORD + AXIS_STRING_DISTANCE);
g2.drawString("Y", Y_AXIS_X_COORD - AXIS_STRING_DISTANCE,
Y_AXIS_FIRST_Y_COORD + AXIS_STRING_DISTANCE / 2);
g2.drawString("(0, 0)", X_AXIS_FIRST_X_COORD - AXIS_STRING_DISTANCE,
Y_AXIS_SECOND_Y_COORD + AXIS_STRING_DISTANCE);
// numerate axis
int xCoordNumbers = 10;
int yCoordNumbers = 10;
int xLength = (X_AXIS_SECOND_X_COORD - X_AXIS_FIRST_X_COORD)
/ xCoordNumbers;
int yLength = (Y_AXIS_SECOND_Y_COORD - Y_AXIS_FIRST_Y_COORD)
/ yCoordNumbers;
// draw x-axis numbers
for(int i = 1; i < xCoordNumbers; i++) {
g2.drawLine(X_AXIS_FIRST_X_COORD + (i * xLength),
X_AXIS_Y_COORD - SECOND_LENGHT,
X_AXIS_FIRST_X_COORD + (i * xLength),
X_AXIS_Y_COORD + SECOND_LENGHT);
g2.drawString(Integer.toString(i),
X_AXIS_FIRST_X_COORD + (i * xLength) - 3,
X_AXIS_Y_COORD + AXIS_STRING_DISTANCE);
}
//draw y-axis numbers
for(int i = 1; i < yCoordNumbers; i++) {
g2.drawLine(Y_AXIS_X_COORD - SECOND_LENGHT,
Y_AXIS_SECOND_Y_COORD - (i * yLength),
Y_AXIS_X_COORD + SECOND_LENGHT,
Y_AXIS_SECOND_Y_COORD - (i * yLength));
g2.drawString(Integer.toString(i),
Y_AXIS_X_COORD - AXIS_STRING_DISTANCE,
Y_AXIS_SECOND_Y_COORD - (i * yLength));
}
//draw first section
int fX1 = 125;
int fY1 = 75;
int fX2 = 135;
int fY2 = 75;
g2.drawLine(fX1, fY1, fX2, fY2);
//draw second section
int sX1 = 130;
int sY1 = 50;
int sX2 = 140;
int sY2 = 150;
g2.drawLine(sX1, sY1, sX2, sY2);
int v1 = (sX2 - sX1) * (fY1 - sY1) - (sY2 - sY1) * (fX1 - sX1);
int v2 = (sX2 - sX1) * (fY2 - sX1) - (sY2 - sY1) * (fX2 - sX1);
int v3 = (fX2 - fX1) *(sY1 - fY1) - (fY2 - fY1) * (sX1 - fX1);
int v4 = (fX2 - fX1) * (sY2 - fY1) - (fY2 - fY1) * (sX2 - fX1);
Boolean result;
if((v1 * v2 < 0) && (v3 * v4 < 0)){
result = true;
}
else{
result = false;
}
System.out.println(result);
int detL1 = det(fX1, fY1, fX2, fY2);
int detL2 = det(sX1, sY1, sX2, sY2);
int x1mx2 = fX1 - fX2;
int x3mx4 = sX1 - sX2;
int y1my2 = fY1 - fY2;
int y3my4 = sY1 - sY2;
int denom = det(x1mx2, y1my2, x3mx4, y3my4);
if(denom == 0){
return;
}
int xnom = det(detL1 ,x1mx2, detL2, x3mx4);
int ynom = det(detL1, y1my2, detL2, y3my4);
int ixOut = xnom / denom;
int iYOut = ynom / denom;
System.out.println("ixOut: ");
System.out.println("iYOut: ");
System.out.println(ixOut);;
System.out.println(iYOut);
}
public int det(int x1, int y1, int x2, int y2){
return x1 * y2 - y1 * x2;
}
}
Use the distance formula, Math.hypot() to calculate the length of the segments. That would be from one end of the line (it's x,y location) to the intersection point. Then from the other lines x,y location to the intersection point. Then divide the smaller length by the larger length. Make certain you use floating point math for the result.
Example:
// some arbitrary line with slope = 5 and y-intercept = 23
UnaryOperator<Integer> y = x->5*x + 23;
int endx = 2;
int endy = y.apply(endx);
int otherendx = 10;
int otherendy = y.apply(otherendx);
int intx = 5;
int inty = y.apply(intx);
double seg1Length = Math.hypot(endx-intx, endy - inty);
double seg2Length = Math.hypot(otherendx-intx, otherendy - inty);
double ratio = Math.min(seg1Length,seg2Length)/Math.max(seg1Length,seg2Length);
prints
Ratio = 0.6
Related
I've looked at several examples of people creating tile maps, and I am unable to get the tile position where my mouse is pointed at.
I am using a spritebatch and GameTile[][] to create the map. Keep in mind that the tiles themselves are isometric and not actually a square.
The method renderMap() is where the map is actually is being rendered. createMap() just sets the initial GameTiles for an empty map.
The map is able to be dragged and zoomed in and out using Ortho camera.
Zooming out gives me an issue as well, the tiles seem to be shifted over on click
public class MapEditor implements GameScene {
private GameContext context;
private SpriteBatch batch;
private OrthographicCamera camera;
public static GameTile[][] tiles; //GameTile.WIDTH = 64 & GameTile.HEIGHT =48
public static final int MAP_WIDTH = 20;
public static final int MAP_HEIGHT = 36;
public MapEditor(GameContext context) {
this.context = context;
tiles = new GameTile[MAP_WIDTH][MAP_HEIGHT];
}
#Override
public void create() {
renderer = new ShapeRenderer();
this.batch = new SpriteBatch();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
public void createMap() {
// Create the sea tiles
for (int x = 0; x < MAP_WIDTH; x++) {
for (int y = 0; y < MAP_HEIGHT; y++) {
if (y < 3 || y > 32) {
if(tiles[x][y] == null) {
tiles[x][y] = safezone;
}
}
else {
if(tiles[x][y] == null) {
tiles[x][y] = cell;
}
}
}
}
}
#Override
public void update(){
// update the camera
camera.update();
}
#Override
public void render() {
batch.setProjectionMatrix(camera.combined);
batch.begin();
Gdx.gl.glViewport(0,0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
renderMap();
batch.end();
}
public int getTileX(float x, float y) {
/*
* getRegionWidth() = TILE_WIDTH_HALF
* getRegionHeight() = TILE_HEIGHT_HALF
* these are the ones being added to worldCoords.x/y
*/
Vector3 worldCoords = camera.unproject(new Vector3(x, y, 0));
return (int)((TILE_WIDTH_HALF * ((-TILE_HEIGHT_HALF + (worldCoords.y + TILE_HEIGHT_HALF)) /
TILE_HEIGHT_HALF) + (worldCoords.x + TILE_WIDTH_HALF)) / TILE_WIDTH_HALF) / 2;
}
public int getTileY(float x, float y) {
/*
* getRegionWidth() = TILE_WIDTH_HALF
* getRegionHeight() = TILE_HEIGHT_HALF
* these are the ones being added to worldCoords.x/y
*/
Vector3 worldCoords = camera.unproject(new Vector3(x, y, 0));
return (int)(((-TILE_HEIGHT_HALF * (TILE_WIDTH_HALF + (worldCoords.x + TILE_WIDTH_HALF)) /
TILE_WIDTH_HALF) + (worldCoords.y + TILE_HEIGHT_HALF)) / TILE_HEIGHT_HALF) / 2;
}
#Override
public boolean handleClick(float x, float y, int button) {
int tileX = getTileX(x,y);
int tileY = getTileY(x,y);
System.out.println("Tile:"+tileX + ","+tileY);
}
private void renderMap() {
for (int i = 0; i < tiles.length; i++) {
for(int j = 0; j < tiles[i].length; j++) {
TextureRegion region = tiles[i][j].getRegion();
int x = (i * GameTile.TILE_WIDTH / 2) - (j * GameTile.TILE_WIDTH / 2) - region.getRegionWidth() / 2;
int y = (i * GameTile.TILE_HEIGHT / 2) + (j * GameTile.TILE_HEIGHT / 2) - region.getRegionHeight() / 2;
if (canDraw(x, y, GameTile.TILE_WIDTH, GameTile.TILE_HEIGHT)) {
batch.draw(region, x, y);
}
}
}
}
Actual tile before doing anything to it;
Actual:
Desired:
Converting Cartesian coordinates to isometric is (sort of) done like this:
float isometricX = cartesianX - cartesianY;
float isometricY = (cartesianX + cartesianY) * 0.5f;
The formula needs to be scaled by the height-to-width ratio of the tiles as well and I think that is where it's going wrong in your code.
Given an unprojected worldMousePosition you can get the coordinates and tile coordinates like this:
float r = (float) TILE_HEIGHT / (float) TILE_WIDTH;
float mapx = (worldMousePosition.x / TILE_HEIGHT + worldMousePosition.y / (TILE_HEIGHT * r)) * r;
float mapy = (worldMousePosition.y / (TILE_HEIGHT * r) - (worldMousePosition.x / TILE_HEIGHT)) * r;
worldPosition = new Vector2(mapx - 0.5f, mapy + 0.5f); // -.5/+.5 because the drawing isn't aligned to the tile, it's aligned to the image
int tileX = (int) worldPosition.x;
int tileY = (int) worldPosition.y;
Full source code for the example above:
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
public class SandboxGame extends Game {
public static final int TILE_NONE = -1;
public static final int MAP_WIDTH = 20;
public static final int MAP_HEIGHT = 36;
public static final int TILE_WIDTH = 64;
public static final int TILE_HEIGHT = 48;
private SpriteBatch batch;
private OrthographicCamera camera;
private BitmapFont font;
private Vector3 unprojectVector = new Vector3();
private Vector2 worldMousePosition = new Vector2();
private Vector2 worldPosition = new Vector2();
private Texture[] textures;
private int[][] tiles = new int[MAP_WIDTH][MAP_HEIGHT];
#Override
public void create() {
batch = new SpriteBatch();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
font = new BitmapFont(Gdx.files.internal("default.fnt"), Gdx.files.internal("default.png"), false);
textures = new Texture[] {
new Texture(Gdx.files.internal("tile.png"))
};
for(int x = 0; x < MAP_WIDTH; ++x) {
for(int y = 0; y < MAP_HEIGHT; ++y) {
int rnd = MathUtils.random(10);
if (rnd < 1)
tiles[x][y] = TILE_NONE;
else
tiles[x][y] = 0;
}
}
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
float scrollSpeed = 64;
float zoomSpeed = 2;
float delta = Gdx.graphics.getDeltaTime();
if (Gdx.input.isKeyPressed(Input.Keys.A))
camera.position.x -= delta * scrollSpeed;
if (Gdx.input.isKeyPressed(Input.Keys.D))
camera.position.x += delta * scrollSpeed;
if (Gdx.input.isKeyPressed(Input.Keys.W))
camera.position.y += delta * scrollSpeed;
if (Gdx.input.isKeyPressed(Input.Keys.S))
camera.position.y -= delta * scrollSpeed;
if (Gdx.input.isKeyPressed(Input.Keys.Q))
camera.zoom = Math.min(camera.zoom + zoomSpeed * delta, 8.0f);
if (Gdx.input.isKeyPressed(Input.Keys.E))
camera.zoom = Math.max(camera.zoom - zoomSpeed * delta, 0.5f);
camera.update();
int mx = Gdx.input.getX();
int my = Gdx.input.getY();
camera.unproject(unprojectVector.set(mx, my, 0.0f));
worldMousePosition.set(unprojectVector.x, unprojectVector.y);
float r = (float) TILE_HEIGHT / (float) TILE_WIDTH;
float mapx = (worldMousePosition.x / TILE_HEIGHT + worldMousePosition.y / (TILE_HEIGHT * r)) * r;
float mapy = (worldMousePosition.y / (TILE_HEIGHT * r) - (worldMousePosition.x / TILE_HEIGHT)) * r;
worldPosition = new Vector2(mapx - 0.5f, mapy + 0.5f); // -.5/+.5 because the drawing isn't aligned to the tile, it's aligned to the image
int tileX = (int) worldPosition.x;
int tileY = (int) worldPosition.y;
batch.setProjectionMatrix(camera.combined);
batch.begin();
for (int col = MAP_WIDTH - 1; col >= 0; --col) {
for (int row = MAP_HEIGHT - 1; row >= 0; --row) {
if (tiles[col][row] != TILE_NONE) {
Texture texture = textures[tiles[col][row]];
int x = (col * TILE_WIDTH / 2) - (row * TILE_WIDTH / 2);
int y = (col * TILE_HEIGHT / 2) + (row * TILE_HEIGHT / 2);
batch.setColor(col == tileX && row == tileY ? Color.GRAY : Color.WHITE);
batch.draw(texture, x, y);
}
}
}
if (Gdx.input.isKeyPressed(Input.Keys.SPACE)) {
for (int col = MAP_WIDTH - 1; col >= 0; --col) {
for (int row = MAP_HEIGHT - 1; row >= 0; --row) {
int x = (col * TILE_WIDTH / 2) - (row * TILE_WIDTH / 2);
int y = (col * TILE_HEIGHT / 2) + (row * TILE_HEIGHT / 2);
font.draw(batch, String.format("(%d, %d)", col, row), x, y);
}
}
}
String str = String.format("World position (%.2f, %.2f), Tile (%d, %d)", worldPosition.x, worldPosition.y, (int)worldPosition.x, (int)worldPosition.y);
font.draw(batch, str, worldMousePosition.x, worldMousePosition.y);
batch.end();
}
}
I cant respond to bornander's post, but my tweak would be at
int tileX = (int) Math.Floor(worldPosition.x);
int tileY = (int) Math.Floor(worldPosition.y);
Where simple (int) cast will provide wrong position around 0 with negative values, if there are tiles, while using Math.Floor will work as intended.
I'm working on a java programming exercise where we have to draw a circular spiral using the drawArc method so that the result looks similar to this:
I've been working on this for a while and this is what I have so far:
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class CircSpiral extends JPanel {
public void paintComponent(Graphics g) {
int x = 100;
int y = 120;
int width = 40;
int height = 60;
int startAngle = 20;
int arcAngle = 80;
for (int i = 0; i < 5; i++) {
g.drawArc(x, y, width, height, startAngle, arcAngle);
g.drawArc(x + 10, y + 10, width, height, startAngle + 10, arcAngle);
x = x + 5;
y = y + 5;
startAngle = startAngle - 10;
arcAngle = arcAngle + 10;
}
}
public static void main(String[] args) {
CircSpiral panel = new CircSpiral();
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(panel);
application.setSize(300, 300);
application.setVisible(true);
}
}
My code gives me this result:
I know the problem lies in my arguments for the drawArc method because the numbers aren't right, but I don't know how to go about making the numbers go in a circular manner. Any help is appreciated. Thank you!
Your idea is almost right. I did some modifications. You need to inverse the angle to draw the other side of the spiral and use fixed point to startAngle.
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class CircSpiral extends JPanel {
public void paintComponent(Graphics g) {
int x = getSize().width / 2 - 10;
int y = getSize().height/ 2 - 10;
int width = 20;
int height = 20;
int startAngle = 0;
int arcAngle = 180;
int depth = 10;
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
// g.drawArc(x + 10, y + 10, width, height, startAngle + 10, -arcAngle);
// x = x - 5;
y = y - depth;
width = width + 2 * depth;
height = height + 2 * depth;
g.drawArc(x, y, width, height, startAngle, -arcAngle);
} else {
// g.drawArc(x + 10, y + 10, width, height, startAngle + 10, arcAngle);
x = x - 2 * depth;
y = y - depth;
width = width + 2 * depth;
height = height + 2 * depth;
g.drawArc(x, y, width, height, startAngle, arcAngle);
}
}
}
public static void main(String[] args) {
CircSpiral panel = new CircSpiral();
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(panel);
application.setSize(300, 300);
application.setVisible(true);
}
}
If this were my project, yes I'd draw my arcs in a loop, but within the loop, I'd try to make the arc's bounding box bigger but still centered over the same location. To do this I'd decrement x and y by some small constant, say DELTA (which I'd set to == 1), and I'd increment width and height by 2 * DELTA. I'd also leave my arcAngle unchanged but rather would change my startAngle in the loop like so: startAngle = startAngle - arcAngle;.
For example, this:
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class CircSpiral extends JPanel {
private static final int DELTA = 1;
private static final int ARC_ANGLE = 20;
private static final int PREF_W = 300;
private static final int PREF_H = PREF_W;
private static final int LOOP_MAX = 400;
public void paintComponent(Graphics g) {
int x = PREF_W / 2;
int y = PREF_H / 2;
int width = 1;
int height = 1;
int startAngle = 0;
int arcAngle = ARC_ANGLE;
for (int i = 0; i < LOOP_MAX; i++) {
g.drawArc(x, y, width, height, startAngle, arcAngle);
x = x - DELTA;
y = y - DELTA;
width += 2 * DELTA;
height += 2 * DELTA;
startAngle = startAngle - arcAngle;
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
public static void main(String[] args) {
CircSpiral panel = new CircSpiral();
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(panel);
application.pack();
application.setLocationRelativeTo(null);
application.setVisible(true);
}
}
Would result in this:
The following code will output this image:
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class CircSpiral extends JPanel {
public void paintComponent(Graphics g) {
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
int numIterations = 5;
int arcWidth = 10;
int arcGrowDelta = 30;
for (int i = 0; i < numIterations; i++) {
g.drawArc(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth, 2 * arcWidth, 0, 180);
arcWidth += arcGrowDelta;
g.drawArc(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth - arcGrowDelta, 2 * arcWidth, 180, 180);
}
}
public static void main(String[] args) {
CircSpiral panel = new CircSpiral();
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(panel);
application.setSize(300, 300);
application.setVisible(true);
}
}
The idea is very simple, just draw the upper half of a circle, like this:
Then increment the arc size by a constant factor and draw the bottom half of the circle but making the end point of this circle and the upper circle match, for it, just substrate the arcGrowDelta from the bottom circle width:
And repeat.
This is my solution:
package mainpack;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JPanel;
public class SpiralPanel extends JPanel {
private static final long serialVersionUID = 1L;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
int width = 10;
int height = 10;
int startAngle = 0;
int arcAngle = 180;
int x = (getWidth() - width) / 2;
int y = (getHeight() - height) / 2;
int i = 0;
int t = 0;
while (i < 36) {
g2d.drawArc(x + t, y, width, height, startAngle, arcAngle);
if (i % 2 == 0) {
t -= 10;
}
y -= 5;
width += 10;
height += 10;
startAngle += 180;
i++;
}
}
}
I am a beginner to java and finally managed how to create the spiral.
Here is my code:
int lineLength = 20; //starting line length
int x = getWidth() / 2; //start drawing from center of JPanel
int y = getHeight() / 2; //start drawing from center of JPanel
for( int counter = 0; counter < 10; counter++ )
{
g.drawArc( x, y, lineLength, lineLength, 0, 180 ); //draws top semicircle of equal width and height
lineLength += 20; //increases arc diameter
x -= 20; //moves x coordinate left
g.drawArc( x, y - 10, lineLength, lineLength, 0, -180 ); //draws bottom semicircle; 'y - 10' joins the 2 semicircles
lineLength += 20; //increases arc diameter
y -= 20; //moves y coordinate up
}
If you're willing to let some good old trigonometry do the work, you could use this:
import java.awt. *;
import javax.swing. *;
import java.math.*;
public class Spiral extends JFrame {
public Spiral()
{
// Set Window
setTitle("Spirale");
setSize(1500, 1500);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public void paint(Graphics g)
{
super.paint(g);
for(double i = 1; i < 50000; i++)
{
int locY = 600 - (int) (Math.cos((Math.PI*i)/1800)*i/50);
int locX = 600 - (int) (Math.sin((Math.PI*i)/1800)*i/50);
g.drawLine(locX, locY, locX, locY);
}
}
public static void main(String[] args) {
new Spiral();
}
}
The issue I'm having is that the output png only shows recursion on the bottom left corner, and I cant figure out why. Could someone please point me in the right direction, and Sorry about the messy comments.
Thank you.
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
public class Assignment12 {
static int WIDTH = 0;
static int HEIGHT = 0;
static ArrayList<Double> points = new ArrayList<Double>();
public static void main(String[] args) {
if (args.length == 0) {
WIDTH = 800;
HEIGHT = 693;
} else {
Integer W = Integer.valueOf(args[0]);
Integer H = Integer.valueOf(args[0]);
WIDTH = W;
HEIGHT = H;
}
// DONE //*********stuff to do ***********create the final ordered pairs
// of the biggest triangle
// done// *********stuff to do *********** set the values of the ordered
// pairs
// to the dimensions of the rectangle
final double topX = WIDTH / 2;
final double topY = 0;
final double leftX = 0;
final double leftY = HEIGHT - 1;
final double rightX = WIDTH - 1;
final double rightY = HEIGHT - 1;
sierpinski(topX, topY, leftX, leftY, rightX, rightY);
// ##############jeffs code#########
// BufferedImage creates memory space for storing image data
BufferedImage img = new BufferedImage(WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB);
// Graphics2D provides a canvas on which to draw shapes, text, other
// images
Graphics2D g2d = img.createGraphics();
// *********stuff to do *********** do the back ground stuffs
// Clear background to white
g2d.setColor(Color.white);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
// start drawing lines in the correct color.
// Red line from where to where?
g2d.setColor(Color.red);
for (int i = 0; i < points.size(); i = i + 4) {
g2d.drawLine(points.get(i).intValue(),
points.get(i + 1).intValue(), points.get(i + 2).intValue(),
points.get(i + 3).intValue());
}
// g2d.drawLine(0, 0, WIDTH - 1, HEIGHT - 1);
// done// *********stuff to do *********** initialize the recursive
// function to
// done// *********stuff to do *********** get rid of these oval things
// there
// just for reference,
// Draw some random ovals
// for (int i = 0; i < 100; i++) {
// g2d.setColor(new Color((float) Math.random(),
// (float) Math.random(), (float) Math.random()));
// g2d.fillOval((int) (Math.random() * WIDTH),
// (int) (Math.random() * HEIGHT), (int) (Math.random() * 50),
// (int) (Math.random() * 50));
// }
// Finalize the canvas
g2d.dispose();
// Write the image out as a PNG-format file
try {
ImageIO.write(img, "png", new File("out.png"));
} catch (IOException e) {
e.printStackTrace();
}
}
// *********stuff to do *********** create the recursive function for the
// triangles
private static void sierpinski(double topX, double topY, double leftX,
double leftY, double rightX, double rightY) {
// base case area of the triangle reaches x
// (Ax(By-Cy)+Bx(Cy-Ay)+Cx(Ay-By))/2
if (((leftX * (topY - rightY)) + (topX * (rightY - leftY)) + (rightX * (leftY - topY)) / 2) > 10) {// <--
// that 10 should be a static value or just pulled directly from
// args
// start recursive for all three respective points
double leftMidX = (topX + leftX) / 2;
double leftMidY = (topY + leftY) / 2;
double rightMidX = (topX + rightX) / 2;
double rightMidY = (topY + rightY) / 2;
// ***these are just called topMid for convenience. they are the mid
// point values to the bottom line of the triangle.
double botMidX = (leftX + rightX) / 2;
double botMidY = (leftY + rightY) / 2;
// top ... top stays the same
sierpinski(topX, topY, leftMidX, leftMidY, rightMidX, rightMidY);
// left
sierpinski(leftMidX, leftMidY, leftX, leftY, botMidX, botMidY);
// right
sierpinski(rightMidX, rightMidY, botMidX, botMidY, rightX, rightY);
} else {
points.add(topX);
points.add(topY);
points.add(rightX);
points.add(rightY);
points.add(topX);
points.add(topY);
points.add(leftX);
points.add(leftY);
points.add(leftX);
points.add(leftY);
points.add(rightX);
points.add(rightY);
// draw the lines
// g2d.drawLine(topX, topY, rightX, rightY);// right
// g2d.drawLine(topX, topY, leftX, leftY);// left
// g2d.drawLine(leftX, leftY, rightX, rightY);// bot
}
}
}
To draw a Sierpinski triangle calculate the points correctly. The following code here demonstrates it. More elaborated example can be found # github.
For a refference you can view a different approach to calculate triangle's points (calculates the Pascal's triangle and uses places of odd values in it as points for sierpinski triangle) # github.
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import java.util.List;
public class Sierpinski
{
public static int WIDTH = 800;
public static int HEIGHT = 693;
public static List<Double> points = new ArrayList<>();
public static void main(String[] args)
{
if (0 != args.length)
{
WIDTH = Integer.valueOf(args[0]);
HEIGHT = Integer.valueOf(args[0]);
}
// DONE //*********stuff to do ***********create the final ordered pairs
// of the biggest triangle
// done// *********stuff to do *********** set the values of the ordered
// pairs
// to the dimensions of the rectangle
final double topX = WIDTH / 2;
final double topY = 0;
final double leftX = 0;
final double leftY = HEIGHT - 1;
final double rightX = WIDTH - 1;
final double rightY = HEIGHT - 1;
sierpinski(topX, topY, leftX, leftY, rightX, rightY);
// ##############jeffs code#########
// BufferedImage creates memory space for storing image data
BufferedImage img = new BufferedImage(WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB);
// Graphics2D provides a canvas on which to draw shapes, text, other
// images
Graphics2D g2d = img.createGraphics();
// *********stuff to do *********** do the back ground stuffs
// Clear background to white
g2d.setColor(Color.white);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
// start drawing lines in the correct color.
// Red line from where to where?
g2d.setColor(Color.red);
for (int i = 0; i < points.size(); i = i + 4)
{
g2d.drawLine(
points.get(i).intValue(),
points.get(i + 1).intValue(),
points.get(i + 2).intValue(),
points.get(i + 3).intValue());
}
// Finalize the canvas
g2d.dispose();
// Write the image out as a PNG-format file
try
{
ImageIO.write(img, "png", new File("out.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
/**
* Triangle base = (Ax(By-Cy)+Bx(Cy-Ay)+Cx(Ay-By))/2
* #return base for the triangle
*/
public static double base(
double topX , double topY ,
double leftX , double leftY ,
double rightX, double rightY )
{
return ( (topX * (rightY - leftY))
+ (rightX* (leftY - topY))
+ (leftX * (topY - rightY ))) / 2;
}
// *********stuff to do *********** create the recursive function for the
// triangles
private static void sierpinski(
double topX , double topY ,
double leftX , double leftY ,
double rightX, double rightY )
{
if (10 < base(topX, topY, leftX, leftY, rightX, rightY))
{// <--
// that 10 should be a static value or just pulled directly from
// args
// start recursive for all three respective points
/**
*
double leftMidX = (topX + leftX ) / 2;
double leftMidY = (topY + leftY ) / 2;
double rightMidX = (topX + rightX) / 2;
double rightMidY = (topY + rightY) / 2;
*/
double leftMidX = leftX + (topX - leftX) / 2;
double leftMidY = topY + (leftY - topY ) / 2;
double rightMidX = topX + (rightX - topX ) / 2;
double rightMidY = leftMidY ;
// ***these are just called topMid for convenience. they are the mid
// point values to the bottom line of the triangle.
double botMidX = topX ;
double botMidY = leftY ;
/*
double botMidX = (leftX + rightX) / 2;
double botMidY = (leftY + rightY) / 2;
*/
// top ... top stays the same
sierpinski(topX, topY, leftMidX, leftMidY, rightMidX, rightMidY);
// left
sierpinski(leftMidX, leftMidY, leftX, leftY, botMidX, botMidY);
// right
sierpinski(rightMidX, rightMidY, botMidX, botMidY, rightX, rightY);
}
else
{
points.add(topX);
points.add(topY);
points.add(rightX);
points.add(rightY);
points.add(topX);
points.add(topY);
points.add(leftX);
points.add(leftY);
points.add(leftX);
points.add(leftY);
points.add(rightX);
points.add(rightY);
}
}
}
I need to make perlin noise like in AS3.0:
bitmapData.perlinNoise(baseX, baseY, numOctaves,
randomSeed, stitch, fractalNoise, grayScale, offsets);
It's seamless noise:
I found a lot of material about it, but I can't make it like in my as3.0 image. Java code:
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Noise extends JPanel{
public static int octaves = 4;
public static int size = 128;
public static float[][][] noise = new float[size][size][octaves];
public static float[][] perlinnoise = new float[size][size];
public static float p = (float) 1/4;
public static Random gen = new Random();
public static float GenerateNoise() {
return gen.nextFloat();
}
public static float SmoothNoise(int x, int y, int z) {
try{
float corners = (noise[x - 1][y - 1][z] + noise[x + 1][y - 1][z] + noise[x - 1][y + 1][z] + noise[x + 1][y + 1][z]) / 16;
float sides = (noise[x - 1][y][z] + noise[x + 1][y][z] + noise[x][y - 1][z] + noise[x][y + 1][z]) / 8;
float center = noise[x][y][z] / 4;
return corners + sides + center;
}catch(Exception e) {
return 0;
}
}
public static float InterpolatedNoise(float x, float y, int pX, int pY, int pZ) {
int intX = (int) x;
int intY = (int) y;
float fracX = x - intX;
float fracY = y - intY;
float v1 = SmoothNoise(pX, pY, pZ);
float v2 = SmoothNoise(pX + 1, pY, pZ);
float v3 = SmoothNoise(pX, pY + 1, pZ);
float v4 = SmoothNoise(pX + 1, pY + 1, pZ);
float i1 = Interpolate(v1, v2, fracX);
float i2 = Interpolate(v3, v4, fracX);
return Interpolate(i1, i2, fracY);
}
public static float Interpolate(float a, float b, float x) {
float ft = (float) (x * 3.1415927);
float f = (float) ((1 - Math.cos(ft)) * 0.5);
return (float) (a * (1 - f) + b * f);
}
public static float Perlin2D(float x, float y, int posX, int posY, int posZ) {
float total = 0;
for(int i = 0; i < octaves; i++) {
double f = Math.pow(2, i);
double a = Math.pow(p, i);
total = (float) (total + InterpolatedNoise((float)(x * f), (float)(y * f), posX, posY, posZ) * a);
}
return total;
}
public static void main(String [] args) {
for(int z = 0; z < octaves; z++) {
for(int y = 0; y < size; y++) {
for(int x = 0; x < size; x++) {
noise[x][y][z] = GenerateNoise();
}
}
}
for(int z = 0; z < octaves; z++) {
for(int y = 0; y < size; y++) {
for(int x = 0; x < size; x++) {
perlinnoise[x][y] = Perlin2D(x / (size - 1), y / (size - 1), x, y, z) / octaves;
}
}
}
JFrame f = new JFrame("Perlin Noise");
f.setSize(400, 400);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new Noise());
f.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
for(int y = 0; y < size; y++) {
for(int x = 0; x < size; x++) {
g.setColor(new Color(perlinnoise[x][y], perlinnoise[x][y], perlinnoise[x][y]));
g.fillRect(x * 2, y * 2, 2, 2);
}
}
repaint();
}
}
Help please!
The trick is, the Perlin noise does not use pseudo-random generator, it uses a function that takes an argument and returns predefined value for that argument, but when argument shifts by 1, the value jumps almost randomly. Check the sources for the permutation formulae, the init() method makes a permutation that then is used to make the entire noise.
In my program I want to draw a simple score line graph. I have a text file and on each line is an integer score, which I read in and want to pass as argument to my graph class. I'm having some trouble implementing the graph class and all the examples I've seen have their methods in the same class as their main, which I won't have.
I want to be able to pass my array to the object and generate a graph, but when calling my paint method it is asking me for a Graphics g... This is what I have so far:
public class Graph extends JPanel {
public void paintGraph (Graphics g){
ArrayList<Integer> scores = new ArrayList<Integer>(10);
Random r = new Random();
for (int i : scores){
i = r.nextInt(20);
System.out.println(r);
}
int y1;
int y2;
for (int i = 0; i < scores.size(); i++){
y1 = scores.get(i);
y2 = scores.get(i+1);
g.drawLine(i, y1, i+1, y2);
}
}
}
For now I have inserted a simple random number generator to fill up my array.
I have an existing frame and basically want to instantiate the Graph class and mount the panel onto my frame. I'm really sorry that this question seems so jumbled by the way, but I've had little sleep...
The code in my main statement is:
testFrame = new JFrame();
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Graph graph = new Graph();
testFrame.add(graph);
I'm not sure exactly what an SSCE is but this is my attempt at one:
public class Test {
JFrame testFrame;
public Test() {
testFrame = new JFrame();
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Graph graph = new Graph();
testFrame.add(graph);
testFrame.setBounds(100, 100, 764, 470);
testFrame.setVisible(true);
}
Graph.java
public class Graph extends JPanel {
public Graph() {
setSize(500, 500);
}
#Override
public void paintComponent(Graphics g) {
Graphics2D gr = (Graphics2D) g; // This is if you want to use Graphics2D
// Now do the drawing here
ArrayList<Integer> scores = new ArrayList<Integer>(10);
Random r = new Random();
for (int i : scores) {
i = r.nextInt(20);
System.out.println(r);
}
int y1;
int y2;
for (int i = 0; i < scores.size() - 1; i++) {
y1 = (scores.get(i)) * 10;
y2 = (scores.get(i + 1)) * 10;
gr.drawLine(i * 10, y1, (i + 1) * 10, y2);
}
}
}
Problems with your code and suggestions:
Again you need to change the preferredSize of the component (here the Graph JPanel), not the size
Don't set the JFrame's bounds.
Call pack() on your JFrame after adding components to it and before calling setVisible(true)
Your foreach loop won't work since the size of your ArrayList is 0 (test it to see that this is correct). Instead use a for loop going from 0 to 10.
You should not have program logic inside of your paintComponent(...) method but only painting code. So I would make the ArrayList a class variable and fill it inside of the class's constructor.
For example:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawGraph extends JPanel {
private static final int MAX_SCORE = 20;
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private static final int BORDER_GAP = 30;
private static final Color GRAPH_COLOR = Color.green;
private static final Color GRAPH_POINT_COLOR = new Color(150, 50, 50, 180);
private static final Stroke GRAPH_STROKE = new BasicStroke(3f);
private static final int GRAPH_POINT_WIDTH = 12;
private static final int Y_HATCH_CNT = 10;
private List<Integer> scores;
public DrawGraph(List<Integer> scores) {
this.scores = scores;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double xScale = ((double) getWidth() - 2 * BORDER_GAP) / (scores.size() - 1);
double yScale = ((double) getHeight() - 2 * BORDER_GAP) / (MAX_SCORE - 1);
List<Point> graphPoints = new ArrayList<Point>();
for (int i = 0; i < scores.size(); i++) {
int x1 = (int) (i * xScale + BORDER_GAP);
int y1 = (int) ((MAX_SCORE - scores.get(i)) * yScale + BORDER_GAP);
graphPoints.add(new Point(x1, y1));
}
// create x and y axes
g2.drawLine(BORDER_GAP, getHeight() - BORDER_GAP, BORDER_GAP, BORDER_GAP);
g2.drawLine(BORDER_GAP, getHeight() - BORDER_GAP, getWidth() - BORDER_GAP, getHeight() - BORDER_GAP);
// create hatch marks for y axis.
for (int i = 0; i < Y_HATCH_CNT; i++) {
int x0 = BORDER_GAP;
int x1 = GRAPH_POINT_WIDTH + BORDER_GAP;
int y0 = getHeight() - (((i + 1) * (getHeight() - BORDER_GAP * 2)) / Y_HATCH_CNT + BORDER_GAP);
int y1 = y0;
g2.drawLine(x0, y0, x1, y1);
}
// and for x axis
for (int i = 0; i < scores.size() - 1; i++) {
int x0 = (i + 1) * (getWidth() - BORDER_GAP * 2) / (scores.size() - 1) + BORDER_GAP;
int x1 = x0;
int y0 = getHeight() - BORDER_GAP;
int y1 = y0 - GRAPH_POINT_WIDTH;
g2.drawLine(x0, y0, x1, y1);
}
Stroke oldStroke = g2.getStroke();
g2.setColor(GRAPH_COLOR);
g2.setStroke(GRAPH_STROKE);
for (int i = 0; i < graphPoints.size() - 1; i++) {
int x1 = graphPoints.get(i).x;
int y1 = graphPoints.get(i).y;
int x2 = graphPoints.get(i + 1).x;
int y2 = graphPoints.get(i + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.setStroke(oldStroke);
g2.setColor(GRAPH_POINT_COLOR);
for (int i = 0; i < graphPoints.size(); i++) {
int x = graphPoints.get(i).x - GRAPH_POINT_WIDTH / 2;
int y = graphPoints.get(i).y - GRAPH_POINT_WIDTH / 2;;
int ovalW = GRAPH_POINT_WIDTH;
int ovalH = GRAPH_POINT_WIDTH;
g2.fillOval(x, y, ovalW, ovalH);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
List<Integer> scores = new ArrayList<Integer>();
Random random = new Random();
int maxDataPoints = 16;
int maxScore = 20;
for (int i = 0; i < maxDataPoints ; i++) {
scores.add(random.nextInt(maxScore));
}
DrawGraph mainPanel = new DrawGraph(scores);
JFrame frame = new JFrame("DrawGraph");
frame.setDefaultCloseOperation(JFrame.EXIT_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();
}
});
}
}
Which will create a graph that looks like so:
Just complementing Hovercraft Full Of Eels's solution:
I reworked his code, tweaked it a bit, adding a grid, axis labels and now the Y-axis goes from the minimum value present up to the maximum value. I planned on adding a couple of getters/setters but I didn't need them, you can add them if you want.
Here is the Gist link, I'll also paste the code below: GraphPanel on Gist
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GraphPanel extends JPanel {
private int width = 800;
private int heigth = 400;
private int padding = 25;
private int labelPadding = 25;
private Color lineColor = new Color(44, 102, 230, 180);
private Color pointColor = new Color(100, 100, 100, 180);
private Color gridColor = new Color(200, 200, 200, 200);
private static final Stroke GRAPH_STROKE = new BasicStroke(2f);
private int pointWidth = 4;
private int numberYDivisions = 10;
private List<Double> scores;
public GraphPanel(List<Double> scores) {
this.scores = scores;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double xScale = ((double) getWidth() - (2 * padding) - labelPadding) / (scores.size() - 1);
double yScale = ((double) getHeight() - 2 * padding - labelPadding) / (getMaxScore() - getMinScore());
List<Point> graphPoints = new ArrayList<>();
for (int i = 0; i < scores.size(); i++) {
int x1 = (int) (i * xScale + padding + labelPadding);
int y1 = (int) ((getMaxScore() - scores.get(i)) * yScale + padding);
graphPoints.add(new Point(x1, y1));
}
// draw white background
g2.setColor(Color.WHITE);
g2.fillRect(padding + labelPadding, padding, getWidth() - (2 * padding) - labelPadding, getHeight() - 2 * padding - labelPadding);
g2.setColor(Color.BLACK);
// create hatch marks and grid lines for y axis.
for (int i = 0; i < numberYDivisions + 1; i++) {
int x0 = padding + labelPadding;
int x1 = pointWidth + padding + labelPadding;
int y0 = getHeight() - ((i * (getHeight() - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding);
int y1 = y0;
if (scores.size() > 0) {
g2.setColor(gridColor);
g2.drawLine(padding + labelPadding + 1 + pointWidth, y0, getWidth() - padding, y1);
g2.setColor(Color.BLACK);
String yLabel = ((int) ((getMinScore() + (getMaxScore() - getMinScore()) * ((i * 1.0) / numberYDivisions)) * 100)) / 100.0 + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(yLabel);
g2.drawString(yLabel, x0 - labelWidth - 5, y0 + (metrics.getHeight() / 2) - 3);
}
g2.drawLine(x0, y0, x1, y1);
}
// and for x axis
for (int i = 0; i < scores.size(); i++) {
if (scores.size() > 1) {
int x0 = i * (getWidth() - padding * 2 - labelPadding) / (scores.size() - 1) + padding + labelPadding;
int x1 = x0;
int y0 = getHeight() - padding - labelPadding;
int y1 = y0 - pointWidth;
if ((i % ((int) ((scores.size() / 20.0)) + 1)) == 0) {
g2.setColor(gridColor);
g2.drawLine(x0, getHeight() - padding - labelPadding - 1 - pointWidth, x1, padding);
g2.setColor(Color.BLACK);
String xLabel = i + "";
FontMetrics metrics = g2.getFontMetrics();
int labelWidth = metrics.stringWidth(xLabel);
g2.drawString(xLabel, x0 - labelWidth / 2, y0 + metrics.getHeight() + 3);
}
g2.drawLine(x0, y0, x1, y1);
}
}
// create x and y axes
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, padding + labelPadding, padding);
g2.drawLine(padding + labelPadding, getHeight() - padding - labelPadding, getWidth() - padding, getHeight() - padding - labelPadding);
Stroke oldStroke = g2.getStroke();
g2.setColor(lineColor);
g2.setStroke(GRAPH_STROKE);
for (int i = 0; i < graphPoints.size() - 1; i++) {
int x1 = graphPoints.get(i).x;
int y1 = graphPoints.get(i).y;
int x2 = graphPoints.get(i + 1).x;
int y2 = graphPoints.get(i + 1).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.setStroke(oldStroke);
g2.setColor(pointColor);
for (int i = 0; i < graphPoints.size(); i++) {
int x = graphPoints.get(i).x - pointWidth / 2;
int y = graphPoints.get(i).y - pointWidth / 2;
int ovalW = pointWidth;
int ovalH = pointWidth;
g2.fillOval(x, y, ovalW, ovalH);
}
}
// #Override
// public Dimension getPreferredSize() {
// return new Dimension(width, heigth);
// }
private double getMinScore() {
double minScore = Double.MAX_VALUE;
for (Double score : scores) {
minScore = Math.min(minScore, score);
}
return minScore;
}
private double getMaxScore() {
double maxScore = Double.MIN_VALUE;
for (Double score : scores) {
maxScore = Math.max(maxScore, score);
}
return maxScore;
}
public void setScores(List<Double> scores) {
this.scores = scores;
invalidate();
this.repaint();
}
public List<Double> getScores() {
return scores;
}
private static void createAndShowGui() {
List<Double> scores = new ArrayList<>();
Random random = new Random();
int maxDataPoints = 40;
int maxScore = 10;
for (int i = 0; i < maxDataPoints; i++) {
scores.add((double) random.nextDouble() * maxScore);
// scores.add((double) i);
}
GraphPanel mainPanel = new GraphPanel(scores);
mainPanel.setPreferredSize(new Dimension(800, 600));
JFrame frame = new JFrame("DrawGraph");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
It looks like this:
Or simply use the JFreechart library - http://www.jfree.org/jfreechart/ .
There exist many open source projects that handle all the drawing of line charts for you with a couple of lines of code. Here's how you can draw a line chart from data in a couple text (CSV) file with the XChart library. Disclaimer: I'm the lead developer of the project.
In this example, two text files exist in ./CSV/CSVChartRows/. Notice that each row in the files represents a data point to be plotted and that each file represents a different series. series1 contains x, y, and error bar data, whereas series2 contains just x and y, data.
series1.csv
1,12,1.4
2,34,1.12
3,56,1.21
4,47,1.5
series2.csv
1,56
2,34
3,12
4,26
Source Code
public class CSVChartRows {
public static void main(String[] args) throws Exception {
// import chart from a folder containing CSV files
XYChart chart = CSVImporter.getChartFromCSVDir("./CSV/CSVChartRows/", DataOrientation.Rows, 600, 400);
// Show it
new SwingWrapper(chart).displayChart();
}
}
Resulting Plot
Override the paintComponent method of your panel so you can custom draw. Like this:
#Override
public void paintComponent(Graphics g) {
Graphics2D gr = (Graphics2D) g; //this is if you want to use Graphics2D
//now do the drawing here
...
}
Hovercraft Full Of Eels' answer is very good, but i had to change it a bit in order to get it working on my program:
int y1 = (int) ((this.height - 2 * BORDER_GAP) - (values.get(i) * yScale - BORDER_GAP));
instead of
int y1 = (int) (scores.get(i) * yScale + BORDER_GAP);
because if i used his way the graphic would be upside down
(you'd see it if you used hardcoded values (e.g 1,3,5,7,9) instead of random values)