I am trying to make a digital clock in this way:
As shown in the image 1 i want the clock to be reflected.
What i have tried:
I tried using java (Graphics2D)g by rotating the string and the using substring but i got this problem:
My code for this:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Calendar;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class DigitalClock extends JPanel {
JLabel label = new JLabel();
int c = 0;
Font font = null;
JFrame frame = new JFrame();
//Constructor
public DigitalClock(){
frame.setSize(700,500);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.setVisible(true);
DigitalThread();
}
//Paint Method
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
//The background
g2d.setColor(Color.BLACK);
g2d.fillRect(0,0,500,100);
//Show Time
g2d.setColor(Color.WHITE);
g2d.setFont(new Font(g.getFont().toString(),10,15));
g2d.drawString(timeNow(),100, 25);
//Show time Reflected
g2d.rotate(Math.PI,100,25);
g2d.drawString(timeNowRot(timeNow()),45, 20);
}
//Change time Value with this Thread
public void DigitalThread(){
new Thread(new Runnable(){
public void run(){
boolean flag=true;
while(flag==true){
try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
repaint();
}
}}).start();
}
//Return time
public String timeNow(){
return zero(Calendar.getInstance().get(Calendar.HOUR_OF_DAY))+":"+
zero(Calendar.getInstance().get(Calendar.MINUTE))+":"+zero(Calendar.getInstance().get(Calendar.SECOND));
}
//Return time reflected
public String timeNowRot(String time){
return time.substring(time.length()-1,time.length())+time.substring(time.length()-2,time.length()-1)+":";
}
//Add Zero if value<10
public String zero(int num){
if(num<10) return "0"+num;
else return ""+num;
}
}
I can achieve this by using java 2d? is there a method to rotate a string again vertically so i have not this problem thanks..
Use scale(x, y) which multiplies the x and y coordinates.
g2d.scale(1.0, -1.0);
g2.drawString(....);
g2d.scale(1.0, -1.0); // Undo
This does a transformation where the y axis is reversed.
You could also use shear for a paralellogram look.
A g2d.scale(-1.0, 1.0); would draw the string backwards. Can be used in combination with the rotation.
It allt depends on the usage of any rotation and the order, how to scale. Without rotation: scale y by -1:
good
ƃooq
Actually i found the answer based n your answers thanks all:
It is and irony but the next day after the question i had a lesson at university about that...
Info about scaling:
g2d.scale(1,-1); /*scale by y axes(Flipping vertical
|
flip here (scale(-1,1) ) | here it was
_ _ _ _ _|_ _ _ _ _
|
flip here (scale(-1,-1) ) | fliped here( scale(1,-1) )
|
*/
g2d.drawString(timeNow(),10,-27);
I also added some more effect so the clock be more realistic like:
g2d.setPaint(new GradientPaint(0,-15,color,0,-50,Color.BLACK));
Your link shows a clock face AND its reflection. If that's what you want to do then this is the way.
Draw the clock face into an Image, instead of direct to the Component. Then you render the resulting image into the component twice using drawImage - once the normal way, and once transformed so it looks reflected.
This uses slightly more memory, but only temporarily, and it saves you having to do the paint twice.
If you only want to draw the reflected clock face, then set the image transformation properties in the Graphics to get the reflection.
I'll leave the details as an exercise to the reader.
Related
I have a weird issue that every other guide and answer seems to contradict, but the issue seems to be deeper. OS-level deep.
System details:
Ubuntu 18.04, Unity window manager, nVidia graphics (proprietary driver)
Tried with the following Java VMs:
-OpenJDK 11 & 17,
-Temurin 11 & 17,
-JBR-17
I have an application where I draw an image on a canvas, zoomed (so it's pixelated), and I can pan around and edit with the mouse (sort of like photoshop). I do this by defining a small rectangle in the image and drawing that to the entire panel (at 4K resolution):
g.drawImage(image,
x, // dst
y,
x + visibleImageWidth * blockSize,
y + visibleImageHeight * blockSize,
ul.x, // src
ul.y,
ul.x + visibleImageWidth,
ul.y + visibleImageHeight,
this);
This works fine statically, but when I start working it with the mouse, it progressively slows down to a crawl.
I analyzed this, and it seems that the mouse fires events at 1000Hz. Then paintComponent() somehow manages to finish within that same 1ms. The OS however chokes on the amount of visual data thrown at it, and every (visual) update takes longer than the last. As long as I keep dragging the mouse, the OS crawls to a complete stop. (It seems everything non-graphical still works at normal speed, e.g. my program keeps processing input) Also visual updates of other programs stop, so it's like the graphics card or driver chokes on the data and can't process/discard it fast enough. When I let go of the mouse it stays frozen until next visual update. Then all programs instantly update their visuals and everything is back to normal.
The (heavyweight) JPanel that I have doesn't collate repaint() calls where I expect it should. Every time I call it, it immediately (from my perspective) calls paintComponent(), which finishes within 1ms, before the next call to repaint().
* Why is Java so insistent on sending graphics data to the OS at such a ridiculous speed? My monitor runs at 60Hz, not 1000Hz.
I found a very dirty workaround that at least lets me use the program in any reasonable way at all: Adding
try {
Thread.sleep(10);
} catch (InterruptedException ignored) {
}
at the end of the paintComponent() method. The program now works super smooth, without visible tearing or microstuttering (even though 10ms != 60Hz).
Why do I need this delay to, of all things, speed up graphics? How can I make Java respect the monitor's refresh rate in a 'neater' way?
[Edit] MSSCC
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class Temp extends JPanel {
public static void main(String... args) {
System.out.println(LocalDate.now());
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setContentPane(new Temp());
f.setExtendedState(Frame.MAXIMIZED_BOTH);
f.setVisible(true);
});
}
private final BufferedImage image = new BufferedImage(10000, 10000, BufferedImage.TYPE_INT_RGB);
public Temp() {
super(null);
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
int x = e.getX() / 5;
int y = e.getY() / 5;
int rgb = ThreadLocalRandom.current().nextInt(0xFFFFFF);
int[] pixels = new int[100];
Arrays.fill(pixels, rgb);
image.getRaster().setDataElements(x, y, 8, 8, pixels);
repaint();
((Frame)getTopLevelAncestor()).setTitle(" (" + x + ", " + y + ')');
}
});
}
#Override
public void paintComponent(Graphics g) {
g.drawImage(image,
0, 0, getWidth(), getHeight(),
0, 0, getWidth() / 5, getHeight() / 5,
null);
}
}
Back in the day I created a Paint widget to draw like you'd do in MS Paint - and I definitely did not experience mouse move events firing at the rate you are reporting. As #GilbertLeBlanc commented, it seems like you are probably in an infinite paint loop.
Nevertheless, if this isn't the issue, you can throttle event firing like this
public class Throttle extends MouseMotionAdapter {
public static final long THRESHHOLD = 30; //ms
private lastEvTime = 0;
public void mouseMoved(MouseEvent me) {
long time = System.currentTimeMillis();
if (time > (lastEvTime + THRESHHOLD)) {
lastEvTime = time;
//do graphical update
}
// else do nothing
}
}
Although in this example, the X-Y values are hard-coded, lets assume the user entered the values dynamically and clicked a button to view the results on the screen.
It wouldn't make sense to calculate the frame based on the largest size as the Frame would be too large for the monitor.
What is required to take the X-Y values entered (not matter how large or small) and have the image appear centered within the frame?
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ZoomToXY extends JPanel
{
int x = 0;
public void paint(Graphics g)
{
//Can't see this.
int[] xs2 = {5308, 5306, 4143, 4143, 4120, 4119, 4118, 4117, 4116, 4114, 4112};
int[] ys2 = {4474, 5329, 5306, 5171, 5171, 5173, 5175, 5177, 5179, 5181, 5182};
BasicStroke traceStroke = new BasicStroke (1); //Line thickness
Graphics2D gc = (Graphics2D) g.create();
gc.setColor(Color.RED);
gc.drawPolyline(xs2, ys2, 11);
gc.setStroke(traceStroke);
x++;
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.add(new ZoomToXY());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(20,20, 250,250);
frame.setVisible(true);
}
}
The reason we can't see the polygon or whatever you're making, is because it's outside the frame's bounds. Let's take a look.
frame.setBounds(20,20, 250,250);
This line indicates we will only see what's inside these bounds, though everything outside will also be drawn but not shown. Try drawing a rectangle inside the bounds and see.
g.fillRect(20, 20, 100, 100);
You will see a rectangle. But how can I solve this issue? Since having a frame being 5000px by 5000px isn't going to work on most monitors, either you work with smaller resolutions and therefore smaller coordinates, or you implement a camera. Having a camera you can have as big world as you want and being able to move around in it. But if your frame can only show 100 pixels and your polygon is 1000px, we will only see 10% of it, this problem can easily be solved with zooming. Here is a topic how to implement a gamecamera. With the gameCamera you can simply calculate the center of your image, then translate it, quite simple. If you need assistance just ask.
A frame that is 250x250 is quite small, consider it being a little bigger. Also why set the coordinates as (20, 20)? If you want to center the JFrame to the current monitor just call:
frame.setLocationRelativeTo(null);
I'm making a program where you have a server and a client, and the idea is that you draw on the client jpanel, and the coordinates will then be sent to the server, which will sort of mimic the drawing. I've done that, but the problem is now, that my drawing mechanism is pretty bad. Right now I'm just using an oval that gets drawn over and over again on the coordinate of the mouse, which sometimes leaves spaces between the ovals if you move the mouse too fast.
To better illustrate, here's an SS: http://gyazo.com/6ed1017e9efd6beaa4b5d56052fda260
As you can see, it's only consistent when you move the mouse relatively slow, but as soon as you move it a bit fast, it leaves spaces.
How do I prevent this from happening?
Right now the client just sends x and y coordinates, so here's the server side code:
package com.company;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server extends JPanel{
static MouseData mouseReceive;
static Draw draw;
static int x;
static int y;
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Server server = new Server();
JFrame frame = new JFrame("Server");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(server);
frame.setSize(1024, 600);
frame.setVisible(true);
draw = new Draw(x,y);
ServerSocket serverSock = new ServerSocket(1234);
Socket s = serverSock.accept();
ObjectInputStream in = new ObjectInputStream(s.getInputStream());
while(true) {
mouseReceive = (MouseData) in.readObject();
draw = new Draw(mouseReceive.mouseX,mouseReceive.mouseY);
}
}
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
draw.display(g);
repaint();
}
}
And here's my draw class:
package com.company;
import java.awt.*;
/**
* Created by John on 21/04/2015.
*/
public class Draw {
int xLoc;
int yLoc;
Draw(int x, int y){
xLoc = x;
yLoc = y;
}
public void display(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.fillOval(xLoc,yLoc,7,7);
}
}
I tried finding someone else having the same problem on this site through the search function, but I had no luck in doing so :( If I missed it however, please direct me to that topic!
If anyone could help me out, I'd appreciate it a whole lot! Have a nice day :)
Presuming the Client is using a MouseListener (or MouseMotionListener): the MouseListener can only fire as fast as a certain interval. For example when the mouse is constantly moved your listener will receive a MouseEvent for every interval rather than every pixel. As a result, moving the mouse fast may result in drawing items that are not adjacent to each other. AFAIK, you cannot increase the speed, but you can draw lines between two sequential points making them look continuous (eg by using a List of each event location and using g.drawLine on each two adjacent points in the List).
Other notes:
You should override paintComponent rather than the paint method.
I would recommend calling super.paintComponent in this method. This will clear the component (hence your code will then only draw the last point - see (3))
I would recommend keeping a List of locations to use for drawing, which you can iterate over and draw each circle (or draw a line between adjacent points)
Do NOT call repaint within your painting methods. The idea here is that when a new item is received from the Client, add it to the List in (3) and then call repaint.
I got the head, one arm and the body. I am trying to make another arm using the same first two coordinates, which starts at the bottom of the head, but a negative last (but same number) last two coordinates. I assumed that if I made a negative version, it would just make an opposite version of the line. Instead, its just sticking straight up! I am confused on why this is happening.
import javax.swing.JComponent;
import java.awt.*;
import java.awt.geom.*;
public class StickFigure extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Ellipse2D.Double head = new Ellipse2D.Double(5, 10, 50, 50);
g2.draw(head);
Line2D.Double body=new Line2D.Double(30,60, 30,150);
g2.draw(body);
Line2D.Double arm1=new Line2D.Double(30,60,75,75);
g2.draw(arm1);
Line2D.Double arm2=new Line2D.Double(30,60,-75,-75);
g2.draw(arm2);
}
}
That is the code that is giving me trouble. I am using a viewer which is the following:
import javax.swing.JFrame;
public class Viewer
{
public static void main(String[] arg)
{
JFrame frame = new JFrame();
frame.setSize(1000,500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
StickFigure fig1=new StickFigure();
frame.add(fig1);
frame.setVisible(true);
}
}
Please let me know what I am doing wrong, I would greatly appreciate it.
Line2D.Double arm2=new Line2D.Double(30,60,-75,-75);
You need to think about what you're saying with -75 and -75. Remember those make a coordinate, and (0, 0) represents the top left corner in Swing (unless you're explicitly telling it not to). Those coordinates are offscreen to the northwest.
Try something like:
Line2D.Double arm2=new Line2D.Double(30,60, 45,75);
Try using a positive y last coordinate for both:
Line2D.Double arm1=new Line2D.Double(30,60,75,75);
g2.draw(arm1);
Line2D.Double arm2=new Line2D.Double(30,60,-75,75);
g2.draw(arm2);
You are right that -75 -75 "would just make an opposite version of the line", but when you alter both coordinates you get radial simmetry, that is simmetry around a point (the neck) hence one of your arm is low and the other is up. You want axial symmetry in this case, and for that you only need to flip one coordinate; since people's axis of symmetry is the spine, and it is vertical (y-direction) you need to flip coordinate x only.
I have an application where I want to draw an image that grows in width over time. Specifically, the application listens to the microphone and computes a spectrogram every 6 milliseconds, and I want to draw the updated spectrogram when it comes in. I've been using java.awt.image.BufferedImage to draw the spectrograms, but only for pre-recorded files, so I have a fixed width for the image. What's the best way to do this for a streaming application, where I don't know a priori the width of the image?
One possibility is to just create a new BufferedImage with one extra pixel on the right and copy the data over, but that seems inefficient to do hundreds of times per second. Or I could start with a relatively large width and keep the right side blank until it fills up, and double the width when it does, similar to how an ArrayList amortizes its size - I would only have to copy the data a few times per second, or once every few seconds, or so. Is there a better option?
I would use a combination of what you suggest and an overriding component that only paints a subimage of the total image and returns an appropriate preferred size.
Here is a demo code which shows what I mean:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TestRecording {
public static class MyPanel extends JPanel {
private BufferedImage buffer = new BufferedImage(3000, 100, BufferedImage.TYPE_INT_RGB);
private int width = 0;
private int lastY = 50;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (width > 0) {
BufferedImage sub = buffer.getSubimage(0, 0, width, buffer.getHeight());
g.drawImage(sub, 0, Math.max(0, (getHeight() - buffer.getHeight()) / 2), this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width, 100);
}
protected void drawSomething() {
// Here need to handle growing image
Graphics g = buffer.getGraphics();
g.setColor(Color.GREEN);
int y = new Random().nextInt(buffer.getHeight());
g.drawLine(width, lastY, width + 1, y);
lastY = y;
width += 1;
Rectangle r = new Rectangle();
r.x = getWidth();
// Lame hack to auto-scroll to the end
scrollRectToVisible(r);
revalidate();
repaint();
}
}
protected void initUI() {
JFrame frame = new JFrame(TestRecording.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final MyPanel p = new MyPanel();
JScrollPane scrollpane = new JScrollPane(p);
frame.add(scrollpane);
frame.setSize(400, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer t = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.drawSomething();
}
});
t.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestRecording().initUI();
}
});
}
}
And here is the result:
May be just scale the original image to get desired size.
Image has getScaledInstance(int width, int height, int hints) method
Do you have 1) gui where you draw it, or 2) do you need to realy output image file (or stream)?
if 1)
Draw only what is seen. Extend JComponent, I usually use JPanel and override paintComponent and paint what is visible.
To make it more efficient, create "tiles" - list of eg. BufferedImage of constant width and create them from incoming data and draw to gui only them.
2) Something similar, but you can use one image with relatively low width and draw new data to it. When full, "append" it to so far created "left" image (initially empty). This way you frequently modify small image and big not so frequently.
If end comes before filling whole right image, join only filled part with left.
You could try to gradually increase size of right image (*X, eg *1.5 or *2) to reduce number of joining images at cost of using more memory.
You could store those "images" as byte(or int) array (1D array representing 2D, but store it by columns, not by rows for more efficient modifications), this way you can store it bit more efficiently if you know that you need some unusual ammount of bits per pixels, because some colors will never be in result image.
If image gets too big, save it to disk and clear your left image and later join them together or use them separately.