Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
How can I produce 5 x 7 dot matrix type digits for a clock project?
Here's an example of what I'm looking for.
I manually coded the position of each dot in each digit.
Is there a better way to create dot matrix digits?
By better I mean easier to code, easier to verify, makes use of existing fonts or other components, or anything else an experienced Swing developer might consider as an important consideration.
The rest of this question is the context for the naysayers that claim I didn't put enough effort into formulating my question.
Since I'm providing an answer to this question, I'm not posting fake bad code in the question. Sure, we appreciate it when people show their efforts. The code and text in my answer should supplement the question, in my opinion.
A few days ago, as I'm writing this, someone posted a question about making a clock with dot-matrix digits. The person posting the question provided code and a picture of what they had created. The person had created a BufferedImage where they had hardcoded the position of each square dot in his 4 x 7 dot matrix digits.
I was impressed with how much work the person had put into his BufferedImage, so I went off to my Eclipse to figure out how to generalize what he had done.
While I was away, people commented and closed his question. By the time I got back with my answer, the person had deleted his question.
Since I had what I thought was a good answer, I searched for an appropriate question. When I didn't find an appropriate question, I created a question.
Sure, I focused more on my answer than my question. Silly me, I thought that the answer would help provide context for the question.
Well, I hope that this explanation sheds some more light on why I thought this topic was important enough to create a question and ask for answers.
Unfortunately, you have to code the position of each dot in each digit.
However, you can do this in a flexible way.
Here's what I mean. A 5 x 7 dot matrix digit can be represented as a two-dimensional int array. It could also be represented as a boolean array, but a matrix of 0 and 1 values is easier to visually verify.
As an example. here's a method to code the zero digit. You can see that the ones create a visual outline that can be easily verified.
private int[][] defineZeroMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 0, 1, 1, 1, 0 };
matrix[1] = new int[] { 1, 0, 0, 0, 1 };
matrix[2] = new int[] { 1, 0, 0, 0, 1 };
matrix[3] = new int[] { 1, 0, 0, 0, 1 };
matrix[4] = new int[] { 1, 0, 0, 0, 1 };
matrix[5] = new int[] { 1, 0, 0, 0, 1 };
matrix[6] = new int[] { 0, 1, 1, 1, 0 };
return matrix;
}
It took me about 15 minutes to code all of the dot matrix digit matrices.
It's possible to code a dot matrix digit using a sparse matrix. Unfortunately, that idea leads to more code that's visually harder to verify.
Here's an example of the dot matrix digit two coded as a sparse matrix of coordinates.
private Point[] defineTwoCoordinates() {
Point[] array = new Point[14];
array[0] = new Point(0, 1);
array[1] = new Point(1, 0);
array[2] = new Point(2, 0);
array[3] = new Point(3, 0);
array[4] = new Point(4, 1);
array[5] = new Point(4, 2);
array[6] = new Point(3, 3);
array[7] = new Point(2, 4);
array[8] = new Point(1, 5);
array[9] = new Point(0, 6);
array[10] = new Point(1, 6);
array[11] = new Point(2, 6);
array[12] = new Point(3, 6);
array[13] = new Point(4, 6);
return array;
}
After we've coded all of the digit matrices, we'll create an array of matrices.
private int[][][] matrices;
The leftmost index is the digit, from 0 to 9. The second index is the row of the digit matrix. The third index is the column of the digit matrix.
Finally, we extend a JPanel and override the paintComponent method to actually paint the dot matrix digit.
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(dotColor);
for (int row = 0; row < matrices[digit].length; row++) {
for (int column = 0; column < matrices[digit][row].length; column++) {
if (matrices[digit][row][column] != 0) {
int x = margin + column * pixelWidth;
int y = margin + row * pixelWidth;
g.fillOval(x, y, dotWidth, dotWidth);
}
}
}
}
Here's a complete runnable example of how you would create a dot matrix digit panel by extending a JPanel, and use several dot matrix digit panels to create whatever GUI you want.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DotMatrixDigits implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new DotMatrixDigits());
}
#Override
public void run() {
JFrame frame = new JFrame("Dot Matrix Digits");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(defineTopPanel(), BorderLayout.BEFORE_FIRST_LINE);
frame.add(defineBottomPanel(), BorderLayout.AFTER_LAST_LINE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel defineTopPanel() {
JPanel panel = new JPanel();
panel.add(new DotMatrixDigit(0, Color.YELLOW, Color.BLACK));
panel.add(new DotMatrixDigit(1, Color.YELLOW, Color.BLACK));
panel.add(new DotMatrixDigit(2, Color.YELLOW, Color.BLACK));
panel.add(new DotMatrixDigit(3, Color.YELLOW, Color.BLACK));
panel.add(new DotMatrixDigit(4, Color.YELLOW, Color.BLACK));
return panel;
}
private JPanel defineBottomPanel() {
JPanel panel = new JPanel();
panel.add(new DotMatrixDigit(5, Color.YELLOW, Color.BLACK));
panel.add(new DotMatrixDigit(6, Color.YELLOW, Color.BLACK));
panel.add(new DotMatrixDigit(7, Color.YELLOW, Color.BLACK));
panel.add(new DotMatrixDigit(8, Color.YELLOW, Color.BLACK));
panel.add(new DotMatrixDigit(9, Color.YELLOW, Color.BLACK));
return panel;
}
/**
* <p>
* The <code>DotMatrixDigit</code> class creates a dot-matrix digit panel by
* extending <code>JPanel</code>. The dot matrix digit is 5 positions across and
* 7 positions down. The size of the dot and distance between dots are defined
* in the constructor of this class. The shape of the dot is determined in the
* <code>paintComponent</code> method.
* </p>
*
* #author Gilbert G. Le Blanc
* #version 1.8 - 20 October 2020
*
* #see JPanel
* #see Color
*/
public class DotMatrixDigit extends JPanel {
private static final long serialVersionUID = 1L;
/** int field to hold the digit to display **/
private int digit;
/** int field to hold the width of the dot in pixels **/
private int dotWidth;
/** int field to hold the distance between the
* top left corner of the dots in pixels **/
private int pixelWidth;
/** int field to hold the margin size in
* pixels surrounding the digit **/
private int margin;
private final Color dotColor;
private int[][][] matrices;
/**
* <p>
* This constructor creates a dot matrix digit panel. The preferred size of the
* panel is determined by the pixel width of each dot, including the space
* between the dots.
* </p>
*
* #param digit - The initial digit to display from 0 through 9.
* #param dotColor - The <code>Color</code> of the dots.
* #param backgroundColor - The background <code>Color</code> of the dot matrix
* digit panel.
*
*/
public DotMatrixDigit(int digit, Color dotColor, Color backgroundColor) {
this.digit = digit;
this.dotColor = dotColor;
this.dotWidth = 10;
this.pixelWidth = 15;
this.margin = dotWidth;
this.matrices = defineDigitMatricies();
int width = 4 * pixelWidth + dotWidth + margin + margin;
int height = 6 * pixelWidth + dotWidth + margin + margin;
this.setBackground(backgroundColor);
this.setPreferredSize(new Dimension(width, height));
}
private int[][][] defineDigitMatricies() {
int[][][] matrices = new int[10][][];
matrices[0] = defineZeroMatrix();
matrices[1] = defineOneMatrix();
matrices[2] = defineTwoMatrix();
matrices[3] = defineThreeMatrix();
matrices[4] = defineFourMatrix();
matrices[5] = defineFiveMatrix();
matrices[6] = defineSixMatrix();
matrices[7] = defineSevenMatrix();
matrices[8] = defineEightMatrix();
matrices[9] = defineNineMatrix();
return matrices;
}
private int[][] defineZeroMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 0, 1, 1, 1, 0 };
matrix[1] = new int[] { 1, 0, 0, 0, 1 };
matrix[2] = new int[] { 1, 0, 0, 0, 1 };
matrix[3] = new int[] { 1, 0, 0, 0, 1 };
matrix[4] = new int[] { 1, 0, 0, 0, 1 };
matrix[5] = new int[] { 1, 0, 0, 0, 1 };
matrix[6] = new int[] { 0, 1, 1, 1, 0 };
return matrix;
}
private int[][] defineOneMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 0, 0, 1, 0, 0 };
matrix[1] = new int[] { 0, 1, 1, 0, 0 };
matrix[2] = new int[] { 1, 0, 1, 0, 0 };
matrix[3] = new int[] { 0, 0, 1, 0, 0 };
matrix[4] = new int[] { 0, 0, 1, 0, 0 };
matrix[5] = new int[] { 0, 0, 1, 0, 0 };
matrix[6] = new int[] { 1, 1, 1, 1, 1 };
return matrix;
}
private int[][] defineTwoMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 0, 1, 1, 1, 0 };
matrix[1] = new int[] { 1, 0, 0, 0, 1 };
matrix[2] = new int[] { 0, 0, 0, 0, 1 };
matrix[3] = new int[] { 0, 0, 0, 1, 0 };
matrix[4] = new int[] { 0, 0, 1, 0, 0 };
matrix[5] = new int[] { 0, 1, 0, 0, 0 };
matrix[6] = new int[] { 1, 1, 1, 1, 1 };
return matrix;
}
private int[][] defineThreeMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 0, 1, 1, 1, 0 };
matrix[1] = new int[] { 1, 0, 0, 0, 1 };
matrix[2] = new int[] { 0, 0, 0, 0, 1 };
matrix[3] = new int[] { 0, 0, 1, 1, 0 };
matrix[4] = new int[] { 0, 0, 0, 0, 1 };
matrix[5] = new int[] { 1, 0, 0, 0, 1 };
matrix[6] = new int[] { 0, 1, 1, 1, 0 };
return matrix;
}
private int[][] defineFourMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 0, 0, 0, 1, 0 };
matrix[1] = new int[] { 0, 0, 1, 1, 0 };
matrix[2] = new int[] { 0, 1, 0, 1, 0 };
matrix[3] = new int[] { 1, 0, 0, 1, 0 };
matrix[4] = new int[] { 1, 1, 1, 1, 1 };
matrix[5] = new int[] { 0, 0, 0, 1, 0 };
matrix[6] = new int[] { 0, 0, 0, 1, 0 };
return matrix;
}
private int[][] defineFiveMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 1, 1, 1, 1, 1 };
matrix[1] = new int[] { 1, 0, 0, 0, 0 };
matrix[2] = new int[] { 1, 0, 0, 0, 0 };
matrix[3] = new int[] { 1, 1, 1, 1, 0 };
matrix[4] = new int[] { 0, 0, 0, 0, 1 };
matrix[5] = new int[] { 1, 0, 0, 0, 1 };
matrix[6] = new int[] { 0, 1, 1, 1, 0 };
return matrix;
}
private int[][] defineSixMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 0, 1, 1, 1, 0 };
matrix[1] = new int[] { 1, 0, 0, 0, 1 };
matrix[2] = new int[] { 1, 0, 0, 0, 0 };
matrix[3] = new int[] { 1, 1, 1, 1, 0 };
matrix[4] = new int[] { 1, 0, 0, 0, 1 };
matrix[5] = new int[] { 1, 0, 0, 0, 1 };
matrix[6] = new int[] { 0, 1, 1, 1, 0 };
return matrix;
}
private int[][] defineSevenMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 1, 1, 1, 1, 1 };
matrix[1] = new int[] { 0, 0, 0, 0, 1 };
matrix[2] = new int[] { 0, 0, 0, 0, 1 };
matrix[3] = new int[] { 0, 0, 0, 1, 0 };
matrix[4] = new int[] { 0, 0, 1, 0, 0 };
matrix[5] = new int[] { 0, 0, 1, 0, 0 };
matrix[6] = new int[] { 0, 0, 1, 0, 0 };
return matrix;
}
private int[][] defineEightMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 0, 1, 1, 1, 0 };
matrix[1] = new int[] { 1, 0, 0, 0, 1 };
matrix[2] = new int[] { 1, 0, 0, 0, 1 };
matrix[3] = new int[] { 0, 1, 1, 1, 0 };
matrix[4] = new int[] { 1, 0, 0, 0, 1 };
matrix[5] = new int[] { 1, 0, 0, 0, 1 };
matrix[6] = new int[] { 0, 1, 1, 1, 0 };
return matrix;
}
private int[][] defineNineMatrix() {
int[][] matrix = new int[7][];
matrix[0] = new int[] { 0, 1, 1, 1, 0 };
matrix[1] = new int[] { 1, 0, 0, 0, 1 };
matrix[2] = new int[] { 1, 0, 0, 0, 1 };
matrix[3] = new int[] { 0, 1, 1, 1, 1 };
matrix[4] = new int[] { 0, 0, 0, 0, 1 };
matrix[5] = new int[] { 1, 0, 0, 0, 1 };
matrix[6] = new int[] { 0, 1, 1, 1, 0 };
return matrix;
}
/**
* <p>
* The <code>setDigit</code> method sets the digit to display and repaints the
* panel.
* </p>
*
* #param digit - A digit from 0 through 9.
*/
public void setDigit(int digit) {
this.digit = digit;
this.repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(dotColor);
for (int row = 0; row < matrices[digit].length; row++) {
for (int column = 0; column < matrices[digit][row].length; column++) {
if (matrices[digit][row][column] != 0) {
int x = margin + column * pixelWidth;
int y = margin + row * pixelWidth;
g.fillOval(x, y, dotWidth, dotWidth);
}
}
}
}
}
}
Is there a better way to create dot matrix digits?
Don't know if 'better' (depends on whether effort or accuracy is the primary consideration) but there is a way to calculate the array based on the Shape of the digit.
Here is the result for a (bold) version of the default Monospaced font on this machine.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class FontToDotMatrix {
private JComponent ui = null;
Shape[] shapes = new Shape[10];
JComboBox fonts;
PixelArray[] pixelArrays = new PixelArray[10];
FontToDotMatrix() {
initUI();
}
public final void initUI() {
if (ui != null) {
return;
}
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
String[] fontFamilies = GraphicsEnvironment.
getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
fonts = new JComboBox(fontFamilies);
ui.add(fonts, BorderLayout.PAGE_START);
JPanel digitPanel = new JPanel(new GridLayout(2, 5, 4, 4));
ui.add(digitPanel);
digitPanel.setBackground(Color.RED);
for (int ii = 0; ii < 10; ii++) {
PixelArray pixelArray = new PixelArray();
pixelArrays[ii] = pixelArray;
digitPanel.add(pixelArray);
}
ActionListener listener = (ActionEvent e) -> {
for (int ii = 0; ii < 10; ii++) {
pixelArrays[ii].updatePixels(getLitPixels("" + ii));
}
};
fonts.addActionListener(listener);
fonts.setSelectedItem("Monospaced");
}
private Shape moveShapeToCenter(Shape shape) {
int w = 50;
int h = 70;
Rectangle2D b = shape.getBounds2D();
double xOff = -b.getX() + ((w - b.getWidth()) / 2d);
double yOff = -b.getY() + ((h - b.getHeight()) / 2d);
AffineTransform move = AffineTransform.getTranslateInstance(xOff, yOff);
return move.createTransformedShape(shape);
}
private boolean[][] getLitPixels(String digit) {
Font font = new Font(fonts.getSelectedItem().toString(), Font.BOLD, 70);
Shape shape = getShapeOfCharacter(font, digit);
Rectangle2D rect = shape.getBounds2D();
double h = rect.getHeight();
double ratio = 70d / h;
AffineTransform scale = AffineTransform.getScaleInstance(ratio, ratio);
shape = moveShapeToCenter(scale.createTransformedShape(shape));
boolean[][] bools = new boolean[5][7];
for (int yy = 0; yy < 7; yy++) {
for (int xx = 0; xx < 5; xx++) {
Point point = new Point((xx * 10) + 5, (yy * 10) + 5);
bools[xx][yy] = shape.contains(point);
}
}
return bools;
}
public JComponent getUI() {
return ui;
}
private Shape getShapeOfCharacter(Font font, String digit) {
BufferedImage bi = new BufferedImage(
1, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
FontRenderContext frc = g.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, digit);
return gv.getOutline();
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
FontToDotMatrix o = new FontToDotMatrix();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}
class PixelArray extends JPanel {
JLabel[][] labels = new JLabel[5][7];
PixelArray() {
setLayout(new GridLayout(7, 5));
BufferedImage bi = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
ImageIcon icon = new ImageIcon(bi);
for (int yy = 0; yy < labels[0].length; yy++) {
for (int xx = 0; xx < labels.length; xx++) {
JLabel l = new JLabel(icon);
labels[xx][yy] = l;
l.setOpaque(true);
add(l);
}
}
}
public void updatePixels(boolean[][] bools) {
for (int xx = 0; xx < labels.length; xx++) {
for (int yy = 0; yy < labels[0].length; yy++) {
JLabel l = labels[xx][yy];
if (bools[xx][yy]) {
l.setBackground(Color.WHITE);
} else {
l.setBackground(Color.BLACK);
}
}
}
}
}
I am learning about 2d images. We were given a template to use and told do various things. Everything seems to be working fine, except the image is already being drawn rotated. I do not understand why. This is the image.
public static int[][] letterN = {
{0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0},
{0, 1, 1, 0, 0, 0, 0, 1, 1, 0}};
This is the code from the program: I dont understand why the image is popping up rotated. All of the other letters are being rotated as well.
package cmsc325animate;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CMSC325Animate extends JPanel {
// A counter that increases by one in each frame.
private int frameNumber;
// The time, in milliseconds, since the animation started.
private long elapsedTimeMillis;
// This is the measure of a pixel in the coordinate system
// set up by calling the applyLimits method. It can be used
// for setting line widths, for example.
private float pixelSize;
static int translateX = 0;
static int translateY = 0;
static double rotation = 0.0;
static double scaleX = 1.0;
static double scaleY = 1.0;
ImageTemplate myImages = new ImageTemplate();
BufferedImage tImage = myImages.getImage(ImageTemplate.letterT);
BufferedImage nImage = myImages.getImageN(ImageTemplate.letterN);
BufferedImage oImage = myImages.getImageO(ImageTemplate.letterO);
BufferedImage aImage = myImages.getImageA(ImageTemplate.letterA);
BufferedImage hImage = myImages.getImageH(ImageTemplate.letterH);
BufferedImage heartImage = myImages.getImageHeart(ImageTemplate.heart);
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
JFrame window;
window = new JFrame("Java Animation"); // The parameter shows in the window title bar.
final CMSC325Animate panel = new CMSC325Animate(); // The drawing area.
window.setContentPane(panel); // Show the panel in the window.
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // End program when window closes.
window.pack(); // Set window size based on the preferred sizes of its contents.
window.setResizable(false); // Don't let user resize window.
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
window.setLocation( // Center window on screen.
(screen.width - window.getWidth()) / 2,
(screen.height - window.getHeight()) / 2);
Timer animationTimer; // A Timer that will emit events to drive the animation.
final long startTime = System.currentTimeMillis();
// Taken from AnimationStarter
// Modified to change timing and allow for recycling
animationTimer = new Timer(1600, new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
if (panel.frameNumber > 4) {
panel.frameNumber = 0;
} else {
panel.frameNumber++;
}
panel.elapsedTimeMillis = System.currentTimeMillis() - startTime;
panel.repaint();
}
});
window.setVisible(true); // Open the window, making it visible on the screen.
animationTimer.start(); // Start the animation running.
}
public CMSC325Animate() {
// Size of Frame
setPreferredSize(new Dimension(800, 600));
}
// This is where all of the action takes place
// Code taken from AnimationStarter.java but modified to add the specific Images
// Also added looping structure for Different transformations
protected void paintComponent(Graphics g) {
/* First, create a Graphics2D drawing context for drawing on the panel.
* (g.create() makes a copy of g, which will draw to the same place as g,
* but changes to the returned copy will not affect the original.)
*/
Graphics2D g2 = (Graphics2D) g.create();
/* Turn on antialiasing in this graphics context, for better drawing.
*/
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
/* Fill in the entire drawing area with white.
*/
g2.setPaint(Color.WHITE);
g2.fillRect(0, 0, getWidth(), getHeight()); // From the old graphics API!
/* Here, I set up a new coordinate system on the drawing area, by calling
* the applyLimits() method that is defined below. Without this call, I
* would be using regular pixel coordinates. This function sets the value
* of the global variable pixelSize, which I need for stroke widths in the
* transformed coordinate system.
*/
// Controls your zoom and area you are looking at
applyWindowToViewportTransformation(g2, -75, 75, -75, 75, true);
AffineTransform savedTransform = g2.getTransform();
System.out.println("Frame is " + frameNumber);
switch (frameNumber) {
case 1: // First frame is unmodified.
translateX = 0;
translateY = 0;
scaleX = 1.0;
scaleY = 1.0;
rotation = 0;
break;
case 2: // Second frame translates each image by (-10, 12).
translateX = -10;
translateY = 12;
break;
case 3: // Third frame rotates each image by 55 degrees Counter
translateX = -10;
translateY = 12;
rotation = 55 * Math.PI / 180.0;
break;
// Can add more cases as needed
case 4: // 4th frame rotates each image by 75 degress Clockwise
translateX = -10;
translateY = 12;
rotation = 55 * Math.PI / 180.0;
rotation = 75 * Math.PI * 180;
break;
case 5: // Scales image 3.5 for x and 1.5 for y
translateX = -10;
translateY = 12;
rotation = 55 * Math.PI / 180.0;
rotation = 75 * Math.PI * 180;
scaleX = scaleX * 3.5;
scaleY = scaleY * 1.5;
break;
default:
break;
} // End switch
g2.translate(translateX, translateY); // Move image.
// To offset translate again
g2.translate(-10,10);
g2.rotate(rotation); // Rotate image.
g2.scale(scaleX, scaleY); // Scale image.
g2.drawImage(tImage, 100, 0, this); // Draw image.
g2.setTransform(savedTransform);
// draw nImage
g2.translate(translateX, translateY); // Move image.
// To offset translate again
// This allows you to place your images across your graphic
g2.translate(-30, 30);
g2.rotate(rotation); // Rotate image.
g2.scale(scaleX, scaleY); // Scale image.
g2.drawImage(nImage, 0, 0, this); // Draw image.
g2.setTransform(savedTransform);
// You can add more shapes/images as needed
// draw o Image
g2.translate(translateX, translateY); //move image
g2.translate(-10, 10);
g2.rotate(rotation);
g2.scale(scaleX, scaleY);
g2.drawImage(oImage, 0, 0, this);
g2.setTransform(savedTransform);
g2.translate(translateX, translateY); //move image
g2.translate(10, -10);
g2.rotate(rotation);
g2.scale(scaleX, scaleY);
g2.drawImage(aImage, 0, 0, this);
g2.setTransform(savedTransform);
g2.translate(translateX, translateY); //move image
g2.translate(30, -30);
g2.rotate(rotation);
g2.scale(scaleX, scaleY);
g2.drawImage(hImage, 0, 0, this);
g2.setTransform(savedTransform);
g2.translate(translateX, translateY); //move image
g2.translate(50, -50);
g2.rotate(rotation);
g2.scale(scaleX, scaleY);
g2.drawImage(heartImage, 0, 0, this);
g2.setTransform(savedTransform);
}
// Method taken directly from AnimationStarter.java Code
private void applyWindowToViewportTransformation(Graphics2D g2,
double left, double right, double bottom, double top,
boolean preserveAspect) {
int width = getWidth(); // The width of this drawing area, in pixels.
int height = getHeight(); // The height of this drawing area, in pixels.
if (preserveAspect) {
// Adjust the limits to match the aspect ratio of the drawing area.
double displayAspect = Math.abs((double) height / width);
double requestedAspect = Math.abs((bottom - top) / (right - left));
if (displayAspect > requestedAspect) {
// Expand the viewport vertically.
double excess = (bottom - top) * (displayAspect / requestedAspect - 1);
bottom += excess / 2;
top -= excess / 2;
} else if (displayAspect < requestedAspect) {
// Expand the viewport vertically.
double excess = (right - left) * (requestedAspect / displayAspect - 1);
right += excess / 2;
left -= excess / 2;
}
}
g2.scale(width / (right - left), height / (bottom - top));
g2.translate(-left, -top);
double pixelWidth = Math.abs((right - left) / width);
double pixelHeight = Math.abs((bottom - top) / height);
pixelSize = (float) Math.max(pixelWidth, pixelHeight);
}
}
I have a new problem with the repaint methode.
There is a frame in which I paint several graphics together. When I click in a specific range of coordinates, it should change the status of one graphic and then repaint this specific range. But this repaint doesn't work...
This is the method that initialize the graphics (the declaration of the graphics isn't shown):
private void initComponents() {
Painter painter = new Painter();
setExtendedState(MAXIMIZED_BOTH);
int width = (int) getContentPane().getBounds().getWidth();
int height = (int) getContentPane().getBounds().getHeight();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
g0 = new Gleis(25, 1, 4, 0, 0);
g1 = new Gleis(26, 1, 0, 1, 0);
k0 = new KnopfRot(26, 1, 0, false);
g2 = new Gleis(27, 1, 0, 2, 0);
g3 = new Gleis(28, 1, 0, 3, 0);
g4 = new Gleis(29, 1, 0, 4, 0);
g5 = new Gleis(30, 1, 0, 5, 0);
g6 = new Gleis(31, 1, 0, 6, 0);
g7 = new Gleis(32, 1, 0, 7, 0);
g8 = new Gleis(33, 1, 0, 8, 0);
g9 = new Gleis(34, 1, 0, 9, 0);
g10 = new Gleis(35, 1, 0, 10, 0);
g11 = new Gleis(36, 1, 0, 11, 0);
g12 = new Gleis(37, 1, 0, 12, 0);
g13 = new Gleis(38, 1, 0, 13, 0);
k1 = new KnopfRot(38, 1, 1, false);
painter.addGleis(g0);
painter.addGleis(g1);
painter.addKnopfRot(k0);
painter.addGleis(g2);
painter.addGleis(g3);
painter.addGleis(g4);
painter.addGleis(g5);
painter.addGleis(g6);
painter.addGleis(g7);
painter.addGleis(g8);
painter.addGleis(g9);
painter.addGleis(g10);
painter.addGleis(g11);
painter.addGleis(g12);
painter.addGleis(g13);
painter.addKnopfRot(k1);
this.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
double x = e.getX();
double y = e.getY();
frameMouseClicked(x, y);
}
});
this.getContentPane().add(painter);
this.setVisible(true);
}
public void frameMouseClicked(double x, double y) {
if(x >= 306 && x <= 314 && y >= 55 && y <= 63){ //x+11 y+32
Painter painter = new Painter();
painter.updateKnopfRot(k0, 26, 1, 306, 55);
repaint();
}
else{}
}
Then my class KnopfRot:
public class KnopfRot {
int xposition;
int yposition;
int type;
int id;
boolean status;
//Konstruktor
public KnopfRot(int xpos, int ypos, int id, boolean status){
this.xposition = xpos * 11 + 12;
this.yposition = ypos * 11 + 12;
this.id = id; //zur eindeutigen Bezeichnung der Gleiselemente
this.status = status;
}
public void draw(Graphics2D g) {
if (!status) {
Ellipse2D.Double aussen = new Ellipse2D.Double(xposition, yposition, 8, 8);
Ellipse2D.Double innen = new Ellipse2D.Double(xposition + 1, yposition + 1, 6, 6);
g.setColor(Color.black);
g.fill(aussen);
g.setColor(Color.red);
g.fill(innen);
}
else if (status){
Ellipse2D.Double aussen = new Ellipse2D.Double(xposition, yposition, 7, 7);
Ellipse2D.Double innen = new Ellipse2D.Double(xposition + 1, yposition + 1, 5, 5);
g.setColor(Color.black);
g.fill(aussen);
g.setColor(Color.red);
g.fill(innen);
}
else {}
}
}
And the Painter-Class with the method updateKnopfRot which is callen in the MouseEvent:
public class Painter extends JPanel {
private List<Gleis> gleisList = new ArrayList<>();
private List<KnopfRot> knopfList = new ArrayList<>();
//fügt ein neues Element der Liste gleisList hinzu
public void addGleis(Gleis gleis) {
gleisList.add(gleis);
}
public void addKnopfRot(KnopfRot knopf) {
knopfList.add(knopf);
}
public void updateKnopfRot(KnopfRot knopf, int x, int y, int xpos, int ypos) {
knopfList.remove(knopf);
addKnopfRot(new KnopfRot(x, y, 0, true));
}
//paint-Methode geht jedes Element der Liste gleisList durch und führt die draw-Methode in Gleis.java aus
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (Gleis gleis : gleisList) {
gleis.draw(g2);
}
for (KnopfRot knopf : knopfList) {
knopf.draw(g2);
}
}
}
Sorry for so much code but I can't reduce it to explain/show the problem.
Can somebody help?
You're creating a new Painter(...) object within your frameMouseClicked(...) method, and then changing the state of this object, but this will have no effect on the completely different visualized Painter object. Your solution -- don't create a new Painter object, but rather use a reference to the original, and then try to change its state.
Regarding:
Sorry for so much code but I can't reduce it to explain/show the problem.
If this answer doesn't solve your problem, then yes, you can reduce this code and make it runnable as we've discussed in the past. Yes it will take some effort on your part to create and post a valid MCVE, but it's worth it.
How to add multiple finish line in traverse? example the player needs to go to the 1st finish line, then to next, then up to the last. this is the code. i want to have 1 up to 4 traversing point. this is the code. i dunno where to put the statement.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MazeSearchGUI extends JPanel implements ActionListener, Runnable {
private static final long serialVersionUID = 1L;
private final int WALL = 0; // A wall position value
private final int EMPTY = 1; // An empty position value
private final int TRIED = 3; // Previously tried path value
private final int PATH = 7; // Successful path value
private final int HEADERSZ = 60; // Height in header area
private int width, height; // Width and Height
private Image brick; // Brick images for walls
private Image flag; // Flag finish line images
private Image mole; // Flag finish line images
private int brickWidth, brickHeight; // Brick dimensions
private int flagWidth, flagHeight; // Flag image dimensions
private int moleWidth, moleHeight; // Flag image dimensions
private Color backCol = Color.white; // Header background color
private Color mazeCol = Color.yellow; // Maze background color
private Color triedCol = Color.lightGray; // Tried path color
private Color successCol = Color.green; // Successful path color
private Timer timer; // Timer to start game
private int startDelay = 3000; // Delay to start traversal
private int stepDelay = 1000; // Delay between each step
private static JPanel mainPanel; // Main game panel
private static String headLabel; // String at top
private static boolean success = false; // Flags successful path
private static int xPos, yPos; // Current position
// Data structure of Grid
private static int[][] grid = { { 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1 },
{ 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1 },
{ 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0 },
{ 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1 },
{ 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1 },
{ 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };
// No Arg Constructor - the thread process runs this
public MazeSearchGUI() {
}
// One Arg Constructor - main panel process runs this
public MazeSearchGUI(String label) {
headLabel = label;
backCol = new Color(255, 182, 193);
xPos = 0;
yPos = 0;
brick = new ImageIcon("brick.png").getImage();
flag = new ImageIcon("flag.png").getImage();
mole = new ImageIcon("mole.png").getImage();
MediaTracker track = new MediaTracker(this);
track.addImage(brick, 0);
track.addImage(flag, 0);
track.addImage(mole, 0);
try {
track.waitForAll();
} catch (InterruptedException e) {
}
brickWidth = brick.getWidth(null);
brickHeight = brick.getHeight(null);
flagWidth = flag.getWidth(null);
flagHeight = flag.getHeight(null);
moleWidth = mole.getWidth(null);
moleHeight = mole.getHeight(null);
width = brickWidth * grid[0].length;
height = brickHeight * grid.length + HEADERSZ;
setBackground(backCol);
setPreferredSize(new Dimension(width, height));
setFocusable(true);
timer = new Timer(startDelay, this);
timer.start();
}
// runs once just to start the traversal thread
public void actionPerformed(ActionEvent event) {
timer.stop();
(new Thread(new MazeSearchGUI())).start();
}
// starts the traversal thread
public void run() {
if (traverse(0, 0)) {
success = true;
headLabel = "I Found A Way Out!";
mainPanel.repaint();
// JOptionPane.showMessageDialog(null,"A successful path was found!");
} else {
headLabel = "There's No Way Out!";
mainPanel.repaint();
// JOptionPane.showMessageDialog(null,"There is no way out of this maze!");
}
}
// Updates the window
public void paintComponent(Graphics page) {
super.paintComponent(page);
page.setColor(backCol);
page.fillRect(0, 0, width, HEADERSZ);
page.setColor(mazeCol);
page.fillRect(0, HEADERSZ, width, height - HEADERSZ);
page.setColor(Color.black);
page.setFont(new Font("Arial", Font.BOLD, 24));
FontMetrics metrics = page.getFontMetrics();
int labelWidth = metrics.stringWidth(headLabel);
page.drawString(headLabel, (width - labelWidth) / 2, HEADERSZ - 20);
page.drawImage(flag, grid[0].length * brickWidth
- (brickWidth + flagWidth) / 2, height
- (brickHeight + flagHeight) / 2, null);
for (int row = 0; row < grid.length; ++row) {
for (int col = 0; col < grid[row].length; ++col) {
if (grid[row][col] == WALL)
page.drawImage(brick, col * brickWidth, row * brickHeight
+ HEADERSZ, null);
else if (success) {
if (grid[row][col] == PATH) {
page.setColor(successCol);
page.fillRect(col * brickWidth, row * brickHeight
+ HEADERSZ, brickWidth, brickHeight);
}
} else if (grid[row][col] == TRIED
&& (row != yPos || col != xPos)) {
page.setColor(triedCol);
page.fillRect(col * brickWidth, row * brickHeight
+ HEADERSZ, brickWidth, brickHeight);
}
}
}
page.drawImage(mole, (xPos + 1) * brickWidth - (brickWidth + moleWidth)
/ 2, HEADERSZ + (yPos + 1) * brickHeight
- (brickHeight + moleHeight) / 2, null);
}
// Attempts to recursively traverse the maze. Inserts special
// characters indicating locations that have been tried and that
// eventually become part of the solution.
public boolean traverse(int row, int col) {
boolean done = false;
if (valid(row, col)) {
pause(stepDelay); // half second waits
xPos = col;
yPos = row;
grid[row][col] = TRIED; // this cell has been tried
mainPanel.repaint();
if (row == grid.length - 1 && col == grid[0].length - 1)
done = true; // the maze is solved
else {
done = traverse(row + 1, col); // down
if (!done)
done = traverse(row, col + 1); // right
if (!done)
done = traverse(row - 1, col); // up
if (!done)
done = traverse(row, col - 1); // left
}
if (done) // this location is part of the final path
grid[row][col] = PATH;
}
return done;
}
// Determines if a specific location is valid.
private boolean valid(int row, int col) {
// check if cell is in the bounds of the matrix
if (row >= 0 && row < grid.length && col >= 0 && col < grid[row].length)
// check if cell is not blocked and not previously tried
if (grid[row][col] == EMPTY)
return true;
return false;
}
public void pause(long millisecs) {
try {
Thread.sleep(millisecs);
} catch (InterruptedException e) {
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Maze Search GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainPanel = new MazeSearchGUI("Trying To Traverse This Maze...");
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setVisible(true);
}
}
Your code seems pretty procedural (meaning you have lots of code in a single class, without having different objects with state and behaviour). Applying changes will be very difficult and complex. Try to write object oriented code with different classes, each having their own state and responsibility.
This way you could have one class for the character sprite, one for the grid, one for the traversing points. If you want the points to have a certain order in which they need to be visited, an option would be to implement the state pattern.
Have a look at this https://en.wikipedia.org/wiki/State_pattern
and this http://gameprogrammingpatterns.com/state.html for the state pattern.
For more specific answers, please reduce your code example to the minimum needed to understand your question, and provide more information about the game itself.
In an application, while trying to rotate an object using touch, I noticed drift in position of object after sometime (without any translation applied !!). The rotation is only about z-axis and works perfectly, but drift happens only after few rotations.
ds will be used for translation (using up-down button).
_uNozzleCentreMatrix and _ModelMatrixNozzle will use ds if I correct this.
private static final float[] _uNozzleCentre = new float[]{0.0f, 0.333605f, 0.0f, 1.0f};
protected static float[] _uNozzleCentreMatrix = new float[4];
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.setLookAtM(GLES20Renderer._ViewMatrix, 0, 0, 0, 7f, 0, 0, 0, 0, 1, 0);
Matrix.frustumM(GLES20Renderer._ProjectionMatrix, 0, -ratio, ratio, -1, 1, 2, 8);
Matrix.setIdentityM(GLES20Renderer._ModelMatrixNozzle, 0);
}
private static void updateModel(int upDown, float xAngle, float yAngle, float zAngle) {
//ds = GLES20Renderer._upDown - GLES20Renderer._lastUpDown;
ds = 0; // ds changes with button up-down, but now it is made 0, so button up-down will not affect it
Matrix.multiplyMV(GLES20Renderer._uNozzleCentreMatrix, 0, GLES20Renderer._ModelMatrixNozzle, 0, GLES20Renderer._uNozzleCentre, 0);
if(Math.abs(ds) > 0) {
} else {
if(GLES20Renderer._zAngle >= 360) {
GLES20Renderer._zAngle = GLES20Renderer._zAngle - 360;
}
if(GLES20Renderer._zAngle <= -360) {
GLES20Renderer._zAngle = GLES20Renderer._zAngle + 360;
}
Matrix.translateM(GLES20Renderer._ModelMatrixNozzle, 0, GLES20Renderer._uNozzleCentreMatrix[0], GLES20Renderer._uNozzleCentreMatrix[1], 0);
Matrix.rotateM(GLES20Renderer._ModelMatrixNozzle, 0, GLES20Renderer._zAngle, 0, 0, 1);
Matrix.rotateM(GLES20Renderer._ModelMatrixNozzle, 0, -GLES20Renderer._lastZAngle, 0, 0, 1);
Matrix.translateM(GLES20Renderer._ModelMatrixNozzle, 0, -GLES20Renderer._uNozzleCentreMatrix[0], -GLES20Renderer._uNozzleCentreMatrix[1], 0);
}
Matrix.multiplyMM(GLES20Renderer._MVPMatrixNozzle, 0, GLES20Renderer._ViewMatrix, 0, GLES20Renderer._ModelMatrixNozzle, 0);
Matrix.multiplyMM(GLES20Renderer._MVPMatrixNozzle, 0, GLES20Renderer._ProjectionMatrix, 0, GLES20Renderer._MVPMatrixNozzle, 0);
GLES20Renderer._lastZAngle = zAngle;
}
Apk for download:
http://www.pixdip.com/opengles/rotation/rotation.apk
(Try swiping a longer horizontal area from extreme left to right to observe drift early. Please be patient! Drift can take 20 seconds to occur)
For those whom it did not happen, here is the automated apk:
http://www.pixdip.com/opengles/rotation/automatic.apk
and the edited part of code:
Matrix.translateM(GLES20Renderer._ModelMatrixNozzle, 0, GLES20Renderer._uNozzleCentreMatrix[0], GLES20Renderer._uNozzleCentreMatrix[1], 0);
Matrix.rotateM(GLES20Renderer._ModelMatrixNozzle, 0, GLES20Renderer._zAngle, 0, 0, 1);
//Matrix.rotateM(GLES20Renderer._ModelMatrixNozzle, 0, -GLES20Renderer._lastZAngle, 0, 0, 1);
Matrix.translateM(GLES20Renderer._ModelMatrixNozzle, 0, -GLES20Renderer._uNozzleCentreMatrix[0], -GLES20Renderer._uNozzleCentreMatrix[1], 0);
Sometimes floating point errors get accumulated because of matrix stack.
This can be removed by using separate matrices for some critical transformations:
private static float[] _TMatrix = new float[16];
private static float[] _ModelMatrix = new float[16];
Matrix.setIdentity(Renderer._ModelMatrix);
Matrix.setIdentity(Renderer._TMatrix);
Matrix.translate(Renderer._ModelMatrix, xmov,ymov,0);
Matrix.translate(Renderer._TMatrix, -xmov,-ymov,0);
Matrix.multiply(Renderer._ModelMatrix, Renderer._TMatrix, Renderer._ModelMatrix);
// will result in an identity model matrix, without any floating point errors