Start angle and extent values using Java's Arc2D give unexpected results - java

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);

Related

Rotating a triangle around its axis

I have got a circle and a triangle inside. I need this figure to rotate around its axis and each time it rotates on a certain angle, it should be painted, overall to get something kind of 'ornament'(on the screen below). I've been trying with Graphics2D, but it went bad. How can I do that?
Code:
import org.omg.CORBA.TIMEOUT;
import javax.swing.*;
import java.awt.*;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
public class Main extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame("Laboratorna 1");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(new Main());
frame.setSize(1200, 800);
frame.setVisible(true);
}
public void paintComponent(Graphics g){
int num_2 = 8;
int bigOval_h = 300, bigOval_w = 300;
g.setColor(Color.BLUE);
g.drawOval(0+500, 0, bigOval_h, bigOval_w);
g.drawLine(150+500, 0, 20+500, 225);
g.drawLine(150+500, 0, 280+500, 225);
g.drawLine(20+500, 225,280+500, 225);
g.setColor(Color.RED);
}
}
The trick is to perform an affine transform for each angle. In addition you have to define the pivot point for each transform which equals the center of the circle. The following modifications of the paintComponent-method should possibly do the job.
public void paintComponent(Graphics g){
int num_2 = 8;
int bigOval_h = 300, bigOval_w = 300;
g.setColor(Color.BLUE);
g.drawOval(0 + 500, 0, bigOval_h, bigOval_w);
// REMOVE -------------------------------------------
// g.drawLine(150+500, 0, 20+500, 225);
// g.drawLine(150+500, 0, 280+500, 225);
// g.drawLine(20+500, 225,280+500, 225);
// REMOVE -------------------------------------------
g.setColor(Color.RED);
// ADD -------------------------------------------------------------------
// Create, transform and draw the lines
Line2D lin1 = new Line2D.Float(150f + 500f, 0f, 20f + 500f, 225f);
Line2D lin2 = new Line2D.Float(150f + 500f, 0f, 280f + 500f, 225f);
Line2D lin3 = new Line2D.Float(20f + 500f, 225f, 280f + 500f, 225f);
double pivotX = 500.0 + bigOval_w / 2.0; // center of the circle (x)
double pivotY = 0.0 + bigOval_h / 2.0; // center of the circle (y)
for (int i = 0; i < num_2; i++) {
AffineTransform affineTransform = AffineTransform.getRotateInstance(Math.toRadians(360.0 / num_2 * i), pivotX, pivotY);
((Graphics2D)g).draw(affineTransform.createTransformedShape(lin1));
((Graphics2D)g).draw(affineTransform.createTransformedShape(lin2));
((Graphics2D)g).draw(affineTransform.createTransformedShape(lin3));
}
// ADD -------------------------------------------------------------------
}
The output is:
One way to define a polygon is radius, rotation, and number of sides. A triangle is a 3 sided polygon. So a triangle class can simply save radius and rotation and then compute the three vertices of the polygon for drawing around a center point.
public class Triangle {
private final int radius;
private final double rotation;
public Triangle(int radius, double rotation) {
this.radius = radius;
this.rotation = rotation;
}
public void paintComponent(Graphics2D g, Point center) {
int[] xVertices = new int[3];
int[] yVertices = new int[3];
xVertices[0] = (int) (center.getX() - (Math.cos(rotation) * radius));
yVertices[0] = (int) (center.getY() - (Math.sin(rotation) * radius));
xVertices[1] = (int) (center.getX() - (Math.cos(Math.PI * 0.66667 + rotation) * radius));
yVertices[1] = (int) (center.getY() - (Math.sin(Math.PI * 0.66667 + rotation) * radius));
xVertices[2] = (int) (center.getX() - (Math.cos(Math.PI * 1.33333 + rotation) * radius));
yVertices[2] = (int) (center.getY() - (Math.sin(Math.PI * 1.33333 + rotation) * radius));
Polygon polygon = new Polygon(xVertices, yVertices, 3);
g.setColor(Color.RED);
g.drawPolygon(polygon);
}
}
This would give two triangles rotated PI radians.
Triangle t = new Triangle(100, 0.0);
t.paintComponent((Graphics2D)g, new Point(250,250));
Triangle t2 = new Triangle(100, Math.PI);
t2.paintComponent((Graphics2D)g, new Point(250,250));

Translate, Scale, Rotate Yin Yang Symbol

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);

Displaying fan blades in Java

I do not understand the significance of multiplying by 0.4 in this line:
int radius = (int)(Math.min(getWidth(), getHeight()) * 0.4)
in the code below. how are they calculating the radius beneath
//Displaying fan blades in Java
package graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
#SuppressWarnings("serial")
class DrawFan extends JFrame {
public DrawFan() {
setTitle("The Four Fan Blades");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
add(new BladesPanel());
}
class BladesPanel extends JPanel {
protected void paintComponent(Graphics g){
super.paintComponent(g);
int xCenter = getWidth() / 2;
int yCenter = getHeight() / 2;
int radius = (int)(Math.min(getWidth(), getHeight()) * 0.4);
int x = xCenter - radius;
int y = yCenter - radius;
g.fillArc(x, y, 2 * radius, 2 * radius, 0, 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 90, 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 180, 30);
g.fillArc(x, y, 2 * radius, 2 * radius, 270, 30);
}
}
public static void main(String[] args) {
DrawArcs2 fanBlades = new DrawArcs2();
fanBlades.setSize(300,300);
fanBlades.setVisible(true);
}
}
In this line:
int radius = (int)(Math.min(getWidth(), getHeight()) * 0.4);
They first get what number is bigger in your JPanel: the width or the height with this part (Math.min(getWidth(), getHeight())
Imagine height is 400 and width 300. 400 is going to be selected. Now, you want to paint a circle inside this 400-wide rectangle. To fit your circle you need to make it at least 1/2 400 points. That's the same as 400*0.5. To fit better the circle inside, they choose to multiply by 0.4 (or 40% the size of the taller width/height)

Line crosses Rectangle - how to find the cross points?

I'm drawing a rectangle onto a canvas and a line from the center of the rectangle to some random point in the coordinate space.
Now, I want to truncate the line by the length that is inside the rectangle so that the line starts at the rectangle edge.
How could I do this?
Example
The rectangle could be defined by 2 points: Pstart(1, 3), Pend(3,
1)
The center point can be computed to : P(2, 2)
Now draw the line from P(2, 2) to Q(10, 2).
As I know the width of the rectangle is 2, I could tell the line to start at P(4, 2) instead of P(2, 2).
This gets more complicated when the point is not parallel to one of the XY axis. Moreover the length inside the rectangle will be different amount for diagonal lines.
How can I calculate the start offset for the line point with respect to the center of the rectangle and the end point of the line?
Probably I'd have to find the point where the line would cross the rectangle, and then just let the line start at the cross point. But how could I get this point?
Honestly, I don't understand the maths, but...
Essentially, you have 5 lines. The original line and the 4 lines of the rectangle. So if you break it down to a simple line intersection of line problem it should become a little easier...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class IntersectPoint {
public static void main(String[] args) {
new IntersectPoint();
}
public IntersectPoint() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = (int) (getWidth() * 0.2f);
int y = (int) (getHeight() * 0.2f);
int width = (int) (getWidth() * 0.6f);
int height = (int) (getHeight() * 0.6f);
int x1 = x;
int y1 = 0;
int x2 = x + width;
int y2 = getHeight();
Line2D line = new Line2D.Double(x1, y1, x2, y2);
Rectangle2D rect = new Rectangle2D.Double(x, y, width, height);
Graphics2D g2d = (Graphics2D) g.create();
g2d.draw(rect);
g2d.draw(line);
g2d.setColor(Color.RED);
Point2D[] ps = getIntersectionPoint(line, rect);
for (Point2D p : ps) {
if (p != null) {
g2d.fill(new Ellipse2D.Double(p.getX() - 4, p.getY() - 4, 8, 8));
}
}
g2d.dispose();
}
public Point2D[] getIntersectionPoint(Line2D line, Rectangle2D rectangle) {
Point2D[] p = new Point2D[4];
// Top line
p[0] = getIntersectionPoint(line,
new Line2D.Double(
rectangle.getX(),
rectangle.getY(),
rectangle.getX() + rectangle.getWidth(),
rectangle.getY()));
// Bottom line
p[1] = getIntersectionPoint(line,
new Line2D.Double(
rectangle.getX(),
rectangle.getY() + rectangle.getHeight(),
rectangle.getX() + rectangle.getWidth(),
rectangle.getY() + rectangle.getHeight()));
// Left side...
p[2] = getIntersectionPoint(line,
new Line2D.Double(
rectangle.getX(),
rectangle.getY(),
rectangle.getX(),
rectangle.getY() + rectangle.getHeight()));
// Right side
p[3] = getIntersectionPoint(line,
new Line2D.Double(
rectangle.getX() + rectangle.getWidth(),
rectangle.getY(),
rectangle.getX() + rectangle.getWidth(),
rectangle.getY() + rectangle.getHeight()));
return p;
}
public Point2D getIntersectionPoint(Line2D lineA, Line2D lineB) {
double x1 = lineA.getX1();
double y1 = lineA.getY1();
double x2 = lineA.getX2();
double y2 = lineA.getY2();
double x3 = lineB.getX1();
double y3 = lineB.getY1();
double x4 = lineB.getX2();
double y4 = lineB.getY2();
Point2D p = null;
double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d != 0) {
double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
p = new Point2D.Double(xi, yi);
}
return p;
}
}
}
Look at Liang-Barsky algorithm used for line clipping by rectangle.
Vertices of the rectangle: a, b, c, d. Represent the x and y coord of each one like ax, ay, etc.
endpoints of the line: x, y
The line follows y = mx + b, and goes either up or down, right or left. That narrows down your possible rectangle edges for crossing to 2.
Use y = mx + b to determine the vertical coordinate at which it crosses the horizontal line, and the horizontal component at which it crosses the vertical line. Either only one of these will actually be on your rectangle (i.e., contained within one of the rectangle edges), or it will intersect at a corner.

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();
}
}

Categories