Rotate a group of Graphics objects around a single point? - graphics

I found an image online (http://i.stack.imgur.com/y1oT4.png) and I'm trying to take the sun and sky and make them rotate around the center of the screen, such that the sun and its rays appear to be spinning.
I intend to use a timer to control the movement, but I can't figure out how to rotate by an arbitrary angle. In other words, I know how to rotate by increments of 90 (switch the width and height), but what I'm trying to do is group a set of objects and rotate them around a single point.
I've looked around and found the AffineTransform() method, but I can't figure out if this is really what I need or how to use it if it is.
EDIT: Does this solve my problem? How to rotate Graphics in Java I will try it and update.
EDIT: It got me closer, but did not fix it. It returns this runtime error:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at FallScene.rotateBack(FallScene.java:77)
at SceneDriver$1TimerListener.actionPerformed(SceneDriver.java:66)
at javax.swing.Timer.fireActionPerformed(Timer.java:312)
at javax.swing.Timer$DoPostEvent.run(Timer.java:244)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:705)
at java.awt.EventQueue.access$000(EventQueue.java:101)
at java.awt.EventQueue$3.run(EventQueue.java:666)
at java.awt.EventQueue$3.run(EventQueue.java:664)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDo
main.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:675)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThre
ad.java:211)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.
java:128)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThre
ad.java:117)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Press any key to continue...
The call at FallScene.rotateBack(FallScene.java:77) is:
bg.rotate(Math.toRadians(deg));
...which goes to:
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// Get the size of the component window
int w = getWidth();
int h = getHeight();
// The Graphics2D object for the BACKGROUND
Graphics2D bg = (Graphics2D)g;
// Sun
Color solarYellow = new Color(255, 218, 0);
bg.setPaint(solarYellow);
Ellipse2D.Double sun = new Ellipse2D.Double((w / 2) - 150, (h / 2) - 150, 300, 300);
bg.fill(sun); bg.draw(sun);
}

If you still need it, I think this operational and commented code should help you understand how to draw it.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.util.TimerTask;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SunRotate extends JComponent
{
public static void main(String[] args) {
final SunRotate sunRotate = new SunRotate(45);
JFrame f = new JFrame();
f.setContentPane(sunRotate);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setSize(new Dimension(800, 600));
f.setVisible(true);
new java.util.Timer().scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
sunRotate.deltaAngle(.3f);
sunRotate.repaint();
}
}, 16, 16); // every 16 milliseconds
}
private float angle;
public void deltaAngle(float delta) {
angle += delta;
}
public SunRotate(float angle) {
this.angle = angle;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
// Recover Graphics2D
Graphics2D g2 = (Graphics2D) g;
// Move and rotate
g2.translate(w/2.0, h/2.0);
g2.rotate(Math.toRadians(angle));
// draw around 0,0
Color solarYellow = new Color(255, 218, 0);
g2.setPaint(solarYellow);
Ellipse2D.Double sun = new Ellipse2D.Double( -150, -150, 300, 300);
g2.fill(sun);
{ // draw some rays because the sun is round so we don't see the rotation
// make a ray (triangle)
Path2D ray = new Path2D.Float();
ray.moveTo(0, 0);
ray.lineTo(1000, 50);
ray.lineTo(1000, -50);
ray.closePath();
// draw N rays, rotating each time
int N = 20;
for (int i = 0; i < N; i++) {
g2.fill(ray);
g2.rotate(Math.PI * 2 / N);
}
}
}
}

Related

how to pass value to paint method

I am just at very beginnig in Java coding, so far I could solve my problems finding solutions on internet, but this time I just stucked.
I want to create a grid(pattern) of filled rectangles, and it works fine, but then I would like to change colour of rectangles on specific position, and this part is just not working. I have no idea why, what am I doing wrong ?
// **file : MainWindow.java **
import java.util.Scanner;
import javax.swing.JFrame;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
public class MainWindow {
JFrame frame = new JFrame();
int pos_X;
int pos_Y;
// constructor for frame
public MainWindow (String title) {
frame.setSize(1000, 1000);
frame.setTitle(title);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for (int i=0;i<26;i++){ // works fine
for (int j=0;j<26;j++){
Shape shape = new Shape (110+i*20,110+j*20,19,19,5,100,220); //(x,y, width,height, R-colour, G-colour, B-colour)
frame.add(shape);
frame.setVisible(true); }}
pos_X= 15;
pos_Y = 15;
Shape shape = new Shape (110+pos_X*20,110+pos_Y*20,19,19,250,0,20); // it doesn't work ???
frame.add(shape);
frame.setVisible(true);
}
public static void main(String[] args){
new MainWindow(" my window ");
}
}
// **file : Shape.java **
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Color;
import java.awt.Color.*;
import javax.swing.JComponent;
public class Shape extends JComponent{
int width, height, xcoord, ycoord, col_r, col_g,col_b;
//constructor
public Shape (int x, int y ,int w,int h, int k, int l, int m)
{
this.width = w;
this.height = h;
this.xcoord = x;
this.ycoord = y;
this.col_r = k;
this.col_g = l;
this.col_b = m;
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D) g;
Color x= new Color( col_r,col_g, col_b );
g.setColor(x);
g.fillRect(xcoord, ycoord, width, height);
}
}
If I understand right, what you want could be done ensuring the shape has drawn after the grid, so
pos_X= 15;
pos_Y = 15;
Shape shape = new Shape (110+pos_X*20,110+pos_Y*20,19,19,250,0,20); // it doesn't work ???
frame.setComponentZOrder(shape,0)
frame.add(shape);
frame.setVisible(true);
...
please note the
frame.setComponentZOrder(shape,0)
which ensure that the colored shape is drawn above the others.
In alternative your
P.S.
if it doesn't work try frame.setComponentZOrder(shape,frame.getComponentCount())
See also here for an alternative

TextureRegion cuts texture in a wrong way

I want to have a background texture with 3 rectangles and i want to create animation with them, - texture
But first rectangle cuts in a proper way and two others are cut in a dumb way
Proper way,
Dumb way #1,
Dumb way #2
Here is my code.
public class MainMenu implements Screen{
Texture background_main;
TextureRegion[] background_textures;
Animation background_animation;
SpriteBatch batch;
TextureRegion current_frame;
float stateTime;
BobDestroyer game;
OrthographicCamera camera;
public MainMenu(BobDestroyer game){
this.game = game;
}
#Override
public void show() {
camera = new OrthographicCamera();
camera.setToOrtho(true, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch = new SpriteBatch();
background_main = new Texture(Gdx.files.internal("main_menu_screen/Background.png"));
background_textures = new TextureRegion[3];
for(int i = 0; i<3; i++){
background_textures[i] = new TextureRegion(background_main,0, 0+72*i, 128, 72+72*i);
}
background_animation = new Animation(2f,background_textures);
}
#Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
stateTime += Gdx.graphics.getDeltaTime();
current_frame = background_animation.getKeyFrame(stateTime, true);
batch.begin();
batch.draw(current_frame,0, 0,Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
batch.end();
}
}
If I understand you correctly, are you trying to create 3 TextureRegions of the same width/height? If yes, your issue may be with:
new TextureRegion(background_main,0, 0+72*i, 128, 72+72*i)
I think you'd want:
new TextureRegion(background_main,0, 0+72*i, 128, 72)
As the 128x72 is the width/height (not x/y positions) of your next TextureRegions, and do you not want them all the same height (72) as opposed to varying heights (72+72*i)?

JavaFX reset graphics Context after scaling a canvas

I'm trying to make a chart without using any 3rd party libs. I've a zoom feature which scales the Canvas correctly, but now I need to redraw everything inside the canvas once again.
But when I do scaling the GrapphicsContext also scales and blots. I want to readjust the blotting and show points in normal drawing once zoomed. How can I achieve this?
Here is simple snippet that I'm redrawing:
private void redrawImage(Canvas canvas, int scale) {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.scale(scale, scale);
gc.setStroke(Color.RED);
gc.setLineWidth(2);
gc.strokeRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.setStroke(Color.GREEN);
gc.strokeLine(0, 0, canvas.getWidth(), canvas.getHeight());
gc.strokeLine(0, canvas.getHeight(), canvas.getWidth(), 0);
gc.setStroke(Color.BLUE);
gc.strokeText("TEXT", 50, 50);
}
Even I remove gc.scale(X,Y) I still see the blotted points or text, I want the scale to be always 1, but I should also zoom or scale simultaneously.
What I want to achieve is like the GoogleMaps overlaying, you see the objects when zoomed in or out are recalibrated and adjusted to a viewable scale. This is exactly what I want to achieve.
I don't know if I understand you correct.
Your redrawImage method should get an double and not an int value for the scale. Even if you try to scale to 1.9 the cast from double to int will bring it down to 1.
I've made a little example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.FlowPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class JavaFXApplication1 extends Application {
#Override
public void start(Stage primaryStage) {
Canvas canvas = new Canvas(100, 100);
Canvas canvas2 = new Canvas(100, 100);
FlowPane root = new FlowPane();
root.getChildren().addAll(canvas, canvas2);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
redrawImage(canvas, 0.5);
redrawImage(canvas2, 1);
}
private void redrawImage(Canvas canvas, double scale) {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.scale(scale, scale);
gc.setStroke(Color.RED);
gc.setLineWidth(2);
gc.strokeRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.setStroke(Color.GREEN);
gc.strokeLine(0, 0, canvas.getWidth(), canvas.getHeight());
gc.strokeLine(0, canvas.getHeight(), canvas.getWidth(), 0);
gc.setStroke(Color.BLUE);
gc.strokeText("TEXT", 50, 50);
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
With the following result:

JavaFX 2.2 MouseEvent in SubScene does not work properly

I am trying to get this to work for sometime and I cannot figure out what wrong with my code. This leads me to believe there is some issues in SubScene Mouse listener. Any idea is appreciated.
Basically I have a scene contains two subscenes, one for the toolbar and one for the floor which has bunch of lines making it looks like tiles. I added mouse listeners so that when I clicked on the floor and move the mouse, the camera will move as if I am walking on the floor.
The problem is that the floor only recognize mouse event when I clicked on the intersection between the first vertical and the first horizontal line (yup, took me a while to figure that out). Mouse event should occur everywhere on entire floor.
Here is the code.
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.SubScene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class FloorTest extends Application {
double mousex, mousey;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
Group bargroup = new Group();
SubScene bar = new SubScene(bargroup, 300, 20, true, SceneAntialiasing.DISABLED);
bargroup.getChildren().add(btn);
Group floorgroup = new Group();
SubScene floor = new SubScene(floorgroup, 300, 250, true, SceneAntialiasing.DISABLED);
ObservableList<Node> list = floorgroup.getChildren();
for(int i = 0; i < (300/20); i++)
{
double x = i * 20;
Line line = new Line(x, 0, x, 250);
list.add(line);
}
for(int i = 0; i < (250/20); i++)
{
double y = i * 20;
Line line = new Line(0, y, 300, y);
list.add(line);
}
PerspectiveCamera camera = new PerspectiveCamera(false);
camera.setNearClip(0.1);
camera.setFarClip(10000.0);
camera.setTranslateZ(-200);
floor.setCamera(camera);
floor.setOnMousePressed((MouseEvent event) -> {
mousex = event.getSceneX();
mousey = event.getSceneY();
});
floor.setOnMouseDragged((MouseEvent event) -> {
double x = event.getSceneX();
double y = event.getSceneY();
camera.relocate(camera.getLayoutX() + (x - mousex), camera.getLayoutY() + (y - mousey));
});
Group mainroot = new Group();
mainroot.getChildren().addAll(floor, bar);
Scene scene = new Scene(mainroot, 300, 250, true);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
It seems that setting the subscene.setPickOnBounds(true) should help in proper recognition of the mouse events for the whole subscene. Tested with javafx 8.
Please try using scene instead of floor variable in event handlers
Example:
scene.setOnMousePressed((MouseEvent event) -> {
mousex = event.getSceneX();
mousey = event.getSceneY();
});
scene.setOnMouseDragged((MouseEvent event) -> {
double x = event.getSceneX();
double y = event.getSceneY();
camera.relocate(camera.getLayoutX() + (x - mousex), camera.getLayoutY() + (y - mousey));
});
That helps me

How to make rotated text look good with Java2D

My question is not about how to rotate text with Java2D; I know how to do that. What I don't know is how to make the rotated text "look good." For example, if you create a text box in PowerPoint and rotate it, the text appears sharp and clear no matter the rotation angle. However, text drawn with g2D.drawString() looks okay at 0 or 90 degrees but not so good at other angles. Is there a way to manipulate the text to clean or sharpen it up? If so, then if someone could point me to where look to learn how to do this I would be so thankful.
Below is a little program that illustrates what I'm talking about. The bigger font isn't too bad when rotated but still doesn't look very professional. The smaller font when rotated is terrible.
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class RotateTest extends JPanel {
String message = "How does this text look?";
public RotateTest() {
this.setPreferredSize(new Dimension(640, 280));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2D.setFont(new Font("MyriadPro", Font.BOLD, 20));
g2D.drawString(message, 80, 20);
AffineTransform orig = g2D.getTransform();
double angle = Math.toRadians(7.0);
g2D.rotate(-angle, -10, 80);
g2D.drawString(message, 80, 80);
g2D.setTransform(orig);
angle = Math.toRadians(30.0);
g2D.rotate(-angle, -40, 80);
g2D.drawString(message, 60, 260);
g2D.setTransform(orig);
g2D.setFont(new Font("MyriadPro", Font.BOLD, 12));
g2D.drawString(message, 380, 20);
angle = Math.toRadians(7.0);
g2D.rotate(-angle, -10, 80);
g2D.drawString(message, 380, 120);
g2D.setTransform(orig);
angle = Math.toRadians(30.0);
g2D.rotate(-angle, -40, 80);
g2D.drawString(message, 320, 400);
g2D.setTransform(orig);
}
private void display() {
JFrame f = new JFrame("RotateTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new RotateTest().display();
}
});
}
}
I once had a similar problem, and solved it by drawing the text with high precision to an image, then drawing the rotated image.
Here's the code:
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class RotatedText extends JPanel {
String message = "How does this text look?";
public RotatedText() {
this.setPreferredSize(new Dimension(640, 280));
}
public BufferedImage createStringImage(Graphics g, String s) {
int w = g.getFontMetrics().stringWidth(s) + 5;
int h = g.getFontMetrics().getHeight();
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D imageGraphics = image.createGraphics();
imageGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
imageGraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
imageGraphics.setColor(Color.BLACK);
imageGraphics.setFont(g.getFont());
imageGraphics.drawString(s, 0, h - g.getFontMetrics().getDescent());
imageGraphics.dispose();
return image;
}
private void drawString(Graphics g, String s, int tx, int ty, double theta, double rotx, double roty) {
AffineTransform aff = AffineTransform.getRotateInstance(theta, rotx, roty);
aff.translate(tx, ty);
Graphics2D g2D = ((Graphics2D) g);
g2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2D.drawImage(createStringImage(g, s), aff, this);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(new Font("MyriadPro", Font.BOLD, 20));
drawString(g, message, 80, 20, 0, 0, 0);
drawString(g, message, 80, 80, -Math.toRadians(7.0), -10, 80);
drawString(g, message, 60, 260, -Math.toRadians(30.0), -40, 80);
g.setFont(new Font("MyriadPro", Font.BOLD, 12));
drawString(g, message, 380, 20, 0, 0, 0);
drawString(g, message, 380, 120, -Math.toRadians(7.0), -10, 80);
drawString(g, message, 320, 400, -Math.toRadians(30.0), -40, 80);
}
private void display() {
JFrame f = new JFrame("RotateTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new RotatedText().display();
}
});
}
}
I haven't got the time to test this but will the following code help?:
Graphics2D g2d;
g2d.setRenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
This will only work on Graphics2D. If you're using normal Graphics you can cast your Graphics object to the 2D version like so:
Graphics2D g2d = (Graphics2D) g; //if Graphics object name is g.
Let me know!
Good luck

Resources