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:
Related
I have a circle, and then a triangle in the middle of the circle. I want to rotate the triangle around the center of the triangle. Kind of like rotating it 360 degrees. This is my code so far
public void paintComponent(Graphics g)
{
g.setColor(Color.BLACK);
g.drawArc(120, 120, 100, 100, 0, 360);
g.setColor(Color.RED);
int[] x = {160, 170, 180};
int[] y = {150, 190, 150};
g.drawPolygon(x, y, 3);
}
public static void main (String[] args)
{
JFrame frame = new JFrame("SpinningCircle");
frame.setSize(400, 400);
frame.setLocation(0, 0);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new SpinningCircle());
frame.setVisible(true);
}
`
Thanks for help.
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);
}
}
}
}
Code: http://www.lwjgl.org/wiki/index.php?title=Slick-Util_Library_-_Part_1_-_Loading_Images_for_LWJGL
Output:
(It's supposed to be just a rectangle; but the extra lines at the right, bottom, and the pixel at the bottom right appear.)
I tested it out with .jpg also, it appears that the only difference is that the line at the bottom acquires the same width as the rectangle to be displayed.
Can anyone help me with why / how to fix this / how to import an image correctly?
import java.io.IOException;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.Color;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;
public class TextureExample {
/** The texture that will hold the image details */
private Texture texture;
/**
* Start the example
*/
public void start() {
initGL(800,600);
init();
while (true) {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
render();
Display.update();
Display.sync(100);
if (Display.isCloseRequested()) {
Display.destroy();
System.exit(0);
}
}
}
/**
* Initialise the GL display
*
* #param width The width of the display
* #param height The height of the display
*/
private void initGL(int width, int height) {
try {
Display.setDisplayMode(new DisplayMode(width,height));
Display.create();
Display.setVSyncEnabled(true);
} catch (LWJGLException e) {
e.printStackTrace();
System.exit(0);
}
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
// enable alpha blending
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glViewport(0,0,width,height);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, width, height, 0, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
}
/**
* Initialise resources
*/
public void init() {
try {
// load texture from PNG file
texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/image.png"));
System.out.println("Texture loaded: "+texture);
System.out.println(">> Image width: "+texture.getImageWidth());
System.out.println(">> Image height: "+texture.getImageHeight());
System.out.println(">> Texture width: "+texture.getTextureWidth());
System.out.println(">> Texture height: "+texture.getTextureHeight());
System.out.println(">> Texture ID: "+texture.getTextureID());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* draw a quad with the image on it
*/
public void render() {
Color.white.bind();
texture.bind(); // or GL11.glBind(texture.getTextureID());
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(0,0);
GL11.glVertex2f(100,100);
GL11.glTexCoord2f(1,0);
GL11.glVertex2f(100+texture.getTextureWidth(),100);
GL11.glTexCoord2f(1,1);
GL11.glVertex2f(100+texture.getTextureWidth(),100+texture.getTextureHeight());
GL11.glTexCoord2f(0,1);
GL11.glVertex2f(100,100+texture.getTextureHeight());
GL11.glEnd();
}
/**
* Main Class
*/
public static void main(String[] argv) {
TextureExample textureExample = new TextureExample();
textureExample.start();
}
}
The resolution of textures need to be a power of two. Once they are initialized, they can be resized to whatever dimensions are needed.
How can I create a circle (or ellipse) javafx.scene.shape.Path in JavaFX 2?
I've found some examples using CubicCurveTo:
Path path = new Path();
path.getElements().add(new CubicCurveTo(30, 10, 380, 120, 200, 120));
but I don't understand that Bézier coordinates. I need a full circle path for animations.
You can utilize the ArcTo path element to draw circle or ellipse path:
public class ArcToDemo extends Application {
private PathTransition pathTransitionEllipse;
private PathTransition pathTransitionCircle;
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setResizable(false);
primaryStage.setScene(new Scene(root, 600, 460));
// Ellipse path example
Rectangle rect = new Rectangle(0, 0, 40, 40);
rect.setArcHeight(10);
rect.setArcWidth(10);
rect.setFill(Color.ORANGE);
root.getChildren().add(rect);
Path path = createEllipsePath(200, 200, 50, 100, 45);
root.getChildren().add(path);
pathTransitionEllipse = PathTransitionBuilder.create()
.duration(Duration.seconds(4))
.path(path)
.node(rect)
.orientation(OrientationType.ORTHOGONAL_TO_TANGENT)
.cycleCount(Timeline.INDEFINITE)
.autoReverse(false)
.build();
// Cirle path example
Rectangle rect2 = new Rectangle(0, 0, 20, 20);
rect2.setArcHeight(10);
rect2.setArcWidth(10);
rect2.setFill(Color.GREEN);
root.getChildren().add(rect2);
Path path2 = createEllipsePath(400, 200, 150, 150, 0);
root.getChildren().add(path2);
pathTransitionCircle = PathTransitionBuilder.create()
.duration(Duration.seconds(2))
.path(path2)
.node(rect2)
.orientation(OrientationType.ORTHOGONAL_TO_TANGENT)
.cycleCount(Timeline.INDEFINITE)
.autoReverse(false)
.build();
}
private Path createEllipsePath(double centerX, double centerY, double radiusX, double radiusY, double rotate) {
ArcTo arcTo = new ArcTo();
arcTo.setX(centerX - radiusX + 1); // to simulate a full 360 degree celcius circle.
arcTo.setY(centerY - radiusY);
arcTo.setSweepFlag(false);
arcTo.setLargeArcFlag(true);
arcTo.setRadiusX(radiusX);
arcTo.setRadiusY(radiusY);
arcTo.setXAxisRotation(rotate);
Path path = PathBuilder.create()
.elements(
new MoveTo(centerX - radiusX, centerY - radiusY),
arcTo,
new ClosePath()) // close 1 px gap.
.build();
path.setStroke(Color.DODGERBLUE);
path.getStrokeDashArray().setAll(5d, 5d);
return path;
}
#Override
public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
pathTransitionEllipse.play();
pathTransitionCircle.play();
}
public static void main(String[] args) {
launch(args);
}
}
Good reference of the features of ArcTo is ArcTo (JavaFX 8). Allthough it is version 8, the meanings of the features are similar.
Output:
This is an updated version of #Uluk Biy's answer.
import javafx.animation.PathTransition;
import javafx.animation.PathTransition.OrientationType;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.ArcTo;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class PathTDemo extends Application
{
private PathTransition pathTransitionEllipse;
private PathTransition pathTransitionCircle;
private void init(Stage primaryStage)
{
Group root = new Group();
primaryStage.setResizable(false);
primaryStage.setScene(new Scene(root, 600, 460));
// Ellipse path example
Rectangle rect = new Rectangle(0, 0, 40, 40);
rect.setArcHeight(10);
rect.setArcWidth(10);
rect.setFill(Color.ORANGE);
root.getChildren().add(rect);
Path path = createEllipsePath(200, 200, 50, 100, 45);
root.getChildren().add(path);
pathTransitionEllipse = new PathTransition();
pathTransitionEllipse.setDuration(Duration.seconds(4));
pathTransitionEllipse.setPath(path);
pathTransitionEllipse.setNode(rect);
pathTransitionEllipse.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransitionEllipse.setCycleCount(Timeline.INDEFINITE);
pathTransitionEllipse.setAutoReverse(false);
// Cirle path example
Rectangle rect2 = new Rectangle(0, 0, 20, 20);
rect2.setArcHeight(10);
rect2.setArcWidth(10);
rect2.setFill(Color.GREEN);
root.getChildren().add(rect2);
Path path2 = createEllipsePath(400, 200, 150, 150, 0);
root.getChildren().add(path2);
pathTransitionCircle = new PathTransition();
pathTransitionCircle.setDuration(Duration.seconds(2));
pathTransitionCircle.setPath(path2);
pathTransitionCircle.setNode(rect2);
pathTransitionCircle.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransitionCircle.setCycleCount(Timeline.INDEFINITE);
pathTransitionCircle.setAutoReverse(false);
}
private Path createEllipsePath(double centerX, double centerY, double radiusX, double radiusY, double rotate)
{
ArcTo arcTo = new ArcTo();
arcTo.setX(centerX - radiusX + 1); // to simulate a full 360 degree celcius circle.
arcTo.setY(centerY - radiusY);
arcTo.setSweepFlag(false);
arcTo.setLargeArcFlag(true);
arcTo.setRadiusX(radiusX);
arcTo.setRadiusY(radiusY);
arcTo.setXAxisRotation(rotate);
Path path = new Path();
path.getElements().addAll(
new MoveTo(centerX - radiusX, centerY - radiusY),
arcTo,
new ClosePath()); // close 1 px gap.
path.setStroke(Color.DODGERBLUE);
path.getStrokeDashArray().setAll(5d, 5d);
return path;
}
#Override
public void start(Stage primaryStage) throws Exception
{
init(primaryStage);
primaryStage.show();
pathTransitionEllipse.play();
pathTransitionCircle.play();
}
public static void main(String[] args)
{
launch(args);
}
}
I solved same problem by animation of rotateProperty of container. Just two lines for creating animation.
animationTimeLine = new Timeline(60, new KeyFrame(Duration.seconds(5), new KeyValue(circlePane.rotateProperty(), 360.0)));
animationTimeLine.setCycleCount(INDEFINITE);
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