This question has been asked before, but the answers don't solve the problem, so I ask it again.
It was suggested that instead of using g2.drawLine, you could use g2.draw(line), where line is a Line2D.Double. However, as you can see from the screenshot, the lines are still drawn as if they ended on an integer pixel (each set of 10 lines is exactly parallel).
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Line2D;
public class FrameTestBase extends JFrame {
public static void main(String args[]) {
FrameTestBase t = new FrameTestBase();
t.add(new JComponent() {
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (int i = 0; i < 50; i++) {
double delta = i / 10.0;
double y = 5 + 5*i;
Shape l = new Line2D.Double(5, y, 200, y + delta);
g2.draw(l);
// Base case, with ints, looks exactly the same:
// g2.drawLine(5, (int)y, 200, (int)(y + delta));
}
}
});
t.setDefaultCloseOperation(EXIT_ON_CLOSE);
t.setSize(220, 300);
t.setVisible(true);
}
}
So is it impossible in Swing to correctly render lines that don't end exactly on a pixel?
Try setting the following:
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Taken from this answer:
Java - Does subpixel line accuracy require an AffineTransform?
Here is the result for comparison:
Related
I'm drawing a graph with two points of each point having a line with a weight.
for example graph: point "15" to point "16" line with the weight of 1.872 and point "16" to point "15" with the weight of 1.567.
take a look at my graph for now:
I want to draw a String with always parallel (adjacent) to the line.
I calculated the slope for the straight and the angel I did calculate is the arctan of this slope:
I had use this function to rotate the string:
public static void drawRotate(Graphics2D g2d, double x, double y, double angle, String text) {
g2d.translate((float)x,(float)y);
g2d.rotate(Math.toRadians(angle));
g2d.drawString(text,0,0);
g2d.rotate(-Math.toRadians(angle));
g2d.translate(-(float)x,-(float)y);
}
With the angle of arctan((y2-y1)/(x2-x1)= the slope of the line ) and it didn't work well.
How can I rotate this String to run parallel always to the line I draw?
My goal: Draw the String like this example
Here is a quick demo to be used as a guide on how it might be done. I omitted some things like the arrowheads since that is just busy work. And I guesstimated on the label positions. I would recommend you read about the three argument version of Graphics.rotate() and RenderingHints and anti-aliasing to smooth the lines.
You may want to write general methods to facilitate positioning the text and labels based on font size.
But I believe your primary problem was doing int division when calculating the slope.
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GraphicsExample extends JPanel {
JFrame f = new JFrame("Draw Vector");
final static int WIDTH = 500;
final static int HEIGHT = 500;
String A = "1.567 [B->A]";
String B = "1.862 [A->B]";
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> new GraphicsExample().start());
}
public void start() {
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int x1= 50;int y1 = 400;
int x2 = 400; int y2 = 200;
// copy the graphics context.
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int diameter = 20;
drawLine(g2d,x1+diameter/2, y1+diameter/2, x2+diameter/2, y2+diameter/2);
g2d.setFont(new Font("Arial", Font.PLAIN, 18));
drawEndPoint(g2d,x1,y1, diameter, "A");
drawEndPoint(g2d,x2,y2, diameter, "B");
double angle = Math.atan((double)(y1-y2)/(x1-x2));
g2d.rotate(angle,x1,y1);
// based on font, this computes the placement of the Strings
FontMetrics fm = g2d.getFontMetrics();
int width = SwingUtilities.computeStringWidth(fm, A); // use for both
g2d.setColor(Color.black);
g2d.drawString(A, x1 + ((x2-x1) - width)/2, y1);
g2d.drawString(B, x1 + ((x2-x1) - width)/2, y1+ 30);
// discard the context.
g2d.dispose();
}
public void drawEndPoint(Graphics2D g2d, int x, int y, int diameter, String label) {
g2d.setColor(Color.BLUE);
g2d.drawString(label, x, y);
g2d.fillOval(x,y,diameter, diameter);
g2d.setColor(Color.RED);
g2d.setStroke(new BasicStroke(2f));
g2d.drawOval(x,y,diameter, diameter);
}
public void drawLine(Graphics2D g2d, int x1, int y1, int x2, int y2) {
g2d.setColor(Color.RED);
g2d.setStroke(new BasicStroke(3f));
g2d.drawLine(x1,y1,x2,y2);
}
}
Shows
I tried to create a code that printed n random circles in a 500 x 500 frame but it didn't work.
Can somebody tell me why this code isn't running?
When I run this code, it lets me enter the number of random circles I want but the frame always appear to be empty - no circles are drawn.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.util.Scanner;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class RandomCircles extends JComponent
{
private int n;
public RandomCircles(int N)
{
n = N;
}
public void PaintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
double x = Math.random() * 500;
double y = Math.random() * 500;
double diameter = Math.random() * 500;
// Making sure the circle stays within the frame
for (int i = 0; i < n; i++)
{
while(x + diameter <= 500 || y + diameter <= 500)
{
Ellipse2D.Double circle
= new Ellipse2D.Double(x, y, diameter, diameter);
g2.draw(circle);
}
}
}
public static void main(String[]args)
{
Scanner in = new Scanner(System.in);
System.out.println("Enter number of circles here: ");
int n = in.nextInt();
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setTitle("Random Circles");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
RandomCircles circle = new RandomCircles(n);
frame.add(circle);
// Add PaintComponent method somewhere here?
frame.setVisible(true);
}
}
I have a feeling that I need to add in the public void PaintComponent(Graphics g) somewhere to print it out, but I am not sure how.
The problem is on this line:
public void PaintComponent(Graphics g)
You are attempting to override the paintComponent(Graphics) method. You need to be careful that you get the name and the parameters right. Notice you spelled your method with an upper case P.
It is advisable that you add the annotation #Override on methods that are supposed to override a super class' method. That way you get a notification if you get the signature wrong.
So your method should look like this:
#Override
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
...
}
== Edit after comments ==
The while loop is also causing an issue. Try doing this instead.
for (int i = 0; i < n; i++)
{
double x = Math.random() * 500;
double y = Math.random() * 500;
double diameter = Math.random() * 500;
Ellipse2D.Double circle
= new Ellipse2D.Double(x, y, diameter, diameter);
g2.draw(circle);
}
Notice that I moved the random number generation inside the loop. This does not guarantee the circle fitting in the frame but that is something you can modify later.
I'm trying to draw a triangle with a border using the Graphics.drawPolygon() method
The triangle is properly drawn, but how can I calculate the 3 points of the border?
I already did it with a circle, but I can't seem to find a solution for triangle.
A requirement of the instructor as that it cannot use Graphics2D.
My code:
if (xPoints != null && yPoints != null) {
int[] nXPoints = new int[] { xPoints[0] - borderThickness, xPoints[1] - borderThickness,
xPoints[2] - borderThickness };
int[] nYPoints = new int[] { yPoints[0] - borderThickness, yPoints[1] - borderThickness,
yPoints[2] - borderThickness };
g.setColor(borderColor);
g.fillPolygon(nXPoints, nYPoints, 3);
g.setColor(fillColor);
g.fillPolygon(xPoints, yPoints, 3);
}
Edit:
Expected result
Use the Graphics methods drawPolygon() to render the outline and fillPolygon() to fill its interior; both have the required signature, as shown here.
Because "operations that draw the outline of a figure operate by traversing an infinitely thin path between pixels with a pixel-sized pen," cast the graphics context to Graphics2D so that you can use draw() and fill() on the corresponding Shape. This will allow you to specify the outline using setStroke(), illustrated here.
I need it to have a custom thickness…I also don't want to use Graphics2D.
Custom thickness is not supported in the Graphics API. As suggested here, the actual graphics context received by paintComponent() is an instance of Graphics2D, which does support custom stroke geometry.
The things is teacher haven't taught me Graphics2D, so I'm not supposed to use it.
Then simply paint the larger triangle and then the smaller. If this isn't working, then you have an error in you calculation of the larger triangle, and you should edit your question to include a complete example.
I'm looking for a way to do it without Graphics2D…a guy has interpreted the question properly in this comment.
As #Marco13 observes, you need a triangle that is larger than the original one by the borderThickness. You can use AffineTransform to do the scaling. While Graphics can't fill() an arbitrary Shape, such as one created by AffineTransform, it can clip the rendering as required. In the example below, a unit triangle is translated and scaled to the center of an N x N panel; a copy is enlarged by delta. Note how rendering is clipped first to the larger background figure and then to the smaller foreground figure.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/39812618/230513
*/
public class GraphicsBorder {
private static class GraphicsPanel extends JPanel {
private static final int N = 256;
private static final Color FILL = new Color(0x990099);
private static final Color BORDER = new Color(0x66FFB2);
private final Shape fore;
private final Shape back;
public GraphicsPanel(Polygon polygon, int delta) {
AffineTransform a1 = new AffineTransform();
a1.translate(N / 2, N / 2);
a1.scale(N / 3, N / 3);
fore = a1.createTransformedShape(polygon);
AffineTransform a2 = new AffineTransform();
a2.translate(N / 2, N / 2 - delta / 3);
a2.scale(N / 3 + delta, N / 3 + delta);
back = a2.createTransformedShape(polygon);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(BORDER);
g.setClip(back);
g.fillRect(0, 0, N, N);
g.setColor(FILL);
g.setClip(fore);
g.fillRect(0, 0, N, N);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(N, N);
}
}
private void display() {
JFrame f = new JFrame("GraphicsBorder");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Polygon p = new Polygon();
p.addPoint(0, -1);
p.addPoint(1, 1);
p.addPoint(-1, 1);
f.add(new GraphicsPanel(p, 16));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new GraphicsBorder()::display);
}
}
I'm currently working on a program which enables user to draw various geometric shapes. However, I got some issues on calculating and placing the angle objects onto my Canvas panel accurately. The angle object is basically an extension of the Arc2D object, which provides a additional method called computeStartAndExtent(). Inside my Angle class, this method computes and finds the necessary starting and extension angle values:
private void computeStartAndExtent()
{
double ang1 = Math.toDegrees(Math.atan2(b1.getY2() - b1.getY1(), b1.getX2() - b1.getX1()));
double ang2 = Math.toDegrees(Math.atan2(b2.getY2() - b2.getY1(), b2.getX2() - b2.getX1()));
if(ang2 < ang1)
{
start = Math.abs(180 - ang2);
extent = ang1 - ang2;
}
else
{
start = Math.abs(180 - ang1);
extent = ang2 - ang1;
}
start -= extent;
}
It is a bit buggy code that only works when I connect two lines to each other, however, when I connect a third one to make a triangle, the result is like the following,
As you see the ADB angle is the only one that is placed correctly. I couldn't figure how to overcome this. If you need some additional info/code please let me know.
EDIT: b1 and b2 are Line2D objects in computeStartAndExtent() method.
Thank you.
There are some of things that can be made to simplify the calculation:
Keep the vertices ordered, so that it is always clear how to calculate the vertex angles pointing away from the corner
Furthermore, always draw the polygon to the same direction; then you can always draw the angles to the same direction. The example below assumes the polygon is drawn clockwise. The same angle calculation would result in the arcs drawn outside given a polygon drawn counterclockwise.
Example code; is not quite the same as yours as I don't have your code, but has similar functionality:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Arc2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Polygon extends JPanel {
private static final int RADIUS = 20;
private final int[] xpoints = {
10, 150, 80, 60
};
private final int[] ypoints = {
10, 10, 150, 60
};
final Arc2D[] arcs;
Polygon() {
arcs = new Arc2D[xpoints.length];
for (int i = 0; i < arcs.length; i++) {
// Indices of previous and next corners
int prev = (i + arcs.length - 1) % arcs.length;
int next = (i + arcs.length + 1) % arcs.length;
// angles of sides, pointing outwards from the corner
double ang1 = Math.toDegrees(Math.atan2(-(ypoints[prev] - ypoints[i]), xpoints[prev] - xpoints[i]));
double ang2 = Math.toDegrees(Math.atan2(-(ypoints[next] - ypoints[i]), xpoints[next] - xpoints[i]));
int start = (int) ang1;
int extent = (int) (ang2 - ang1);
// always draw to positive direction, limit the angle <= 360
extent = (extent + 360) % 360;
arcs[i] = new Arc2D.Float(xpoints[i] - RADIUS, ypoints[i] - RADIUS, 2 * RADIUS, 2 * RADIUS, start, extent, Arc2D.OPEN);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(160, 160);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawPolygon(xpoints, ypoints, xpoints.length);
Graphics2D g2d = (Graphics2D) g;
for (Shape s : arcs) {
g2d.draw(s);
}
}
public static void main(String args[]){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Polygon");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Polygon());
frame.pack();
frame.setVisible(true);
}
});
}
}
Results in:
import javax.swing.*;
import java.awt.*;
public class JFrameAnimationTest extends JFrame {
public static void main(String[] args) throws Exception{
AnimationPanel animation = new AnimationPanel();
JFrameAnimationTest frame = new JFrameAnimationTest();
frame.setSize(600, 480);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(animation);
frame.setVisible(true);
for(int i = 0; i < 100; i++) {
animation.incX(1);
//animation.incY(1);
animation.repaint();
Thread.sleep(10);
}
}
}
class AnimationPanel extends JPanel {
int x = 10;
int y = 10;
public AnimationPanel() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawRect(x, y, 20, 20);
g.fillRect(x, y, 20, 20);
}
protected void incX(int X) {
x += X;
}
protected void incY(int Y) {
y += Y;
}
}
So anyways theres my code. It probably looks a bit jumbled as I am not used to stackoverflow just yet so I apologize.
Here's my question: This program makes this small rectangle slowly move to the right; how can I add rotation to the rectangles movement during that time period?
Note: I haven't actually compiled this code, but you get the gist.
public void paintComponent( Graphics g )
{
super.paintComponent( g );
Graphics2D g2d = (Graphics2D) g;
// The 20x20 rectangle that you want to draw
Rectangle2D rect = new Rectangle2D.Double( 0, 0, 20, 20 );
// This transform is used to modify the rectangle (an affine
// transform is a way to do operations like translations, rotations,
// scalings, etc...)
AffineTransform transform = new AffineTransform();
// 3rd operation performed: translate the rectangle to the desired
// x and y position
transform.translate( x + 10, y + 10 );
// 2nd operation performed: rotate the rectangle around the origin
transform.rotate( rotation );
// 1st operation performed: translate the rectangle such that it is
// centered on the origin
transform.translate( -10, -10 );
// Apply the affine transform
Shape s = transform.createTransformedShape( rect );
// Fill the shape with the current paint
g2d.fill( s );
// Stroke the edge of the shape with the current paint
g2d.draw( s );
}
Also note that you should really be using something like a javax.swing.Timer when you modify x, y, and rotation and when you call repaint(). That way all of it happens on the event dispatch thread.