The code below plots some simple x-y data, but it has two problems that I do not know how to fix.
First, it plots negative values for some of the data points, which means lines extending southward below the x-axis. Since the data points are selected at random, you may have to resize the frame a bit in order to view new random numbers to be plotted in a way that shows this bug. All data values will be positive, so I want all deflections to project northward above the blue bottom marker line, and I need to make sure that no deflections extend southward below the blue bottom marker line.
Second, the y-axis label takes up too much real estate on the screen. It needs to be rotated -90 degrees. However, all the examples I have seen for this involve rotating the entire panel using a graphics2d object. I do not want to rotate the entire panel. Instead, I just want to rotate the text of the y-axis label.
Can anyone show me how to change the code below to fix these two specific problems?
The code is in the following two files:
GUI.java
import java.awt.*;
import javax.swing.*;
class GUI{
GUI() {
// Create a new JFrame container.
JFrame jfrm = new JFrame("X-Y Plot");
// Specify FlowLayout for the layout manager.
jfrm.getContentPane().setLayout(new FlowLayout());
int frameHeight = 400;
int frameWidth = 300;
// Give the frame an initial size.
jfrm.setSize(frameWidth, frameHeight);
// Terminate the program when the user closes the application.
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create a text-based label.
JVertLabel myVLabel = new JVertLabel("y-axis label");
int width = myVLabel.WIDTH;
PaintPanel myPP = new PaintPanel(frameWidth-width-50-20,frameHeight-70);
jfrm.add(myPP);
jfrm.add(myVLabel);// Add the label to the frame.
// Display the frame.
jfrm.setVisible(true);
}
public static void main(String args[]) {
// Create the frame on the event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {public void run(){new GUI();}});
}
public class JVertLabel extends JComponent {
private String text;
public JVertLabel(String s) {
text = s;
}//constructor
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.rotate(Math.toRadians(-90));
g2d.drawString(text, 0, 0);
}
}
}
PaintPanel.java
import java.awt.*;
import javax.swing.*;
import java.util.*;
class PaintPanel extends JPanel {
Insets ins; // holds the panel's insets
Random rand; // used to generate random numbers
PaintPanel(int w, int h) {
setOpaque(true);// Ensure that panel is opaque.
setPreferredSize(new Dimension(w, h));// Set preferred dimension as specfied.
rand = new Random();
}
protected void paintComponent(Graphics g) {// Override paintComponent() method.
super.paintComponent(g);// Always call superclass method first.
int height = getHeight();// Get height of component.
int width = getWidth();// Get width of component.
ins = getInsets();// Get the insets.
// Get dimensions of text
Graphics2D g2d = (Graphics2D) g;
Font font = new Font("Serif", Font.PLAIN, 12);
FontMetrics fontMetrics = g2d.getFontMetrics();
String xString = ("x-axis label");
int xStrWidth = fontMetrics.stringWidth(xString);
int xStrHeight = fontMetrics.getHeight();
String yString = "y-axis-label";
int yStrWidth = fontMetrics.stringWidth(yString);
int yStrHeight = fontMetrics.getHeight();
int leftStartPlotWindow = ins.left + 5 + yStrWidth;
int hPad = 3;
// Fill panel by plotting random data in a bar graph.
for (int i = leftStartPlotWindow + hPad; i <= width - leftStartPlotWindow - hPad + yStrWidth + 1; i += 4) {
int h = Math.abs(rand.nextInt(height - ins.bottom));//Get rand# betw/0 and max height of drawing area.
// If generated value w/in or too close to border, change it to just outside border.
if (h <= ins.top) {
h = ins.top + 1;
}
g.drawLine(i, Math.abs(height - ins.bottom - xStrHeight - 5), i, h);// Draw a line that represents data.
}
g.setColor(Color.blue);
g.drawRect(leftStartPlotWindow, ins.bottom + 2, width - leftStartPlotWindow - ins.right - hPad, height - xStrHeight - 6);
g.setColor(Color.red);
g.drawRect(ins.left, ins.bottom, width - ins.left - 1, height - ins.bottom - 1);
g.drawString(xString, (width / 2) - (xStrWidth / 2), height - ins.bottom - 6);
g.drawString(yString, ins.left, height / 2);
}
}
All data values will be positive, so I want all deflections to project northward above the blue bottom marker line, and I need to make sure that no deflections extend southward below the blue bottom marker line.
You need to calculate the random height so that all values fit into the space available. So the calculation would be something like:
int randomHeight = panelHeight - offset.top - offset.bottom - heightForTheXAxisText;
Then you don't have to worry about negative values or the top of the line extending outside the bounds of the panel.
all the examples I have seen for this involve rotating the entire panel using a graphics2d object. I do not want to rotate the entire panel. Instead, I just want to rotate the text of the y-axis label.
Set the rotation of the Graphics object, the draw the text, then restore the rotation of the Graphics object back to 0.
Or, you create create a new Graphcis object from the current Graphics object, then apply the rotation, draw the text and then dispose of the temporaray Graphics object.
JFreeChart addresses both issues by default, as shown in this example.
In your example,
You'll have to create the data model before trying to render it. Then you can scan it for min and max to determine the limits of your range axis. List<Double> may be a suitable choice.
You can rotate the range label by altering the graphics context's AffineTransform, as shown in RotateText.
Related
When I resize the window, my custom JTextField changes size. It only does this once, the first time I resize the window. It's related to this line inside paintComponent:
setMargin(new Insets(2, 25, 2, 2));
Running that command does not resize the text field until I resize the window. After resizing the window, running that command causes JTextField to become larger. Actual size of the window does not matter. The first time window size is changed, JTextField becomes larger and then it stays large until the end of time. I would prefer if the field was large as soon as I start the program, and obviously, I don't want it to randomly change size.
How can I fix the size of this JTextField so that it does not randomly change?
Here is the entire class:
/**
* From https://gmigdos.wordpress.com/2010/03/30/java-a-custom-jtextfield-for-searching/
* #author Georgios Migdos <cyberpython#gmail.com> */
public class JIconTextField extends JTextField {
private Icon icon;
private Insets dummyInsets;
public JIconTextField(int columns) throws IOException {
super(columns);
Border border = UIManager.getBorder("TextField.border");
JTextField dummy = new JTextField();
this.dummyInsets = border.getBorderInsets(dummy);
String path = "find-16x16.png";
InputStream is = Main.class.getClassLoader().getResourceAsStream(path);
setIcon(new ImageIcon(ImageIO.read(is)));
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int textX = 2;
if(this.icon!=null){
int iconWidth = icon.getIconWidth();
int iconHeight = icon.getIconHeight();
int x = dummyInsets.left + 5;//this is our icon's x
textX = x+iconWidth+2; //this is the x where text should start
int y = (this.getHeight() - iconHeight)/2;
icon.paintIcon(this, g, x, y);
}
setMargin(new Insets(2, textX, 2, 2));
}
}
Further to the comments, if you cut the code out from the paintComponent method and paste it into the constructor, you will get your desired result.
The paintComponent method for actual painting, i.e. blanking the background of the field and drawing the text. Generally, unless you specifically want to change the way the component is drawn, you should not need to override this method.
I want to get the exact height of my string in pixels on my panel. So I wrote a program that draws the string, and then draws a rectangle around it.
Using FontMetrics I used the getStringBounds method to get me the enclosing rectangle.
However it looks wrong :
I was expecting the rectangle to perfectly enclose my text, but there is space at the top (And a tiny bit of space on the left and right). Why is it giving me this result?
Here is my code :
public class Test extends JPanel {
#Override
protected void paintComponent(Graphics g) {
Font font = new Font("Arial", Font.PLAIN, 60);
g.setFont(font);
FontMetrics fm = this.getFontMetrics(font);
String str = "100dhgt";
Rectangle2D rect = fm.getStringBounds(str, g);
int x = 5;
int y = 100;
g.drawRect(x, y - (int)rect.getHeight(), (int)rect.getWidth(), (int)rect.getHeight());
g.drawString(str, x, y);
}
public static void main(String[] args) {
JFrame f = new JFrame();
Test test = new Test();
f.add(test);
f.setVisible(true);
f.setSize(400, 400);
}
}
Regarding your rectangle, you have to consider the font's descend (how far is it below the line)
g.drawString(str, x, y - fm.getDescent());
Also note that the font height usually considers some kind of line spacing. In this case fm.getDescent() + fm.getAscent() = 68 whereas fm.getHeight() = 70
The space at the top can be explained by your not taking account of the descent (which takes me back to one of my favorite methods from java 1.0: getMaxDecent)
Otherwise, the box looks pretty good. The only other advice I can offer is that fm.getStringBounds works better with some fonts than it does with others
Up till now I have used the code given below to load image in a new new JFrame and coordinates of pixels of image are hard-coded in the code itself. However, I wish to do it dynamically i.e mark points clicked by mouse and store all the coordinates in an array or so.
The code is running properly, Image is chosen using JFileChooser in another class and the path is passed as a parameter to this class.
Please help me out.
public class GraphicsSet extends JFrame{
Font f ;
BufferedImage baseImage ;
File file ;
String Gimage;
JFrame imageFrame;
public GraphicsSet(String Image)
{
super("Frame") ;
// imageFrame = new JFrame("Click on to select points");
file = new File(Image) ;
Gimage = Image;
try
{
baseImage = ImageIO.read(file) ;
}
catch(IOException e)
{
System.out.println(e) ;
}
int imageWidth = baseImage.getWidth();
int imageHeight = baseImage.getHeight();
System.out.println(imageWidth);
setBounds(0,0,imageWidth,imageHeight) ;
setVisible(true) ;
//setDefaultCloseOperation (EXIT_ON_CLOSE) ;
}
public void paint(Graphics g)
{
g.drawImage(baseImage, 0, 0, null) ;
String[] coordsText = new String[]{
"264.33,329.94","244.24,382.57","243.00,328.88",
"264.33,329.94","272.06,331.59","278.30,341.00",
"284.28,350.02","282.18,367.78","275.24,375.79",
"272.89,378.50","269.26,380.27","266.00,381.66",
"259.36,384.50","258.52,383.52","252.00,383.09",
"244.24,382.57","238.62,383.56","232.21,377.61",
"228.01,373.71","225.52,365.66","226.13,360.00",
"226.13,360.00","227.55,354.00","227.55,354.00",
"228.20,350.96","227.74,347.67","228.74,345.00",
"230.78,339.55","237.90,331.81","243.00,328.88",
"248.10,327.42","249.02,328.30","254.00,328.88"
};
// The polygons will be stored in instances of Path2D.Float. After we create an instance
// of Path2D.Float we must set its vertices -- the easiest way to do this is through the
// moveTo(x,y) and lineTo(x,y) methods.
Path2D.Float regionOfInterest = new Path2D.Float();
// We must store the first X,Y coordinates so we can close the path, by creating a line
// to the last point to the first one.
boolean isFirst = true;
double firstX=0,firstY=0;
// For each of the X,Y coordinates, parse and store them on the Path2D.Float.
for(String s:coordsText)
{
String[] xy = s.split(",");
double x = Double.parseDouble(xy[0]);
double y = Double.parseDouble(xy[1]);
if (isFirst)
{
regionOfInterest.moveTo(x,y);
firstX = x;
firstY = y;
isFirst = false;
}
else { regionOfInterest.lineTo(x,y); }
}
// Close the path.
regionOfInterest.lineTo(firstX,firstY);
// We have the path that define the region of interest. In order to dim the image regions
// outside of this path we must create another path that contains everything but the
// region of interest.
// First we create a path for the whole image -- a rectangle with the image's coordinates.
Path2D.Float pathForWholeImage = new Path2D.Float();
pathForWholeImage.moveTo(0,0);
pathForWholeImage.lineTo(baseImage.getWidth(),0);
pathForWholeImage.lineTo(baseImage.getWidth(),baseImage.getHeight());
pathForWholeImage.lineTo(0,baseImage.getHeight());
pathForWholeImage.lineTo(0,0);
// In order to use Constructive Area Geometry (CAG) operations we must use the Area class.
// First we create an Area with the path for the whole image...
Area wholeImage = new Area(pathForWholeImage);
// .. then we subtract the region of interest from this Area.
wholeImage.subtract(new Area(regionOfInterest));
// Now we have a Path2D.Float for the region of interest and an Area for the rest of the image.
// To draw and paint them we need a graphic context, which we will get from the image itself.
Graphics2D g2d = (Graphics2D)baseImage.getGraphics();
// We want antialiasing!
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
// Fill the rest of the image with a transparent (100/255) white.
g2d.setColor(new Color(255,255,255,100));
g2d.fill(wholeImage);
// Draw the region of interest with a thick, almost opaque red line.
g2d.setStroke(new BasicStroke(5f));
g2d.setColor(new Color(255,0,0,200));
g2d.draw(regionOfInterest);
// Create a new Frame to show the results.
JFrame frame = new JFrame();
//imageFrame.setTitle("Highlighting image regions");
// Create an ImageIcon/Label to show the image.
ImageIcon icon = new ImageIcon(baseImage);
JLabel label = new JLabel(icon);
// Add it to the content pane.
imageFrame.getContentPane().add(new JScrollPane(label));
// Set some GUI parameters.
//imageFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
imageFrame.pack();
//frame.setVisible(true);
}
If I understood well what you need, this could help:
ArrayList<Float> coordsX = new ArrayList<Float>();
ArrayList<Float> coordsY = new ArrayList<Float>();
addMouseMotionListener(this);
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e){
coordsX.add(e.getX()); //Storing coordinate X
coordsY.add(e.getY()); //Storing coordinate Y
//Add this code to draw a circle each time you click.
int r = 6; //Radius of the circle/point.
int x = e.getX()-(r/2); //Position X (mouse will be in the center of the point)
int y = e.getY()-(r/2); //Position Y (mouse will be in the center of the point)
Graphics g = getGraphics(); //Getting the Graphic object
g.setColor(Color.red); //Setting color to red
g.fillOval(x, y, r, r); //Drawing the circle/point
g.dispose();
}
});
Just an example that I think solve what you need, how to store coordinates is up to you.
UPDATE:
In order to draw a point use fillOval(x, y, r, r) method from Graphics class.
Btw, if you use mouseClicked() event, sometimes you will see how your clicks appear to not have effect (to click doesn't draw or store anything), this is because the program is detecting the mouseDragged() event from MouseMotionListener Interface. To solve this you can change the current event mouseClicked() for mousePressed() event from MouseListener Interface as I already did in the code.
If you want a bigger point, just increase its radius, or decrease it if you want it smaller.
This question already has answers here:
Adding Image to a Polygon
(2 answers)
Closed 8 years ago.
I'm trying to basically remake Asteroids in java, but I'm going to use a bald eagle as a ship that shoots down Soviet flags. Right now, my bald eagle image is a square with a white outline around the eagle. I would like to remove this, is there any way to map this in a one-to-one fashion to a polygon of sorts?
Here's my code, though I don't know exactly how this will help anything:
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException {
GameTest t = new GameTest();
}
public static class GameTest extends JFrame {
private static final int WINDOW_WIDTH = 800;
private static final int WINDOW_HEIGHT = 500;
private GamePanel gamePanel;
public GameTest() throws IOException {
super("Deep Fried Freedom");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setLayout(new BorderLayout());
gamePanel = new GamePanel();
add(gamePanel);
center(this);
setVisible(true);
}
public static void center(JFrame frame) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Point center = ge.getCenterPoint();
int w = frame.getWidth();
int h = frame.getHeight();
int x = center.x - w / 2, y = center.y - h / 2;
frame.setBounds(x, y, w, h);
frame.validate();
}//end of center method
}
}
public class GamePanel extends JPanel {
public static BufferedImage baldEagleImage;
public GamePanel() throws IOException {
super();
baldEagleImage = ImageIO.read(new File("baldeagleimage.jpg"));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);// set color black
g.fillRect(0, 0, getWidth(), getHeight()); // paint background
g.drawImage(baldEagleImage, 350, 175, null);//paint the launcher
}//end paintComponent method
}//end GamePanel class
You have several ways that you can achieve this effect. Your best bet would be to use the alpha channel of your image. Just open your image in an image editing tool such as Gimp. In this set the background of your image to transparent around your image.
Another option (which is not the best) but fulfils your request is to use a paint stroke in Java2D. Have a look at using the java2d clipping feature. You can get a tutorial on this here
Usually you will have a Java object that represents the ship, and it has x and y coordinates called something like centerX and centerY. That gives you the center of the ship on your screen within the bounds of the viewable area. You modify these values when you want the ship to move up and down, and you g.drawImage the image you want to use at these coordinates as well (plus any offsets as needed to make the image appear centered to your liking).
A common method is to have a thread started upon initialization, and in that thread is a while(true) block that does an update() method on all objects that need to be updated, and then a Thread.sleep(17) that mimics a framerate of about 60 frames per second. In this method, your ship has its X and Y coords updated and then the image drawn at that location every 17 milliseconds, and that's how you get a ship (and any other object for that matter) to appear like they are moving around on the screen.
How can I mousedrag different BufferedImages in Java2D?
For instance, if I have ten or more images, how can I move that images which my mouse is over?
Now I'm importing an BufferedImage with
BufferedImage img = new BufferdImage(new File("filename"));
And I'm painting this with Graphics2D with
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.drawImage(img, x1, y1, null);
g2d.drawImage(img2, x2, y2,null);
}
Everytime I'm moving on a image I'm repaint()-ing the entire screen.
My mousemove class is as follows
class MouseMotionHandler extends MouseMotionAdapter {
#Override
public void mouseDragged(MouseEvent e) {
x1 = e.getX() - (img.getWidth() / 2);
y1 = e.getY() - (img.getHeight() / 2);
repaint();
}
}
With this method I'm able to "drag" one picture, but what to do when I will drag more individually?
Use the BufferedImage to create an ImageIcon which you use to create a JLabel. Then you add the JLabel to the panel that uses a null layout. No custom painting code is required to do this.
Now if you want to drag the label around you can use the Component Mover.
You can try making a custom component that contains only a single image. Along with your painting and mouse motion handling code, the component overrides the contains method so that it returns true only if the coordinates are within the image.
These components are then stacked in a JLayeredPane, (hopefully) only moving the images that the mouse is on top of.
From what you ask I suppose that your current repainting logic is global. You need to apply it to every image you have. So, if you for instance display every image in JPanel attach MouseMotionListener to every such panel and make this logic happen in JPanel.
If you post more code - especially of the component you show your images in - I will be able to go into more details.
Here's is a simple example that implements dragging for either single- or multiple-selections. The object Node would correspond roughly to your object Card.
Addendum: Also considered the Overlap Layout mentioned in this answer to a related question. Instead of List<Node>, your program would manage a List<Card>, where each Card is a JLabel having a card image.
I should make tree arrays:
one for the x-values
one for the y-values
one for the BufferedImages
So, something like this:
int[] xValues = new int[10];
int[] yValues = new int[10];
BufferedImage[] imgs = new BufferedImage[10];
Then the
class MouseMotionHandler extends MouseMotionAdapter {
#Override
public void mouseDragged(MouseEvent e) {
for (int i = 0; i < 10; i++)
{
xValues[i] = e.getX() - (imgs[i].getWidth() / 2);
yValues[i] = e.getY() - (imgs[i].getHeight() / 2);
}
repaint();
}
}
Then paint them like this:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
for (int i = 0; i < 10; i++)
{
g2d.drawImage(imgs[i], xValues[i], yValues[i], null);
}
}
I think something like this is what you need.
Here's the code for my JLayeredPane init. My problem here is that my images don't show up...
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new java.awt.Dimension(500, 410));
layeredPane.setBorder(javax.swing.BorderFactory.createTitledBorder(
"Center deck"));
for(BufferedImage imgs : images){
JLabel label = new JLabel(new ImageIcon(imgs));
layeredPane.add(label, JLayeredPane.DEFAULT_LAYER);
}
add(layeredPane);