I need to draw some squares inside other square but I don't know how to rotate my squares by center of them and make them smaller?
Here is a picture how it should looks like at the end:
Now I have some code which draw squares which make circle.
class MySquare extends JComponent {
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < 20; i++) {
g2d = (Graphics2D) g.create();
g2d.rotate(Math.toRadians(45 - (i * 10)), 100, 100);
// Difrent colors:
if (i % 2 == 0)
g2d.setColor(Color.black);
else
g2d.setColor(Color.green);
g2d.fillRect(50, 50, 100, 100);
}
}
}
public class DrawRect {
public static void main(String[] a) {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(30, 30, 800, 800);
window.getContentPane().add(new MySquare());
window.setVisible(true);
}
}
You could first draw your figure around the origin (that's easy) and then translate:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(400, 400);
frame.setVisible(true);
frame.add(new JPanel() {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
double alpha = Math.toRadians(5);
double factor = 1 / (Math.sin(alpha) + Math.cos(alpha));
double size = 200;
g2d.translate(size, size);
for (int i = 0; i < 20; i++) {
g2d.setColor(i % 2 == 0 ? Color.black : Color.green);
int intSize = (int) Math.round(size);
g2d.fillRect(-intSize / 2, -intSize / 2, intSize, intSize);
size = size * factor;
g2d.rotate(alpha);
}
}
});
}
}
Related
I am trying to make a program that draws a circle in the center of a JFrame and am drawing the circle using paintComponent. My goal is to have the circle centered in the frame even if the JFrame is resized. I have tried and searched for different things but nothing has worked. I am guessing that I have to use repaint() and timers but don't know how exactly. My code is as follows:
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageFrame extends JFrame {
private static final long serialVersionUID = 1L;
int width = 40;
int height = 40;
int x;
int y;
JPanel panel = new JPanel() {
private static final long serialVersionUID = 2L;
public void paintComponent(Graphics g) {
super.paintComponents(g);
g.drawOval(x, y, width, height);
}
};
public ImageFrame() {
add(panel);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400, 300);
x = (getWidth()/2) - (width/2)-20;
y = (getHeight()/2) - (height/2)-40;
setLocationRelativeTo(null);
setVisible(true);
}
}
Update:
I have added TrashGod's method but it says to remove the #Override and then if ran, the JFrame opens but with no circle. The code is below and I have edited out the paintComponent from my old code.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageFrame extends JFrame implements {
private static final long serialVersionUID = 1L;
public ImageFrame() {
addMouseListener(this);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400, 300);
setLocationRelativeTo(null);
setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Dimension size = this.getSize();
int d = 200;
int x = (size.width - d) / 2;
int y = (size.height - d) / 2;
g.fillOval(x, y, d, d);
g.setColor(Color.blue);
g.drawOval(x, y, d, d);
}
}
This example centers the circle and adjusts the size to the smaller dimension, but you can make d constant. The essential step is to render relative to the panel's current size. Resize the enclosing frame to see the effect. Adding RenderingHints, seen here, makes the drawing smother.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Dimension size = this.getSize();
int d = 200;
int x = (size.width - d) / 2;
int y = (size.height - d) / 2;
g.fillOval(x, y, d, d);
g.setColor(Color.blue);
g.drawOval(x, y, d, d);
}
Changes to the example:
$ diff OldSwingPaint.java SwingPaint.java
38a39,41
> Graphics2D g2d = (Graphics2D) g;
> g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
> RenderingHints.VALUE_ANTIALIAS_ON);
40c43
< int d = Math.min(size.width, size.height) - 10;
---
> int d = 200;
So I've been working on a Tic Tac Toe App using MVC. I'm having difficulties implementing the mouse Listener in the views page. I need each panel of the board to be clickable. Can someone help me?
public class TicTacToeView extends JFrame{
private JButton oButton, xButton;
public JPanel board;
public ArrayList<Shape> shapes;
public TicTacToeView(){
shapes = new ArrayList<Shape>();
JPanel topPanel=new JPanel();
topPanel.setLayout(new FlowLayout());
add(topPanel, BorderLayout.NORTH);
add(board=new Board(), BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 500);
}
private class Board extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
int w=getWidth();
int h=getHeight();
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(Color.WHITE);
g2d.fill(new Rectangle2D.Double(0, 0, w, h));
g2d.setPaint(Color.BLACK);
g2d.setStroke(new BasicStroke(4));
g2d.draw(new Line2D.Double(0, h/3, w, h/3));
g2d.draw(new Line2D.Double(0, h*2/3, w, h*2/3));
g2d.draw(new Line2D.Double(w/3, 0, w/3, h));
g2d.draw(new Line2D.Double(w*2/3, 0, w*2/3, h));
//draw circles and xs by visiting elements in the array List.
for(Shape shape : shapes){
g2d.setPaint(Color.BLUE);
g2d.draw(shape);
}
}
}
public void addMouseListener(MouseListener ml){
}
}
It would be easier if each cell was it's own Shape (like a Rectangle), but, with your current code, you could use something like...
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
int w = getWidth();
int h = getHeight();
selectedCell = null;
for (int col = 0; col < 3 && selectedCell == null; col++) {
for (int row = 0; row < 3; row++) {
int x = (w / 3) * col;
int y = (h / 3) * row;
Rectangle cell = new Rectangle(x, y, w / 3, h / 3);
if (cell.contains(e.getPoint())) {
System.out.println("In");
selectedCell = cell;
repaint();
break;
}
}
}
}
});
Now, all this does, is loops through each column and row, creating a Rectangle and checking to see if it contains the MouseEvent point. If it finds a match, it assigns the cell's rectangle to a instance field, which I used to highlight the in your paint method.
This is why it would be easier to have each cell stored as a Rectangle in a List
As a runnable example...
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TicTacToeView extends JFrame {
private JButton oButton, xButton;
public JPanel board;
public ArrayList<Shape> shapes;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new TicTacToeView();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public TicTacToeView() {
shapes = new ArrayList<Shape>();
JPanel topPanel = new JPanel();
topPanel.setLayout(new FlowLayout());
add(topPanel, BorderLayout.NORTH);
add(board = new Board(), BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private class Board extends JPanel {
private Rectangle selectedCell = null;
public Board() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
int w = getWidth();
int h = getHeight();
selectedCell = null;
for (int col = 0; col < 3 && selectedCell == null; col++) {
for (int row = 0; row < 3; row++) {
int x = (w / 3) * col;
int y = (h / 3) * row;
Rectangle cell = new Rectangle(x, y, w / 3, h / 3);
if (cell.contains(e.getPoint())) {
System.out.println("In");
selectedCell = cell;
repaint();
break;
}
}
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(Color.WHITE);
g2d.fill(new Rectangle2D.Double(0, 0, w, h));
if (selectedCell != null) {
g2d.setColor(Color.BLUE);
g2d.fill(selectedCell);
}
g2d.setPaint(Color.BLACK);
g2d.setStroke(new BasicStroke(4));
g2d.draw(new Line2D.Double(0, h / 3, w, h / 3));
g2d.draw(new Line2D.Double(0, h * 2 / 3, w, h * 2 / 3));
g2d.draw(new Line2D.Double(w / 3, 0, w / 3, h));
g2d.draw(new Line2D.Double(w * 2 / 3, 0, w * 2 / 3, h));
//draw circles and xs by visiting elements in the array List.
for (Shape shape : shapes) {
g2d.setPaint(Color.BLUE);
g2d.draw(shape);
}
}
}
}
So actually I made it harder than it really is. Basically, you just have to pass the MouseListener from the controller(which implements the MouseListener) into the views and add it to a JPanel which in this case, is "board".
So in the views page, looks something like this.
public void addMouseListener(MouseListener ml){
this.board.addMouseListener(ml);
}
You then put what you want to do for each mouse event in the controller.
Hope someone finds this helpful.
I am currently experimenting with the Graphics2D and the KeyListener, and I am currently testing out rotating objects (in this case a pentagon). Now, it's working in the sense that it rotates, except it is supposed to rotate by 1 radian each time VK_RIGHT or VK_LEFT are pressed.
However, currently it does that for the first key press only. From then on, it creates a pattern of rotating it by 1, 2, 3, 4, 5... and so on radians each time (the nth keypress rotates it by nth radians) instead of just 1 radian per keypress.
Creating the JFrame:
import javax.swing.JFrame;
public class Main {
public Main() {
JFrame window = new JFrame("Rotating Hexagons");
window.setSize(800,600);
window.setLocationRelativeTo(null);
window.setResizable(false);
window.setContentPane(new RotatingHexagon());
window.pack();
window.setVisible(true);
}
public static void main(String[]args) {
new Main();
}
}
RotatingHexagon Class:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
public class RotatingHexagon extends JPanel implements KeyListener {
private Polygon poly;
private int[] xpoints = { 0, -10, -7, 7, 10 };
private int[] ypoints = { -10, -2, 10, 10, -2 };
private int rotation = 0;
public RotatingHexagon() {
setPreferredSize(new Dimension(800,600));
setFocusable(true);
requestFocus();
}
public void init() {
poly = new Polygon(xpoints, ypoints, xpoints.length);
addKeyListener(this);
}
public void paint(Graphics g) {
init();
Graphics2D g2d = (Graphics2D) g;
int width = getSize().width;
int height = getSize().height;
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, width, height);
g2d.setColor(Color.WHITE);
g2d.drawString(rotation + " radians", 10, 20);
g2d.translate(width / 2, height / 2);
g2d.scale(20, 20);
g2d.rotate(Math.toRadians(rotation));
g2d.setColor(new Color(255, 100, 100));
g2d.fill(poly);
g2d.setColor(Color.WHITE);
g2d.draw(poly);
}
public void keyPressed(KeyEvent k) {
switch(k.getKeyCode()) {
case KeyEvent.VK_LEFT:
rotation--;
if (rotation < 0) rotation = 359;
repaint();
break;
case KeyEvent.VK_RIGHT:
rotation++;
if (rotation > 360) rotation = 0;
repaint();
break;
}
}
public void keyReleased(KeyEvent k) {}
public void keyTyped(KeyEvent k) {}
}
I really don't have any idea why it isn't just rotating 1 radian each time, so any help is appreciated.
Thanks.
the reason is calling the init() function in the paint() method again and again. So the KeyListener is added multiple times, causing that it is called multiple times, incrementing your counter more every time you press the key.
Move it to the constructor:
public RotatingHexagon() {
setPreferredSize(new Dimension(800,600));
setFocusable(true);
requestFocus();
addKeyListener(this);
}
public void init() {
poly = new Polygon(xpoints, ypoints, xpoints.length);
}
Andy
You should probally just use a persistant AffineTransform to do the rotation. They are a lot more powerfull.
I also saw several issues in your code, you are calling the init method each frame - this could be 60 times per second. In this you are adding a new keylistener each frame. You are also creating a new polygon which would slow down performance.
I've made some changes to your code and and i've used AffineTransforms as an example. Have a look and see if this helps.
package com.joey.testing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class AffineTransformTest extends JPanel implements KeyListener {
private Polygon poly;
private int[] xpoints = { 0, -10, -7, 7, 10 };
private int[] ypoints = { -10, -2, 10, 10, -2 };
private int rotation = 0;
AffineTransform transform;
AffineTransform rotationTransform;
AffineTransform translateTransform;
public AffineTransformTest() {
setPreferredSize(new Dimension(800,600));
setFocusable(true);
requestFocus();
//Do Init here - no point in creating new polygon each frame.
//It also adds the key listener each time
init();
updateTransforms();
}
public void init() {
poly = new Polygon(xpoints, ypoints, xpoints.length);
transform = new AffineTransform();
rotationTransform = new AffineTransform();
translateTransform = new AffineTransform();
addKeyListener(this);
}
//Use Paint Compoent
#Override
public void paintComponent(Graphics g) {
//Always call super to clear the screen
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int width = getSize().width;
int height = getSize().height;
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, width, height);
g2d.setColor(Color.WHITE);
g2d.drawString(rotation + " radians", 10, 20);
//Store old transform so we can apply it
AffineTransform old = g2d.getTransform();
//Add Transform and move polygon
g2d.setTransform(transform);
g2d.setColor(new Color(255, 100, 100));
g2d.fill(poly);
g2d.setColor(Color.WHITE);
g2d.draw(poly);
g2d.setTransform(old);
}
public void keyPressed(KeyEvent k) {
switch(k.getKeyCode()) {
case KeyEvent.VK_LEFT:
rotation--;
if (rotation < 0) rotation = 359;
repaint();
break;
case KeyEvent.VK_RIGHT:
rotation++;
if (rotation > 360) rotation = 0;
repaint();
break;
}
updateTransforms();
}
public void updateTransforms(){
//Resets transform to rotation
rotationTransform.setToRotation(Math.toRadians(rotation));
translateTransform.setToTranslation(getWidth()/2, getHeight()/2);
//Chain the transforms (Note order matters)
transform.setToIdentity();
transform.concatenate(translateTransform);
transform.concatenate(rotationTransform);
}
public void keyReleased(KeyEvent k) {}
public void keyTyped(KeyEvent k) {}
public static void main(String input[]){
JFrame f= new JFrame("AffineTransform");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(100, 100);
f.setResizable(true);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(new AffineTransformTest());
f.show();
}
}
Hello i'm trying to do a string inside a rectangle to make custom menus in java I'm using a canvas and doing the following method, but I can't seem to get it right!
public void render(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.WHITE);
Font font = new Font("Verdana", Font.PLAIN, 20);
g2d.setFont(font);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
FontMetrics fm = root.getFontMetrics(font);
g2d.drawString(option, (int)getX() - fm.stringWidth(option)/2, (int) getY() + fm.getHeight());
g2d.drawRect((int)getX() - fm.stringWidth(option)/2 - 20, (int) getY() - fm.getHeight() - 10, (int)getX() - fm.stringWidth(option)/2 + 40 , (int) getY() - fm.getHeight() + 10);
}
The basic problem you have is that you are using the components x/y position where the Graphics context is already translated so that 0x0 is the top/left corner of the component.
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.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawText {
public static void main(String[] args) {
new DrawText();
}
public DrawText() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBackground(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
g2d.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
render(g);
g2d.dispose();
}
public void render(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.WHITE);
Font font = new Font("Verdana", Font.PLAIN, 20);
g2d.setFont(font);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
FontMetrics fm = g2d.getFontMetrics();
String option = "This is a test";
int x = (getWidth() - fm.stringWidth(option)) / 2;
int y = ((getHeight() - fm.getHeight()) / 2);
g2d.drawString(option, x, y + fm.getAscent());
g2d.drawRect(
(int)x - 20,
(int)y - 10,
(int)fm.stringWidth(option) + 40,
(int)fm.getHeight() + 20);
}
}
}
For example...
Centering String in Panel
Java center text in rectangle
Updated...
If, each menu item is printed within a single component, then the concept provided above should work. If you are printing multiple items within a single component, you could use something like...
public void render(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.WHITE);
Font font = new Font("Verdana", Font.PLAIN, 20);
g2d.setFont(font);
int x = 0;
int y = 0;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
FontMetrics fm = g2d.getFontMetrics();
String option = "This is a test";
while (x < getWidth()) {
while (y < getHeight()) {
int width = fm.stringWidth(option);
int height= fm.getHeight();
g2d.drawString(option, x + 20, y + fm.getAscent() + 10);
width += 40;
height += 20;
g2d.drawRect(
(int) x,
(int) y,
(int) width,
(int) height);
x += width;
y += height;
}
}
}
I am looking for the simplest way to draw some
text around an ellipse object on my app.
I need to create a feeling of "cuddling".
So far, I've used the Graphics2D class to print my drawings
on screen and my "canvas" is a BufferedImage.
The width and height of my ellipses are constant at 50,50 respectively.
Any suggestions?
Here's an example of curved text:
// slightly modified from the original:
// http://examples.oreilly.com/9781565924840/examples/RollingText.java
import javax.swing.*;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
public class RollingText extends JFrame {
RollingText() {
super("RollingText v1.0");
super.setSize(650, 350);
super.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
String s = "What's our vector, Victor?";
Font font = new Font("Serif", Font.PLAIN, 24);
FontRenderContext frc = g2.getFontRenderContext();
g2.translate(40, 80);
GlyphVector gv = font.createGlyphVector(frc, s);
int length = gv.getNumGlyphs();
for (int i = 0; i < length; i++) {
Point2D p = gv.getGlyphPosition(i);
double theta = (double) i / (double) (length - 1) * Math.PI / 4;
AffineTransform at = AffineTransform.getTranslateInstance(p.getX(), p.getY());
at.rotate(theta);
Shape glyph = gv.getGlyphOutline(i);
Shape transformedGlyph = at.createTransformedShape(glyph);
g2.fill(transformedGlyph);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new RollingText().setVisible(true);
}
});
}
}
which produces: