Compass needle is drawn at wrong position - java

I am working on a compass in java. First I created a UI prototyp for my compass and created a working SSCCE of it, where you can click on the panel and the needle is pointed to another direction, which differs about 15 degree. This is the code:
public class TestFrame extends JFrame {
public TestFrame() {
initComponents();
}
public void initComponents() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocation(new Point((int) (Toolkit.getDefaultToolkit().getScreenSize().width / 2) - 400, (int) (Toolkit.getDefaultToolkit().getScreenSize().height / 2) - 250));
setSize(500, 500);
setVisible(true);
CompassPanel c = new CompassPanel();
add(c, BorderLayout.CENTER);
}
public class CompassPanel extends JPanel {
Image bufImage;
Graphics bufG;
private int circleX, circleY, circleRadius;
private int[] xPoints, yPoints;
private double rotationAngle = Math.toRadians(0);
public CompassPanel() {
setVisible(true);
addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseClicked(MouseEvent e) {
rotationAngle = rotationAngle + Math.toRadians(15);
repaint();
}
});
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
circleRadius = (int) (getWidth() * 0.7);
circleX = 50;
circleY = 50;
g2d.setColor(Color.BLACK);
for (int angle = 0; angle <= 360; angle += 5) {
double sin = Math.sin(Math.toRadians(angle));
double cos = Math.cos(Math.toRadians(angle));
int x1 = (int) ((circleX + circleRadius / 2) - cos * (circleRadius * 0.37) - sin * (circleRadius * 0.37));
int y1 = (int) ((circleY + circleRadius / 2) + sin * (circleRadius * 0.37) - cos * (circleRadius * 0.37));
g2d.setColor(Color.BLACK);
g2d.drawLine(x1, y1, (circleX + circleRadius / 2), (circleY + circleRadius / 2));
}
g2d.setFont(new Font("Arial", Font.BOLD, 11));
g2d.drawString("WEST", circleX - 45, circleY + circleRadius / 2 + 4);
g2d.drawString("EAST", circleX + circleRadius + 13, circleY + circleRadius / 2 + 4);
g2d.drawString("NORTH", circleX + circleRadius / 2 - 14, circleY - 15);
g2d.drawString("SOUTH", circleX + circleRadius / 2 - 14, circleY + circleRadius + 25);
g2d.setColor(Color.WHITE);
g2d.fillOval(circleX, circleY, circleRadius, circleRadius);
g2d.setColor(Color.BLACK);
g2d.drawOval(circleX, circleY, circleRadius, circleRadius);
xPoints = new int[] { (int) (circleX + circleRadius / 2),
(int) (circleX + circleRadius * 0.25),
(int) (circleX + circleRadius / 2),
(int) (circleX + circleRadius * 0.75) };
yPoints = new int[] { (int) (circleY + 30),
(int) (circleY + circleRadius * 0.85),
(int) (circleY + circleRadius * 0.6),
(int) (circleY + circleRadius * 0.85) };
Polygon fillPoly = new Polygon(xPoints, yPoints, 4);
Polygon outerPoly = new Polygon(xPoints, yPoints, 4);
int rotationX = circleX + (circleRadius / 2);
int rotationY = circleX + (circleRadius / 2);
g2d.setColor(Color.green);
g2d.fillOval(rotationX, rotationY, 5, 5);
AffineTransform a = g2d.getTransform().getRotateInstance(rotationAngle, rotationX, rotationY);
g2d.setTransform(a);
g2d.setColor(Color.RED);
g2d.fillPolygon(fillPoly);
g2d.setColor(Color.black);
g2d.draw(outerPoly);
}
#Override
public void update(Graphics g) {
int w = this.getSize().width;
int h = this.getSize().height;
if (bufImage == null) {
bufImage = this.createImage(w, h);
bufG = bufImage.getGraphics();
}
bufG.setColor(this.getBackground());
bufG.fillRect(0, 0, w, h);
bufG.setColor(this.getForeground());
paint(bufG);
g.drawImage(bufImage, 0, 0, this);
}
public void setRotationAngle(int angle) {
rotationAngle = angle;
}
}
public static void main(String[] args) {
new TestFrame();
}
}
When I implement this panel into my application, the needle is not drawn as it is in the SSCCE. It is drawn a bit higher and on the left of the position it is meant to be. When I click on the panel and rotate the needle, the rotation works fine and the needle is painted where it belongs.
I add the panel like this to my application. The sensorPanel is a JPanel in a TabbedPane.
public JPanel createSensorPanel(){
sensorPanel = new JPanel(new MigLayout("fill, insets 3"));
CompassPanel compassPanel = new CompassPanel();
sensorPanel.add(compassPanel, "wrap");
return sensorPanel;
}
Why is the Polygon of the needle not drawn at the position it is drawn in the SSCCE?
EDIT:
Here is a picture of the problem.

If you look closer what's going on on your image, than you'll see, that affine transform is the problem.
Looks like that affine transforms are related to top parent not the current child component (see java Affine Transform correct order).
The quick and dirty solution would be to translate the arrow as the parents are translated (based on this answer)
AffineTransform a = g2d.getTransform().
getRotateInstance(rotationAngle, rotationX, rotationY)
.translate(parent.getX(), parent.getY()) // use instance of real parent instead
.translate(tabpane.getX(), tabpane.getY()); // use instance of JTabbedPane instead
But much more convenient would be some common solution, something like this one.

Related

java get center of each shape

hello i'm having trouble drawing arrows from center of a circle to center of the next one, each circle is from a xml elsewhere let's consider size is 14.
why does my code seems not to do the trick?
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.io.File;
import java.io.IOException;
import java.util.Vector;
import javax.swing.JFrame;
Just setting the frame
public class GRAPHICS_REPRESENTATION extends JFrame {
private Vector<Point> point_tab;
public GRAPHICS_REPRESENTATION() {
super("graphic");
point_tab = new Vector<Point>(0);
setSize(800, 800);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
Drawing a circle (found and reworked from here)
// Convenience method to draw from center with radius
public void drawCircle(Graphics cg, int xCenter, int yCenter, int r) {
Graphics2D cg2 = (Graphics2D) cg.create();
cg2.drawOval(xCenter - r, yCenter - r, r, r);
}// end drawCircle
the problem method
private void disposeScene(Graphics g1, int x1, int y1, int r) {
Graphics2D g = (Graphics2D) g1.create();
AffineTransform at = g.getTransform();
AffineTransform at2 = new AffineTransform();
at2 = AffineTransform.getScaleInstance(0.5, 0.5); // scaling à faire en 1er
g.transform(at2); // prise en compte de la matrice
at2 = AffineTransform.getTranslateInstance(getWidth() / 2 + x1 + r / 2, getHeight() / 2 - y1 - r / 2); // centrage
g.transform(at2);
double angle = (double) Math.cos(0);
for (int i = 0; i < 14; i++) {
angle = Math.rint(angle / (i + 1));
g.setColor(Color.RED);
drawCircle(g, 0, 0, 2 * r);
Point center = new Point();
center.setLocation(g.getTransform().getTranslateX() / 2 + r / 2 + x1 / 2,
g.getTransform().getTranslateY() / 2 - r / 2 - y1 / 2);
point_tab.add(center);
at2.concatenate(AffineTransform.getRotateInstance(angle)); // rotation autour d'un centre
g.transform(at2);
System.out.println(point_tab.elementAt(i).getX() + "," + point_tab.elementAt(i).getY());
g.setColor(Color.BLUE);
if (i + 1 < point_tab.size()) {
drawArrow(g, point_tab.elementAt(i).getX(), point_tab.elementAt(i).getY(),
point_tab.elementAt(i + 1).getX(), point_tab.elementAt(i + 1).getY());
} else {
drawArrow(g, point_tab.elementAt(i).getX(), point_tab.elementAt(i).getY(),
point_tab.elementAt(0).getX(), point_tab.elementAt(0).getY());
}
}
g.setTransform(at);
}
drawing an arrow from src to dst (found here and reworked)
private final int ARR_SIZE = 4;
private void drawArrow(Graphics g1, double xsrc, double ysrc, double xdst, double ydst) {
Graphics2D g = (Graphics2D) g1.create();
double dx = xdst - xsrc, dy = ydst - ysrc; // distances
double angle = Math.atan2(dy, dx);
int len = (int) Math.sqrt(dx * dx + dy * dy);
AffineTransform at = g.getTransform();
AffineTransform at2 = new AffineTransform();
at2=AffineTransform.getTranslateInstance(xsrc, ysrc);
at2.concatenate(AffineTransform.getRotateInstance(angle));
g.transform(at2);
// Draw horizontal arrow starting in (0, 0)
g.drawLine(0, 0, len, 0);
g.fillPolygon(new int[] { len, len - ARR_SIZE, len - ARR_SIZE, len }, new int[] { 0, -ARR_SIZE, ARR_SIZE, 0 },
4);
g.setTransform(at);
}
main method
public void paint(Graphics g) {
disposeScene(g, getWidth()/4,getHeight()/4,40);
}
Here the solution I found by myself
/**
* #brief draw the scene with all shapes set up
* #param g1
* #param x1
* #param y1
* #param xtg
* #param r
*/
private void disposeScene(Graphics g1, int x1, int y1, XML_TO_GRAPH xtg, int r) {
Graphics2D g = (Graphics2D) g1.create();
g.translate(getWidth() / 2, getHeight() / 2);
drawCircle(g, 0, 0, (x1+y1)/2); // cercle fictif
for (int i = 0; i < xtg.getsceneVector().size(); i++) {
double angle = Math.toRadians(360.0); // radian mieux 2pi
angle = (angle*i / xtg.getsceneVector().size());
System.out.println("angle(" + i + ")=" + angle);
g.setColor(Color.RED);
drawCircle(g, (int) (Math.cos(angle) * x1), (int) (Math.sin(angle) * y1), r);
Point center_big_circle = new Point();
center_big_circle.setLocation((0), (0)); // centre du grand cercle fictif
Point center_little_circle = new Point();
center_little_circle.setLocation((Math.cos(angle) * (x1)), (Math.sin(angle) * (y1))); // décalage vers les
// bords
System.out.println("centre(" + i + ")=" + center_little_circle.getX() + "," + center_little_circle.getY());
point_tab.add(center_little_circle);
g.setColor(Color.BLACK);
drawString(g, (int) center_little_circle.getX()-r, (int) center_little_circle.getY()-r, 16, "scène : " + i);
System.out.println(point_tab.elementAt(i).getX() + "," + point_tab.elementAt(i).getY());
g.setColor(Color.BLUE);
if (i + 1 < point_tab.size()) {// si existe
drawArrow(g, point_tab.elementAt(i).getX(), point_tab.elementAt(i).getY(),
point_tab.elementAt(i + 1).getX(), point_tab.elementAt(i + 1).getY());
// g.setColor(Color.BLACK);
// drawString(g, (int)(( point_tab.elementAt(i+1).getX()-
point_tab.elementAt(i).getX())/2), (int) ((point_tab.elementAt(i+1).getY()-
point_tab.elementAt(i).getY())/2), 16, "X : " +
xtg.getcount_occurence().toString());
//idem
drawArrow(g, point_tab.elementAt(i+1).getX(), point_tab.elementAt(i+1).getY(),
point_tab.elementAt(i).getX(), point_tab.elementAt(i).getY());
// drawString(g, (int)(( point_tab.elementAt(i).getX()-
point_tab.elementAt(i+1).getX())/2), (int) ((point_tab.elementAt(i).getY()-
point_tab.elementAt(i+1).getY())/2), 16, "X : " +
xtg.getcount_occurence().toString());
}
}
}
and
// Convenience method to draw from center with radius
/**
*
* #param cg
* #param xCenter
* #param yCenter
* #param r
*/
public void drawCircle(Graphics cg, int xCenter, int yCenter, int r) {
Graphics2D cg2 = (Graphics2D) cg.create();
System.out.println("Center at: " + (xCenter - r) + "," + (yCenter - r));
cg2.drawOval(xCenter - r, yCenter - r, 2 * r, 2 * r);
}// end drawCircle
and convenient drawString method :
/**
*
* #param g1
* #param x
* #param y
* #param size
* #param str
*/
private void drawString(Graphics g1, int x, int y, int size, String str) {
Graphics2D g = (Graphics2D) g1.create();
g.setFont(new Font("Times New Roman", Font.PLAIN, size));
g.drawString(str, x, y);
}
and paint :
public void paint(Graphics g) {
disposeScene(g, (int) (getWidth() / 3), (int) (getHeight() / 3), 20);
}

Using JSlider to Scale Drawing

First question ever here. I'm trying to scale a custom drawing with JSlider. However, it doesn't do anything and I cannot for the life of me figure out why. My code grabs a custom shape and draws it fine initially, but it won't scale.
class DrawFrame extends JFrame {
private int CarWidth = 50;
private CarShape shape = new CarShape(150, 150, CarWidth);
public DrawFrame()
{
setTitle("Draw a Car");
setSize(400, 400);
JSlider slider = new JSlider(JSlider.VERTICAL, 1, 100, 50);
slider.setMajorTickSpacing(5);
slider.setPaintTicks(true);
slider.addChangeListener((new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
int x = (int)source.getValue();
CarWidth = x;
repaint();
}
}));
add(slider, BorderLayout.WEST);
add(shape);
}
}
public class CarShape extends JPanel {
private int x;
private int y;
private int width;
public CarShape(int x, int y, int width)
{
this.x = x;
this.y = y;
this.width = width;
}
public void update(int x){
x = width;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle2D.Double body
= new Rectangle2D.Double(x, y + width / 6,
width - 1, width / 6);
Ellipse2D.Double frontTire
= new Ellipse2D.Double(x + width / 6, y + width / 3,
width / 6, width / 6);
Ellipse2D.Double rearTire
= new Ellipse2D.Double(x + width * 2 / 3, y + width / 3,
width / 6, width / 6);
// The bottom of the front windshield
Point2D.Double r1
= new Point2D.Double(x + width / 6, y + width / 6);
// The front of the roof
Point2D.Double r2
= new Point2D.Double(x + width / 3, y);
// The rear of the roof
Point2D.Double r3
= new Point2D.Double(x + width * 2 / 3, y);
// The bottom of the rear windshield
Point2D.Double r4
= new Point2D.Double(x + width * 5 / 6, y + width / 6);
Line2D.Double frontWindshield
= new Line2D.Double(r1, r2);
Line2D.Double roofTop
= new Line2D.Double(r2, r3);
Line2D.Double rearWindshield
= new Line2D.Double(r3, r4);
g2.draw(body);
g2.draw(frontTire);
g2.draw(rearTire);
g2.draw(frontWindshield);
g2.draw(roofTop);
g2.draw(rearWindshield);
}
}
public class SliderTester {
public static void main(String[] args)
{
DrawFrame frame = new DrawFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Every time the change listener is called, it creates a new CarShape object, but this has no effect on the CarShape object that is displayed. Better perhaps would be to resize the visualized object... OK, did you just change the code on me or am I imagining things?
Now you're changing CarWidth (which should be re-named carWidth), but that's not going to change the state of the visualized CarShape object. Instead give your CarShape class a setCarWidth(int width) method, one that changes its state, and then call that method within your stateChange method.
e.g.,
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;
#SuppressWarnings("serial")
class DrawFrame extends JFrame {
private int carWidth = 50;
private CarShape shape = new CarShape(150, 150, carWidth);
public DrawFrame() {
setTitle("Draw a Car");
setSize(400, 400);
JSlider slider = new JSlider(JSlider.VERTICAL, 1, 100, 50);
slider.setMajorTickSpacing(5);
slider.setPaintTicks(true);
slider.addChangeListener((new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
carWidth = (int) source.getValue();
shape.setCarWidth(carWidth);
repaint();
}
}));
add(slider, BorderLayout.WEST);
add(shape);
}
}
#SuppressWarnings("serial")
class CarShape extends JPanel {
private int x;
private int y;
private int width;
public CarShape(int x, int y, int width) {
this.x = x;
this.y = y;
this.width = width;
}
public void setCarWidth(int w) {
this.width = w;
}
// this method is just messed up -- you're setting the parameter!
public void update(int x) {
x = width; // no!!!
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle2D.Double body = new Rectangle2D.Double(x, y + width / 6,
width - 1, width / 6);
Ellipse2D.Double frontTire = new Ellipse2D.Double(x + width / 6, y
+ width / 3, width / 6, width / 6);
Ellipse2D.Double rearTire = new Ellipse2D.Double(x + width * 2 / 3, y
+ width / 3, width / 6, width / 6);
// The bottom of the front windshield
Point2D.Double r1 = new Point2D.Double(x + width / 6, y + width / 6);
// The front of the roof
Point2D.Double r2 = new Point2D.Double(x + width / 3, y);
// The rear of the roof
Point2D.Double r3 = new Point2D.Double(x + width * 2 / 3, y);
// The bottom of the rear windshield
Point2D.Double r4 = new Point2D.Double(x + width * 5 / 6, y + width / 6);
Line2D.Double frontWindshield = new Line2D.Double(r1, r2);
Line2D.Double roofTop = new Line2D.Double(r2, r3);
Line2D.Double rearWindshield = new Line2D.Double(r3, r4);
g2.draw(body);
g2.draw(frontTire);
g2.draw(rearTire);
g2.draw(frontWindshield);
g2.draw(roofTop);
g2.draw(rearWindshield);
}
}
public class SliderTester {
public static void main(String[] args) {
DrawFrame frame = new DrawFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}

How to make star shape in Java?

I'm trying to make some shapes with Java. I created two rectangles with two different colors but I want to create a star shape and I can't find useful source to help me doing this.
Here is my code:
import java.awt.*;
import javax.swing.*;
public class shapes extends JPanel{
#Override
public void paintComponent(Graphics GPHCS){
super.paintComponent(GPHCS);
GPHCS.setColor(Color.BLUE);
GPHCS.fillRect(25,25,100,30);
GPHCS.setColor(Color.GRAY);
GPHCS.fillRect(25,65,100,30);
GPHCS.setColor(new Color(190,81,215));
GPHCS.drawString("This is my text", 25, 120);
}
}
You could try using a polygon and some basic math:
int midX = 500;
int midY = 340;
int radius[] = {118,40,90,40};
int nPoints = 16;
int[] X = new int[nPoints];
int[] Y = new int[nPoints];
for (double current=0.0; current<nPoints; current++)
{
int i = (int) current;
double x = Math.cos(current*((2*Math.PI)/max))*radius[i % 4];
double y = Math.sin(current*((2*Math.PI)/max))*radius[i % 4];
X[i] = (int) x+midX;
Y[i] = (int) y+midY;
}
g.setColor(Color.WHITE);
g.fillPolygon(X, Y, nPoints);
You can also use existing classes e.g. http://java-sl.com/shapes.html for regular polygons and stars.
The Polygon class can be considered as a legacy class that has been there since Java 1.0, but should hardly be used any more in new code. The odd way of specifying the x/y coordinates in separate arrays, and, more importantly, the fact that it only supports int[] arrays limits its application areas. Although it implements the Shape interface, there are more modern implementations of this interface that can be used to represent polygons. In most cases, describing the polygon as a Path2D is easier and more flexible. One can create a Path2D p = new Path2D.Double(); and then do a sequence of moveTo and lineTo calls to geneate the desired shape.
The following program shows how the Path2D class may be used to generate star shapes. The most important method is the createStar method. It is very generic. It receives
the center coordinates for the star
the inner and outer radius of the star
the number of rays that the star should have
the angle where the first ray should be (i.e. the rotation angle of the star)
If desired, a simpler method may be wrapped around this one - as with the createDefaultStar example in the code below.
The program shows different stars, painted as lines and filled with different colors and radial gradient paints, as examples:
The complete program as a MCVE:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DrawStarShape
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new DrawStarShapePanel());
f.setSize(600, 600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class DrawStarShapePanel extends JPanel
{
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLACK);
g.draw(createDefaultStar(50, 200, 200));
g.setPaint(Color.RED);
g.fill(createStar(400, 400, 40, 60, 10, 0));
g.setPaint(new RadialGradientPaint(
new Point(400, 200), 60, new float[] { 0, 1 },
new Color[] { Color.RED, Color.YELLOW }));
g.fill(createStar(400, 200, 20, 60, 8, 0));
g.setPaint(new RadialGradientPaint(
new Point(200, 400), 50, new float[] { 0, 0.3f, 1 },
new Color[] { Color.RED, Color.YELLOW, Color.ORANGE }));
g.fill(createStar(200, 400, 40, 50, 20, 0));
}
private static Shape createDefaultStar(double radius, double centerX,
double centerY)
{
return createStar(centerX, centerY, radius, radius * 2.63, 5,
Math.toRadians(-18));
}
private static Shape createStar(double centerX, double centerY,
double innerRadius, double outerRadius, int numRays,
double startAngleRad)
{
Path2D path = new Path2D.Double();
double deltaAngleRad = Math.PI / numRays;
for (int i = 0; i < numRays * 2; i++)
{
double angleRad = startAngleRad + i * deltaAngleRad;
double ca = Math.cos(angleRad);
double sa = Math.sin(angleRad);
double relX = ca;
double relY = sa;
if ((i & 1) == 0)
{
relX *= outerRadius;
relY *= outerRadius;
}
else
{
relX *= innerRadius;
relY *= innerRadius;
}
if (i == 0)
{
path.moveTo(centerX + relX, centerY + relY);
}
else
{
path.lineTo(centerX + relX, centerY + relY);
}
}
path.closePath();
return path;
}
}
I have 2 method.
1)
public static Bitmap drawStar(int W, int H, int color, boolean andRing)
{
Path path = new Path();
Bitmap output = Bitmap.createBitmap(W, H, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
float midW ,min ,fat ,half ,radius;
if(andRing)
{
midW = W / 2;
min = Math.min(W, H);
half = min / 2;
midW = midW - half;
fat = min / 17;
radius = half - fat;
paint.setStrokeWidth(fat);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(midW + half, half, radius, paint);
path.reset();
paint.setStyle(Paint.Style.FILL);
path.moveTo( half * 0.5f, half * 0.84f);
path.lineTo( half * 1.5f, half * 0.84f);
path.lineTo( half * 0.68f, half * 1.45f);
path.lineTo( half * 1.0f, half * 0.5f);
path.lineTo( half * 1.32f, half * 1.45f);
path.lineTo( half * 0.5f, half * 0.84f);
}
else
{
min = Math.min(W, H);
half = min/2;
path.reset();
paint.setStyle(Paint.Style.FILL);
path.moveTo( half * 0.1f , half * 0.65f);
path.lineTo( half * 1.9f , half * 0.65f);
path.lineTo( half * 0.40f , half * 1.65f);
path.lineTo( half , 0 );
path.lineTo( half * 1.60f, half * 1.65f);
path.lineTo( half * 0.1f, half * 0.65f);
}
canvas.drawPath(path, paint);
return output;
}
2)
public static Bitmap drawStar(int W,int H,int spikes,int innerRadius,int outerRadius, int backColor,boolean border, int borderColor)
{
if(W < 10)
W = 10;
if(H < 10)
H = 10;
if(spikes < 5)
spikes = 5;
int smallL = W;
if(H < W)
smallL = H;
if(outerRadius > smallL/2)
outerRadius = smallL/2;
if(innerRadius < 5)
innerRadius = 5;
if(border)
{
outerRadius -=2;
innerRadius -=2;
}
Path path = new Path();
Bitmap output = Bitmap.createBitmap(W, H, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(backColor);
int cx = W/2;
int cy = H/2;
double rot = Math.PI / 2 * 3;
float x,y;
double step = Math.PI / spikes;
path.moveTo(cx, cy - outerRadius);
for (int i = 0; i < spikes; i++)
{
x = (float) (cx + Math.cos(rot) * outerRadius);
y = (float) (cy + Math.sin(rot) * outerRadius);
path.lineTo(x, y);
rot += step;
x = (float) (cx + Math.cos(rot) * innerRadius);
y = (float) (cy + Math.sin(rot) * innerRadius);
path.lineTo(x, y);
rot += step;
}
path.lineTo(cx, cy - outerRadius);
path.close();
canvas.drawPath(path, paint);
if(border)
{
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(borderColor);
canvas.drawPath(path, paint);
}
return output;
}

How to rotate a polygon in given angle

I am creating a compass, which is drawn on a JPanel. I translated the center of the coordinate system to the center of the JPanel. Now I want to rotate the needle in this point by a given angle, which comes from another part of the program. I tried the AffineTransform, but I did not understand how to get it right. When I tried, the needle was painted outside the circle.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TestFrame extends JFrame {
public TestFrame() {
initComponents();
}
public void initComponents() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocation(new Point((int) (Toolkit.getDefaultToolkit().getScreenSize().width / 2) - 400, (int) (Toolkit
.getDefaultToolkit().getScreenSize().height / 2) - 250));
setSize(500, 500);
setVisible(true);
CompassPanel c = new CompassPanel();
add(c, BorderLayout.CENTER);
}
public class CompassPanel extends JPanel {
private int circleX, circleY, circleRadius;
private int[] xPoints, yPoints;
public CompassPanel() {
setVisible(true);
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
AffineTransform a = new AffineTransform();
a.translate(getWidth() / 2.0, getHeight() / 2.0);
g2d.transform(a);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
circleRadius = (int) (getWidth() * 0.7);
circleX = (int) (-circleRadius * 0.5);
circleY = (int) (-circleRadius * 0.5);
g2d.setColor(Color.BLACK);
for (int angle = -45; angle <= 315; angle += 5) {
double sin = Math.sin(Math.toRadians(angle));
double cos = Math.cos(Math.toRadians(angle));
int x1 = (int) ((circleX + circleRadius / 2) - cos * (circleRadius * 0.37) - sin
* (circleRadius * 0.37));
int y1 = (int) ((circleY + circleRadius / 2) + sin * (circleRadius * 0.37) - cos
* (circleRadius * 0.37));
g.setColor(Color.BLACK);
g.drawLine(x1, y1, (circleX + circleRadius / 2), (circleY + circleRadius / 2));
}
g2d.setFont(new Font("Arial", Font.BOLD, 11));
g2d.drawString("WEST", circleX - 40, circleY + circleRadius / 2 + 4);
g2d.drawString("EAST", circleX + circleRadius + 13, circleY + circleRadius / 2 + 4);
g2d.drawString("NORTH", circleX + circleRadius / 2 - 14, circleY - 15);
g2d.drawString("SOUTH", circleX + circleRadius / 2 - 14, circleY + circleRadius + 25);
g2d.setColor(Color.WHITE);
g2d.fillOval(circleX, circleY, circleRadius, circleRadius);
g2d.setColor(Color.BLACK);
g2d.drawOval(circleX, circleY, circleRadius, circleRadius);
xPoints = new int[] { (int) (circleX + circleRadius / 2), (int) (circleX + circleRadius * 0.25),
(int) (circleX + circleRadius / 2), (int) (circleX + circleRadius * 0.75) };
yPoints = new int[] { (int) (circleY + 30), (int) (circleY + circleRadius * 0.85),
(int) (circleY + circleRadius * 0.6), (int) (circleY + circleRadius * 0.85) };
g2d.setColor(Color.RED);
g2d.fillPolygon(xPoints, yPoints, 4);
g2d.setColor(Color.black);
g2d.drawPolygon(xPoints, yPoints, 4);
g2d.setColor(Color.GREEN);
g2d.fillOval(-3, -3, 6, 6);
}
}
public static void main(String[] args) {
new TestFrame();
}
}
I made some modification in your code and added some functions for rotation but not fully working condition and are just to give you a hint of that how it may work.
in the code try to change angle from 0 to 30 it will work but not perfectly Good luck :)
package com.src;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.vecmath.Matrix3d;
public class TestFrame extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
public TestFrame() {
initComponents();
}
public void initComponents() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocation(new Point((int) (Toolkit.getDefaultToolkit().getScreenSize().width / 2) - 400, (int) (Toolkit.getDefaultToolkit().getScreenSize().height / 2) - 250));
setSize(500, 500);
setVisible(true);
CompassPanel c = new CompassPanel();
add(c, BorderLayout.CENTER);
}
public class CompassPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private int circleX, circleY, circleRadius;
private int[] xPoints, yPoints;
public CompassPanel() {
setVisible(true);
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
AffineTransform a = new AffineTransform();
a.translate(getWidth() / 2.0, getHeight() / 2.0);
g2d.transform(a);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
circleRadius = (int) (getWidth() * 0.7);
circleX = (int) (-circleRadius * 0.5);
circleY = (int) (-circleRadius * 0.5);
g2d.setColor(Color.BLACK);
for (int angle = -45; angle <= 315; angle += 5) {
double sin = Math.sin(Math.toRadians(angle));
double cos = Math.cos(Math.toRadians(angle));
int x1 = (int) ((circleX + circleRadius / 2) - cos * (circleRadius * 0.37) - sin * (circleRadius * 0.37));
int y1 = (int) ((circleY + circleRadius / 2) + sin * (circleRadius * 0.37) - cos * (circleRadius * 0.37));
g.setColor(Color.BLACK);
g.drawLine(x1, y1, (circleX + circleRadius / 2), (circleY + circleRadius / 2));
}
g2d.setFont(new Font("Arial", Font.BOLD, 11));
g2d.drawString("WEST", circleX - 40, circleY + circleRadius / 2 + 4);
g2d.drawString("EAST", circleX + circleRadius + 13, circleY + circleRadius / 2 + 4);
g2d.drawString("NORTH", circleX + circleRadius / 2 - 14, circleY - 15);
g2d.drawString("SOUTH", circleX + circleRadius / 2 - 14, circleY + circleRadius + 25);
g2d.setColor(Color.WHITE);
g2d.fillOval(circleX, circleY, circleRadius, circleRadius);
g2d.setColor(Color.BLACK);
g2d.drawOval(circleX, circleY, circleRadius, circleRadius);
xPoints = new int[] { (int) (circleX + circleRadius / 2), (int) (circleX + circleRadius * 0.25), (int) (circleX + circleRadius / 2), (int) (circleX + circleRadius * 0.75) };
yPoints = new int[] { (int) (circleY + 30), (int) (circleY + circleRadius * 0.85), (int) (circleY + circleRadius * 0.6), (int) (circleY + circleRadius * 0.85) };
int angle =30;
xPoints = rotateXPoints(xPoints, yPoints, angle);
yPoints = rotateYPoints(xPoints, yPoints, angle);
g2d.setColor(Color.RED);
g2d.fillPolygon(xPoints, yPoints, 4);
g2d.setColor(Color.black);
Polygon p = new Polygon(xPoints, yPoints, 4);
// g2d.drawPolygon(xPoints, yPoints, 4);
g2d.draw(p);
g2d.setColor(Color.GREEN);
g2d.fillOval(-3, -3, 6, 6);
}
public int[] rotateXPoints(int[] xPoints,int[] yPoints,int angle) {
for (int i = 0; i < xPoints.length; i++) {
Point p = new Point(xPoints[i], yPoints[i]);
p = rotatePoint(p, new Point(0, 0), angle);
xPoints[i] = p.x;
}
return xPoints;
}
public int[] rotateYPoints(int[] xPoints,int[] yPoints,int angle) {
for (int i = 0; i < yPoints.length; i++) {
Point p = new Point(xPoints[i], yPoints[i]);
p = rotatePoint(p, new Point(0, 0), angle);
yPoints[i] = p.y;
}
return yPoints;
}
public Point rotatePoint(Point pt, Point center, double angle)
{
angle = ((angle/180)*Math.PI);
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 static void main(String[] args) {
new TestFrame();
}
}

Transparent circular dialog in Java

I'm building a Poker Odds Calc app in Java. I want to select a new card by clicking the card's placeholder which is basically an extended JPanel that I "draw" the card's face and has a mouseListener.
What I have imagined to do is that when I clicked the card, I would like a round menu to pop up around the mouse cursor having a circle in the middle cut in four with each suite in a quarter and a ring around it cut in thirteen for the value of the card. Then I will select suit and value and it would disappear. Do you know any way I could do this? I researched a bit and I think it can be done with JavaFX by making a transparent JDialog but I'm not sure.
Is there a way to draw a totally custom shaped JComponent like a JButton shaped for each quarter of the circle etc.? I have some experience in Java but not GUI building.
Thanks in advance for your time.
edit: Used your comment and have answered my question about the circular dialog (don't know if it's the best way to do it but works for now). Now, is there anyway I know in which area the click belongs (if the click was on a useful area) without hardcoding the coordinates?
I would suggest doing custom graphics rather than trying to customize JButton and so on. When you click on the JPanel you can draw the circle and so on using the java.awt.Shape interfaces and its various implementations such as java.awt.geom.Ellipse2D.
These shapes come with contains() method that can tell you if a point is in the Shape or not. This way, when the user next clicks on the JPanel, you can determine which shape the user clicked on by going through all the shapes and checking.
The code to create the graphics is this in case anyone needs it:
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D.Double;
import javax.swing.JDialog;
/**
*
* #author Dimitris Klimis <dnklimis at gmail.com>
*/
public class CardChooser extends JDialog implements MouseListener {
int sizeX = 140;
int sizeY = sizeX; //in case I don't want it to be circle
int x, y;
Point point;
public CardChooser(Point point) {
x = point.x;
y = point.y;
this.point = point;
this.initComponents();
}
public static int[] getCard(Point point) {
int[] output = {0, 0};
CardChooser chooser = new CardChooser(point);
return output;
}
#Override
public void paint(Graphics g) {
if (g instanceof Graphics2D) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//Drawing the transparent dialog
g2.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.0f));
g2.fillRect(0, 0, getWidth(), getHeight());
//Drawing the circles
g2.setColor(Color.BLACK);
drawCircle(g2, 100, new GradientPaint(0.0f, 0.0f, Color.darkGray, (float) getWidth(), (float) getHeight(), Color.lightGray, false));
drawLines(g2, 13, 100);
int smallCircle = 38;
drawCircle(g2, smallCircle + 3, Color.GRAY);
drawCircle(g2, smallCircle, new GradientPaint((float) (getWidth() * 0.25), (float) (getHeight() * 0.25), Color.lightGray, (float) (getWidth() * 0.75), (float) (getHeight() * 0.75), Color.darkGray, false));
drawLines(g2, 4, smallCircle);
drawCircle(g2, 10, Color.LIGHT_GRAY);
drawSuiteLetters(g2);
drawCardValues(g2);
drawClosingX(g2);
} else {
super.paint(g);
}
}
private void drawCircle(Graphics2D g2, int percentage, Paint fill) {
double perc = (double) percentage / 100.0;
Ellipse2D ellipse = new Ellipse2D.Double(((1 - perc) / 2) * sizeX, ((1 - perc) / 2) * sizeY, perc * sizeX, perc * sizeY);
g2.setPaint(fill);
g2.fill(ellipse);
g2.setColor(Color.BLACK);
g2.draw(ellipse);
}
private void drawLines(Graphics2D g2, int outOf, int percentage) {
double rads = Math.toRadians(360.0 / outOf);
double perc = (double) percentage / 100.0;
Double zeroAxis = new Point.Double(sizeX / 2.0, sizeY / 2.0);
for (int i = 0; i < outOf; i++) {
g2.draw(new Line2D.Double(zeroAxis.x, zeroAxis.y, zeroAxis.x + (zeroAxis.x * perc * Math.sin(rads * i)), zeroAxis.y + (zeroAxis.y * perc * Math.cos(rads * i))));
}
}
private void drawSuiteLetters(Graphics2D g2) {
Double zeroAxis = new Point.Double(sizeX / 2.0, sizeY / 2.0);
g2.setFont(new Font("Courier New", Font.BOLD, 25));
g2.drawString("\u2660", (float) zeroAxis.x - 18, (float) zeroAxis.y - 5);//spades
g2.drawString("\u2663", (float) zeroAxis.x + 3, (float) zeroAxis.y + 20);//clubs
g2.setColor(Color.RED);
g2.drawString("\u2665", (float) zeroAxis.x + 3, (float) zeroAxis.y - 3);//hearts
g2.drawString("\u2666", (float) zeroAxis.x - 18, (float) zeroAxis.y + 19);//diamonds
g2.setColor(Color.BLACK);
}
private void drawCardValues(Graphics2D g2) {
Double zeroAxis = new Point.Double((sizeX / 2.0) - 8, 21);
float xx = (float) zeroAxis.x;
float yy = (float) zeroAxis.y;
g2.setFont(new Font("Arial", Font.BOLD, 24));
String[] letters = {"A", "K", "Q", "J", "T", "9", "8", "7", "6", "5", "4", "3", "2"};
float[] xPosition = {0, 25, 46, 63, 58, 42, 15, -10, -37, -53, -58, -46, -25};
float[] yPosition = {0, 7, 23, 50, 80, 102, 115, 115, 102, 80, 50, 23, 7};
for (int i = 0; i < 13; i++) {
g2.drawString(letters[i], xx + xPosition[i], yy + yPosition[i]);
}
}
private void drawClosingX(Graphics2D g2) {
Double zeroAxis = new Point.Double(sizeX / 2.0, sizeY / 2.0);
g2.draw(new Line2D.Double(zeroAxis.x - 5, zeroAxis.y - 5, zeroAxis.x + 5, zeroAxis.y + 5));
g2.draw(new Line2D.Double(zeroAxis.x - 5, zeroAxis.y + 5, zeroAxis.x + 5, zeroAxis.y - 5));
}
private void initComponents() {
this.addMouseListener(this);
this.setBounds(x - (sizeX / 2), y - (sizeY / 2), sizeX + 1, sizeX + 1);
this.setUndecorated(true);
this.setModal(true);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
this.setVisible(true);
}
public void mouseClicked(MouseEvent e) {
this.dispose();
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
}
PS. I extended JDialog cause I couldn't get JPanel to show up...

Categories