There is a circle (planet), drawn through fillOval(). I need to write the text (number) on the circle, so that it must be always aligned exactly in the center. There is a center of the circle and its radius.
I understand that I need to calculate the width and length of the text (tried by getStringBounds), but does not work correctly.
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Planet planet : planets) {
g2d.setColor(planet.getColor());
g2d.fillOval((int) planet.getX() - planet.getRadius(), (int) planet.getY() - planet.getRadius(),
planet.getRadius() * 2, planet.getRadius() * 2);
g.setFont(new Font("Serif", Font.PLAIN, 20));
g.setColor(Color.black);
String s = String.valueOf(250);
FontMetrics fm = g.getFontMetrics();
double textWidth = fm.getStringBounds(s, g).getWidth();
g.setColor(Color.white);
g.drawString(s, (int) ((int) planet.getX() - textWidth/2), ((int) planet.getY() + fm.getMaxAscent() / 2));}}
You're using planet.getX()-planet.getRadius() and planet.getY() - planet.getRadius() to calculate your centered point of the circle.
Then you have to add the Text you want to paint at the centered position, which is calculated
planet.getX() - planet.getRadius() - textWidth/2 //or centered point - textWidth/2
which is your x-coordinate and
planet.getY() - planet.getRadius() + fm.getMaxAscent() / 2 //or centered point + fm.getMaxAscent()/2
which will be the y-coordinate.
But you are using planet.getX() - textWidth/2
This origin point has to be the center of the circle, not planet.getX()
In the past I've had to use both the ascent/descent values to calculate actual font height and it looks like you're only using ascent. Something to consider: not all strings will have the same max ascent/descent for a given font, so while the string "Dojo" may appear to be properly vertically aligned, the strings "cujo" and "coco" might not.
Another suggestion: instead of calculating the half-width of your text and using that to offset your drawing starting position, why not just center justify the text?
Related
I am sitting with my son, trying to implement a school homework. The task is to write a program that draws X and Y axis and functions, e.g. Sinus or x² into a awt.Canvas. The issue we are struggeling with is that the root, Point(0,0) of the Canvas is designed to be in the upper left corner. The cartesian coordinate system that we have to have, has the origin in the lower left corner. So we tried to apply a AffineTransform and translate in the paint method of the Canvas, which in essence works but has two issues:
1st, for whatever reason the related translation doesn't really moves the origin to the bottom but about 100 pixels to high (see image).
When we put in the below code an additional offset of about 100 pixels with tx.translate(0, -(getHeight()+100)); it looks about right.Same issue seems to be true on the right side. There is also unintended free space. We colored the background of the containing Frame in black and the Canvas in grey to exclude an artefact between these two containers. But doesn't seem to be the case.
2nd, and that concerns us more, is the side effect that all text, when e.g. adding values to the axes will also be fliped, as you see at our debug info in the plotAxes method.
Here is what we have done so far..
public class PlotterView extends Canvas {
protected int MINWIDTH = 500;
protected int MINHEIGHT = 400;
Point[][] lines;
public PlotterView() {
Dimension dim = new Dimension(MINWIDTH, MINHEIGHT);
setPreferredSize(dim);
setBackground(Color.LIGHT_GRAY);
}
protected void plotAxes(Graphics2D g) {
Color defaultColor = g.getColor(); // save to restore defaults in the end
int originX = 5; // x origin of both axes - shift right
int originY = 5; // y origin of both axis - shift up
// Debug info to compare
g.setColor(Color.BLACK);
g.drawString("X: " + originX + "; Y: " + originY, originX, originY);
// X-Axis
g.setColor(Color.RED);
g.drawLine(originX, originY, MINWIDTH-20, originY);
g.drawLine(MINWIDTH-20, originY, MINWIDTH-30, originY-5);
g.drawLine(MINWIDTH-20, originY, MINWIDTH-30, originY+5);
// Y-Axis
g.setColor(Color.BLUE);
g.drawLine(originX, originY, originX, MINHEIGHT-20);
g.drawLine(originX, MINHEIGHT-20, originX-5, MINHEIGHT-30);
g.drawLine(originX, MINHEIGHT-20, originX+5, MINHEIGHT-30);
// Restore defaults
g.setColor(defaultColor);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
tx.translate(0, -getHeight());
g2.setTransform(tx);
plotAxes(g2);
}
}
One alternative would be to implement a method that "adjusts" every x-value from upper left to lower left, but that feels like a kind of botch job.
How to solve this right? Thank you in advance
Thank you for the feedback. I figured out that issue number 1 is born by using the AffineTransform. If I apply the scale and translate on the g2 directly the issue disappears.
Issue number 2 is a conflict of interest. We couldn't manage to find and apply the "three magic lines of code" (or whatever number would be required) to from thereon programm in a cartesian coordinate system. Instead we are converting all y-values into this top-level-origin coordinate system. Makes the code hard to read, but with the help debugging we managed.
thanks for checking out my question.
I am working a menu for the game that I'm making, and I want to center the game's title on the main menu. I've looked at several questions here on Stack Overflow, but couldn't find an answer.
Problem
The problem that I'm having is that whenever I call my centerString() method, it puts it in the center of the y-axis, but not of the x-axis. What am I doing wrong here?
Code
Method code
private void centerString(String txt, int width, int height, Graphics g, Font font) {
FontMetrics metrics = g.getFontMetrics();
int x = (width - metrics.stringWidth(txt)) / 2;
int y = (metrics.getAscent() + (height - (metrics.getAscent() + metrics.getDescent())) / 2);
g.setFont(font);
g.drawString(txt, x, y);
}
Where it's called
final Font fnt = new Font("Arial", Font.BOLD, 36);
centerString("ThatMarioEngine", toInt(screenSize.getWidth()), toInt(screenSize.getHeight()), g, fnt);
I have fixed the issue by making using the windows size, instead of the screen size. The centering issue was fixed by putting g.setFont(font); before I get the FontMetrics.
I have a requirements to create a web service that will generate user's icon based on their initials. Similar to this Android project but on the server side using Java.
The size of that image should be dynamic. I already have the code that will create a rectangle with two letters in the middle but it is not scaling the text.
Here is my code so far:
public BufferedImage getAbbreviationImage(int height, int width, String abbreviation) throws IOException {
int centerX = width/2;
int centerY = height/2;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.SCALE_SMOOTH);
Graphics2D g = bufferedImage.createGraphics();
Font font = new Font("Helvetica", Font.BOLD, 90);
g.setFont(font);
g.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
g.setColor(Color.decode("#3f404c"));
g.fillRect(0, 0, width, height);
// get the bounds of the string to draw.
FontMetrics fontMetrics = g.getFontMetrics();
Rectangle stringBounds = fontMetrics.getStringBounds(abbreviation, g).getBounds();
FontRenderContext renderContext = g.getFontRenderContext();
GlyphVector glyphVector = font.createGlyphVector(renderContext, abbreviation);
Rectangle visualBounds = glyphVector.getVisualBounds().getBounds();
// calculate the lower left point at which to draw the string. note that this we
// give the graphics context the y corridinate at which we want the baseline to
// be placed. use the visual bounds height to center on in conjuction with the
// position returned in the visual bounds. the vertical position given back in the
// visualBounds is a negative offset from the basline of the text.
int textX = centerX - stringBounds.width/2;
int textY = centerY - visualBounds.height/2 - visualBounds.y;
g.setColor(Color.WHITE);
g.drawString(abbreviation, textX, textY);
g.dispose();
return bufferedImage;
}
Is there any Java library that can do something like this already so that I don't have to write my own code. If not, then what would be the best approach to scale text based on the image size?
Credits:
Some of my code was take from HERE
You need to set the size of the font which you attach to the Graphics2D object. From the oracle docs:
public abstract void drawString(String str,
int x,
int y)
Renders the text of the specified String, using the current text
attribute state in the Graphics2D context
You should set the size of the font you use appropriately to match the dimensions of the rectangle. Something like this:
int lFontSize = 90 * (originalRectangleWidth / newRectangleWidth);
Font font = new Font("Helvetica", Font.BOLD, lFontSize );
where:
90 is the reference font size (this is what you are setting in the example)
originalRectangleWidth would be the size of the rectangle you use when the font looks good with size 90
newRectangleWidth would be the new rectangle width
References:
Graphics2D (oracle ref)
Font (oracle ref)
I am trying to plot a graph using the java 2d graphics library and I thought I had it. I want to plot in the coordinate system where 0,0 is in the center of the panel on the left edge. I used the following code and it seemed to give me the result I needed.
private void doDraw(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
AffineTransform saveAT = g2d.getTransform();
// get the height of the panel
int height = getHeight();
// Find the middle of the panel
double yTrans = ((double)height)/2.0;
AffineTransform tform = AffineTransform.getTranslateInstance( 0.0, yTrans);
g2d.setTransform(tform);
//draw the line for the x-axis.
g2d.drawLine(0, 0, 100, 0);
//restore the old transform
g2d.setTransform(saveAT);
}
This plotted the origin centered in the window.
The problem shows itself when I added a menu. Then the origin was offset in the y direction about twice the size of the menu higher then it should be. Do I need to account for the size of the menu and other containers that I add to the panel?
private void doDraw(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
int height = getHeight();
double yTrans = ((double)height)/2.0;
AffineTransform tform = AffineTransform.getTranslateInstance( 0.0, yTrans);
g2d.transform(tform);
//draw the line for the x-axis.
g2d.drawLine(0, 0, 100, 0);
}
works, thank you for your help
You might try the approach outlined here. Override paintComponent() to obtain a graphics context relative to the enclosing panel, rather than the enclosing frame.
To center the origin at the left edge, use
g2d.translate(0, h / 2);
To get upright, cartesian coordinates, use
g2d.scale(1, -1);
I'm quite new to graphics in java and I'm trying to create a shape that clips to the bottom of another shape. Here is an example of what I'm trying to achieve:
Where the white line at the base of the shape is the sort of clipped within the round edges.
The current way I am doing this is like so:
g2.setColor(gray);
Shape shape = getShape(); //round rectangle
g2.fill(shape);
Rectangle rect = new Rectangle(shape.getBounds().x, shape.getBounds().y, width, height - 3);
Area area = new Area(shape);
area.subtract(new Area(rect));
g2.setColor(white);
g2.fill(area);
I'm still experimenting with the clip methods but I can't seem to get it right. Is this current method ok (performance wise, since the component repaints quite often) or is there a more efficient way?
I think your original idea about using the clip methods was the right way to do it. This works for me:
static void drawShapes(Graphics2D g, int width, int height,
Shape clipShape) {
g.setPaint(Color.BLACK);
g.fillRect(0, 0, width, height);
g.clip(clipShape);
int centerX = width / 2;
g.setPaint(new GradientPaint(
centerX, 0, Color.WHITE,
centerX, height, new Color(255, 204, 0)));
g.fillRect(0, 0, width, height);
g.setPaint(Color.WHITE);
int whiteRectHeight = height * 4 / 5;
g.fillRect(0, whiteRectHeight,
width, height - whiteRectHeight);
}
Is this current method ok (performance wise, since the component repaints quite often) ..
Subtracting shapes is how I'd go about it. The objects could be a few instances or (possibly) a single instance that is transformed as needed.
A text demo., using scaling & fading.
Here's one with simple lines (..and dots, ..and it is animated).
Of course, if the image is purely additive, use a BufferedImage as the canvas & display it in a JLabel/ImageIcon combo. As in both of those examples.