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();
}
}
Related
I am trying to understand the use of Java's Arc2D. Don't know how to calculate the start angle and extent when creating an arc out of an ellipse (not a circle). I want an arc of an ellipse with ry = 2 * rx where the start angle is at 45 deg and the extent is 90 deg. But instead I have to send a start angle of ~26 deg and an extent of ~127 deg to get what I expect. The included code generates an image with 3 arcs: green is a circle arc which behaves as expected, red is an ellipse arc with the expected parameters but unexpected results and blue is an arc with unexpected parameters but the expected result. Am I misinterpreting something here? In addition, at start angle of 0 deg and extent 90 deg the results are as expected??? Thx.
package example;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Arc2D;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
public class ExampleArc2D {
public static void main(String[] args) {
try {
int width = 800;
int height = 800;
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D gfx2D = bi.createGraphics();
gfx2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gfx2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
gfx2D.setFont(new Font(Font.MONOSPACED, Font.BOLD, 16));
FontMetrics fm = gfx2D.getFontMetrics();
gfx2D.setColor(Color.BLACK);
gfx2D.fillRect(0, 0, bi.getWidth(), bi.getHeight());
gfx2D.setStroke(new BasicStroke(2f));
int x = 5;
int y = 0;
int centerX = width / 2;
int centerY = height / 2 + 200;
int circleRadius = 200;
int ellipseRadiusX = circleRadius;
int ellipseRadiusY = 2 * circleRadius;
// gfx2D.setColor(new Color(255, 255, 255, 50));
// gfx2D.draw(new Ellipse2D.Double(centerX - circleRadius, centerY - circleRadius, 2 * circleRadius, 2 * circleRadius));
// gfx2D.draw(new Ellipse2D.Double(centerX - ellipseRadiusY, centerY - ellipseRadiusY, 2 * ellipseRadiusY, 2 * ellipseRadiusY));
// gfx2D.draw(new Ellipse2D.Double(centerX - ellipseRadiusX, centerY - ellipseRadiusY, 2 * ellipseRadiusX, 2 * ellipseRadiusY));
double startAngle = 45.0;
double extent = 90.0;
Arc2D circle = new Arc2D.Double(centerX - circleRadius, centerY - circleRadius, 2 * circleRadius, 2 * circleRadius, startAngle, extent, Arc2D.PIE);
gfx2D.setColor(Color.GREEN);
gfx2D.draw(circle);
y += fm.getAscent();
gfx2D.drawString("ARC FROM CIRCLE WITH EXPECTED PARMS AND RESULT:", x, y);
y += fm.getAscent();
gfx2D.drawString(String.format("start angle %.1f° and extent %.1f°", startAngle, extent), x, y);
startAngle = 45.0;
extent = 90.0;
Arc2D unexpectedEllipse = new Arc2D.Double(centerX - ellipseRadiusX, centerY - ellipseRadiusY, 2 * ellipseRadiusX, 2 * ellipseRadiusY, startAngle, extent, Arc2D.PIE);
gfx2D.setColor(Color.RED);
gfx2D.draw(unexpectedEllipse);
y += fm.getAscent();
gfx2D.drawString("UNEXPECTED ARC FROM ELLIPSE WITH EXPECTED PARMS AND UNEXPECTED RESULT:", x, y);
y += fm.getAscent();
gfx2D.drawString(String.format("start angle %.1f° and extent %.1f°", startAngle, extent), x, y);
startAngle = 26.4;
extent = 127.2;
Arc2D expectedEllipse = new Arc2D.Double(centerX - ellipseRadiusX, centerY - ellipseRadiusY, 2 * ellipseRadiusX, 2 * ellipseRadiusY, startAngle, extent, Arc2D.PIE);
gfx2D.setColor(Color.BLUE);
gfx2D.draw(expectedEllipse);
y += fm.getAscent();
gfx2D.drawString("EXPECTED ARC FROM ELLIPSE WITH UNEXPECTED PARMS AND EXPECTED RESULT:", x, y);
y += fm.getAscent();
gfx2D.drawString(String.format("start angle %.1f° and extent %.1f°", startAngle, extent), x, y);
y += fm.getAscent();
gfx2D.drawString("even though it is clear that the start angle is at 45° and the extent is 90°???", x, y);
gfx2D.setColor(Color.YELLOW);
gfx2D.drawRect(centerX - ellipseRadiusX, centerY - ellipseRadiusY, ellipseRadiusX * 2, ellipseRadiusY * 2);
gfx2D.setColor(Color.ORANGE);
gfx2D.drawLine(centerX, centerY, centerX + ellipseRadiusX, centerY - ellipseRadiusY);
x = centerX + ellipseRadiusX + 5;
y = centerY - ellipseRadiusY;
gfx2D.drawString("this is 45° as far", x, y);
y += fm.getAscent();
gfx2D.drawString("as the ellipse is", x, y);
y += fm.getAscent();
gfx2D.drawString("concerned!", x, y);
gfx2D.dispose();
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
// ImageIO.write(bi, "png", new File("examplearc2d.png"));
}
catch(Throwable t) {
t.printStackTrace();
}
}
}
UPDATE: Found a way to get the angles but it was too clunky so took another approach. I used a clipping area to solve the problem. I solved my problem but didn't really answer the question. Won't pursue it any further. So basically I draw the ellipse but apply a clipping area so only the arc is drawn.
package example;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ExampleArc2D3
extends JFrame {
private Canvas canvas;
private JSlider ryrxSld;
private JSlider startAngleSld;
private JSlider extentSld;
private JSlider angleSld;
public ExampleArc2D3() {
super("Example Arc2D");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container cp = getContentPane();
cp.add(canvas = new Canvas(), BorderLayout.CENTER);
JPanel southPanel = new JPanel(new BorderLayout());
southPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
cp.add(southPanel, BorderLayout.SOUTH);
JPanel labelsPanel = new JPanel(new GridLayout(0, 1, 0, 0));
southPanel.add(labelsPanel, BorderLayout.WEST);
JPanel fieldsPanel = new JPanel(new GridLayout(0, 1, 0, 0));
southPanel.add(fieldsPanel, BorderLayout.CENTER);
labelsPanel.add(new JLabel("ry/rx: "));
fieldsPanel.add(ryrxSld = new JSlider(10, 25, 20));
labelsPanel.add(new JLabel("Start Angle: "));
fieldsPanel.add(startAngleSld = new JSlider(0, 3600, 0));
labelsPanel.add(new JLabel("Extent: "));
fieldsPanel.add(extentSld = new JSlider(0, 3600, 300));
labelsPanel.add(new JLabel("Protractor: "));
fieldsPanel.add(angleSld = new JSlider(0, 3600, 0));
ChangeHandler ch = new ChangeHandler();
ryrxSld.addChangeListener(ch);
startAngleSld.addChangeListener(ch);
extentSld.addChangeListener(ch);
angleSld.addChangeListener(ch);
setSize(1000, 1020);
setVisible(true);
}
private class Canvas
extends JComponent {
public void refresh() {
invalidate();
repaint();
}
#Override
public void paintComponent(Graphics gfx) {
super.paintComponent(gfx);
Graphics2D gfx2D = (Graphics2D) gfx;
Dimension size = getSize();
try {
int width = size.width;
int height = size.height;
gfx2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gfx2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
gfx2D.setFont(new Font(Font.MONOSPACED, Font.BOLD, 16));
FontMetrics fm = gfx2D.getFontMetrics();
gfx2D.setColor(Color.BLACK);
gfx2D.fillRect(0, 0, width, height);
double startAngle = startAngleSld.getValue() / 10.0;
double extent = extentSld.getValue() / 10.0;
int x = 5;
int y = 0;
gfx2D.setColor(Color.WHITE);
y += fm.getAscent();
gfx2D.drawString(String.format("ry/rx: %.1f", ryrxSld.getValue() / 10.0), x, y);
y += fm.getAscent();
gfx2D.drawString(String.format("start angle %.1f° and extent %.1f°", startAngle, extent), x, y);
int centerX = width / 2;
int centerY = 460;
double ellipseRadiusX = 200;
double ellipseRadiusY = ryrxSld.getValue() * ellipseRadiusX / 10.0;
gfx2D.setColor(new Color(255, 255, 255, 60));
gfx2D.setStroke(new BasicStroke(1f));
gfx2D.draw(new Ellipse2D.Double(centerX - ellipseRadiusX, centerY - ellipseRadiusX, 2 * ellipseRadiusX, 2 * ellipseRadiusX));
gfx2D.draw(new Ellipse2D.Double(centerX - ellipseRadiusY, centerY - ellipseRadiusY, 2 * ellipseRadiusY, 2 * ellipseRadiusY));
gfx2D.draw(new Ellipse2D.Double(centerX - ellipseRadiusX, centerY - ellipseRadiusY, 2 * ellipseRadiusX, 2 * ellipseRadiusY));
gfx2D.setStroke(new BasicStroke(2f));
gfx2D.setStroke(new BasicStroke(1f));
gfx2D.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 10));
fm = gfx2D.getFontMetrics();
// clip area
Shape clipArea = buildClipArea(centerX, centerY, ellipseRadiusX, ellipseRadiusY, startAngle, extent);
gfx2D.setColor(new Color(255, 0, 0, 120));
gfx2D.draw(clipArea);
Shape oldClip = gfx2D.getClip();
gfx2D.setClip(clipArea);
gfx2D.setColor(Color.BLUE);
gfx2D.setStroke(new BasicStroke(2f));
gfx2D.draw(new Ellipse2D.Double(centerX - ellipseRadiusX, centerY - ellipseRadiusY, 2 * ellipseRadiusX, 2 * ellipseRadiusY));
gfx2D.setClip(oldClip);
double angle = angleSld.getValue() / 10.0;
gfx2D.setColor(Color.CYAN);
double len = 800.0;
gfx2D.draw(new Line2D.Double(centerX, centerY, centerX + len * Math.cos(Math.toRadians(angle)), centerY - len * Math.sin(Math.toRadians(angle))));
gfx2D.drawString(String.format("%.1f°", angle), centerX + 5, centerY + fm.getAscent());
}
catch(Throwable t) {
t.printStackTrace();
}
}
private Shape buildClipArea(double centerX,
double centerY,
double ellipseRadiusX,
double ellipseRadiusY,
double startAngle,
double extent) {
double radius = Math.max(ellipseRadiusX, ellipseRadiusY);
Arc2D arc = new Arc2D.Double(centerX - radius, centerY - radius, 2 * radius, 2 * radius, 0, extent, Arc2D.PIE);
AffineTransform at = new AffineTransform();
at.rotate(Math.toRadians(-startAngle), centerX, centerY);
return at.createTransformedShape(arc);
}
}
private class ChangeHandler
implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
canvas.refresh();
}
}
public static void main(String[] args) {
Runnable runner = new Runnable() {
#Override
public void run() {
new ExampleArc2D3();
}
};
SwingUtilities.invokeLater(runner);
}
}
The API for the Arc2D class states:
The angles are specified relative to the non-square framing rectangle such that 45 degrees always falls on the line from the center of the ellipse to the upper right corner of the framing rectangle
Not really sure what that means or how that provides insight on how to calculate the appropriate angles to use to achieve your desired result
However, it can be demonstrated by adding the following code at the bottom of your class:
gfx2D.setColor( Color.YELLOW );
gfx2D.drawRect(centerX - ellipseRadiusX, centerY - ellipseRadiusY, ellipseRadiusX * 2, ellipseRadiusY * 2);
gfx2D.setColor( Color.ORANGE );
gfx2D.drawLine(centerX, centerY, centerX + ellipseRadiusX, centerY - ellipseRadiusY);
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);
}
I need help with Translating, Rotating, and Scaling a Yin-Yang Symbol in Java. Can anyone provide suggestions I'll post the code below. I'm trying to use affine transformation does anyone have a way to make sure every part follows suit when I decide to move rotate, translate, scale?
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
public class ScaleTranslationRotation extends JFrame
{
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
int xLeft,yUpper,width, height;
xLeft = this.getInsets().left;
yUpper = this.getInsets().top;
width = this.getWidth()-xLeft-this.getInsets().right;
height = this.getHeight()-this.getInsets().top-this.getInsets().bottom;
int xCenter = getWidth() / 2;
int yCenter = getHeight() / 2;
int radius = (int)(Math.min(getWidth(), getHeight()) * 0.2);
int x = xCenter - radius;
int y = yCenter - radius;
AffineTransform yUp = new AffineTransform();
g.setColor(Color.blue);
g.fillOval(x, y, 2 * radius, 2 * radius);
g.setColor(Color.cyan);
g.drawOval(x, y, 2 * radius, 2 * radius);
g.setColor(Color.cyan);
g.fillArc(x, y, 2 * radius, 2 * radius, 270, 180);
g.setColor(Color.blue);
g.fillOval(x + (radius / 2), y, radius, radius);
g.setColor(Color.cyan);
g.fillOval(x + (radius / 2), y + radius, radius, radius);
g.setColor(Color.cyan);
g.fillOval(x + radius - (radius / 4), y + radius / 4, radius / 2, radius / 2);
g.setColor(Color.blue);
g.fillOval(x + radius - (radius / 4), y + radius + radius / 4, radius / 2, radius / 2);
g2d.setStroke(new BasicStroke(1.0f));
g.setColor(Color.red);
drawSimpleCoordinateSystem(width,height, g2d);
grid(width, height, g2d);
}
public static void drawSimpleCoordinateSystem(int x, int y, Graphics2D g2d){
int xOffset = 30;
int yOffset = 50;
int step = 20;
String s;
//Remember the actual font.
Font fo = g2d.getFont();
//Use a small font.
g2d.setFont(new Font("sansserif",Font.PLAIN,9));
//x-axis.
g2d.drawLine(xOffset,yOffset,x,yOffset);
//Marks and labels for the x-axis.
for (int i=xOffset+step; i<=x; i=i+step)
{
g2d.drawLine(i,yOffset-2,i,yOffset+2);
g2d.drawString(String.valueOf(i),i-7,yOffset-7);
}
//y-axis.
g2d.drawLine(xOffset,yOffset,xOffset,y);
//Marks and labels for the y-axis.
s=" "; //for indention of numbers < 100
for (int i=yOffset+step; i<=y; i=i+step){
g2d.drawLine(xOffset-2,i,xOffset+2,i);
if (i>99){s="";}
g2d.drawString(s+String.valueOf(i),xOffset-25,i+5);
}
//Reset to the original font.
g2d.setFont(fo);
}
public void grid(int x, int y, Graphics2D g2d){
int xOffset = 30;
int yOffset = 50;
int step = 20;
g2d.drawLine(xOffset, yOffset, x, yOffset);
for(int i = xOffset+step; i<= x; i = i+step){
g2d.drawLine(i, yOffset, i, y);
}
g2d.drawLine(xOffset, yOffset, xOffset, y);
for(int i = yOffset+step; i<=y; i = i+step){
g2d.drawLine(xOffset, i, x, i);
}
}
public static void main(String[] args)
{
ScaleTranslationRotation f = new ScaleTranslationRotation();
f.setTitle("STR");
f.setSize(1920,1020);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Just put your code yin-yang symbol code in a method
private void drawSymbol(int x, int y, int radius, Graphics g) {
g.setColor(Color.blue);
g.fillOval(x, y, 2 * radius, 2 * radius);
g.setColor(Color.cyan);
g.drawOval(x, y, 2 * radius, 2 * radius);
g.setColor(Color.cyan);
g.fillArc(x, y, 2 * radius, 2 * radius, 270, 180);
g.setColor(Color.blue);
g.fillOval(x + (radius / 2), y, radius, radius);
g.setColor(Color.cyan);
g.fillOval(x + (radius / 2), y + radius, radius, radius);
g.setColor(Color.cyan);
g.fillOval(x + radius - (radius / 4), y + radius / 4, radius / 2, radius / 2);
g.setColor(Color.blue);
g.fillOval(x + radius - (radius / 4), y + radius + radius / 4, radius / 2, radius / 2);
}
And then use an Affine transformation before calling the method to perform the requested transformation.
In the following I draw the symbol three times with different transformation
Remember to restore the original affine transformation to the initial value:
drawSymbol(x,y,radius,g);
//save original transformation
AffineTransform oldt=((Graphics2D) g).getTransform();
//create new transformation and apply it
AffineTransform yUp = new AffineTransform();
yUp.translate(0,-100);
((Graphics2D) g).setTransform(yUp);
drawSymbol(x,y,radius,g);
//create anothernew transformation and apply it
yUp = new AffineTransform();
yUp.translate(-100,0);
((Graphics2D) g).setTransform(yUp);
drawSymbol(x,y,radius,g);
//restore old transforamtion
((Graphics2D) g).setTransform(oldt);
g2d.setStroke(new BasicStroke(1.0f));
g.setColor(Color.red);
drawSimpleCoordinateSystem(width,height, g2d);
grid(width, height, g2d);
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.
Can anyone guide me how to code the arrow line in different direction.
wa and wl is positive the rectangle will be on top of the x-axis. Below example shown if wl is negative and wa is positive. The code below shown how i code the rectangle shape. x1 is the varaible to state where to start at x axis. e1 is the length of the shape, wa1 and wl1 is the height. wsign to determine the height wa1 or wl1 should display at negative side or positive side.
if (Math.abs(wl1) > Math.abs(wa1)) {
y_scale = (load_y0 - 40) / (double) Math.abs(wl1);
} else {
y_scale = (load_y0 - 40) / (double) Math.abs(wa1);
}
g.drawLine((int) ((double) x0 + x1 * x_scale), (int) (load_y),
(int) ((double) x0 + x1 * x_scale),
(int) (load_y + (wa1 * y_scale) * -1));
g.drawLine((int) ((double) x0 + (x1 + e1) * x_scale),
(int) (load_y), (int) ((double) x0 + (x1 + e1)
* x_scale), (int) (load_y + (wl1 * y_scale)
* -1));
g.drawLine((int) ((double) x0 + x1 * x_scale),
(int) (load_y + (wa1 * y_scale * -1)),
(int) ((double) x0 + (x1 + e1) * x_scale),
(int) (load_y + (wl1 * y_scale) * -1));
Here is a simple routine (adopted from here) for drawing arbitrary arrows:
import static java.awt.geom.AffineTransform.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;
public class Main {
public static void main(String args[]) {
JFrame t = new JFrame();
t.add(new JComponent() {
private final int ARR_SIZE = 4;
void drawArrow(Graphics g1, int x1, int y1, int x2, int y2) {
Graphics2D g = (Graphics2D) g1.create();
double dx = x2 - x1, dy = y2 - y1;
double angle = Math.atan2(dy, dx);
int len = (int) Math.sqrt(dx*dx + dy*dy);
AffineTransform at = AffineTransform.getTranslateInstance(x1, y1);
at.concatenate(AffineTransform.getRotateInstance(angle));
g.transform(at);
// 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);
}
public void paintComponent(Graphics g) {
for (int x = 15; x < 200; x += 16)
drawArrow(g, x, x, x, 150);
drawArrow(g, 30, 300, 300, 190);
}
});
t.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
t.setSize(400, 400);
t.setVisible(true);
}
}
Result:
Simple arrow sample
/**
* #param fromPoint end of the arrow
* #param rotationDeg rotation angle of line
* #param length arrow length
* #param wingsAngleDeg wingspan of arrow
* #return Path2D arrow shape
*/
public static Path2D createArrowForLine(
Point2D fromPoint,
double rotationDeg,
double length,
double wingsAngleDeg) {
double ax = fromPoint.getX();
double ay = fromPoint.getY();
double radB = Math.toRadians(-rotationDeg + wingsAngleDeg);
double radC = Math.toRadians(-rotationDeg - wingsAngleDeg);
Path2D resultPath = new Path2D.Double();
resultPath.moveTo(length * Math.cos(radB) + ax, length * Math.sin(radB) + ay);
resultPath.lineTo(ax, ay);
resultPath.lineTo(length * Math.cos(radC) + ax, length * Math.sin(radC) + ay);
return resultPath;
}