Draw with Lines in Java

How to draw lines in Java

A very simple example of a swing component to draw lines. It keeps internally a list with the lines that have been added with the method addLine. Each time a new line is added, repaint is invoked to inform the graphical subsytem that a new paint is required.

The class also includes some example of usage.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class LinesComponent extends JComponent{

private static class Line{
final int x1;
final int y1;
final int x2;
final int y2;
final Color color;

public Line(int x1, int y1, int x2, int y2, Color color) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.color = color;
}
}

private final LinkedList<Line> lines = new LinkedList<Line>();

public void addLine(int x1, int x2, int x3, int x4) {
addLine(x1, x2, x3, x4, Color.black);
}

public void addLine(int x1, int x2, int x3, int x4, Color color) {
lines.add(new Line(x1,x2,x3,x4, color));
repaint();
}

public void clearLines() {
lines.clear();
repaint();
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Line line : lines) {
g.setColor(line.color);
g.drawLine(line.x1, line.y1, line.x2, line.y2);
}
}

public static void main(String[] args) {
JFrame testFrame = new JFrame();
testFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
final LinesComponent comp = new LinesComponent();
comp.setPreferredSize(new Dimension(320, 200));
testFrame.getContentPane().add(comp, BorderLayout.CENTER);
JPanel buttonsPanel = new JPanel();
JButton newLineButton = new JButton("New Line");
JButton clearButton = new JButton("Clear");
buttonsPanel.add(newLineButton);
buttonsPanel.add(clearButton);
testFrame.getContentPane().add(buttonsPanel, BorderLayout.SOUTH);
newLineButton.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
int x1 = (int) (Math.random()*320);
int x2 = (int) (Math.random()*320);
int y1 = (int) (Math.random()*200);
int y2 = (int) (Math.random()*200);
Color randomColor = new Color((float)Math.random(), (float)Math.random(), (float)Math.random());
comp.addLine(x1, y1, x2, y2, randomColor);
}
});
clearButton.addActionListener(new ActionListener() {

@Override
public void actionPerformed(ActionEvent e) {
comp.clearLines();
}
});
testFrame.pack();
testFrame.setVisible(true);
}

}

How to draw a line in java?

You're not overriding the method correctly. The argument to paintComponent is of type Graphics, not Graphics2D, but you can cast to Graphics2D. Also you need to add the Game panel to the frame as a content pane:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Game extends JPanel
{

@Override
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
g2.drawLine(30, 40, 80, 100);
}

public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(400, 420);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Game game = new Game();
frame.setContentPane(game);

frame.setVisible(true);
frame.invalidate();
}

}

Draw with lines in Java

A little animation to show you the logic you need to be looking for in terms of line rotation. Think of the line like a hand on a clock. How would to animate a hand on a clock. It's pretty much the exact same concept. The only difference is that the x1 (the x point for the center point of the clock hand), instead of remaining still, it moves along the x axis (which is the y1 constant) while the hand is turning. So for every tick of the clock (hand rotation), the x location is also moved horizontally. That's the way I looked at it.

Sample Image

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main {

public static void main(String[] a) {

JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.getContentPane().add(new MyCanvas());
window.pack();
window.setVisible(true);
}
}

class MyCanvas extends JPanel {

int x1 = 0;
int rotate = 50;

List<Line> lines;

Timer timer = null;
public MyCanvas() {
lines = new ArrayList<>();

timer = new Timer(75, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (rotate < -50) {
((Timer) e.getSource()).stop();
} else {
lines.add(new Line(x1, rotate));
repaint();
x1 += 5;
rotate--;
}
}
});

JButton start = new JButton("Start the Magic");
start.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
add(start);

}

public Dimension getPreferredSize() {
return new Dimension(502, 400);
}

private static final long serialVersionUID = 1L;

public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
for (Line line : lines) {
line.drawLine(g);
}

}

class Line {

int x1;
int rotate;
int y1 = 200;

public Line(int x1, int rotate) {
this.x1 = x1;
this.rotate = rotate;
}

void drawLine(Graphics g) {
int Radius = (int) (Math.min(getWidth(), getHeight()) * 0.4);

int sLength = (int) (Radius * 0.9);
int xSecond = (int) (x1 + sLength * Math.sin(rotate * (2 * Math.PI / 100)));
int ySecond = (int) (y1 - sLength * Math.cos(rotate * (2 * Math.PI / 100)));
g.setColor(Color.GREEN);
g.drawLine(x1, y1, xSecond, ySecond);
}
}
}

Draw line inside canvas

First, you need to go read Painting in AWT and Swing to get a better understanding of how painting works in Swing and AWT.

Next, you need to go read through the JavaDocs for Canvas to better understand what functionality you can override.

One of the difficult concepts to understand is, you don't actually control the painting system, that's taken care of for you (it's like black magic), you just need to work with it, by overriding the appropriate methods and interacting with the API to request updates when needed.

The biggest problem with your code is public void paint(Graphics g, Drawing d) {. Nothing is going to call it, as it's not a method that the paint system recognises. It'd also question why you need to pass a reference of Drawing to an instance of Drawing, not sure about all that.

The other issue you're having is ...

Drawing dr = new Drawing(100, 100, 200, 200);
JFrame frame = new JFrame("My Drawing");
Canvas canvas = new Drawing();
canvas.setBackground(Color.white);

frame.add(dr);

frame.add(canvas);

You're adding two instances of Drawing to the JFrame, because of the way the default, BorderLayout works, only the second one will ever get laid out, the first will be ignored.

Again, not sure why, just add the first one and be done with it.

Something that "works" might look something like...

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;

public class Drawing extends Canvas {

int x1;
int y1;
int x2;
int y2;

public static void main(String[] args) {

Drawing dr = new Drawing(100, 100, 200, 200);
JFrame frame = new JFrame("My Drawing");
frame.add(dr);
frame.pack();
frame.setVisible(true);
}

@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}

@Override
public void paint(Graphics g) {
super.paint(g);
g.drawLine(x1, y1, x2, y2);
}

Drawing(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}

Drawing() {

}
}

You seem to be making fundamental mistakes with the use of the API and I would strongly encourage you to spend some time reading through Creating a GUI With JFC/Swing. You'll also find that using something like JPanel instead of Canvas will give you better performance and results

Drawing multiple lines with Java

public static int x1,y1,x2,y2;
  1. Don't use static attributes unless you can explain why they need to be declared static.
  2. These attributes are declared as part of the Tugas1 class. It's a panel. It does not need these attributes and this is part of the problem. Remove them.
  3. Wrap the 4 numbers and color in a POJO (let's call it a ColoredLine) and add those to a LinkedList<ColoredLine>.
  4. Then iterate that list & paint each one in turn.

Draw a movable line between two java graphics

The basic idea is, you need to generate some kind of relationship between the objects you want to link. You could make this relationship implicit (Person contains Fruit) or non-implicit, where the relationship is stored/managed externally, which you do is up to you (I like the implicit approach as it sets out your intentions)

Your codes a bit, odd, sorry, but it is, so I've made some modifications. I did think of using a Path2D for a lot of it, but that would mean everything code painted in the same color. The main point is to define some kind of commonality between the objects, while strictly speaking not required, because almost all the work between them is the same, why not...

public interface Paintable {
public void paint(JComponent parent, Graphics2D g2d);
public boolean contains(Point p);
public void moveTo(Point2D p);
public Rectangle2D getBounds();
}

Then I created a class to manage the relationships...

public class Relationship {

private Paintable parent;
private Paintable child;

public Relationship(Paintable parent, Paintable child) {
this.parent = parent;
this.child = child;
}

public Paintable getChild() {
return child;
}

public Paintable getParent() {
return parent;
}

}

Now, this means that your fruits could belong to more then one person (or other fruits), so if this goes against your rules, you will need to devise a different relationship algorithm (maybe something more implicit)

Now, when ever you update the UI, you simply paint the relationships and the objects...

@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();

for (Relationship relationship : relationships) {

Point2D p1 = new Point2D.Double(relationship.getParent().getBounds().getCenterX(), relationship.getParent().getBounds().getCenterY());
Point2D p2 = new Point2D.Double(relationship.getChild().getBounds().getCenterX(), relationship.getChild().getBounds().getCenterY());

g2.draw(new Line2D.Double(p1, p2));

}

for (Person p : persons) {
p.paint(this, g2);
}
for (Fruit f : fruits) {
f.paint(this, g2);
}

g2.dispose();
}

Move me

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Example extends JPanel {

private List<Person> persons;
private List<Fruit> fruits;
private Point2D offset;
private static Font baseFont;
private Random randomGenerator;
private Person person;
private Fruit bubble;
private static final int W = 640;
private static final int H = 480;

private Paintable selectedShape;

private List<Relationship> relationships;

public Example() {
persons = new ArrayList<>(); // Stores the person's names & coords
fruits = new ArrayList<>(); // Stores the person's name and what fruits he sells & coords
relationships = new ArrayList<>(25);

randomGenerator = new Random();
baseFont = new Font("Sans Serif", Font.BOLD, 12);

String person1 = "Jimmy";
String person2 = "Sally";

String fruit1 = "Banana";
String fruit2 = "Apple";
String fruit3 = "Orange";
String fruit4 = "Watermelon";
String fruit5 = "Pineapple";
String fruit6 = "Grapes";

Person person = new Person(person1, 50, 50);
addPerson(person);

Fruit bubble = new Fruit(fruit1, baseFont, 150, 50);
addFruit(bubble);
relate(person, bubble);
bubble = new Fruit(fruit2, baseFont, 150, 100);
addFruit(bubble);
relate(person, bubble);
bubble = new Fruit(fruit3, baseFont, 150, 150);
addFruit(bubble);
relate(person, bubble);

person = new Person(person2, 50, 150);
addPerson(person);

bubble = new Fruit(fruit4, baseFont, 150, 200);
addFruit(bubble);
relate(person, bubble);
bubble = new Fruit(fruit5, baseFont, 150, 250);
addFruit(bubble);
relate(person, bubble);
bubble = new Fruit(fruit6, baseFont, 150, 300);
addFruit(bubble);
relate(person, bubble);

this.setFont(baseFont);
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
for (Paintable p : getShapes()) {
if (p.contains(e.getPoint())) {
// Selected
selectedShape = p;
offset = new Point2D.Double(e.getX() - p.getBounds().getX(), e.getY() - p.getBounds().getY());
break;
}
}
}

public void mouseReleased(MouseEvent e) {
selectedShape = null;
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {

@Override
public void mouseDragged(MouseEvent e) {
if (selectedShape != null) {

Point2D p = new Point2D.Double(e.getX() - offset.getX(), e.getY() - offset.getX());

selectedShape.moveTo(p);
}
repaint();
}
});
}

protected List<Paintable> getShapes() {
ArrayList<Paintable> shapes = new ArrayList<>(fruits);
shapes.addAll(persons);
return shapes;
}

public void addPerson(Person person) {
persons.add(person);
repaint();
}

public void addFruit(Fruit fruit) {
fruits.add(fruit);
repaint();
}

@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();

for (Relationship relationship : relationships) {

Point2D p1 = new Point2D.Double(relationship.getParent().getBounds().getCenterX(), relationship.getParent().getBounds().getCenterY());
Point2D p2 = new Point2D.Double(relationship.getChild().getBounds().getCenterX(), relationship.getChild().getBounds().getCenterY());

g2.draw(new Line2D.Double(p1, p2));

}

for (Person p : persons) {
p.paint(this, g2);
}
for (Fruit f : fruits) {
f.paint(this, g2);
}

g2.dispose();
}

@Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame f = new JFrame();
f.add(new Example());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}

protected void relate(Person person, Fruit bubble) {
relationships.add(new Relationship(person, bubble));
}

public class Relationship {

private Paintable parent;
private Paintable child;

public Relationship(Paintable parent, Paintable child) {
this.parent = parent;
this.child = child;
}

public Paintable getChild() {
return child;
}

public Paintable getParent() {
return parent;
}

}

public interface Paintable {

public void paint(JComponent parent, Graphics2D g2d);

public boolean contains(Point p);

public void moveTo(Point2D p);

public Rectangle2D getBounds();

}

public class Fruit implements Paintable {

private static final long serialVersionUID = 1L;
String fruit;
Font font;

private Ellipse2D bounds;

public Fruit(String fruit, Font font, int x, int y) {
this.fruit = fruit;
this.font = font;
bounds = new Ellipse2D.Double(x, y, 40, 90);
}

public String getFruit() {
return fruit;
}

@Override
public boolean contains(Point p) {
return bounds.contains(p);
}

@Override
public void moveTo(Point2D p) {
bounds = new Ellipse2D.Double(p.getX(), p.getY(), 40, 90);
}

@Override
public void paint(JComponent parent, Graphics2D g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setFont(font);
FontMetrics fm = g2.getFontMetrics();
int height = fm.getHeight();
int width = fm.stringWidth(fruit);

g2.setColor(Color.WHITE);
g2.fill(bounds);
g2.setColor(Color.BLACK);
g2.draw(bounds);

double centreX = bounds.getX() + bounds.getWidth() / 2d;
double centreY = bounds.getY() + bounds.getHeight() / 2d;
g2.drawString(fruit, (int) (centreX - width / 2), (int) (centreY + height / 4));

g2.dispose();
}

@Override
public Rectangle2D getBounds() {
return bounds.getBounds2D();
}

}

public class Person implements Paintable {

String person;
private Rectangle2D bounds;

public Person(String person, int x, int y) {
this.person = person;
bounds = new Rectangle2D.Double(x, y, 40, 90);
}

@Override
public void paint(JComponent parent, Graphics2D g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.translate(bounds.getX(), bounds.getY());
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawOval(0, 0, 20, 20); // head
g2.drawLine(10, 20, 10, 50); // bodbounds.getY()
g2.drawLine(10, 20, 25, 40); // right hand
g2.drawLine(10, 20, 0 - 5, 40); // left hand
g2.drawLine(10, 50, 0 - 5, 70); // left leg
g2.drawLine(10, 50, 25, 70); // right leg
g2.drawString(person, 0 - 15, 85);
g2.dispose();
}

public String getPerson() {
return person;
}

@Override
public boolean contains(Point p) {
return bounds.contains(p);
}

@Override
public void moveTo(Point2D p) {
bounds = new Rectangle2D.Double(p.getX(), p.getY(), 40, 90);
}

@Override
public Rectangle2D getBounds() {
return bounds.getBounds2D();
}
}

}

I would encourage you to have a look at Path2D, at least for the stick figure, it will make life eaiser



Related Topics



Leave a reply



Submit