Related
I'm trying to draw a rotated shape at a given point. To give an example, in the following image, the red rectangle is a non-rotated rectangle drawn at a point and then the blue rectangle is rotated and drawn at the same position. The blue rectangle is the outcome I'm aiming for.
I've been experimenting and trying different methods. Currently, here is what I used for the image:
Point point = new Point(300, 300);
Dimension dim = new Dimension(200, 100);
double radians = Math.toRadians(30);
g.setColor(new java.awt.Color(1f, 0f, 0f, .5f));
g.fillRect(point.x, point.y, dim.width, dim.height);
translate(g, dim, radians);
g.rotate(radians, point.getX(), point.getY());
g.setColor(new java.awt.Color(0f, 0f, 1f, .5f));
g.fillRect(point.x, point.y, dim.width, dim.height);
private static void translate(Graphics2D g, Dimension dim, double radians) {
if (radians > Math.toRadians(360)) {
radians %= Math.toRadians(360);
}
int xOffsetX = 0;
int xOffsetY = 0;
int yOffsetX = 0;
int yOffsetY = 0;
if (radians > 0 && radians <= Math.toRadians(90)) {
xOffsetY -= dim.getHeight();
} else if (radians > Math.toRadians(90) && radians <= Math.toRadians(180)) {
xOffsetX -= dim.getWidth();
xOffsetY -= dim.getHeight();
yOffsetY -= dim.getHeight();
} else if (radians > Math.toRadians(180) && radians <= Math.toRadians(270)) {
xOffsetX -= dim.getWidth();
yOffsetX -= dim.getWidth();
yOffsetY -= dim.getHeight();
} else {
yOffsetX -= dim.getWidth();
}
int x = rotateX(xOffsetX, xOffsetY, radians);
int y = rotateY(yOffsetX, yOffsetY, radians);
g.translate(x, y);
}
private static int rotateX(int x, int y, double radians) {
if (x == 0 && y == 0) {
return 0;
}
return (int) Math.round(x * Math.cos(radians) - y * Math.sin(radians));
}
private static int rotateY(int x, int y, double radians) {
if (x == 0 && y == 0) {
return 0;
}
return (int) Math.round(x * Math.sin(radians) + y * Math.cos(radians));
}
This works for rectangles but doesn't work for other types of shapes. I'm trying to figure out if there is a way to accomplish this for every type of shape. Also note that the code is just for testing purposes and there are a lot of bad practices in it, like calling Math.toRadians so much.
Something like this?
It can be achieved using a rotate transform first, then using the bounds of the rotated shape as a basis, the translate transform can be used to shift it back to meet the top most y and leftmost x values of the original rectangle.
See the getImage() method for one implementation of that.
int a = angleModel.getNumber().intValue();
AffineTransform rotateTransform = AffineTransform.getRotateInstance((a*2*Math.PI)/360d);
// rotate the original shape with no regard to the final bounds
Shape rotatedShape = rotateTransform.createTransformedShape(rectangle);
// get the bounds of the rotated shape
Rectangle2D rotatedRect = rotatedShape.getBounds2D();
// calculate the x,y offset needed to shift it to top/left bounds of original rectangle
double xOff = rectangle.getX()-rotatedRect.getX();
double yOff = rectangle.getY()-rotatedRect.getY();
AffineTransform translateTransform = AffineTransform.getTranslateInstance(xOff, yOff);
// shift the new shape to the top left of original rectangle
Shape rotateAndTranslateShape = translateTransform.createTransformedShape(rotatedShape);
Here is the complete source code:
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.EmptyBorder;
public class TransformedShape {
private JComponent ui = null;
JLabel output = new JLabel();
JToolBar tools = new JToolBar("Tools");
ChangeListener changeListener = (ChangeEvent e) -> {
refresh();
};
int pad = 5;
Rectangle2D.Double rectangle = new Rectangle2D.Double(pad,pad,200,100);
SpinnerNumberModel angleModel = new SpinnerNumberModel(30, 0, 90, 1);
public TransformedShape() {
initUI();
}
private BufferedImage getImage() {
int a = angleModel.getNumber().intValue();
AffineTransform rotateTransform = AffineTransform.getRotateInstance((a*2*Math.PI)/360d);
Shape rotatedShape = rotateTransform.createTransformedShape(rectangle);
Rectangle2D rotatedRect = rotatedShape.getBounds2D();
double xOff = rectangle.getX()-rotatedRect.getX();
double yOff = rectangle.getY()-rotatedRect.getY();
AffineTransform translateTransform = AffineTransform.getTranslateInstance(xOff, yOff);
Shape rotateAndTranslateShape = translateTransform.createTransformedShape(rotatedShape);
Area combinedShape = new Area(rotateAndTranslateShape);
combinedShape.add(new Area(rectangle));
Rectangle2D r = combinedShape.getBounds2D();
BufferedImage bi = new BufferedImage((int)(r.getWidth()+(2*pad)), (int)(r.getHeight()+(2*pad)), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(new Color(255,0,0,127));
g.fill(rectangle);
g.setColor(new Color(0,0,255,127));
g.fill(rotateAndTranslateShape);
g.dispose();
return bi;
}
private void addModelToToolbar(String label, SpinnerNumberModel model) {
tools.add(new JLabel(label));
JSpinner spinner = new JSpinner(model);
spinner.addChangeListener(changeListener);
tools.add(spinner);
}
public final void initUI() {
if (ui!=null) return;
ui = new JPanel(new BorderLayout(4,4));
ui.setBorder(new EmptyBorder(4,4,4,4));
ui.add(output);
ui.add(tools,BorderLayout.PAGE_START);
addModelToToolbar("Angle", angleModel);
refresh();
}
private void refresh() {
output.setIcon(new ImageIcon(getImage()));
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}
TransformedShape o = new TransformedShape();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
};
SwingUtilities.invokeLater(r);
}
}
You have a shape, any shape.
You have a point (px,py) and you want to rotate the shape around this point and angle ag measured counter-clokwise.
For each point of the shape the proccess has three steps:
Translate to (px,py)
Rotate
Translate back to (0,0)
The translation is fully simple
xNew = xOld - px
yNew = yOld - py
The rotation is a bit less simple
xRot = xNew * cos(ag) - yNew * sin(ag)
yRot = xNew * sin(ag) + yNew * cos(ag)
Finally the translation back:
xDef = xRot + px
yDef = yRot + py
A bit of explanation: Any transformation can be seen in two ways: 1) I move the shape 2) I move the axis-system. If you think about it, you'll find that the trasnsformation is relative: seen from the axis point of view or seen from the shape point of view.
So, you can say "I want coordinates in the translated system", or you can also say "I want the coordinates of the translated shape".
It doesn't matter what point of view you chose, the equations are the same.
I'm explaining this so much, just to achieve you realize which is the positive direction of the angle: clockwise or counter-clockwise.
What I'm basically trying to do is rotate a BufferedImage and then draw it based on it's new centre point.
So, originally I would've just done;
graphics.drawImage(image, x - (image.getWidth() / 2), y - (image.getHeight() / 2), this);
However, I'm unsure how to do it when I rotate the image using AffineTransform.
If I remember from basic Maths at school I'll need to use Cos for the X and Sin for the Y, however I've been googling for an algorithm but can't seem to come across anything.
I found out how to get the coordinates of the rotated point. If x1,y1 are the original coordinates and at the AffineTransform then you can get the coordinates of the rotated point in the original coordinate system with
Point2D point1origCoords = at.transform(new Point2D.Double(x1,y1),null);
The coordinates of the point in the new coordinate system after the affine transformation would be the same as the original coordinates. They stay the same because it's the coordinate system that gets transformed instead, so you need to apply the affine transformation to them.
Here is a demo of a rotation about the origin:
And the code used to generate it:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Triangle extends JPanel {
public void drawTriangle(Graphics2D g,
int x1, int y1, int x2, int y2, int x3, int y3,
int s, Color c, AffineTransform at) throws NoninvertibleTransformException{
g.setStroke(new BasicStroke(3.0f));
g.setPaint(c);
int d = 10;
g.drawLine(x1*s, y1*s, x2*s, y2*s);
g.drawLine(x2*s, y2*s, x3*s, y3*s);
g.drawLine(x3*s, y3*s, x1*s, y1*s);
Ellipse2D center_of_triangle = new Ellipse2D.Double(.33*(x1+x2+x3)*s, .33*(y1+y2+y3)*s,4,4);
g.draw(center_of_triangle);
float dash1[] = {2.0f};
BasicStroke dashed =
new BasicStroke(1.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER,
10.0f, dash1, 0.0f);
g.setStroke(dashed);
g.drawLine(0,0,x1*s,y1*s);
g.drawLine(0,0,x2*s,y2*s);
g.drawLine(0,0,x3*s,y3*s);
g.setColor(Color.black);
Point2D point1origCoords = at.transform(new Point2D.Double(x1,y1),null);
Point2D point2origCoords = at.transform(new Point2D.Double(x2,y2),null);
Point2D point3origCoords = at.transform(new Point2D.Double(x3,y3),null);
g.drawString("("+String.format("%.2f",point1origCoords.getX())+", "
+String.format("%.2f",point1origCoords.getY())+")",
x1*s,y1*s+2*d);
g.drawString("("+String.format("%.2f",point2origCoords.getX())+", "
+String.format("%.2f",point2origCoords.getY())+")", x2*s-4*d,y2*s-d);
g.drawString("("+String.format("%.2f",point3origCoords.getX())+", "
+String.format("%.2f",point3origCoords.getY())+")", x3*s+d,y3*s);
// center of the triangle
Point2D.Double center = new Point2D.Double(.33*(x1+x2+x3), .33*(y1+y2+y3));
Point2D centerorigCoords = at.transform(center,null);
g.drawString("("+String.format("%.2f",centerorigCoords.getX())+", "
+String.format("%.2f",centerorigCoords.getY())+")",
(int)Math.round(center.x*s),(int)Math.round(center.y*s)-d);
}
public void drawGrid(Graphics2D g, int s) {
int w = getWidth();
int h = getHeight();
float dash1[] = {2.0f};
BasicStroke dashed =
new BasicStroke(1.0f,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER,
10.0f, dash1, 0.0f);
g.setStroke(dashed);
g.setColor(Color.gray);
int j = 0;
while( j <= h ) {
g.drawLine(0,h-j,w,h-j);
j+=s;
}
j = 0;
while( j <= w ) {
g.drawLine(j,0,j,h);
j+=s;
}
}
public void paintComponent(Graphics g){
final int s=20;
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.black);
Font big = new Font("Times New Roman", Font.BOLD,20);
g2.setFont(big);
drawGrid(g2,s);
AffineTransform at = new AffineTransform();
// no rotation
at.rotate(Math.toRadians(0));
g2.transform(at);
// rotated triangle
try {
drawTriangle(g2,2,26,3,12,8,21,s,Color.blue,at);
} catch (NoninvertibleTransformException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
at.rotate(Math.toRadians(-45));
g2.transform(at);
try {
drawTriangle(g2,2,26,3,12,8,21,s,Color.magenta,at);
} catch (NoninvertibleTransformException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Window"); //frame is the window
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Triangle panel = new Triangle(); //panel is the graphics area where we can draw
frame.add(panel); //put the panel inside the window
frame.setSize(600,600); //set the window size to 600x600 pixels
frame.setVisible(true);
}
}
How to 'fill' Unicode characters in labels in Swing?
I'm trying to make a user interface for the chess program I've recently programmed
(with chess pieces something like seen above). In it I'm using unicode characters to represent my chess pieces (\u2654 through \u265F).
The problem is as follows:
When I set the background of my chess piece JLabel to something like white, the entire label is filled (in my case it's a 50*50px square of white with the character on top). This leads to my pieces looking like tiles instead of just their pictures.
When I set the label to opaque, I just get a cookie cutter version of my chess piece, not one with its insides filled. E.G.
Is there a way to fill only the character?
If not I guess I'll make a sprite sheet but I like this because I can use the chess pieces' toString() methods for the labels.
Code
import java.awt.*;
import javax.swing.*;
import java.util.Random;
class ChessBoard {
static Font font = new Font("Sans-Serif", Font.PLAIN, 50);
static Random rnd = new Random();
public static void addUnicodeCharToContainer(
String s, Container c, boolean randomColor) {
JLabel l = new JLabel(s);
l.setFont(font);
if (randomColor) {
int r = rnd.nextInt(255);
int g = rnd.nextInt(255);
int b = rnd.nextInt(255);
l.setForeground(new Color(r,g,b));
l.setBackground(new Color(255-r,255-g,255-b));
l.setOpaque(true);
}
c.add(l);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new GridLayout(0,6,4,4));
String[] pieces = {
"\u2654","\u2655","\u2656","\u2657","\u2658","\u2659",
"\u265A","\u265B","\u265C","\u265D","\u265E","\u265F"
};
for (String piece : pieces) {
addUnicodeCharToContainer(piece,gui,false);
}
for (String piece : pieces) {
addUnicodeCharToContainer(piece,gui,true);
}
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
The two rows are generated through the sorcery of Java-2D. The trick is to:
Ignore the 'black' chess pieces on the basis that our color is actually coming from 'the spaces contained by the shape'. Those are larger in the white chess pieces.
Create a GlyphVector that represents the shape of the character. This is important for further operations in Java-2D.
Create a Rectangle the size of the image.
subtract() the shape of the character from the shape of the image.
Break that altered shape into regions.
Fill the regions with the background color, but skip the single region that starts at 0.0,0.0 (representing the outermost region which we need transparent).
Finally, fill the shape of the character itself using the outline color.
Code
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.util.*;
class ChessBoard {
static Font font = new Font(Font.SANS_SERIF, Font.PLAIN, 50);
static Random rnd = new Random();
public static ArrayList<Shape> separateShapeIntoRegions(Shape shape) {
ArrayList<Shape> regions = new ArrayList<Shape>();
PathIterator pi = shape.getPathIterator(null);
int ii = 0;
GeneralPath gp = new GeneralPath();
while (!pi.isDone()) {
double[] coords = new double[6];
int pathSegmentType = pi.currentSegment(coords);
int windingRule = pi.getWindingRule();
gp.setWindingRule(windingRule);
if (pathSegmentType == PathIterator.SEG_MOVETO) {
gp = new GeneralPath();
gp.setWindingRule(windingRule);
gp.moveTo(coords[0], coords[1]);
System.out.println(ii++ + " \t" + coords[0] + "," + coords[1]);
} else if (pathSegmentType == PathIterator.SEG_LINETO) {
gp.lineTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_QUADTO) {
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
} else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
gp.curveTo(
coords[0], coords[1],
coords[2], coords[3],
coords[4], coords[5]);
} else if (pathSegmentType == PathIterator.SEG_CLOSE) {
gp.closePath();
regions.add(new Area(gp));
} else {
System.err.println("Unexpected value! " + pathSegmentType);
}
pi.next();
}
return regions;
}
public static void addColoredUnicodeCharToContainer(
String s, Container c,
Color bgColor, Color outlineColor, boolean blackSquare) {
int sz = font.getSize();
BufferedImage bi = new BufferedImage(
sz, sz, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(
RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_ENABLE);
g.setRenderingHint(
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
FontRenderContext frc = g.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, s);
Rectangle2D box1 = gv.getVisualBounds();
Shape shape1 = gv.getOutline();
Rectangle r = shape1.getBounds();
System.out.println("shape rect: " + r);
int spaceX = sz - r.width;
int spaceY = sz - r.height;
AffineTransform trans = AffineTransform.getTranslateInstance(
-r.x + (spaceX / 2), -r.y + (spaceY / 2));
System.out.println("Box2D " + trans);
Shape shapeCentered = trans.createTransformedShape(shape1);
Shape imageShape = new Rectangle2D.Double(0, 0, sz, sz);
Area imageShapeArea = new Area(imageShape);
Area shapeArea = new Area(shapeCentered);
imageShapeArea.subtract(shapeArea);
ArrayList<Shape> regions = separateShapeIntoRegions(imageShapeArea);
g.setStroke(new BasicStroke(1));
for (Shape region : regions) {
Rectangle r1 = region.getBounds();
if (r1.getX() < 0.001 && r1.getY() < 0.001) {
} else {
g.setColor(bgColor);
g.fill(region);
}
}
g.setColor(outlineColor);
g.fill(shapeArea);
g.dispose();
JLabel l = new JLabel(new ImageIcon(bi), JLabel.CENTER);
Color bg = (blackSquare ? Color.BLACK : Color.WHITE);
l.setBackground(bg);
l.setOpaque(true);
c.add(l);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new GridLayout(0, 6, 4, 4));
String[] pieces = {
"\u2654", "\u2655", "\u2656", "\u2657", "\u2658", "\u2659"
};
boolean blackSquare = false;
for (String piece : pieces) {
addColoredUnicodeCharToContainer(
piece, gui,
new Color(203,203,197),
Color.DARK_GRAY,
blackSquare);
blackSquare = !blackSquare;
}
blackSquare = !blackSquare;
for (String piece : pieces) {
addColoredUnicodeCharToContainer(
piece, gui,
new Color(192,142,60),
Color.DARK_GRAY,
blackSquare);
blackSquare = !blackSquare;
}
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
Chess Board
This is what it might look like as a Chess Board (22.81 Kb).
Sprite sets
Sprite sets of chess pieces (64x64 pixel) rendered from Unicode characters - as a PNG with transparent BG. Each has 6 columns for the pieces x 2 rows for the opponents (total size 384x128 pixels).
Chess pieces with solid fill (bronze/pewter) (11.64Kb).
Chess pieces with gradient fill (gold/silver) (13.61Kb).
Chess pieces with gradient fill (darker cyan/magenta) (13.44Kb).
Code for Chess Board & Sprite Set
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
import javax.imageio.ImageIO;
import java.util.*;
import java.util.logging.*;
class ChessBoard {
/**
* Unicodes for chess pieces.
*/
static final String[] pieces = {
"\u2654", "\u2655", "\u2656", "\u2657", "\u2658", "\u2659"
};
static final int KING = 0, QUEEN = 1, CASTLE = 2,
BISHOP = 3, KNIGHT = 4, PAWN = 5;
public static final int[] order = new int[]{
CASTLE, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, CASTLE
};
/*
* Colors..
*/
public static final Color outlineColor = Color.DARK_GRAY;
public static final Color[] pieceColors = {
new Color(203, 203, 197), new Color(192, 142, 60)
};
static final int WHITE = 0, BLACK = 1;
/*
* Font. The images use the font sizeXsize.
*/
static Font font = new Font("Sans-Serif", Font.PLAIN, 64);
public static ArrayList<Shape> separateShapeIntoRegions(Shape shape) {
ArrayList<Shape> regions = new ArrayList<Shape>();
PathIterator pi = shape.getPathIterator(null);
int ii = 0;
GeneralPath gp = new GeneralPath();
while (!pi.isDone()) {
double[] coords = new double[6];
int pathSegmentType = pi.currentSegment(coords);
int windingRule = pi.getWindingRule();
gp.setWindingRule(windingRule);
if (pathSegmentType == PathIterator.SEG_MOVETO) {
gp = new GeneralPath();
gp.setWindingRule(windingRule);
gp.moveTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_LINETO) {
gp.lineTo(coords[0], coords[1]);
} else if (pathSegmentType == PathIterator.SEG_QUADTO) {
gp.quadTo(coords[0], coords[1], coords[2], coords[3]);
} else if (pathSegmentType == PathIterator.SEG_CUBICTO) {
gp.curveTo(
coords[0], coords[1],
coords[2], coords[3],
coords[4], coords[5]);
} else if (pathSegmentType == PathIterator.SEG_CLOSE) {
gp.closePath();
regions.add(new Area(gp));
} else {
System.err.println("Unexpected value! " + pathSegmentType);
}
pi.next();
}
return regions;
}
public static BufferedImage getImageForChessPiece(
int piece, int side, boolean gradient) {
int sz = font.getSize();
BufferedImage bi = new BufferedImage(
sz, sz, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(
RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_ENABLE);
g.setRenderingHint(
RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
FontRenderContext frc = g.getFontRenderContext();
GlyphVector gv = font.createGlyphVector(frc, pieces[piece]);
Rectangle2D box1 = gv.getVisualBounds();
Shape shape1 = gv.getOutline();
Rectangle r = shape1.getBounds();
int spaceX = sz - r.width;
int spaceY = sz - r.height;
AffineTransform trans = AffineTransform.getTranslateInstance(
-r.x + (spaceX / 2), -r.y + (spaceY / 2));
Shape shapeCentered = trans.createTransformedShape(shape1);
Shape imageShape = new Rectangle2D.Double(0, 0, sz, sz);
Area imageShapeArea = new Area(imageShape);
Area shapeArea = new Area(shapeCentered);
imageShapeArea.subtract(shapeArea);
ArrayList<Shape> regions = separateShapeIntoRegions(imageShapeArea);
g.setStroke(new BasicStroke(1));
g.setColor(pieceColors[side]);
Color baseColor = pieceColors[side];
if (gradient) {
Color c1 = baseColor.brighter();
Color c2 = baseColor;
GradientPaint gp = new GradientPaint(
sz/2-(r.width/4), sz/2-(r.height/4), c1,
sz/2+(r.width/4), sz/2+(r.height/4), c2,
false);
g.setPaint(gp);
} else {
g.setColor(baseColor);
}
for (Shape region : regions) {
Rectangle r1 = region.getBounds();
if (r1.getX() < 0.001 && r1.getY() < 0.001) {
} else {
g.fill(region);
}
}
g.setColor(outlineColor);
g.fill(shapeArea);
g.dispose();
return bi;
}
public static void addColoredUnicodeCharToContainer(
Container c,
int piece,
int side,
Color bg,
boolean gradient) {
JLabel l = new JLabel(
new ImageIcon(getImageForChessPiece(piece, side, gradient)),
JLabel.CENTER);
l.setBackground(bg);
l.setOpaque(true);
c.add(l);
}
public static void addPiecesToContainer(
Container c,
int intialSquareColor,
int side,
int[] pieces,
boolean gradient) {
for (int piece : pieces) {
addColoredUnicodeCharToContainer(
c, piece, side,
intialSquareColor++%2 == BLACK ? Color.BLACK : Color.WHITE,
gradient);
}
}
public static void addPiecesToContainer(
Container c,
Color bg,
int side,
int[] pieces,
boolean gradient) {
for (int piece : pieces) {
addColoredUnicodeCharToContainer(
c, piece, side, bg, gradient);
}
}
public static void addBlankLabelRow(Container c, int initialSquareColor) {
for (int ii = 0; ii < 8; ii++) {
JLabel l = new JLabel();
Color bg = (initialSquareColor++ % 2 == BLACK
? Color.BLACK : Color.WHITE);
l.setBackground(bg);
l.setOpaque(true);
c.add(l);
}
}
public static void main(String[] args) {
final int[] pawnRow = new int[]{
PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN
};
Runnable r = new Runnable() {
#Override
public void run() {
int gradient = JOptionPane.showConfirmDialog(
null, "Use gradient fille color?");
boolean gradientFill = gradient == JOptionPane.OK_OPTION;
JPanel gui = new JPanel(new GridLayout(0, 8, 0, 0));
gui.setBorder(new BevelBorder(
BevelBorder.LOWERED,
Color.GRAY.brighter(), Color.GRAY,
Color.GRAY.darker(), Color.GRAY));
// set up a chess board
addPiecesToContainer(gui, WHITE, BLACK, order, gradientFill);
addPiecesToContainer(gui, BLACK, BLACK, pawnRow, gradientFill);
addBlankLabelRow(gui, WHITE);
addBlankLabelRow(gui, BLACK);
addBlankLabelRow(gui, WHITE);
addBlankLabelRow(gui, BLACK);
addPiecesToContainer(gui, WHITE, WHITE, pawnRow, gradientFill);
addPiecesToContainer(gui, BLACK, WHITE, order, gradientFill);
JOptionPane.showMessageDialog(
null,
gui,
"Chessboard",
JOptionPane.INFORMATION_MESSAGE);
JPanel tileSet = new JPanel(new GridLayout(0, 6, 0, 0));
tileSet.setOpaque(false);
int[] tileSetOrder = new int[]{
KING, QUEEN, CASTLE, KNIGHT, BISHOP, PAWN
};
addPiecesToContainer(
tileSet,
new Color(0, 0, 0, 0),
BLACK,
tileSetOrder,
gradientFill);
addPiecesToContainer(
tileSet,
new Color(0, 0, 0, 0),
WHITE,
tileSetOrder,
gradientFill);
int result = JOptionPane.showConfirmDialog(
null,
tileSet,
"Save this tileset?",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (result == JOptionPane.OK_OPTION) {
BufferedImage bi = new BufferedImage(
tileSet.getWidth(),
tileSet.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.createGraphics();
tileSet.paint(g);
g.dispose();
String gradientString = gradientFill ? "gradient" : "solid";
File f = new File(
"chess-pieces-tileset-" + gradientString + ".png");
try {
ImageIO.write(bi, "png", f);
Desktop.getDesktop().open(f);
} catch (IOException ex) {
Logger.getLogger(
ChessBoard.class.getName()).log(
Level.SEVERE, null, ex);
}
}
}
};
SwingUtilities.invokeLater(r);
}
}
See also
Developed out of the GlyphVector code as seen in this answer.
Resulted in UGlys - Unicode Glyphs at GitHub.
The problem I see is that the glyphs were designed to easily distinguish traditional black and white chess pieces. Note also the variation in font design. You may be able to create hue-themed pieces that preserve the black and white distinction using the HSB color space. Green and cyan are pictured below.
Addendum: For reference, here's a Mac OS X screenshot of #Andrew's glyph shape approach. Note the benefit of #Andrew's use of RenderingHints as the image is scaled.
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridLayout;
import java.util.Random;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/** #see https://stackoverflow.com/a/18691662/230513 */
class ChessBoard {
static Font font = new Font("Sans-Serif", Font.PLAIN, 64);
static Random rnd = new Random();
public static void addUnicodeCharToContainer(String s, Container c) {
JLabel l = new JLabel(s);
l.setFont(font);
l.setOpaque(true);
c.add(l);
}
public static void addWhite(String s, Container c, Float h) {
JLabel l = new JLabel(s);
l.setFont(font);
l.setOpaque(true);
l.setForeground(Color.getHSBColor(h, 1, 1));
l.setBackground(Color.getHSBColor(h, 3 / 8f, 5 / 8f));
c.add(l);
}
public static void addBlack(String s, Container c, Float h) {
JLabel l = new JLabel(s);
l.setFont(font);
l.setOpaque(true);
l.setForeground(Color.getHSBColor(h, 5 / 8f, 3 / 8f));
l.setBackground(Color.getHSBColor(h, 7 / 8f, 7 / 8f));
c.add(l);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JPanel gui = new JPanel(new GridLayout(0, 6, 4, 4));
String[] white = {
"\u2654", "\u2655", "\u2656", "\u2657", "\u2658", "\u2659"
};
String[] black = {
"\u265A", "\u265B", "\u265C", "\u265D", "\u265E", "\u265F"
};
for (String piece : white) {
addUnicodeCharToContainer(piece, gui);
}
for (String piece : white) {
addWhite(piece, gui, 2 / 6f);
}
for (String piece : black) {
addUnicodeCharToContainer(piece, gui);
}
for (String piece : black) {
addBlack(piece, gui, 3 / 6f);
}
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
In the end, I found making a spritesheet to be the easier and simpler way to go about the problem. Each piece now corresponds to a graphic within the spritesheet instead of a character/glyph. Because of this, the pieces can't be resized as nicely but that's not the biggest deal.
#Andrew Thompson's idea with the GlyphVector seemed promising, but the matter of separating inner white space from outer white space remains difficult.
One (inefficient) idea I still have is to make a ton of chess piece glyphs starting from a very small font size and with a foregound color of white:
for (int i = 1; i < BOARD_WIDTH/8) {
JLabel chessPiece =new JLabel("\u2654");
chessPiece.setForeground(Color.white);
chessPiece.setFont(new Font("Sans-Serif", Font.PLAIN, i));
add(chessPiece);
}
then add one last chess piece with a black foreground:
JLabel chessPiece =new JLabel("\u2654");
chessPiece.setForeground(Color.black);
chessPiece.setFont(new Font("Sans-Serif", Font.PLAIN, BOARD_WIDTH/8)));
add(chessPiece);
Note that I haven't tested this out.
I've been trying to rotate a polygon around a specified center point but everything I've tried has failed miserably. I've googled for example and found many but not a single one seems to work.
The result I'm trying to replicate is similar with the first answer to this
How to rotate an image gradually in Swing?
The difference is that I need the polygon to actually rotate, just drawing it in an angle won't cut it. (it's for simple physics modelling)
Here's my code, with several different methods I've tried to implement
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JPanel;
public class rotationPanel extends JPanel {
private static final int SIZE = 500;
private static final Shape outline = makeShape();
Point p;
Point p2;
Point p3;
Point p4;
Point[] points;
Point[] npoints;
Point center;
Polygon poly;
double angle;
Timer timer;
long start;
long sleepTime;
static int runTime;
public rotationPanel(){
setSize(500,500);
setBackground(Color.DARK_GRAY);
setVisible(true);
runTime = 100; //ms
start = 0;
sleepTime = 0;
timer = new Timer();
center = new Point(250,250);
p = new Point(200,200);
p2 = new Point(150,150);
p3 = new Point(250,150);
p4 = new Point(200,100);
/*
points = new Point[4];
points[0]=p;
points[1]=p2;
points[2]=p3;
points[3]=p4;
npoints = new Point[4];
npoints[0]=p;
npoints[1]=p2;
npoints[2]=p3;
npoints[3]=p4;
poly = new Polygon();
*/
}
public void mainloop(){
start= System.currentTimeMillis();
//rotate(points,2);
p = rotatePoint(p,center);
p2 = rotatePoint(p2,center);
p3 = rotatePoint(p3,center);
p4 = rotatePoint(p4,center);
repaint();
sleepTime = runTime -(System.currentTimeMillis()-start);
System.out.println("Looped. Sleeping for:" +sleepTime+"ms");
if(sleepTime>0)
timer.schedule(new loop(), sleepTime);
else
mainloop();
}
private static Shape makeShape() {
AffineTransform at = new AffineTransform();
at.translate(SIZE/2, SIZE/2);
at.scale(20, 20);
at.rotate(Math.toRadians(35));
return at.createTransformedShape(initPoly());
}
/** Create a U shaped outline. */
private static Polygon initPoly() {
Polygon poly = new Polygon();
poly.addPoint( 1, 0);
poly.addPoint( 1, -2);
poly.addPoint( 2, -2);
poly.addPoint( 2, 1);
poly.addPoint(-2, 1);
poly.addPoint(-2, -2);
poly.addPoint(-1, -2);
poly.addPoint(-1, 0);
return poly;
}
public void rotatePoint(Point pt, double rotationAngle){
AffineTransform.getRotateInstance
(Math.toRadians(rotationAngle), center.x, center.y)
.transform(pt,pt);
}
public Point rotatePoint(Point pt, Point center)
{
angle = (Math.toRadians(150));
double cosAngle = Math.cos(angle);
double sinAngle = Math.sin(angle);
pt.x = center.x + (int) ((pt.x-center.x)*cosAngle-(pt.y-center.y)*sinAngle);
pt.y = center.y + (int) ((pt.x-center.x)*sinAngle+(pt.y-center.y)*cosAngle);
return pt;
}
public void rotate(Point[] pts, int angle){
AffineTransform.getRotateInstance
(Math.toRadians(angle), center.x, center.y)
.transform(pts,0,npoints,0,4);
points = new Point[4];
points[0]=npoints[0];
points[1]=npoints[1];
points[2]=npoints[2];
points[3]=npoints[3];
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(center.x-4, center.y-4, 8, 8);
g.setColor(Color.YELLOW);
//g.fillRect(p.x-4, p.y-4, 8, 8);
//g.fillRect(p2.x-4, p2.y-4, 8, 8);
//g.fillRect(p3.x-4, p3.y-4, 8, 8);
//g.fillRect(p4.x-4, p4.y-4, 8, 8);
g.fillRect(p.x, p.y, 2, 2);
g.fillRect(p2.x, p2.y, 2, 2);
g.fillRect(p3.x, p3.y, 2, 2);
g.fillRect(p4.x, p4.y, 2, 2);
}
class loop extends TimerTask{
public void run() {
mainloop();
}
}
}
As you didn't help me vary much I was forced to figure this out by myself. Here we go:
The correct approach (or at least one of them) is to use affine transform to the points of the polygon you wish to rotate. The catch is that you cannot rotate the same polygon over and over again as it will severely deform due to the continuous rounding.
So the trick is to keep " an original version" of the polygon and always rotate that one.
Of course, this approach is only critical when rotating the polygon several times. If you want to only rotate it once you can simply use the values from the polygon you want to rotate.
Here's a little example I managed to put together:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class rotationPanel extends JPanel {
private static final long serialVersionUID = 117L;
private static final int SIZE = 500;
// point arrays which contain the points that are rotated around the center
Point[] points1;
Point[] points2;
Point[] points3;
// The center of rotation
Point center;
// the polygons being rotated
Polygon poly1;
Polygon poly2;
Polygon poly3;
// the angle of rotation
double angle;
Timer timer;
long start;
long sleepTime;
static int runTime;
public rotationPanel(){
setSize(500,500);
setBackground(Color.DARK_GRAY);
setVisible(true);
// time loop is set to run at fixed rate of 50 ms
runTime = 50;
start = 0;
sleepTime = 0;
timer = new Timer();
angle = 0;
// initializing the arrays (not neccesary)
points1 = getOriginalPoints(1);
points3 = getOriginalPoints(3);
points2 = getOriginalPoints(2);
// setting the rotation to the middle of the screen
center = new Point(250,250);
// start the looping
mainloop();
}
public void mainloop(){
start= System.currentTimeMillis();
// rotate the points the spcified angle and store the rotated
//points to the correct array
rotatePointMatrix(getOriginalPoints(1),angle,points1);
rotatePointMatrix(getOriginalPoints(2),angle,points2);
rotatePointMatrix(getOriginalPoints(3),angle,points3);
// Make the points into a polygon
poly1 = polygonize(points1);
poly2 = polygonize(points2);
poly3 = polygonize(points3);
// increase the angle by one degree, resulting to rotation in the longer run
angle++;
if (angle>=360){
angle=0;
}
// restatring the sequence
repaint();
sleepTime = runTime -(System.currentTimeMillis()-start);
System.out.println("Looped. Sleeping for:" +sleepTime+"ms");
if(sleepTime>0)
timer.schedule(new loop(), sleepTime);
else
mainloop();
}
public void rotatePointMatrix(Point[] origPoints, double angle, Point[] storeTo){
/* We ge the original points of the polygon we wish to rotate
* and rotate them with affine transform to the given angle.
* After the opeariont is complete the points are stored to the
* array given to the method.
*/
AffineTransform.getRotateInstance
(Math.toRadians(angle), center.x, center.y)
.transform(origPoints,0,storeTo,0,5);
}
public Polygon polygonize(Point[] polyPoints){
//a simple method that makes a new polygon out of the rotated points
Polygon tempPoly = new Polygon();
for(int i=0; i < polyPoints.length; i++){
tempPoly.addPoint(polyPoints[i].x, polyPoints[i].y);
}
return tempPoly;
}
public Point[] getOriginalPoints(int type){
/* In this example the rotated "polygon" are stored in this method.
* The Point is that if we want to rotate a polygon constatnly/frequently
* we cannot use the values of an already rotated polygon as this will
* lead to the polygon deforming severely after few translations due
* to the points being constantly rounded. So the trick is to save the
* original Points of the polygon and always rotate that one to the new
* angle instead of rotating the same one again and again.
*/
Point[] originalPoints = new Point[5];
if(type == 2){
originalPoints[0]= new Point(200, 100);
originalPoints[1]= new Point(250, 50);
originalPoints[2]= new Point(300, 100);
originalPoints[3]= new Point(300, 400);
originalPoints[4]= new Point(200, 400);
}
else if(type == 1){
originalPoints[0]= new Point(210, 150);
originalPoints[1]= new Point(250, 150);
originalPoints[2]= new Point(250, 190);
originalPoints[3]= new Point(230, 220);
originalPoints[4]= new Point(210, 190);
}
else{
originalPoints[0]= new Point(250, 300);
originalPoints[1]= new Point(290, 300);
originalPoints[2]= new Point(290, 340);
originalPoints[3]= new Point(270, 370);
originalPoints[4]= new Point(250, 340);
}
return originalPoints;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.GRAY);
g2d.fillPolygon(poly2);
g2d.setColor(Color.yellow);
g2d.fillPolygon(poly1);
g2d.setColor(Color.yellow);
g2d.fillPolygon(poly3);
g2d.setColor(Color.WHITE);
for(int i=0; i < points1.length; i++){
g2d.fillRect(points1[i].x-1, points1[i].y-1, 3, 3);
g2d.fillRect(points3[i].x-1, points3[i].y-1, 3, 3);
}
g2d.setColor(Color.BLUE);
g2d.fillOval(center.x-4, center.y-4, 8, 8);
g2d.setColor(Color.yellow);
g2d.drawString("Angle: "+angle, 10,450);
}
class loop extends TimerTask{
public void run() {
mainloop();
}
}
public static void main(String[] args){
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new rotationPanel());
f.setSize(500,500);
f.setVisible(true);
}
}
I hope this helps! Don't hesitate to contact me if you run into trouble!
Here is a simple method to build a polygon from a set of points, rotated around a center point, at a specified angle:
/**
* Builds a polygon from a set of points, rotated around a point, at the
* specified rotation angle.
*
* #param centerX the int center x coordinate around which to rotate
* #param centerY the int center y coordinate around which to rotate
* #param xp the int[] of x points which make up our polygon points. This
* array is parallel to the yp array where each index in this array
* corresponds to the same index in the yp array.
* #param yp the int[] of y points which make up our polygon points. This
* array is parallel to the xp array where each index in this array
* corresponds to the same index in the xp array.
* #param rotationAngle the double angle in which to rotate the provided
* coordinates (specified in degrees).
* #return a Polygon of the provided coordinates rotated around the center point
* at the specified angle.
* #throws IllegalArgumentException when the provided x points array is not the
* same length as the provided y points array
*/
private Polygon buildPolygon(int centerX, int centerY, int[] xp, int[] yp, double rotationAngle) throws IllegalArgumentException {
// copy the arrays so that we dont manipulate the originals, that way we can
// reuse them if necessary
int[] xpoints = Arrays.copyOf(xp,xp.length);
int[] ypoints = Arrays.copyOf(yp,yp.length);
if(xpoints.length != ypoints.length){
throw new IllegalArgumentException("The provided x points are not the same length as the provided y points.");
}
// create a list of Point2D pairs
ArrayList<Point2D> list = new ArrayList();
for(int i = 0; i < ypoints.length; i++){
list.add(new Point2D.Double(xpoints[i], ypoints[i]));
}
// create an array which will hold the rotated points
Point2D[] rotatedPoints = new Point2D[list.size()];
// rotate the points
AffineTransform transform = AffineTransform.getRotateInstance(Math.toRadians(rotationAngle), centerX, centerY);
transform.transform(list.toArray(new Point2D[0]), 0, rotatedPoints, 0, rotatedPoints.length);
// build the polygon from the rotated points and return it
int[] ixp = new int[list.size()];
int[] iyp = new int[list.size()];
for(int i = 0; i < ixp.length; i++){
ixp[i] = (int)rotatedPoints[i].getX();
iyp[i] = (int)rotatedPoints[i].getY();
}
return new Polygon(ixp, iyp, ixp.length);
}
So I have a black image that acts as darkness (In my game). I want to show a small portion around the character only. Like so
The red square is the player.
The green bit is the ground in the game (grass).
The black is the shadow/darkness.
What I want to do is cut a Ellipse/hole out of the blank, black image. I want this Ellipse to be centered around the players (The red square) x and y position.
Currently I am using this to get the effect:
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
//draw mask
g.setColor(Color.black);
if(!nearPlayer(x, y)) {
g.drawLine(x, y, x, y);
}
}
}
But, this processes extremely slow and laggs the players movement drastically.
Is this possible?
..the Player is the red square. Basically what i want to do is cut a circle out of the blank, black image around the coordinates of the player relative to said black image.
What DYM by 'black image' - exactly? To me that just looks like the BG is painted black, which would make more sense for any solid color. In that case, just create the red thing using an Area, fill it, then for the border set a stroke & the color to black, and draw it. This example shows how.
The relevant part of that short code is..
public void paintDaisyPart(Graphics2D g, Area daisyArea) {
g.setClip(daisyArea);
g.setColor(Color.YELLOW);
g.fillRect(0, 0, 200, 200);
g.setColor(Color.YELLOW.darker());
g.setClip(null);
g.setStroke(new BasicStroke(3));
g.draw(daisyArea);
}
I must be bored. This is an animated SSCCE version of the code that drew the image above. It is typically showing >130 FPS. And that is on a clunky machine for which I told the guy my spec. was 'cheap' & reminded him twice that I don't play (heavy rendering) games.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class DaisyDisplay {
DaisyDisplay() {
JPanel gui = new JPanel(new BorderLayout(2,2));
final BufferedImage daisy = new BufferedImage(
200,200,BufferedImage.TYPE_INT_RGB);
final JLabel daisyLabel = new JLabel(new ImageIcon(daisy));
gui.add(daisyLabel,BorderLayout.CENTER);
final Daisy daisyPainter = new Daisy();
daisyPainter.setSize(200);
final JLabel fps = new JLabel("FPS: ");
gui.add(fps,BorderLayout.SOUTH);
ActionListener animator = new ActionListener() {
int counter = 0;
long timeLast = 0;
long timeNow = 0;
public void actionPerformed(ActionEvent ae) {
Graphics2D g = daisy.createGraphics();
g.setColor(Color.GREEN.darker());
g.fillRect(0, 0, 200, 200);
daisyPainter.paint(g);
g.dispose();
daisyLabel.repaint();
counter++;
timeNow = System.currentTimeMillis();
if (timeLast<timeNow-1000) {
fps.setText("FPS: " + counter);
counter = 0;
timeLast = timeNow;
}
}
};
Timer timer = new Timer(1,animator);
timer.start();
JOptionPane.showMessageDialog(null, gui);
timer.stop();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new DaisyDisplay();
}
});
}
}
class Daisy {
double size = 200;
Point location;
double offset = 0.0;
public void paint(Graphics2D g) {
Area daisyArea = getDaisyShape();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
offset += .02d;
AffineTransform plain = g.getTransform();
g.setTransform(AffineTransform.getRotateInstance(
offset + (Math.PI*1/8),
100,100));
paintDaisyPart(g,daisyArea);
g.setTransform(AffineTransform.getRotateInstance(
offset + (Math.PI*3/8),
100,100));
paintDaisyPart(g,daisyArea);
g.setTransform(AffineTransform.getRotateInstance(
offset,
100,100));
paintDaisyPart(g,daisyArea);
g.setTransform(AffineTransform.getRotateInstance(
offset + (Math.PI*2/8),
100,100));
paintDaisyPart(g,daisyArea);
g.setTransform(plain);
}
public void setLocation(Point location) {
this.location = location;
}
public void paintDaisyPart(Graphics2D g, Area daisyArea) {
g.setClip(daisyArea);
g.setColor(Color.YELLOW);
g.fillRect(0, 0, 200, 200);
g.setColor(Color.YELLOW.darker());
g.setClip(null);
g.setStroke(new BasicStroke(3));
g.draw(daisyArea);
}
public void setSize(double size) {
this.size = size;
}
public Area getDaisyShape() {
int diameter = (int)size*6/20;
Ellipse2D.Double core = new Ellipse2D.Double(
(size-diameter)/2,(size-diameter)/2,diameter,diameter);
int pad = 10;
int petalWidth = 50;
int petalLength = 75;
Area area = new Area(core);
// left petal
area.add(new Area(new Ellipse2D.Double(
pad,(size-petalWidth)/2,petalLength,petalWidth)));
// right petal
area.add(new Area(new Ellipse2D.Double(
(size-petalLength-pad),(size-petalWidth)/2,petalLength,petalWidth)));
// top petal
area.add(new Area(new Ellipse2D.Double(
(size-petalWidth)/2,pad,petalWidth,petalLength)));
// bottom petal
area.add(new Area(new Ellipse2D.Double(
(size-petalWidth)/2,(size-petalLength-pad),petalWidth,petalLength)));
return area;
}
}