java add text inside circle image - java

I have that image:
And I need to add text to image, so I want to write method that recieved 2 strings and add them to image, for example:
addText(String str, String str2){...};
first string always be 3 chars lenth
second string can be from 1 to 20 chars length
addText("str", "secondstr");
And as result I want to recieve base64 png image string looking like that:
The main problem is how to write text in a 'circle format'? I need to change Y coordinate of each char what I write?
UPDATE1:
Explored link http://www.java2s.com/Code/Java/2D-Graphics-GUI/Drawtextalongacurve.htm, but text always draws from left side, how I can move them to center of my circle?

The important part is this one:
for (int i = 0; i < length; i++) {
Point2D p = gv.getGlyphPosition(i);
double theta = (double) i / (double) (length - 1) * Math.PI / 4;
AffineTransform at = AffineTransform.getTranslateInstance(p.getX(),
p.getY());
at.rotate(theta);
here it defines an angle theta and makes it loop from 0 to length/length*Math.PI/4, so it's a rotation from 0° to 45°. What you need to do is to play with the value theta (using also negative numbers) to make it start and finish where you want.
From the image you posted, it seems that the first string has to go from -20° to 20° (or from 340° to 20°), and the second from 225° to 135°. Note that these degrees have to be transformed in radians to make it work.

Please find example how to write text along some curve here:
draw text along curve

The following sample program will print the string in a circular way. The degIncrement variable is the distance between characters in degrees.
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;
public class RoundTextSample extends JFrame{
public RoundTextSample() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
setBounds((screenSize.width-400)/2, (screenSize.height-300)/2, 400, 300);
add(new SamplePanel());
}
class SamplePanel extends JPanel{
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
addText(g2, "str", "secondstr");
}
}
public void addText(Graphics2D g2, String string1, String string2) {
int radius = 40;
Point center = new Point(120, 120);
double startDegAngle = 0, offsetAngle;
double degIncrement = 15, strImageWidth, charWidth;
boolean isInverted;
Font font = new Font("Serif", Font.PLAIN, 18);
FontRenderContext frc = g2.getFontRenderContext();
charWidth = font.createGlyphVector(frc, "A").getGlyphOutline(0).getBounds2D().getWidth();
// compute start deg for string1
offsetAngle = ((string1.length() * degIncrement) - (degIncrement)) / 2.0;
startDegAngle = 0 - offsetAngle;
isInverted = false; // for top text
addText(g2, string1, font, radius, center, isInverted, startDegAngle, degIncrement);
// compute start deg for string2
offsetAngle = ((string2.length() * degIncrement) - degIncrement) / 2.0;
startDegAngle = 180 + offsetAngle;
isInverted = true; // for bottom text
addText(g2, string2, font, radius, center, isInverted, startDegAngle, degIncrement);
}
public void addText(Graphics2D g2, String text, Font font, int radius, Point center, boolean isInverted, double startDeg, double degIncrement) {
AffineTransform at;
double deg;
deg = startDeg;
FontRenderContext frc = g2.getFontRenderContext();
if (!isInverted) {
radius -= font.createGlyphVector(frc, "A").getGlyphOutline(0).getBounds2D().getHeight();
} else {
degIncrement = 0 - degIncrement;
}
for (int i=0; i<text.length(); i++) {
String s = ""+text.charAt(i);
GlyphVector gv = font.createGlyphVector(frc, s);
Shape glyph = gv.getGlyphOutline(0);
double charImageCenterX = glyph.getBounds2D().getCenterX();
double theta = Math.toRadians(deg);
int xp = (int)Math.round(Math.sin(theta) * radius);
int yp = (int)Math.round(Math.cos(theta) * radius);
yp = 0 - yp;
if (isInverted)
at = new AffineTransform( -1.0, 0.0, 0.0, -1.0, center.x + xp, center.y + yp );
else
at = new AffineTransform( 1.0, 0.0, 0.0, 1.0, center.x + xp, center.x + yp );
at.rotate(theta);
at.translate(0-charImageCenterX, 0);
Shape transformedGlyph = at.createTransformedShape(glyph);
g2.fill(transformedGlyph);
deg += degIncrement;
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new RoundTextSample().setVisible(true);
}
});
}
}
Sample output:
Output Notes:

Related

How to rotate an object around another moving object in java?

I'm quite new to Java and want to program an easy sun system where the moon rotates around the earth and the earth around the sun.
Everything works well except the moon doesn't want to move correctly :/
Because the earth diverges from the moon's initial position, the rotation radius of the moon grows accordingly to that distance. And again when the earth gets closer to the moons inertial position, the rotation radius decreases.
If the initial position is (0;0), it works but the moon hits the sun...
So how can I keep the distance between earth and moon constant?
I'm using AffineTransforms and here is a snippet of my code ;)
Thanks in advance!
Ellipse2D.Double MoonFrame = new Ellipse2D.Double(orbitEarth + orbitMoon - radiusMoon, -radiusMoon, radiusMoon*2, radiusMoon*2);
for (int i = 0; i < 360; i++)
{
theta += Math.PI/30;
AffineTransform TransformMoon = AffineTransform.getRotateInstance(theta,TransformEarth.getTranslateX(),TransformEarth.getTranslateY());
g2d.fill(TransformMond.createTransformedShape(MoonFrame));
}
So, your basic question comes down to "how do I find a point on a circle for a give angle" ... seriously, it's that simple
Based on many hours of googling and trial and error, I basically use the following, more or less.
protected Point pointOnCircle() {
double rads = Math.toRadians(orbitAngle - 180); // Make 0 point out to the right...
int fullLength = Math.round((outterRadius));
// Calculate the outter point of the line
int xPosy = Math.round((float) (Math.cos(rads) * fullLength));
int yPosy = Math.round((float) (Math.sin(rads) * fullLength));
return new Point(xPosy, yPosy);
}
The rest basically comes down to properly handling the compounding nature of transformations,
Basically, this takes a base Graphics context, applies the translation to it (the Earth's position) and creates two other contexts off it to apply additional transformations, one for the Earth and one for the moon...
Graphics2D g2d = (Graphics2D) g.create();
int yPos = (getHeight() - size) / 2;
// Transform the offset
g2d.transform(AffineTransform.getTranslateInstance(xPos, yPos));
Graphics2D earthG = (Graphics2D) g2d.create();
// Rotate around the 0x0 point, this becomes the center point
earthG.transform(AffineTransform.getRotateInstance(Math.toRadians(angle)));
// Draw the "earth" around the center point
earthG.drawRect(-(size / 2), -(size / 2), size, size);
earthG.dispose();
// Removes the last transformation
Graphics2D moonG = (Graphics2D) g2d.create();
// Calclate the point on the circle - based on the outterRadius or
// distance from the center point of the earth
Point poc = pointOnCircle();
int moonSize = size / 2;
// This is only a visial guide used to show the position of the earth
//moonG.drawOval(-outterRadius, -outterRadius, outterRadius * 2, outterRadius * 2);
moonG.fillOval(poc.x - (moonSize / 2), poc.y - (moonSize / 2), moonSize, moonSize);
moonG.dispose();
g2d.dispose();
And because I know how much that would have you scratching your head, a runnable example...
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.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 {
private double angle;
private double orbitAngle;
private int xPos = 0;
private int size = 20;
private int outterRadius = size * 2;
private int delta = 2;
public TestPane() {
new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
xPos += delta;
if (xPos + size >= getWidth()) {
xPos = getWidth() - size;
delta *= -1;
} else if (xPos < 0) {
xPos = 0;
delta *= -1;
}
angle += 4;
orbitAngle -= 2;
repaint();
}
}).start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
protected Point pointOnCircle() {
double rads = Math.toRadians(orbitAngle - 180); // Make 0 point out to the right...
int fullLength = Math.round((outterRadius));
// Calculate the outter point of the line
int xPosy = Math.round((float) (Math.cos(rads) * fullLength));
int yPosy = Math.round((float) (Math.sin(rads) * fullLength));
return new Point(xPosy, yPosy);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int yPos = (getHeight() - size) / 2;
// Transform the offset
g2d.transform(AffineTransform.getTranslateInstance(xPos, yPos));
Graphics2D earthG = (Graphics2D) g2d.create();
// Rotate around the 0x0 point, this becomes the center point
earthG.transform(AffineTransform.getRotateInstance(Math.toRadians(angle)));
// Draw the "earth" around the center point
earthG.drawRect(-(size / 2), -(size / 2), size, size);
earthG.dispose();
// Removes the last transformation
Graphics2D moonG = (Graphics2D) g2d.create();
// Calclate the point on the circle - based on the outterRadius or
// distance from the center point of the earth
Point poc = pointOnCircle();
int moonSize = size / 2;
// This is only a visial guide used to show the position of the earth
//moonG.drawOval(-outterRadius, -outterRadius, outterRadius * 2, outterRadius * 2);
moonG.fillOval(poc.x - (moonSize / 2), poc.y - (moonSize / 2), moonSize, moonSize);
moonG.dispose();
g2d.dispose();
}
}
}
This moves a "Earth" object, which is rotating in one direction and then rotates the moon around it, in the opposite direction
You can simplify your math by concatenating transforms. Work backwards from the last transform to the first, or use preConcatenate to build them in a more natural order.
Compose complex transforms from simple transforms, for example by building an orbital transform from a translate and a rotate:
// Earth transform.
// Set the orbital radius to 1/3rd the panel width
AffineTransform earthTx = AffineTransform.getTranslateInstance(getWidth() / 3, 0);
// Rotate
earthTx.preConcatenate(AffineTransform.getRotateInstance(angle));
Later transforms (e.g. the moon orbiting the earth) can then be built on top of earlier results:
// Moon transform.
// Set the orbital radius to 1/10th the panel width
AffineTransform moonTx = AffineTransform.getTranslateInstance(getWidth() / 10, 0);
// Rotate
moonTx.preConcatenate(AffineTransform.getRotateInstance(angle));
// Add the earth transform
moonTx.preConcatenate(earthTx);
Full example:
public class Orbit {
public static class OrbitPanel extends JComponent {
int width;
int height;
public OrbitPanel(int width, int height) {
this.width = width;
this.height = height;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Clear the background.
g2.setColor(getBackground());
g2.fillRect(0, 0, getWidth(), getHeight());
// Sun transform. Just centre it in the window.
AffineTransform sunTx = AffineTransform.getTranslateInstance(getWidth() / 2, getHeight() / 2);
// Draw the sun
g2.setTransform(sunTx);
drawBody(g2, 30, Color.YELLOW);
// Orbital period.
// One rotation every 10s.
double percentRotation = System.currentTimeMillis() % 10000 / 10000.0;
// To radians.
double angle = Math.PI * 2 * percentRotation;
// Earth transform.
// Set the orbital radius to 1/3rd the panel width
AffineTransform earthTx = AffineTransform.getTranslateInstance(getWidth() / 3, 0);
// Rotate
earthTx.preConcatenate(AffineTransform.getRotateInstance(angle));
// Add the sun transform
earthTx.preConcatenate(sunTx);
// Draw the earth
g2.setTransform(earthTx);
drawBody(g2, 10, Color.BLUE);
// Moon transform.
// Set the orbital radius to 1/10th the panel width
AffineTransform moonTx = AffineTransform.getTranslateInstance(getWidth() / 10, 0);
// Rotate
moonTx.preConcatenate(AffineTransform.getRotateInstance(angle));
// Add the earth transform (already includes the sun transform)
moonTx.preConcatenate(earthTx);
// Draw the moon
g2.setTransform(moonTx);
drawBody(g2, 5, Color.DARK_GRAY);
}
private void drawBody(Graphics2D g2, int size, Color color) {
g2.setColor(color);
g2.fillOval(-size / 2, -size / 2, size, size);
}
}
public static void main(String[] args) throws IOException, InterruptedException {
JFrame frame = new JFrame("Orbit");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JComponent orbitPanel = new OrbitPanel(250, 250);
frame.add(orbitPanel);
frame.pack();
frame.setVisible(true);
while (true) {
Thread.sleep(20);
orbitPanel.repaint();
}
}
}

Painting an arbitrary collection of points quickly

I am writing a darts application, and have implemented a Dartboard which is painted as a BufferedImage.
When rendering the dartboard, I first iterate over the co-ordinates of the BufferedImage and calculate the 'segment' that it resides in. I wrap this up into a DartboardSegment, which is basically just a collection of points with a small amount of extra structure (what number on the board it corresponds to, etc).
Currently, to actually render the dartboard I paint each point individually, like the following:
for (Point pt : allPoints)
{
DartboardSegment segment = getSegmentForPoint(pt);
Color colour = DartboardUtil.getColourForSegment(segment);
int rgb = colour.getRGB();
int x = (int)pt.getX();
int y = (int)pt.getY();
dartboardImage.setRGB(x, y, rgb);
}
Obviously this takes some time. It's not an intolerable amount (~2-3s to paint a 500x500 area), but I'd like to eliminate this 'lag' if I can. In other areas of my application I have encountered alternate methods (such as Graphics.fillRect()) which are much faster.
I've seen that there is a fillPolgyon() method on the Graphics class, however I don't think I can easily convert my segments into polygons because their shapes vary (e.g. the shape of a triple, a circle for the bullseye...). Is there a faster way in java to paint an arbitrary array of Points at once, rather than looping through and painting individually?
The code that I want to write is something like:
for (DartboardSegment segment : allSegments)
{
Color colour = DartboardUtil.getColourForSegment(segment);
Polgyon poly = segment.toPolygon();
Graphics gfx = dartboardImage.getGraphics();
gfx.setColor(colour);
gfx.fillPolygon(poly);
}
I don't think I can easily convert my segments into polygons because their shapes vary (e.g. the shape of a triple, a circle for the bullseye...)
Here is something that may give you some ideas.
You can create Shape objects to represent each area of the dartboard:
import java.awt.*;
import java.util.*;
import javax.swing.*;
import java.awt.geom.*;
public class Dartboard extends JPanel
{
private ArrayList<DartboardSegment> segments = new ArrayList<DartboardSegment>();
private int size = 500;
private int radius = size / 2;
private int border = 25;
private int doubleSize = size - (2 * border);
private int tripleSize = size / 2;
private int thickness = 10;
public Dartboard()
{
createSegmentWedges();
int innerRadius = size - (2 * border);
Shape outer = new Ellipse2D.Double(0, 0, size, size);
Shape inner = new Ellipse2D.Double(border, border, innerRadius, innerRadius);
Area circle = new Area( outer );
circle.subtract( new Area(inner) );
segments.add( new DartboardSegment(circle, Color.BLACK) );
createBullsEye();
}
private void createSegmentWedges()
{
int angle = -99;
for (int i = 0; i < 20; i++)
{
// Create the wedge shape
GeneralPath path = new GeneralPath();
path.moveTo(250, 250);
double radians1 = Math.toRadians( angle );
double x1 = Math.cos(radians1) * radius;
double y1 = Math.sin(radians1) * radius;
path.lineTo(x1 + 250, y1 + 250);
angle += 18;
double radians2 = Math.toRadians( angle );
double x2 = Math.cos(radians2) * radius;
double y2 = Math.sin(radians2) * radius;
path.lineTo(x2 + 250, y2 + 250);
path.closePath();
Color wedgeColor = (i % 2 == 0) ? Color.BLACK : Color.WHITE;
segments.add( new DartboardSegment(path, wedgeColor) );
// Create the double/triple shapes
Color color = (i % 2 == 0) ? Color.RED : Color.GREEN;
createShape(doubleSize, path, color);
createShape(tripleSize, path, color);
}
}
private void createShape(int outerSize, GeneralPath path, Color color)
{
int outerOffset = (size - outerSize) / 2;
int innerSize = outerSize - (2 * thickness);
int innerOffset = (size - innerSize) / 2;
Shape outer = new Ellipse2D.Double(outerOffset, outerOffset, outerSize, outerSize);
Shape inner = new Ellipse2D.Double(innerOffset, innerOffset, innerSize, innerSize);
Area circle = new Area( outer );
circle.subtract( new Area(inner) );
circle.intersect( new Area(path) );
segments.add( new DartboardSegment(circle, color) );
}
private void createBullsEye()
{
int radius1 = 40;
int offset1 = (size - radius1) / 2;
Ellipse2D.Double bullsEye1 = new Ellipse2D.Double(offset1, offset1, radius1, radius1);
segments.add( new DartboardSegment(bullsEye1, Color.GREEN) );
int radius2 = 20;
int offset2 = (size - radius2) / 2;
Ellipse2D.Double bullsEye2 = new Ellipse2D.Double(offset2, offset2, radius2, radius2);
segments.add( new DartboardSegment(bullsEye2, Color.RED) );
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
for (DartboardSegment segment: segments)
{
g2d.setColor( segment.getColor() );
g2d.fill( segment.getShape() );
}
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(500, 500);
}
class DartboardSegment
{
private Shape shape;
private Color color;
public DartboardSegment(Shape shape, Color color)
{
this.shape = shape;
this.color = color;
}
public Shape getShape()
{
return shape;
}
public Color getColor()
{
return color;
}
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("DartBoard");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Dartboard());
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
After a bit more digging, I think one solution to this is to do the following. It's not the neatest, but I think it will work:
int i = 0;
for (int y=0; y<height; y++)
{
for (int x=0; x<width; x++)
{
Point pt = new Point(x, y);
DartboardSegment segment = getSegmentForPoint(pt);
Color colour = DartboardUtil.getColourForSegment(segment);
pixels[i] = colorToUse.getRGB();
i++;
}
}
dartboardImage.setRGB(0, 0, width, height, pixels, 0, width);
I am open to better suggestions, however!

Drawing using Graphics java [duplicate]

I need to:
1.) move the origin and also rotate the coordinate plane so that x-values progress rightward and y-values progress upward from the new origin(which needs to be the bottom left corner of the inner, blue rectangle in the code below). This will enable me to plot points at x,y coordinate pairs in the code below.
2.) plot rotated labels for the tic marks on the y-axis of the data plot.
The code below sets up this problem. It works, except for two problems:
1.) the data points are being plotted with the upper left hand corner as the origin and y-values descending downward
2.) the labels for the tic marks on the y-axis are not being drawn on the screen
Can anyone show me how to fix the code below so that it fixes these two problems and does what the first paragraph above describes?
The code is in the following two java files:
DataGUI.java
import java.awt.*;
import java.util.ArrayList;
import javax.swing.*;
class DataGUI extends JFrame{
DataGUI() {
super("X,Y Plot");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(800, 400));
this.pack();
this.setSize(new Dimension(800, 600));
this.setLocationRelativeTo(null);
setLayout(new GridLayout());
ArrayList<Double> myDiffs = new ArrayList<Double>();
myDiffs.add(25.0);
myDiffs.add(9.0);
myDiffs.add(7.0);
myDiffs.add(16.0);
myDiffs.add(15.0);
myDiffs.add(6.0);
myDiffs.add(2.0);
myDiffs.add(8.0);
myDiffs.add(2.0);
myDiffs.add(27.0);
myDiffs.add(14.0);
myDiffs.add(12.0);
myDiffs.add(19.0);
myDiffs.add(10.0);
myDiffs.add(11.0);
myDiffs.add(8.0);
myDiffs.add(19.0);
myDiffs.add(2.0);
myDiffs.add(16.0);
myDiffs.add(5.0);
myDiffs.add(18.0);
myDiffs.add(23.0);
myDiffs.add(9.0);
myDiffs.add(4.0);
myDiffs.add(8.0);
myDiffs.add(9.0);
myDiffs.add(3.0);
myDiffs.add(3.0);
myDiffs.add(9.0);
myDiffs.add(13.0);
myDiffs.add(17.0);
myDiffs.add(7.0);
myDiffs.add(0.0);
myDiffs.add(2.0);
myDiffs.add(3.0);
myDiffs.add(33.0);
myDiffs.add(23.0);
myDiffs.add(26.0);
myDiffs.add(12.0);
myDiffs.add(12.0);
myDiffs.add(19.0);
myDiffs.add(14.0);
myDiffs.add(9.0);
myDiffs.add(26.0);
myDiffs.add(24.0);
myDiffs.add(13.0);
myDiffs.add(19.0);
myDiffs.add(2.0);
myDiffs.add(7.0);
myDiffs.add(28.0);
myDiffs.add(15.0);
myDiffs.add(2.0);
myDiffs.add(5.0);
myDiffs.add(17.0);
myDiffs.add(2.0);
myDiffs.add(16.0);
myDiffs.add(19.0);
myDiffs.add(2.0);
myDiffs.add(31.0);
DataPanel myPP = new DataPanel(myDiffs,this.getHeight(),this.getWidth());
this.add(myPP);
this.setVisible(true);// Display the panel.
}
public static void main(String[] args){
DataGUI myDataGUI = new DataGUI();
myDataGUI.setVisible(true);
}
}
DataPanel.java (Note: I edited the code below to include trashgod's suggestions, but it still does not work.)
import java.awt.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;
class DataPanel extends JPanel {
Insets ins; // holds the panel's insets
ArrayList<Double> myDiffs;
double maxDiff = Double.NEGATIVE_INFINITY;
double minDiff = Double.POSITIVE_INFINITY;
double maxPlot;
DataPanel(ArrayList<Double> Diffs, int h, int w){
setOpaque(true);// Ensure that panel is opaque.
setPreferredSize(new Dimension(w, h));
setMinimumSize(new Dimension(w, h));
setMaximumSize(new Dimension(w, h));
myDiffs = Diffs;
repaint();
this.setVisible(true);
}
protected void paintComponent(Graphics g){// Override paintComponent() method.
super.paintComponent(g);
//get data about plotting environment and about text
int height = getHeight();
int width = getWidth();
ins = getInsets();
Graphics2D g2d = (Graphics2D)g;
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();
String titleString ="Title of Graphic";
int titleStrWidth = fontMetrics.stringWidth(titleString);
int titleStrHeight = fontMetrics.getHeight();
int leftMargin = ins.left;
//set parameters for inner rectangle
int hPad=10;
int vPad = 6;
int testLeftStartPlotWindow = ins.left+5+(3*yStrHeight);
int testInnerWidth = width-testLeftStartPlotWindow-ins.right-hPad;
getMaxMinDiffs();
getMaxPlotVal();
double increment = 5.0;
int numTicks = (int)(maxPlot/increment);//will use numTicks for: remainder, leftStartPlotWindow, innerRectangle+labels+tickmarks
int remainder = testInnerWidth%numTicks;
int leftStartPlotWindow = testLeftStartPlotWindow-remainder;
System.out.println("remainder is: "+remainder);
int bottomPad = (3*xStrHeight)-vPad;
int blueTop = ins.bottom+(vPad/2)+titleStrHeight;
int blueHeight = height-bottomPad-blueTop;
int blueWidth = blueHeight;
int blueBottom = blueHeight+blueTop;
//plot outer rectangle
g.setColor(Color.red);
int redWidth = width-leftMargin-1;
g.drawRect(leftMargin, ins.bottom, redWidth, height-ins.bottom-1);
//write top label
g.setColor(Color.black);
g.drawString(titleString, leftStartPlotWindow+((blueWidth/2)-(titleStrWidth/2)), titleStrHeight);
// fill, then plot, inner rectangle
g.setColor(Color.white);
g.fillRect(leftStartPlotWindow, blueTop, blueWidth, blueHeight);
g.setColor(Color.blue);
g.drawRect(leftStartPlotWindow, blueTop, blueWidth, blueHeight);
//scale the diffs to fit window
double Scalar = blueWidth/maxPlot;
ArrayList<Double> scaledDiffs = new ArrayList<Double>();
for(int e = 0;e<myDiffs.size();e++){scaledDiffs.add(myDiffs.get(e)*Scalar);}
//plot the scaled Diffs
AffineTransform at = g2d.getTransform();//save the graphics context's transform
g2d.translate(leftStartPlotWindow, blueTop);//translate origin to bottom-left corner of blue rectangle
g2d.scale(1, -1);//invert the y-axis
for(int w = 0;w<scaledDiffs.size();w++){
if(w>0){
double prior = scaledDiffs.get(w-1);
int priorInt = (int)prior;
double current = scaledDiffs.get(w);
int currentInt = (int)current;
g2d.drawOval(priorInt, currentInt, 4, 4);
}
}
g2d.setTransform(at);//restore the transform for conventional rendering
//write x-axis label
g.setColor(Color.red);
g.drawString(xString, leftStartPlotWindow+((blueWidth/2)-(xStrWidth/2)), height-ins.bottom-vPad);
//write y-axis label
g2d.rotate(Math.toRadians(-90), 0, 0);//rotate text 90 degrees counter-clockwise
g.drawString(yString, -(height/2)-(yStrWidth/2), yStrHeight);
g2d.rotate(Math.toRadians(+90), 0, 0);//rotate text 90 degrees clockwise
// draw tick marks on x-axis
NumberFormat formatter = new DecimalFormat("#0.0");
double k = (double)blueWidth/(double)numTicks;
double iteration = 0;
for(int h=0;h<=numTicks;h++){
int xval = (int)(h*k);
g.setColor(Color.red);
g.drawLine(leftStartPlotWindow+xval, blueBottom+2, leftStartPlotWindow+xval, blueBottom+(xStrHeight/2));//draw tick marks
g.drawString(formatter.format(iteration),leftStartPlotWindow+xval-(fontMetrics.stringWidth(Double.toString(iteration))/2),blueBottom+(xStrHeight/2)+13);
iteration+=increment;
}
// draw tick marks on y-axis
iteration = 0;
for(int h=0;h<=numTicks;h++){
int yval = (int)(h*k);
g.setColor(Color.red);
g.drawLine(leftStartPlotWindow-2, blueBottom-yval, leftStartPlotWindow-(yStrHeight/2), blueBottom-yval);//draw tick marks
g2d.rotate(Math.toRadians(-90), 0, 0);//rotate text 90 degrees counter-clockwise
g.drawString(formatter.format(iteration),leftStartPlotWindow-2,blueBottom-(fontMetrics.stringWidth(Double.toString(iteration))/2));
g2d.rotate(Math.toRadians(+90), 0, 0);//rotate text 90 degrees clockwise
iteration+=increment;
}
}
void getMaxMinDiffs(){// get max and min of Diffs
for(int u = 0;u<myDiffs.size();u++){
if(myDiffs.get(u)>maxDiff){maxDiff = myDiffs.get(u);}
if(myDiffs.get(u)<minDiff){minDiff = myDiffs.get(u);}
}
}
void getMaxPlotVal(){
maxPlot = maxDiff;
maxPlot += 1;//make sure maxPlot is bigger than the max data value
while(maxPlot%5!=0){maxPlot+=1;}//make sure maxPlot is a multiple of 5
}
}
Also, as always, links to articles or tutorials on the topic are much appreciated.
One approach is shown in SineTest. In outline,
Save the graphics context's transform.
Graphics2D g2d = (Graphics2D) g;
AffineTransform at = g2d.getTransform();
Translate the origin to the center.
g2d.translate(w / 2, h / 2);
Invert the y-axis.
g2d.scale(1, -1);
Render using cartesian coordinates.
Restore the transform for conventional rendering.
g2d.setTransform(at);
Apologies for somewhat incomplete answer, but this may get your gears turning. Java draws things the way you described them: It considers the top left corner of the screen to be 0, 0 and draws x increasing to the right and y increasing downwards. If you make the line that states
g2d.drawOval(priorInt, currentInt, 4, 4);
into
g2d.drawOval(blueWidth - priorInt, blueHeight - currentInt, 4, 4);
it should yield the correct results for your first issue. I need a bit more info on the second problem to help you with that one though. Are they just off the screen or are the getting drawn over by something else? Try flipping +s and -s around to see if you can get the correct result if that is the case.

Java AWT rotating ball

Oh boy, trigonometry is so hard! I kinda need some help, It's a simple program that is supposed to rotate a ball around the center of the screen... Here is my code:
import java.awt.*;
import javax.swing.*;
public class Window {
private int x;
private int y;
private int R = 30;
private double alpha = 0;
private final int SPEED = 1;
private final Color COLOR = Color.red;
public static void main(String[] args) {
new Window().buildWindow();
}
public void buildWindow() {
JFrame frame = new JFrame("Rotation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800,600);
frame.setVisible(true);
frame.add(new DrawPanel());
while(true) {
try {
Thread.sleep(60);
alpha += SPEED;
frame.repaint();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#SuppressWarnings("serial")
class DrawPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.blue);
Font font = new Font("Arial",Font.PLAIN,12);
g.setFont(font);
g.drawString(String.format("Angle: %.2f ", alpha), 0, 12);
g.setColor(Color.black);
g.drawLine(this.getWidth()/2,0, this.getWidth()/2, this.getHeight());
g.drawLine(0, this.getHeight()/2, this.getWidth(), this.getHeight()/2);
x = (int) ((this.getWidth() / 2 - R / 2 ) + Math.round((R + 20) * Math.sin(alpha)));
y = (int) ((this.getHeight() / 2 - R / 2 ) + Math.round((R + 20) * Math.cos(alpha)));
g.setColor(COLOR);
g.fillOval(x, y, R, R);
}
}
}
This code looks like it's working, but then I've printed Angle[alpha] information to the screen. And when I comment out the alpha+=SPEED and enter the angle manually it does not look like it's working.The angle on the screen doses not correspond to that angle alpha.
So I need suggestions. What should I change? Is my trigonometry wrong? etc...
Three things to note here:
I assume your alpha variable is in degrees since you are adding 20 in each step. However the Math.sin() and Math.cos() methods expect an angle in radians.
Normally 0 deg (or 0 rads) is represented at the "3 o'clock" position. For this you need to switch the sin and cos calls.
Reverse the sign in the y equation to account for the fact that y coordinates start at the top of the screen and increase downwards
With these modifications, your code will work as you expect:
double rads = (alpha * Math.PI) / 180F;
x = (int) ((this.getWidth() / 2 - R / 2 ) + Math.round((R + 20) * Math.cos(rads)));
y = (int) ((this.getHeight() / 2 - R / 2 ) - Math.round((R + 20) * Math.sin(rads)));

How to output a String on multiple lines using Graphics

My Program overrides public void paint(Graphics g, int x, int y); in order to draw some stings using g.drawString(someString, x+10, y+30);
Now someString can be quite long and thus, it may not fit on one line.
What is the best way to write the text on multiple line. For instance, in a rectangle (x1, y1, x2, y2)?
Thanks to Epaga's hint and a couple of examples on the Net (not so obvious to find! I used mainly Break a Line for text layout), I could make a component to display wrapped text. It is incomplete, but at least it shows the intended effect.
class TextContainer extends JPanel
{
private int m_width;
private int m_height;
private String m_text;
private AttributedCharacterIterator m_iterator;
private int m_start;
private int m_end;
public TextContainer(String text, int width, int height)
{
m_text = text;
m_width = width;
m_height = height;
AttributedString styledText = new AttributedString(text);
m_iterator = styledText.getIterator();
m_start = m_iterator.getBeginIndex();
m_end = m_iterator.getEndIndex();
}
public String getText()
{
return m_text;
}
public Dimension getPreferredSize()
{
return new Dimension(m_width, m_height);
}
public void paint(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
FontRenderContext frc = g2.getFontRenderContext();
LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
measurer.setPosition(m_start);
float x = 0, y = 0;
while (measurer.getPosition() < m_end)
{
TextLayout layout = measurer.nextLayout(m_width);
y += layout.getAscent();
float dx = layout.isLeftToRight() ?
0 : m_width - layout.getAdvance();
layout.draw(g2, x + dx, y);
y += layout.getDescent() + layout.getLeading();
}
}
}
Just for fun, I made it fitting a circle (alas, no justification, it seems):
public void paint(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
FontRenderContext frc = g2.getFontRenderContext();
LineBreakMeasurer measurer = new LineBreakMeasurer(m_iterator, frc);
measurer.setPosition(m_start);
float y = 0;
while (measurer.getPosition() < m_end)
{
double ix = Math.sqrt((m_width / 2 - y) * y);
float x = m_width / 2.0F - (float) ix;
int width = (int) ix * 2;
TextLayout layout = measurer.nextLayout(width);
y += layout.getAscent();
float dx = layout.isLeftToRight() ?
0 : width - layout.getAdvance();
layout.draw(g2, x + dx, y);
y += layout.getDescent() + layout.getLeading();
}
}
I am not too sure about dx computation, though.
java.awt.font.TextLayout might be helpful. Here's a snippet of their example code:
Graphics2D g = ...;
Point2D loc = ...;
Font font = Font.getFont("Helvetica-bold-italic");
FontRenderContext frc = g.getFontRenderContext();
TextLayout layout = new TextLayout("This is a string", font, frc);
layout.draw(g, (float)loc.getX(), (float)loc.getY());
Rectangle2D bounds = layout.getBounds();
bounds.setRect(bounds.getX()+loc.getX(),
bounds.getY()+loc.getY(),
bounds.getWidth(),
bounds.getHeight());
g.draw(bounds);
Otherwise you could always use a Swing text element to do the job for you, just pass in the Graphics you want it to paint into.
Incrementally build your string, one word at a time, using Epaga's method to find the length of your string. Once the length is longer than your rectangle, remove the last word and print. Repeat until you run out of words.
It sounds like a bad algorithm, but for each line, it's really O(screenWidth/averageCharacterWidth) => O(1).
Still, use a StringBuffer to build your string!
Had some trouble myself this is my solution:
Graphics2D g=....
FontRenderContext frc = g.getFontRenderContext();
TextLayout layout = new TextLayout(text, font, frc);
String[] outputs = text.split("\n");
for(int i=0; i<outputs.length; i++){
g.drawString(outputs[i], 15,(int) (15+i*layout.getBounds().getHeight()+0.5));
Hope that helps.... simple but it works.
You can use a JLabel and embed the text with html.
JLabel.setText("<html>"+line1+"<br>"+line2);

Categories

Resources