I'm trying to make an eye because why not, so I am trying to make a blinking animation.
Instead of closing the "eyelid" circle, I have chosen to shrink sclera (white bit) along the y-axis so it looks like the eye is closing.
Now I am adding the colored cornea in the center, but when I try to shrink the cornea when the yvalues of the sclera and cornea meet, the cornea looks weird like it is being squished.
Is the any method that would make it so that when I shrink the sclera-circle, the cornea circle would disappear when the bounds of the outer circle are smaller than the inner circle?
Basically like what happens when you resize a JPanel to be smaller than the components. They just disappear under the border of the frame rather than appear outside it.
Code: class for panel:
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.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class EyePanel extends JPanel implements ActionListener{
private int width;
private int height;
private int timeStep = 10;
private Timer timer;
private Random random;
Eye eye;
EyePanel(int width, int height){
this.width = width;
this.height = height;
eye = new Eye(width, height, timeStep);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(width, height));
frame.add(this);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
timer = new Timer(timeStep, this);
random = new Random();
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(new Color(255,255,255));
draw(g);
}
public void draw(Graphics g) {
eye.draw(g);
}
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
}
class for eye:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.*;
import javax.swing.*;
public class Eye {
private int width;
private int height;
private int timeStep;
private int lidDiameter;
private int sclDiameter;
private int corDiameter;
//lid coordinates
private int lidX;
private int lidY;
//sclera coordinates
private int sclX;
private int sclY;
private int sclU; //upper level
private int sclB; //bottom level
private int sclC; //y-level at which eyes are closed
//cornea coordinates
private int corX;
private int corY;
private int corU; //upper level
private int corB; //bottom level
private int corC; //y-level at which eyes are closed
private int blinkDelay = 3; //delay in seconds preset
private int blinkTime; //time till next blink
private int rate = 20; //rate of eye close&open
private boolean closed = false;
Eye(int width, int height, int timeStep){
this.width = width;
this.height = height;
this.timeStep = timeStep;
blinkTime = blinkDelay*(1000/timeStep);
//setup eye variables
if(width>height) lidDiameter = height - (height/10);
else if(height>width) lidDiameter = width - (width/10);
else lidDiameter = width - (height/10);
sclDiameter = lidDiameter-(lidDiameter/10);
corDiameter = sclDiameter/3;
//lid location
lidX = (int)((width-lidDiameter)/2);
lidY = (int)((height-lidDiameter)/2);
//lid location
//sclera location
sclX = (int)(lidX (lidDiameter-sclDiameter)/2);
sclY = (int)(lidY (lidDiameter-sclDiameter)/2);
sclU = sclY;
sclB = sclDiameter;
sclC = (int)(sclY sclDiameter/2);
//sclera location
//cornea location
corX = (int)(sclX (sclDiameter-corDiameter)/10);
corY = (int)(sclY (sclDiameter-corDiameter)/10);
corU = corY;
corB = corDiameter;
corC = (int)(corY corDiameter/2);
//cornea location
//setup eye variables
}
public void close() {
if(sclU != sclC)
sclU = rate;
if(sclB != sclC)
sclB-= rate*2; //bottom limit must rise by (required amt rate of upper limit fall)
if(sclU >= sclC && sclB <= sclC)
closed = true;
}
public void open() {
if(sclU != sclY)
sclU = -rate;
if(sclB != sclDiameter)
sclB-= -rate*2; //bottom limit must rise by (required amt rate of upper limit fall)
if(sclU == sclY && sclB == sclDiameter) {
closed = false;
blinkTime = blinkDelay*(1000/timeStep);
}
}
public void blink() {
if(blinkTime == 0) {
if(!closed)
close();
if(closed)
open();
}
else
blinkTime--;
}
public void draw (Graphics g) {
Graphics2D g2d = (Graphics2D) g;
//eyelid
g2d.setColor(Color.pink);
g2d.fillOval(lidX, lidY, lidDiameter, lidDiameter);
//sclera
g2d.setColor(Color.white);
g2d.fillOval(sclX, sclU, sclDiameter, sclB);
g2d.setColor(Color.black);
g2d.drawLine(sclX, sclC, sclX sclDiameter, sclC);
//cornea
g2d.setColor(Color.cyan);
g2d.fillOval(corX, corU, corDiameter, corB);
g2d.setColor(Color.red);
g2d.drawLine(corX, corC, corX corDiameter, corC);
//trigger blink
blink();
}
}
Main class that starts everything:
public class Main {
public static void main(String[] args) {
new EyePanel(600, 600);
}
}
CodePudding user response:
Under no circumstances should a painting method call any form of logic that alters object state. Painting is triggered by system events and you have very little control over its timing. (Examples of such events are a user moving the window, lowering or raising the window, deiconifying the window, unlocking the screen, and even moving the mouse over the window.)
So, the first thing you want to do is remove this from the Eye class:
//trigger blink
blink();
…and add it to your actionPerformed method:
@Override
public void actionPerformed(ActionEvent e) {
eye.blink();
repaint();
}
Nearly all Swing methods and constructors must be run on the AWT event dispatch thread, so change your main
method to do that:
public static void main(String[] args) {
EventQueue.invokeLater(() -> new EyePanel(600, 600));
}
Your code doesn’t do exactly what you claim (the cornea/iris is not in the center), but I think your question is answerable. The Area class can be used to check whether shapes intersect:
public void draw (Graphics g) {
Graphics2D g2d = (Graphics2D) g;
//eyelid
g2d.setColor(Color.pink);
g2d.fillOval(lidX, lidY, lidDiameter, lidDiameter);
//sclera
Area sclera = new Area(
new Ellipse2D.Float(sclX, sclU, sclDiameter, sclB));
g2d.setColor(Color.white);
g2d.fill(sclera);
g2d.setColor(Color.black);
g2d.drawLine(sclX, sclC, sclX sclDiameter, sclC);
//cornea
Area cornea = new Area(
new Ellipse2D.Float(corX, corU, corDiameter, corB));
Area hiddenCornea = (Area) cornea.clone();
hiddenCornea.subtract(sclera);
if (hiddenCornea.isEmpty()) {
g2d.setColor(Color.cyan);
g2d.fill(cornea);
g2d.setColor(Color.red);
g2d.drawLine(corX, corC, corX corDiameter, corC);
}
}
This will skip drawing the cornea if any part of the cornea is hidden (does not intersect the sclera).