I'm trying to use setStroke and BasicStroke to draw random thickness lines.
here is the painting code
public void paintComponent(Graphics g1) {
Random rand = new Random();
Graphics g2 = (Graphics2D) g1;
//set background color
g2.setColor(Color.white);
g2.fillRect(0, 0, getWidth(), getHeight());
Dimension d = getPreferredSize();
//set line's color
float r = rand.nextFloat();
float g = rand.nextFloat();
float b = rand.nextFloat();
Color randomColor = new Color(r,g,b);
g2.setColor(randomColor);
//set line's stroke
float width = rand.nextFloat();
BasicStroke randomStroke = new BasicStroke(width);
((Graphics2D) g2).setStroke(randomStroke);
for (Line2D.Double line : lines) {
g2.drawLine(
(int)line.getX1(),
(int)line.getY1(),
(int)line.getX2(),
(int)line.getY2()
);
}
}
when I set the width of stroke to a certain number, it can draw correctly. I looked up the BasicStroke class, it have the following parameters:
float width;
int join;
int cap;
float miterlimit;
float[] dash;
float dash_phase;
Besides width, I'm not sure what are the others function.
How can I use the BasicStroke to generate random thickness lines?
I think the biggest problem is that nextFloat() is returning values between 0 and 1 - where I'm guessing you want numbers greater than 1 in order to be able to see any visible difference in line thickness.
Besides width, I'm not sure what are the others function.
Please refer to the Javadocs .
Related
I have very little experience with Java, and I am an amateur programmer. So mind my vocabulary.
I want to be able to stick a static rectangle on top of a rotating rectangle.
So far when I try to add another object it spins with the other image. I have tried setting the rotation to zero but that doesn't seem to work. I have also tried to create another class that draws components separately and added them to the frame using frame.add. I have also tried creating another part to the Draw class that has no effect on the GUI. Here is my current Draw class. Any help is appreciated.
class DrawRectangle extends JPanel {
#Override
public void paintComponent(Graphics g) {
int h = this.getHeight();
int w = this.getWidth();
Graphics2D g2 = (Graphics2D) g;
//draw background
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, w, h);
//draw roatiing rectangle
g2.setColor(Color.CYAN);
Rectangle rRec = new Rectangle(w / 4, h / 4, 2 * w / 4, 2 * h / 4);
double wr = rRec.getX() + rRec.getWidth() / 2;
double hr = rRec.getY() + rRec.getHeight() / 2;
g2.rotate(Math.toRadians(count), wr, hr);
g2.fill(rRec);
g2.fillRect(w / 3, h / 3, 2 * w / 3, 2 * h / 3);
}
public void paintComponent2(Graphics g) {
int h = this.getHeight();
int w = this.getWidth();
Graphics2D g2 = (Graphics2D) g;
}
}
So far when I try to add another object it spins with the other image.
Create a separate Graphics object to do the rotation so you don't affect the properties of the Graphics object passed into the painting method:
//Graphics2D g2 = (Graphics2D) g;
Graphics2D g2 = (Graphics2D)g.create();
// painting code
g2.dispose();
Move your g2.fill(rRec); BEFORE the rotate call, and it should work (I just tested it out).
This way, you will draw your static rectangle before the rotation, perform the rotation, THEN draw your second rectangle. Assuming your count variable is incremented somewhere, it should show the second rectangle being rotated.
I have a function that draws a rotated picture on a g2 component but for some reason I can't give it any colored background...
I was trying to use methods .setColor() and .setBackground() but with no use.
I have seen a similiar case here Graphics2D: Drawing black on white?
but it didn't really help. Here is my function:
public void rotateImage1(double degrees, ImageObserver o){
double sin = Math.abs(Math.sin(Math.toRadians(degrees)));
double cos = Math.abs(Math.cos(Math.toRadians(degrees)));
ImageIcon icon = new ImageIcon(this.spiral);
int w = icon.getIconWidth();
int h = icon.getIconHeight();
int neww = (int)Math.floor(w*cos+h*sin);
int newh = (int)Math.floor(h*cos+w*sin);
BufferedImage blankCanvas = new BufferedImage(neww, newh, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D)blankCanvas.getGraphics();
if(PhotoEdit.black)
g2.setColor(Color.BLACK);
else
g2.setColor(Color.WHITE);
g2.translate((neww-w)/2, (newh-h)/2);
g2.rotate(Math.toRadians(degrees), icon.getIconWidth()/2, icon.getIconHeight()/2);
g2.drawImage(this.spiral, 0, 0, o);
this.spiral = blankCanvas;
}
PhotoEdit.black is a boolean variable that is true if the user selected the checkbox with black background option.
You are setting the color in graphics, but you aren't drawing it to the panel.
You can use g2.setColor(...) and g2.fillRect(...) and specify coordinates that cover the whole panel, and then draw your image on top.
Docs for fillRect: http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics.html#fillRect%28int,%20int,%20int,%20int%29
Question about the circumference of circles. In order to change the outside color of the circle (circumference) I would use
drawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
Just not exactly how to start off after the following code below..after Public void drawArc I dont know where to go
public void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension d = getSize();
for(int i = 0; i < 100; ++i) {
Color color = new Color(generator.nextInt(255), generator.nextInt(255), generator.nextInt(255));
g.setColor(color);
int circleSize = generator.nextInt(d.width / 4);
int x = generator.nextInt(d.width - circleSize);
int y = generator.nextInt(d.height - circleSize);
g.fillOval(x, y, circleSize, circleSize);
g.drawArc(x, y, circleSize, circleSize, 0, 360);
}
}
You are drawing the body of a circle, then drawing its outline, without changing the colour in between. That means you can't actually see the outline of the circle.
I think you should change the colour of the graphics context, before you draw the outline. One way would be to insert
color = new Color(generator.nextInt(255), generator.nextInt(255), generator.nextInt(255));
g.setColor(color);
before the call to drawArc.
You don't need your own drawArc method, you should be calling the Graphics.drawArc() method. x and y are the center of the circle, width and height are the diameter of the circle, and startAngle and arcAngle are where to start and stop drawing the circle. 0 is 3 o'clock. So to draw a complete circle you would use 0 and 360 for startAngle and arcAngle.
I want to move this small rectangle about the circumference of the circle, so that I looks and moves like a canon.
Code
private void doDrawing(Graphics g){
g.setColor(Color.BLUE);
g.fillArc(-CANON_RADIUS/2, this.getHeight()-CANON_RADIUS/2, CANON_RADIUS, CANON_RADIUS, 0, 90);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
Rectangle rect = new Rectangle(CANON_RADIUS/2, this.getHeight()-CANON_RADIUS/2, CANON_WIDTH, CANON_HEIGHT);
AffineTransform transform = new AffineTransform();
transform.rotate(Math.toRadians(-60), rect.getX() + rect.width/2, rect.getY() + rect.height/2);
Shape transformed = transform.createTransformedShape(rect);
g2d.fill(transformed);
}
This code rotates rectangle about its centre. How can I rotate rectangle around the circumference?
First things first, you can use a transformation matrix for this, like you are already using:
http://en.wikipedia.org/wiki/Transformation_matrix
Edit:
looking at your code, you want to rotate your canon around an anchor. Please look at the javadocs:
http://docs.oracle.com/javase/7/docs/api/java/awt/geom/AffineTransform.html
public void rotate(double theta,
double anchorx,
double anchory)
the first argument is your rotation, the last two arguments have to be the middle of your cannon base! like screen.height and 0 for your example:
AffineTransform transform = new AffineTransform();
transform.rotate(Math.toRadians(-60), 0, Screen.height);
Shape transformed = transform.createTransformedShape(rect);
g2d.fill(transformed);
second approach could be move the middle of your rotated rectangle around the radius of your base.
like (pseudocode):
Point p = circle.getPoint();
shape.moveto(p.x-(shape.width/2),p.y-(shape.height/2));
g2d.fill(shape);
I calculate the width of a string by using the stringWidth("str") method on the FontMetrics object. This method only gives me the width from the starting point to the ending point on the baseline. Not the overall width of the string.
Any ideas on how to calculate the overall width?
Most documentation says that I can't rely on the result by adding the width of each char in the string.
Here is my code until now:
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
FontMetrics fm = g.getFontMetrics(new Font("Arial", Font.PLAIN, 6));
int width = fm.stringWidth("Product name");
FontMetrics also has getStringBounds(), not just stringWidth().
You should ask yourself what you need the text width for. If it's for output via e.g. a paintComponent() override, then you should measure the text dimensions there, which makes sure that all factors (e.g. fractionalmetrics, antialiasing) are taken into consideration. Also, you don't have to think about disposing the graphics context - which in your example, you definitely have to, it needs g.dispose()!
The following example for use in a paintComponent() override, e.g. for the JPanel you're using as your ContentPane, draws a text at the center of the component in a font given by you and draws a rectangle around it with some distance, the text being perfectly in its center.
The text size, especially vertically, is not precise, however. A better solution is further down.
Screenshot of this imprecise solution: http://i.imgur.com/vetRjCK.png
Screenshot of precise solution further down: http://i.imgur.com/0A0EdCf.png
final int w = getWidth();
final int h = getHeight();
// CLEAR BACKGROUND
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, w, h);
// ACTIVATE ANTIALIASING AND FRACTIONAL METRICS
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
// PREPARE TEXT, COLOR, FONT
final String text = "The Higgs Boson is ...";
g.setColor(Color.ORANGE);
g.setFont(yourFont);
// PREPARE COORDINATES, AND DRAW TEXT
final FontMetrics fm = g.getFontMetrics();
final Rectangle2D stringBounds = fm.getStringBounds(text, g);
final double x = (w - stringBounds.getWidth()) / 2d;
final double y = (h - stringBounds.getHeight()) / 2d;
g.drawString(text, (int) x, (int) (y + fm.getAscent()));
// TURN OFF ANTIALIASING FOR HIGHER VISUAL PRECISION OF THE LINES
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
// DRAW RECTANGLE BORDER
final double borderDistance = 10d;
final Shape borderRect = new Rectangle2D.Double(x - borderDistance * 2, y - borderDistance, stringBounds.getWidth() + borderDistance * 4, stringBounds.getHeight() + borderDistance * 2);
g.setStroke(new BasicStroke(3f));
g.draw(borderRect);
// DRAW THIN TIGHT RECTANGLE BORDER
final Shape borderRectTight = new Rectangle2D.Double(x, y , stringBounds.getWidth(), stringBounds.getHeight());
g.setStroke(new BasicStroke(1f));
g.setColor(Color.GRAY);
g.draw(borderRectTight);
The following solution is structurally just like the above, but instead of using FontMetrics-calls to loosely derive the text dimensions, it derives the precise text dimensions by converting the text into a Shape first.
// CLEAR BACKGROUND
g.setColor(Color.DARK_GRAY);
g.fillRect(0, 0, w, h);
// ACTIVATE ANTIALIASING AND FRACTIONAL METRICS
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
// PREPARE TEXT, COLOR
final String text = "The Higgs Boson is ...";
g.setColor(Color.ORANGE);
// CREATE GLYPHVECTOR FROM TEXT, CREATE PRELIMINARY SHAPE FOR COORDINATE CALCULATION, CALC COORDINATES
final GlyphVector gv = yourFont.createGlyphVector(g.getFontRenderContext(), text);
final Rectangle2D stringBoundsForPosition = gv.getOutline().getBounds2D();
final double xForShapeCreation = (w - stringBoundsForPosition.getWidth()) / 2d;
final double yForShapeCreation = (h - stringBoundsForPosition.getHeight()) / 2d;
// DERIVE SHAPE AGAIN, THIS TIME IN THE RIGHT PLACE (IT'S NOT THE ONLY POSSIBLE METHOD.)
final Shape textShape = gv.getOutline((float) xForShapeCreation, (float) yForShapeCreation + g.getFontMetrics(yourFont).getAscent());
g.fill(textShape);
// GET PRECISE SHAPE BOUNDS, TURN OFF ANTIALIASING FOR HIGHER VISUAL PRECISION OF THE LINES
final Rectangle2D stringBoundsForEverything = textShape.getBounds2D();// JavaDocs: "Returns a high precision [...] bounding box of the Shape [...] guarantee [...] that the Shape lies entirely within the indicated Rectangle2D."
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
// DRAW RECTANGLE BORDER
final double borderDistance = 10d;
final Shape borderRect = new Rectangle2D.Double(stringBoundsForEverything.getX() - borderDistance * 2, stringBoundsForEverything.getY() - borderDistance, stringBoundsForEverything.getWidth() + borderDistance * 4, stringBoundsForEverything.getHeight() + borderDistance * 2);
g.setStroke(new BasicStroke(3f));
g.draw(borderRect);
// DRAW THIN TIGHT RECTANGLE BORDER
final Shape borderRectTight = new Rectangle2D.Double(stringBoundsForEverything.getX(), stringBoundsForEverything.getY(), stringBoundsForEverything.getWidth(), stringBoundsForEverything.getHeight());
g.setStroke(new BasicStroke(1f));
g.setColor(Color.GRAY);
g.draw(borderRectTight);
you need a graphics object.
graphics.setFont(font);
int lenghtgraphics.getFontMetrics(graphics.getFont()).stringWidth(value);
I built an xml rendering component a few years back and rely very heavily on
SwingUtilities.computeStringWidth(fontMetrics, string);
I get the font metrics from the component that I'm measuring the string for.
myComponent.getFontMetrics(myComponent.getFont());