I have the following code. While it displays the lines, rectangle, oval, and string correctly the image does not load. The image is in the correct directory, just cannot figure out why it isn't displaying...
import java.awt.*; // for Graphics, Image, and Color classes
import java.applet.Applet;
public class GraphicsDemo extends Applet
{
public void paint (Graphics g)
{
Image image;
image = this.getImage(getDocumentBase (), "flower.jpg");
// display smaller complete image in upper left corner of window
g.drawImage(image, 0, 0, 427, 284, // destination topL, botR
0, 0, 640, 427, this); // source topL, botR
// establish color of all lines to be drawn
g.setColor(Color.BLUE);
// draw rectangle around region to be expanded
g.drawRect(200, 60, 120, 120); // topL, width & height
// draw lines between corners of rectangles
g.drawLine(200, 60, 240, 240); // upper left
g.drawLine(320, 60, 600, 240); // upper right
g.drawLine(200, 180, 240, 600); // lower left
g.drawLine(320, 180, 600, 600); // lower right
// display expanded part of original image
g.drawImage(image, 240, 240, 600, 600, // destination topL, botR
300, 90, 480, 270, this); // source topL, botR
// draw rectangle around expanded part of image
g.drawRect(240, 240, 360, 360); // topL, width & height
// create BLUE colored oval and write name on it
g.fillOval(520, 380, 45, 30); // topL, width & height
g.setColor(Color.WHITE); // change color for text
g.drawString("Max", 530, 400); // string & start position
} // end main
} // end class GraphicsDemo
More than likely the image does not exist in the document base. The rest of the code is functionally ok. Add the an init method as follows:
public void init() {
System.out.println(getDocumentBase());
}
and copy the image there to the location displayed.
Some related notes:
You're missing this statement from paint
super.paint(g);
Also would suggest moving this statement
image = this.getImage(getDocumentBase (), "flower.jpg");
to the applet's init method so that the image is not being loaded every time the applet is painted.
Related
I am trying to render basic stuff on a canvas with its GraphicsContext. I set an opaque color for strokes, but the result on screen is slightly transparent.
public class Spielwiese extends Application {
public static void main(String... args) {
launch(args);
}
#Override
public void start(Stage window) {
Canvas canvas = new Canvas(800, 600);
window.setScene(new Scene(new Pane(canvas)));
window.show();
GraphicsContext g = canvas.getGraphicsContext2D();
g.setFill(Color.BLACK);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
g.setStroke(Color.WHITE);
g.strokeRect(16, 16, 64, 64);
}
}
The result:
I tried setting the transparency of the color myself using new Color(1, 1, 1, 1), but it had the same effect.
However, I managed to get an opaque rectangle by calling the g.strokeRect(16, 16, 64, 64); multiple times to stroke over it more than once, but I don't like that "solution".
Is there a way to stroke an opaque shape onto a canvas without stroking over it multiple times?
EDIT: When I copy the statement g.strokeRect(16, 16, 64, 64); and put 4 of those at the end of the start method instead of one, I get an opaque rectangle:
You should add g.setStrokeWidth(2.0); or similar to your code. If you are drawing very thin lines they appear semi-transparent when they are not exactly aligned with the pixel boundaries.
See for more details: https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/shape/Shape.html
I'm currently working on a project with the theme of earth hour, and we are only allowed to use rectangles, circles and triangles. Here's the image i'm tring to create (not exactly, mine will be much more simplified!):
https://www.google.com/search?q=earth+hour&biw=1366&bih=586&source=lnms&tbm=isch&sa=X&ved=0ahUKEwj__5H0vtvQAhXLrlQKHTi8BagQ_AUIBygC#imgrc=fQkBxn0a8LnwbM%3A
(not sure if you could see the link)
But when i'm coding it, i'm running into trouble to rotate those rectangles to stand on the tangent line of the circle. I'm a student just learnt some basics of java, like loops and arrays. So my quesiton is that if there's some understandable way that doesn't involve some complex and exotic methods that could rotate those rectangles? I know it will probably involve some complicated solutions that is beyond my knowledge. But any help is much appreciated.
this is part of the code that i build the building standing perpendicularly to the circle(earth):
// create mid buildings
Color blc = new Color(0, 0, 0);
Rectangle midBld = new Rectangle(240, 220, 20, 40, blc);
midBld.draw(g);
Rectangle midBld1 = new Rectangle(242, 190, 16, 30, blc);
midBld1.draw(g);
Triangle midBld2 = new Triangle(250, 160, 8, 30, blc);
midBld2.draw(g);
Triangle midBld3 = new Triangle(250, 160, -8, 30, blc);
midBld3.draw(g);
A Rectangle cannot be rotated, its edges are always in parallel to the axis. But you can rotate and translate the coordinate system in witch you draw the shapes. From Graphics2D API doc.
All coordinates passed to a Graphics2D object are specified in a device-independent coordinate system called User Space, which is used by applications. The Graphics2D object contains an AffineTransform object as part of its rendering state that defines how to convert coordinates from user space to device-dependent coordinates in Device Space.
Graphics2D also provide two methods that are useful in this task: translate that moves the origin of the coordinates and rotate that, well, rotates the system.
package graphics;
import javax.swing.*;
import java.awt.*;
/**
* Earth Hour
*/
public class RotateRect extends JFrame {
private static final int WIDTH = 400;
private static final int HEIGHT = 400;
public RotateRect() {
this.setSize(WIDTH, HEIGHT);
this.setTitle("Rotate Rectangles");
this.setContentPane(new JPanel() {
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
// Background: White
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, this.getWidth(), this.getHeight());
// Draw "Earth": Center(200, 400), Radius=200
g2.setColor(Color.BLACK);
g2.fillOval(0, 200, 400, 400);
// Move origin to center of the canvas (surface of earth)
g2.translate(200, 200);
// Rotate the coordinate system, relative to the center of earth.
// note x, y are in the translated system
// Transforms are accumulative
g2.rotate(-Math.PI/6, 0, 200);
// Fill a rectangle with top-left corner at (-20, 80) in the rotated system
// It's important to make the rectangle symmetrical to the y-axis, otherwise the building looks
// funny.
// Also, make the building "sunk" a little, so that it's fully on the ground.
g2.fillRect(-20, -80, 40, 100);
g2.rotate(Math.PI/3, 0, 200);
g2.fillRect(-20, -80, 40, 100);
g2.rotate(-Math.PI/6, 0, 200);
g2.fill(new Rectangle(-20, -80, 40, 100));
}
});
}
public static void main(String [] args) {
RotateRect rr = new RotateRect();
rr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
EventQueue.invokeLater(()->rr.setVisible(true));
}
}
It seems that stroking on sub-pixel coordinates became broken in Java 8.
I have three sets of cases, shown on screenshots (columns represent cases, rows represent different stroke widths) :
Java 7u51 (400% scale)
Java 8u60 (400% scale)
Fill and stroke on the same coordinates. Works as intended, stroked area is larger than the filling area.
Stroking is shrunk (by stroke width) and centered (by half of the width) to be inside bounds of the filling region. This part is broken in Java 8 for 1px stroke, where painting occurs on a sub-pixel coordinate (first row); 3px stroke doesn't have this problem (third row). It seems that 0.5 is rounded up for the 1px stroke.
Filling rectangle is shrunk an centered the same way of case 2. I need this on graphics, which support sub-pixel drawing, to make non-overlapping fill when cells are overlapping. Here you can see that fill operation rounds down 0.5 to 0, so it's only stroking problem.
The code is below:
import static java.awt.BasicStroke.*;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class TestCase
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Test case");
frame.setSize(115, 115);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new TestPanel());
frame.setVisible(true);
}
private static class TestPanel extends JPanel
{
TestPanel()
{
setOpaque(true);
}
#Override
protected void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
g2.fill(getBounds());
Rectangle2D rect = new Rectangle2D.Double();
Color background = new Color(0, 255, 255);
Color border = new Color(255, 0, 0, 128);
Stroke STROKE_1PX = new BasicStroke(1, CAP_SQUARE, JOIN_MITER);
Stroke STROKE_2PX = new BasicStroke(2, CAP_SQUARE, JOIN_MITER);
Stroke STROKE_3PX = new BasicStroke(3, CAP_SQUARE, JOIN_MITER);
g2.translate(10, 10);
/**
* Filling and stroking by original coordinates
*/
rect.setRect(0, 0, 25, 25);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_1PX);
g2.draw(rect);
g2.translate(0, 35);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_2PX);
g2.draw(rect);
g2.translate(0, 35);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_3PX);
g2.draw(rect);
/**
* Stroking is shrunk to be inside the filling rect
*/
g2.translate(35, -70);
rect.setRect(0, 0, 25, 25);
g2.setColor(background);
g2.fill(rect);
rect.setRect(0.5, 0.5, 24, 24);
g2.setColor(border);
g2.setStroke(STROKE_1PX);
g2.draw(rect);
g2.translate(0, 35);
rect.setRect(0, 0, 25, 25);
g2.setColor(background);
g2.fill(rect);
rect.setRect(1, 1, 23, 23);
g2.setColor(border);
g2.setStroke(STROKE_2PX);
g2.draw(rect);
g2.translate(0, 35);
rect.setRect(0, 0, 25, 25);
g2.setColor(background);
g2.fill(rect);
rect.setRect(1.5, 1.5, 22, 22);
g2.setColor(border);
g2.setStroke(STROKE_3PX);
g2.draw(rect);
/**
* Filling rect is additionally shrunk and centered
*/
g2.translate(35, -70);
rect.setRect(0.5, 0.5, 24, 24);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_1PX);
g2.draw(rect);
g2.translate(0, 35);
rect.setRect(1, 1, 23, 23);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_2PX);
g2.draw(rect);
g2.translate(0, 35);
rect.setRect(1.5, 1.5, 22, 22);
g2.setColor(background);
g2.fill(rect);
g2.setColor(border);
g2.setStroke(STROKE_3PX);
g2.draw(rect);
}
}
}
As I tested, Java 7 doesn't have this problem (tried on 7u51), Windows (8u77) and Mac (8u60) too. Tried on Ubuntu (8u60 and 8u77) and Linux Mint (8u60) on different machines and the bug was here.
Did anyone faced such issue? Is there any general workaround?
I cannot simply handle 1px case in places where stroking is used. It's because there is a lot of places and I'm working with different Graphics2D implementations and it seems, that from what I've used the issue reproduces only on SunGraphics2D. This means I need to use instanceOf in these places to not break common logic.
From bug report discussion:
It is an xrender bug -Dsun.java2d.xrender=false cures it.
I haven't checked this solution myself as I didn't received any notifications from bug reporting system besides that it was reviewed. And as it was Linux-only problem it was decided to wait for official fixes as there was not so many Linux users among our customers.
I would suggest you extend Graphics2D and add correctional measures there on the places where needed.
That way you can keep your main flow consistent, and add all other "logic" errors can be handled where they need to be handled.
This saves you the overhead of modifying N files and undoing those changes if it gets fixed, and keeping the logic where it should be.
I have the code shown below which can show 2,3,4,5,6,7,8 circles around a larger circle, each equally spaced.
I know the code is written a long way, but I don't know the shorter way to code the values - how to let the code work out the coordinates for me.
My question is in two parts.
How can I group the code so that the first 2 ellipses are a group, the second three ellipses are a group and so on?
How can I cycle through the groups eg,
left mousebutton press = group moves back one
right mousebutton press = group moves forwards one
I have been reading about how to create groups of shape but can see how to do it? If I were able to do that, I think I would have a fighting chance of writing the code if left mouse button pressed, display image+1 etc.
int b, w;
void setup () {
size (600, 600); //size of the sceen
background (255, 255, 255); //colour of the screen
b = 0; //sets value of b
w = 255; //sets value of white
//frameRate (1);
fill (b);
ellipse (300, 190, 20, 20);
ellipse (300, 410, 20, 20);
// above is for 2 ellipses
/*
fill (b);
ellipse (300, 190, 20, 20);
ellipse (395.2627944, 355, 20, 20);
ellipse (204.7372056, 355, 20, 20);
//above is for 3 ellipses
fill (b);
ellipse (300, 190, 20, 20);
ellipse (410, 300, 20, 20);
ellipse (300, 410, 20, 20);
ellipse (190, 300, 20, 20);
//above is for 4 ellipses
fill (b);
ellipse (300, 190, 20, 20);
ellipse (404, 266, 20, 20);
ellipse (364, 388, 20, 20);
ellipse (235, 388, 20, 20);
ellipse (195, 271, 20, 20);
//above is for 5 ellipses
fill (b);
ellipse (300, 190, 20, 20);
ellipse (395, 245, 20, 20);
ellipse (395, 355, 20, 20);
ellipse (300, 410, 20, 20);
ellipse (204, 355, 20, 20);
ellipse (204, 245, 20, 20);
//above is for 6 ellipses
fill (b);
ellipse (300, 190, 20, 20);
ellipse (386, 231, 20, 20);
ellipse (407, 324, 20, 20);
ellipse (348, 399, 20, 20);
ellipse (252, 399, 20, 20);
ellipse (192, 324, 20, 20);
ellipse (214, 231, 20, 20);
//above is for 7 ellipses
fill (b);
ellipse (300, 190, 20, 20);
ellipse (378, 222, 20, 20);
ellipse (410, 300, 20, 20);
ellipse (378, 378, 20, 20);
ellipse (300, 410, 20, 20);
ellipse (222, 378, 20, 20);
ellipse (190, 300, 20, 20);
ellipse (222, 222, 20, 20);
//above is for 8 ellipses
*/
}
void draw() {
//fill (w);
//ellipse (width/2, height/2, 200, 200); // the guide circle. Not needed in final code.
}
haven't I already created each circle as a shape? (I'm a struggling novice here!).
I have wondered if I'd be able to do it this way instead.
Can I create a variable for each ellipse. Then call each ellipse (or several) at a key press.
Something like
int c1, c2, c3, c4, c5, c6;
void setup() {
size (600,600);
background (255,255,255);
c1 = ellipse (300, 190, 20, 20);
c2 = ellipse (300, 410, 20, 20);
//etc
}
void draw() {
if '+' pressed, show c1, c2
if '+' pressed again, show c1, c3, c4
if '+' pressed again, show c1, c2, c5, c6
}
First step is not to reinvent the wheel where you can.
Java Swing has a geometry API, take a look at 2D Graphics and Working with Geometry for more details.
You can create a group of shapes by either using a Map or List or combination of both, depending on your needs
List<Shape> group = new ArrayList<Shape>(25);
group.add(new Ellipse2D.Float(300, 190, 20, 20));
group.add(new Ellipse2D.Float(300, 410, 20, 20));
You could then group the groups using a Map or another List, but that will come down to your needs...
Now, the benefit of this is you get a lot of extra functionality, for example, you and transform the shape, changing it's location or rotation, should you want to, but you also get some additional functionality to determine if a given point falls within the shape
By using a MouseListener, you can capture the mouseClicked event and loop through your collections...
public void mousePressed(MouseEvent evt) {
for (Shape shape : group) {
if (group.contains(evt.getPoint())) {
// The shape was clicked...
}
}
}
The shape API is also supported by the Graphics2D API, allowing you to draw and/or fill the shapes simply by passing the reference of the shape it.
(this is java) I have an oval, representing a unit. I want the colour of the oval to represent the unit's health. So a perfectly healthy unit will be all green. and with the unit's health decreasing the oval starts filling with red from the bottom. so, on 50% health the oval would be red in bottom half and green in the top half, and fully red when the unit's dead.
I'm sure the solution here must be obvious and trivial , but I just can't see it.
thanks a lot
You can draw a red oval in the background, then draw a green intersection of an oval and a rectangle, where the rectangle starts below the oval, then moves further to the top to reveal more of the red oval beneath.
You might like to read up on how to construct complex shapes out of primitives here
Override the paint method something like this:
public void paint(Graphics graphics)
{
super.paint(graphics);
Rectangle originalClipBounds = graphics.getClipBounds();
try
{
graphics.clipRect(100, 100, 100, 25);
graphics.setColor(Color.RED);
graphics.fillOval(100, 100, 100, 100);
}
finally
{
graphics.setClip(originalClipBounds);
}
try
{
graphics.clipRect(100, 125, 100, 75);
graphics.setColor(Color.BLUE);
graphics.fillOval(100, 100, 100, 100);
}
finally
{
graphics.setClip(originalClipBounds);
}
}
Might want to enhance it with some double buffering but you get the gist.
You can set the clip on the graphics when you draw the green. Only things within the clip actually get painted.
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setColor(Color.RED);
g2d.fillOval(10, 10, 200, 100);
g2d.setColor(Color.GREEN);
g2d.setClip(10, 10, 200, 50);
g2d.fillOval(10, 10, 200, 100);
}