How to draw line above text in Java - java

I would like to draw a line above a text in Java. I use Graphics and here is my code :
String s = a.getSequent().toString();
FontMetrics fm = getFontMetrics(getFont());
int textHeight = fm.getHeight();
int textWidth= fm.stringWidth(s);
//Text
g.drawString( s,
(int) ((jPanelWidth- textWidth) / 2),
(int) ((jPanelHeight- textHeight ) / 2));
//Draw line
int x1 = (jPanelWidth- textWidth) / 2;
int x2 = x1 + textWidth; //Problem
int y1 = (jPanelHeight- textHeight *4) / 2;
int y2 = y1;
g.drawLine(x1, y1, x2, y2);
Here is what I have :
I don't understand why the line don't have the same length as my text. The problem is on the value of x2, but why ? Could you help me ?

One of the more obscure concepts to come to grips with is understanding how text is actually rendered.
Rather than been rendered from x/y position down, text is rendered from the baseline up.
This means that the x/y position actually represents the baseline ... just take some time to read that again and if that doesn't help, have a read of Measuring Text
The basic concept is, you want take the x/y position, which represents the baseline and then subtract the ascent
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
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) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
#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(getWidth() / 2, 0, getWidth() / 2, getHeight());
g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
String text = "This is a test";
FontMetrics fm = g2d.getFontMetrics();
int textHeight = fm.getHeight();
int textWidth = fm.stringWidth(text);
int xPos = (getWidth() - textWidth) / 2;
int yPos = ((getHeight() - textHeight) / 2) + fm.getAscent();
g2d.setColor(Color.BLACK);
g2d.drawString(text, xPos, yPos);
g2d.drawLine(xPos, yPos - fm.getAscent(), xPos + textWidth, yPos - fm.getAscent());
g2d.dispose();
}
}
}

As #luk2302 said, here is the solution :
I had : FontMetrics fm = getFontMetrics(getFont());
Now I have : FontMetrics fm = getFontMetrics(g.getFont());
I didn't use the right font.

Related

Drawing super crowded points in Swing

I need to be able to draw very crowded points for a graph, via Swing. E.g: let's look at the following points:
p1=(35.19589389346247,32.10152879327731),
p2 = (35.20319591121872,32.10318254621849),
p3 = (35.20752617756255,32.1025646605042),
p4 = (35.21007339305892,32.10107446554622),
p5 = (35.21310882485876,32.104636394957986),
etc...
I want to draw them, however, as can be seen, their coordinates are very dense to each other. In addition, expanding the scale also didn't work, but just moved the points inside the frame.
Here is a glance at my attempt:
private void Oval(Graphics2D g2, String id, int locationX, int locationY) {
AffineTransform old = g2.getTransform();
g2.scale(4,4);
g2.setPaint(Color.blue);
g2.fillOval(locationX - Radius, locationY - Radius, Radius * 2, Radius * 2);
g2.setPaint(Color.black);
g2.drawString(id, locationX + Radius, locationY - Radius);
g2.setTransform(old);
}
This is the code for the point drawing, considering the panel dimension is (1000,1000).
Here is the output of the points' drawing:
As you can see, they override each other, and it's clearly nothing like I was intended to do. My goal is to separate them so we can see the points clearly.
So, the basic concept is to have some kind of "translation" for the "virtual" coordinates (your points) to the "physical" coordinates (of the screen)
You did try and do this by scaling the graphics context, but the problem with this is it will also scale the size of the balls as well, which isn't really what you want to do.
The following is a really basic example of one possible solution.
It calculates the min/max range of the area represented by the points and then uses the component size to translate the points so that they will fit within the visible space
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GraphPoint {
private String id;
private Point2D point;
public GraphPoint(String id, Point2D point) {
this.id = id;
this.point = point;
}
public String getId() {
return id;
}
public Point2D getPoint() {
return point;
}
}
public class TestPane extends JPanel {
private List<GraphPoint> points;
private int radius = 10;
private double virtualScale = 1.0;
private Point2D minRange;
private Point2D maxRange;
public TestPane() {
points = new ArrayList<>(16);
points.add(new GraphPoint("1", new Point2D.Double(35.19589389346247, 32.10152879327731)));
points.add(new GraphPoint("2", new Point2D.Double(35.20319591121872, 32.10318254621849)));
points.add(new GraphPoint("3", new Point2D.Double(35.20752617756255, 32.1025646605042)));
points.add(new GraphPoint("4", new Point2D.Double(35.21007339305892, 32.10107446554622)));
points.add(new GraphPoint("5", new Point2D.Double(35.21310882485876, 32.104636394957986)));
double minX = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double minY = Double.MAX_VALUE;
double maxY = Double.MIN_VALUE;
for (GraphPoint gp : points) {
minX = Math.min(minX, gp.getPoint().getX());
maxX = Math.max(maxX, gp.getPoint().getX());
minY = Math.min(minY, gp.getPoint().getY());
maxY = Math.max(maxY, gp.getPoint().getY());
}
minRange = new Point2D.Double(minX, minY);
maxRange = new Point2D.Double(maxX, maxY);
double xRange = maxRange.getX() - minRange.getX();
double yRange = maxRange.getY() - minRange.getY();
System.out.println(minRange.getX() + " - " + minRange.getY());
System.out.println(maxRange.getX() + " - " + maxRange.getY());
System.out.println(xRange + " - " + yRange);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (GraphPoint gp : points) {
paintPoint(g2d, gp);
}
g2d.dispose();
}
private void paintPoint(Graphics2D g2d, GraphPoint gp) {
Graphics2D g2 = (Graphics2D) g2d.create();
Point2D translated = translate(gp);
double xPos = translated.getX();
double yPos = translated.getY();
double offset = radius;
g2.translate(xPos - offset, yPos - offset);
g2.setPaint(Color.blue);
g2.fill(new Ellipse2D.Double(0, 0, offset * 2, offset * 2));
g2.dispose();
}
protected Point2D translate(GraphPoint gp) {
double xRange = maxRange.getX() - minRange.getX();
double yRange = maxRange.getY() - minRange.getY();
double offset = radius;
double width = getWidth() - (offset * 2);
double height = getHeight() - (offset * 2);
double xScale = width / xRange;
double yScale = height / yRange;
Point2D original = gp.getPoint();
double x = offset + ((original.getX() - minRange.getX()) * xScale);
double y = offset + ((original.getY() - minRange.getY()) * yScale);
System.out.println(gp.getId() + " " + x + " x " + y);
return new Point2D.Double(x, y);
}
}
}
I have to stress that this is one possible solution and that your actual requirements might differ, but this should give you starting point from which you can define your own algorithm, for example, you could define your own min/max range (ie 34x30 to 36x33)
the String I have tried now for 1 hour and I didn't get it I did edit your code already you help us a lot. the string above the points the id I mean point "0" this string above the point if u can help us or show us it will be appreciated a lot!
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GraphPoint {
private String id;
private Point2D point;
public GraphPoint(String id, Point2D point) {
this.id = id;
this.point = point;
}
public String getId() {
return id;
}
public Point2D getPoint() {
return point;
}
}
public class TestPane extends JPanel {
private List<GraphPoint> points;
private int radius = 10;
private double virtualScale = 1.0;
private Point2D minRange;
private Point2D maxRange;
public TestPane() {
points = new ArrayList<>(16);
points.add(new GraphPoint("1", new Point2D.Double(35.19589389346247, 32.10152879327731)));
points.add(new GraphPoint("2", new Point2D.Double(35.20319591121872, 32.10318254621849)));
points.add(new GraphPoint("3", new Point2D.Double(35.20752617756255, 32.1025646605042)));
points.add(new GraphPoint("4", new Point2D.Double(35.21007339305892, 32.10107446554622)));
points.add(new GraphPoint("5", new Point2D.Double(35.21310882485876, 32.104636394957986)));
double minX = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double minY = Double.MAX_VALUE;
double maxY = Double.MIN_VALUE;
for (GraphPoint gp : points) {
minX = Math.min(minX, gp.getPoint().getX());
maxX = Math.max(maxX, gp.getPoint().getX());
minY = Math.min(minY, gp.getPoint().getY());
maxY = Math.max(maxY, gp.getPoint().getY());
}
minRange = new Point2D.Double(minX, minY);
maxRange = new Point2D.Double(maxX, maxY);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
double insets = fm.getHeight() + radius;
// I'm lazy, so I'm drawing the lines first, then painting
// the points over the top as I can't be bothered to workout
// a clever way to ensure the lines are always painted under
// the dots
GraphPoint lastPoint = null;
for (GraphPoint gp : points) {
if (lastPoint != null) {
paintLine(g2d, lastPoint, gp, insets);
}
lastPoint = gp;
}
for (GraphPoint gp : points) {
paintPoint(g2d, gp, insets);
}
g2d.dispose();
}
private void paintLine(Graphics2D g2d, GraphPoint from, GraphPoint to, double insets) {
Point2D fromPoint = translate(from, insets);
Point2D toPoint = translate(to, insets);
g2d.setColor(Color.RED);
g2d.draw(new Line2D.Double(fromPoint, toPoint));
}
private void paintPoint(Graphics2D g2d, GraphPoint gp, double insets) {
Graphics2D g2 = (Graphics2D) g2d.create();
Point2D translated = translate(gp, insets);
double xPos = translated.getX();
double yPos = translated.getY();
double offset = radius;
g2.translate(xPos - offset, yPos - offset);
g2.setPaint(Color.blue);
g2.fill(new Ellipse2D.Double(0, 0, offset * 2, offset * 2));
FontMetrics fm = g2d.getFontMetrics();
String text = gp.getId();
double x = xPos - (fm.stringWidth(text) / 2);
double y = (yPos - radius - fm.getHeight()) + fm.getAscent();
g2d.drawString(text, (float)x, (float)y);
g2.dispose();
}
protected Point2D translate(GraphPoint gp, double insets) {
double xRange = maxRange.getX() - minRange.getX();
double yRange = maxRange.getY() - minRange.getY();
double offset = insets;
double width = getWidth() - (offset * 2);
double height = getHeight() - (offset * 2);
double xScale = width / xRange;
double yScale = height / yRange;
Point2D original = gp.getPoint();
double x = offset + ((original.getX() - minRange.getX()) * xScale);
double y = offset + ((original.getY() - minRange.getY()) * yScale);
System.out.println(gp.getId() + " " + x + " x " + y);
return new Point2D.Double(x, y);
}
}
}
The trick with the text is in understanding that I've already translated the origin point of the context, so the 0x0 position is actually the middle of the circle, so you need to subtract the text offset from it.
I've also made a slight update to allow the workflow to pass in a insets value which will decrease the available visible area to "help" compensate for the text
another question can make a line with an arrow in the tip
See...
Java make a directed line and make it move
How to draw 2d arrows with Java Swing?
for some ideas. Just beware, this becomes increasingly more complex, as the arrows need to be rotated towards the point they are looking, something like Connect two circles with a line might help

Java Graphics - Remove part of rounded rectangle under text?

I'm trying to render text on top of a rounded rectangle, but I want the part of the rounded rectangle under the text to be cut out. Here's what I want it to look like:
The problem is that I can't find any easy way of doing this. I tried using clearRect, but that just creates a black rectangle, and I want to have an image underneath (for now it's just white).
I then had the idea that maybe I could just fill the area I want to remove of the rectangle with white, then filter out all the white pixels. This didn't work as well as I hoped, as there are still white pixels left over:
Here's the code I have currently:
public static void createRoundedRectImg(int width, int height)
{
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
int padding = 50;
g.setColor(Color.BLUE);
g.drawRoundRect(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
float textSize = 84f;
Font font = g.getFont().deriveFont(textSize).deriveFont(Font.BOLD);
g.setFont(font);
String text = "TEXT";
Rectangle2D stringBounds = g.getFontMetrics(font).getStringBounds(text, g);
int textWidth = (int) stringBounds.getWidth();
int textHeight = (int) (stringBounds.getHeight() + g.getFontMetrics(font).getDescent());
int textX = (width / 2) - (textWidth / 2);
int textY = g.getFontMetrics(font).getDescent() * 2 + padding;
//g.clearRect(textX, textY - textHeight, textWidth, textHeight);
g.setColor(Color.WHITE);
g.fillRect(textX, textY - textHeight, textWidth, textHeight);
g.setColor(Color.GREEN);
g.drawString(text, textX, textY);
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Color c = new Color(img.getRGB(x, y));
if (c.getRGB() == Color.WHITE.getRGB())
img.setRGB(x, y, new Color(0, 0, 0, 255).getRGB());
}
}
g.dispose();
}
Is there a simpler way of just clearing the part of the rounded rectangle under the text? After that is done I want to overlay the whole thing on top of an image, so I need the background to be transparent.
You could use the subtract method of the Area class to remove a rectangular section from a stroked RoundRectangle2D.
float strokeWidth = 1.5f;
RoundRectangle2D roundedRect = new RoundRectangle2D.Double(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
Rectangle2D rectMask = new Rectangle2D.Double(textX, padding-strokeWidth, textWidth, 2*strokeWidth);
Stroke stroke = new BasicStroke(strokeWidth);
Area roundedRectArea = new Area(stroke.createStrokedShape(roundedRect));
roundedRectArea.subtract(new Area(rectMask));
g.setColor(Color.BLACK);
g.fill(roundedRectArea);
g.drawString(text, textX, textY);
Which produces:
Full code:
public static void createRoundedRectImg(int width, int height)
{
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = img.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
float textSize = 84f;
Font font = g.getFont().deriveFont(textSize).deriveFont(Font.BOLD);
g.setFont(font);
int padding = 50;
String text = "TEXT";
Rectangle2D stringBounds = g.getFontMetrics(font).getStringBounds(text, g);
int textWidth = (int) stringBounds.getWidth();
int textX = (width / 2) - (textWidth / 2);
int textY = g.getFontMetrics(font).getDescent() * 2 + padding;
float strokeWidth = 1.5f;
RoundRectangle2D roundedRect = new RoundRectangle2D.Double(padding, padding, width - (padding * 2),
height - (padding * 2), 50, 50);
Rectangle2D rectMask = new Rectangle2D.Double(textX, padding - strokeWidth, textWidth, 2 * strokeWidth);
Stroke stroke = new BasicStroke(strokeWidth);
Area roundedRectArea = new Area(stroke.createStrokedShape(roundedRect));
roundedRectArea.subtract(new Area(rectMask));
g.setColor(Color.BLACK);
g.fill(roundedRectArea);
g.drawString(text, textX, textY);
g.dispose();
try
{
ImageIO.write(img, "png", new File("round.png"));
} catch (IOException e)
{
e.printStackTrace();
}
}
Try this.
public static void createRoundedRectImg(int width, int height)
{
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D)img.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
int padding = 50;
g.setComposite(AlphaComposite.Clear);
g.fillRoundRect(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
g.setComposite(AlphaComposite.SrcOver);
g.setColor(Color.BLUE);
g.drawRoundRect(padding, padding, width - (padding * 2), height - (padding * 2), 50, 50);
float textSize = 84f;
Font font = g.getFont().deriveFont(textSize).deriveFont(Font.BOLD);
g.setFont(font);
String text = "TEXT";
Rectangle2D stringBounds = g.getFontMetrics(font).getStringBounds(text, g);
int textWidth = (int) stringBounds.getWidth();
int textHeight = (int) (stringBounds.getHeight() + g.getFontMetrics(font).getDescent());
int textX = (width / 2) - (textWidth / 2);
int textY = g.getFontMetrics(font).getDescent() * 2 + padding;
g.setColor(Color.WHITE);
g.fillRect(textX, textY - textHeight, textWidth, textHeight);
g.setColor(Color.GREEN);
g.drawString(text, textX, textY);
g.dispose();
}
You could just use a TitledBorder. Since the curve is proportional to the line size I created a RoundedBorder class with most of the code from the paintBorder() method in the API to allow the size of the arc corners to be specified. It is now a simple pixel amount for both width and height of the arc radius.
first, create a RoundedBorder instance. Try 30 for the arc radius.
then, using the BorderFactor, create a TitledBorder instance and pass the rounded instance as the first agument.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Path2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
public class TitledBorderDemo extends JPanel {
JFrame frame = new JFrame();
public static void main(String[] args) {
SwingUtilities
.invokeLater(() -> new TitledBorderDemo().start());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
public void start() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Border b = new RoundedBorder(Color.black, 2, 30);
Border titled = BorderFactory.createTitledBorder(b, "Text",
TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION,
new Font("Arial", Font.BOLD, 48));
setBorder(titled);
frame.add(this);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class RoundedBorder extends LineBorder {
private int arc;
public RoundedBorder(Color color, int lineThickness, int arc) {
super(color, lineThickness);
this.arc = arc;
}
#Override
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {
if ((this.thickness > 0) && (g instanceof Graphics2D)) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color oldColor = g2d.getColor();
g2d.setColor(this.lineColor);
Shape outer;
Shape inner;
int offs = this.thickness;
int size = offs + offs;
outer = new RoundRectangle2D.Float(x, y, width, height,
arc, arc);
inner = new RoundRectangle2D.Float(x + offs, y + offs,
width - size, height - size, arc, arc);
Path2D path = new Path2D.Float(Path2D.WIND_EVEN_ODD);
path.append(outer, false);
path.append(inner, false);
g2d.fill(path);
g2d.setColor(oldColor);
}
}
}
The above, when run, produces the following image.

Does anyone know an approximate text to pixel ratio? (Java String positioning)

Like you see in almost all text-editing softwares, css and more, I want to position my Text with java.awt. My test code for this is:
public class Test {
public static void main(String[] args) {
// creating the frame
JFrame frame = new JFrame("Test");
frame.setSize(1000, 800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
// variables
int x1 = 50;
int x2 = 500;
int y1 = 70;
int y2 = 150;
int y3 = 230;
int width = 400;
int height = 60;
int fontsize = 40;
String text = "This is a test";
frame.add(new JPanel() {
public void paint(Graphics g) {
g.setColor(Color.BLACK);
// borders
g.drawRect(x1, y1, width, height); // top
g.drawRect(x1, y2, width, height); // middle
g.drawRect(x1, y3, width, height); // bottom
g.drawRect(x2, y1, width, height); // left
g.drawRect(x2, y2, width, height); // center
g.drawRect(x2, y3, width, height); // right
g.setFont(new Font("arial", Font.PLAIN, fontsize));
// top
g.drawString(text, x1 + 10, y1 + fontsize); // x1 + 10 (10 is a buffer)
// center
g.drawString(text, x1 + 10, y2 + fontsize + (height - fontsize) / 2);
// bottom
g.drawString(text, x1 + 10, y3 + height);
// left
g.drawString(text, x2, y1 + fontsize);
// middle
g.drawString(text, x2 + width / 2 - text.length() * (fontsize/5), y2 + fontsize + (height - fontsize) / 2);
// right
g.drawString(text, x2 + (width - text.length() * (fontsize/5)), y3 + height);
}
});
frame.setVisible(true);
}
}
Of course it kind of looks messy, but I'm working on it in an API project where I'm implementing ux features atm.
You can use the FontMetrics object of the Graphics object to get information about the Font metrics to help with determining the size of the text:
FontMetrics fm = g.getFontMetrics();
See: Measuring Text tutorial.
Why are you doing custom painting? Why are you not using Swing components with layout managers to manage the UI? We can't give specific advice since we don't know what the goal of your posted code is.

How to rotate shape from its center as well as from center of the screen

I have created Star shape using drawPolygon() but I want to rotate it from the center point of the star as well as from center of the screen.
Here is my code to rotate star from its center:
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
AffineTransform origX = g2d.getTransform();
AffineTransform newX = (AffineTransform) origX.clone();
newX.rotate(Math.toRadians(angle), x, y + 62); // Rotate about center of the star
g2d.setTransform(newX);
g2d.drawPolygon(starX, starY, 5);
g2d.dispose();
g.dispose();
}
If i replace:
newX.rotate(Math.toRadians(angle), x, y + 62);
to
newX.rotate(Math.toRadians(angle), this.getWidth() / 2, this.getHeight() / 2);
I can able to rotate it from center of the screen.
However, I want to achieve both the effects simultaneously.
i.e.: Like the earth which rotate around the sun as well as its own axis.
I have tried creating another AffineTransform object but it overwrite the previous one when I set it using g2d.setTransform(newObj);
Any suggestions will be extremely helpful.
Thank you.
Here is my complete code if you want to see
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
public class Star extends Applet implements Runnable{
private int[] starX;
private int[] starY;
private Thread mainThread;
private int x;
private int y;
private int angleFromCenterShape;
#Override
public void init() {
this.setSize(800, 480);
x = 250;
y = 150;
angleFromCenterShape = 0;
starX = new int[5];
starY = new int[5];
mainThread = new Thread(this);
mainThread.start();
this.setBackground(Color.BLACK);
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
AffineTransform origX = g2d.getTransform();
AffineTransform newX = (AffineTransform) origX.clone();
newX.rotate(Math.toRadians(angleFromCenterShape), x, y + 65); //Rotate about center of the star
g2d.setTransform(newX);
g2d.setColor(Color.red);
g2d.fillPolygon(starX, starY, 5);
g2d.dispose();
g.dispose();
}
#Override
public void run() {
while(true){
angleFromCenterScreen = (angleFromCenterScreen + 1) % 360; // angle loop from 0 to 36o
initStar();
try{
Thread.sleep(30);
}catch(Exception e){}
repaint();
}
}
private void initStar(){
starX[0] = x;
starX[1] = x - 50;
starX[2] = x + 75;
starX[3] = x - 75;
starX[4] = x + 50;
starY[0] = y;
starY[1] = y + 130;
starY[2] = y + 50;
starY[3] = y + 50;
starY[4] = y + 130;
}
}
I have found a workaround for my problem but it is not an exact solution.
However adding another rotate() on the same AffineTransform object mimic the 2 rotations
Here is the code
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
AffineTransform origX = g2d.getTransform();
AffineTransform newX = (AffineTransform) origX.clone();
newX.rotate(Math.toRadians(angle), this.getWidth() / 2, this.getHeight() / 2); // Rotate from the center of screen
newX.rotate(Math.toRadians(angle), x, y + 65); // Rotate from the center of the shape
g2d.setTransform(newX);
g2d.setColor(Color.red);
g2d.fillPolygon(starX, starY, 5);
g2d.dispose();
g.dispose();
}

Drawing graphics in Java

I am trying to draw a circle in side a square and having multiple square circles in java. I am almost done but my output isn't coming out as I wanted to. The picture is what I am trying to do but it's not working out.
Here is my code:
a.awt.*;
public class SquaredCircles {
public static final int WIDTH=400;
public static final int HEIGHT=400;
public static void main (String[] args) {
DrawingPanel panel = new DrawingPanel(WIDTH,HEIGHT);
Graphics g = panel.getGraphics ();
panel.setBackground(new Color(0, 255, 255 ) );
int x = 0;
int y = 0;
int size = 100;
int rows = 5;
int numSquares = 1;
drawManySquares ( g, numSquares, x, y, size, rows );
x = 10;
y = 120;
size = 24;
rows = 4;
numSquares = 4;
drawManySquares( g, numSquares, x, y, size, rows );
x = 150;
y = 20;
size = 40;
rows = 6;
numSquares = 5;
drawManySquares( g, numSquares, x, y, size, rows );
x = 130;
y = 275;
size = 36;
rows = 3;
numSquares = 3;
drawManySquares( g, numSquares, x, y, size, rows );
}
public static void drawManySquares( Graphics g, int numSquares, int x, int y, int size, int rows ) {
for ( int i = 0; i < numSquares; i++ ) {
for ( int j = 0; j < numSquares; j++ ) {
drawOneSquare( g, x + i size, y + j size, size, rows );
}
}
}
public static void drawOneSquare( Graphics g, int x, int y, int size, int rows ) {
g.setColor ( Color.GREEN);
g.fillRect(x , y, size, size);
g.setColor ( Color.YELLOW);
g.fillOval ( x, y, size, size);
g.setColor ( Color.BLACK);
g.drawLine(size / 2, x, size / 2, size);
g.setColor ( Color.BLACK);
g.drawLine(x, size / 2, size, size / 2);
for (int i = 0; i <= rows; i = i + 1) {
g.setColor ( Color.BLACK);
g.drawOval(x + (i* (size/rows)), y+ (i*(size/rows)), size - (i*(size/rows +10 )) , size - (i*(size/rows +10)));
}
}
}
Start by having a look at Painting in AWT and Swing and Performing Custom Painting to see how painting should be done in Swing
Break down your problem into manageable chunks. The first thing you need to be able to do is paint a circle of a given size at a specific location, something like
public void paintCircleAt(Graphics2D g2d, int radius, int centerX, int centerY, Color stroke, Color fill) {
Ellipse2D.Double circle = new Ellipse2D.Double(centerX - radius, centerY - radius, radius * 2, radius * 2);
g2d.setColor(fill);
g2d.fill(circle);
g2d.setColor(stroke);
g2d.draw(circle);
}
So, this allows you to paint a circle of a given radius around the center points of x/y filled and outlined with the specified color, pretty simple.
Now, you need someway to paint a series of circles around the same center point, something like...
public void paintCirclesIn(Graphics2D g2d, int count, int radius, int centerX, int centerY, Color stroke, Color fill) {
System.out.println(radius + "; " + centerX + "; " + centerY);
int delta = radius / count;
int innerRadius = radius;
for (int index = 0; index < count; index++, innerRadius -= delta) {
paintCircleAt(g2d, innerRadius, centerX, centerY, stroke, fill);
}
}
Okay, this basically calculates the difference (delta) between each circle and the paints that many circles with that much difference in their radius from the previous one. Because of the way the painting is done, we start with the outer circle and paint in.
And finally, we need someway to paint a square and circles, something like...
public void paintCirclesInSquare(Graphics2D g2d, int count, int x, int y, int width, int height, Color squareStroke, Color squareFill, Color circleStroke, Color circleFill) {
int centerX = x + (width / 2);
int centerY = y + (height / 2);
int radius = Math.min(centerX, centerY);
Rectangle2D box = new Rectangle2D.Double(x, y, width, height);
g2d.setColor(squareFill);
g2d.fill(box);
g2d.setColor(squareStroke);
g2d.draw(box);
paintCirclesIn(g2d, count, radius, centerX, centerY, circleStroke, circleFill);
g2d.drawLine(centerX, y, centerX, y + height);
g2d.drawLine(x, centerY, x + width, centerY);
}
This, again, simply reuses the existing code we already have and adds to it, painting the square, the circles in the square and finally the lines.
Now, from here, you could write a method which took the number of columns/rows you wanted, the x/y position to start from, the size of each of square, the number of circles you need and the colors and reuse this functionality, but I'll leave that up to you ;)
Runnable example for you to play with...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class CirclesAndSquares {
public static void main(String[] args) {
new CirclesAndSquares();
}
public CirclesAndSquares() {
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() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int x = getWidth() / 2;
int y = getHeight() / 2;
// paintCircleAt(g2d, Math.min(x, y), y, y, Color.BLACK, Color.YELLOW);
// paintCirclesIn(g2d, 5, Math.min(x, y), x, y, Color.BLACK, Color.YELLOW);
paintCirclesInSquare(g2d, 5, 0, 0, getWidth() - 1, getHeight() - 1, Color.BLACK, Color.GREEN, Color.BLACK, Color.YELLOW);
g2d.dispose();
}
public void paintCirclesInSquare(Graphics2D g2d, int count, int x, int y, int width, int height, Color squareStroke, Color squareFill, Color circleStroke, Color circleFill) {
int centerX = x + (width / 2);
int centerY = y + (height / 2);
int radius = Math.min(centerX, centerY);
Rectangle2D box = new Rectangle2D.Double(x, y, width, height);
g2d.setColor(squareFill);
g2d.fill(box);
g2d.setColor(squareStroke);
g2d.draw(box);
paintCirclesIn(g2d, count, radius, centerX, centerY, circleStroke, circleFill);
g2d.drawLine(centerX, y, centerX, y + height);
g2d.drawLine(x, centerY, x + width, centerY);
}
public void paintCirclesIn(Graphics2D g2d, int count, int radius, int centerX, int centerY, Color stroke, Color fill) {
System.out.println(radius + "; " + centerX + "; " + centerY);
int delta = radius / count;
int innerRadius = radius;
for (int index = 0; index < count; index++, innerRadius -= delta) {
paintCircleAt(g2d, innerRadius, centerX, centerY, stroke, fill);
}
}
public void paintCircleAt(Graphics2D g2d, int radius, int centerX, int centerY, Color stroke, Color fill) {
Ellipse2D.Double circle = new Ellipse2D.Double(centerX - radius, centerY - radius, radius * 2, radius * 2);
g2d.setColor(fill);
g2d.fill(circle);
g2d.setColor(stroke);
g2d.draw(circle);
}
}
}

Categories