So I am trying to write a program that gets a function like 3x^2 + x + 8 and then graphs said function. I am using the eval() method to turn a String into a expression but it keeps throwing a NullPointerException.
package Function;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JFrame;
public class FunctionGrapherTest
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
FunctionGrapherComponent comp = new FunctionGrapherComponent();
frame.setSize(600, 600);
frame.setTitle("Function");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(comp);
frame.getContentPane().add(comp.control(), BorderLayout.SOUTH);
Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
int x = (int)((dimension.getWidth() - frame.getWidth()) / 2);
int y = (int)((dimension.getHeight() - frame.getHeight()) / 2);
frame.setLocation(x, y);
}
}
package Function;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
public class FunctionGrapherComponent extends JPanel
{
private static final long serialVersionUID = 1L;
public FunctionGrapherComponent()
{
JPanel field = new JPanel();
JLabel y = new JLabel("y = ", SwingConstants.RIGHT);
field.add(y);
equaField = new JTextField(15);
field.add(equaField);
control = new JPanel();
control.setLayout(new GridLayout(1, 3));
control.add(field);
JButton draw = makeButton("Graph");
control.add(draw);
count = 0;
}
public JPanel control()
{
return control;
}
public JButton makeButton(String label)
{
JButton button = new JButton(label);
class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String equation = equaField.getText();
if(!equation.equals("") || equation != null)
{
equa = equation;
count = 1;
repaint();
}
equaField.setText("");
}
}
ActionListener listener = new ButtonListener();
button.addActionListener(listener);
return button;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
Axes axes = new Axes(xPixel(XMIN), xPixel(XMAX), yPixel(YMIN), yPixel(YMAX),
xPixel(0), yPixel(0), sWidth(1), sHeight(1));
axes.drawAxes(g2);
axes.drawTicks(g2);
if(count == 1)
{
Function func = new Function();
delta = (XMAX - XMIN) / 100;
for(double i = XMIN; i <= (XMAX - delta); i = i + delta)
{
x1 = xPixel(i);
y1 = yPixel(func.functionVal(i, equa));
x2 = xPixel(i + delta);
y2 = yPixel(func.functionVal(i + delta, equa));
func.plot(g2, x1, y1, x2, y2);
}
count = 0;
}
}
public double xPixel(double xuser)
{
return (xuser - XMIN) * (getWidth( ) - 1) / (XMAX - XMIN);
}
public double yPixel(double yuser)
{
return (yuser - YMAX) * (getHeight( ) - 1) / (YMIN - YMAX);
}
public double sHeight(double yuser)
{
return yuser * (getHeight() - 1) / (YMAX - YMIN);
}
public double sWidth(double xuser)
{
return xuser * (getWidth() - 1) / (XMAX - XMIN);
}
private static final double XMIN = -100;
private static final double XMAX = 100;
private static final double YMIN = -100;
private static final double YMAX = 100;
private double delta;
private double x1;
private double y1;
private double x2;
private double y2;
private int count;
private JPanel control;
private JTextField equaField;
private String equa;
}
package Function;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class Function
{
public Function()
{
}
public void plot(Graphics2D g2, double x1, double y1, double x2, double y2)
{
Line2D.Double seg = new Line2D.Double(x1, y1, x2, y2);
g2.draw(seg);
}
public double functionVal(double x, String equa)
{
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("Java");
try
{
funcVal = (double)engine.eval(equa);
}
catch (ScriptException e)
{}
return funcVal;
}
private double funcVal;
}
package Function;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
public class Axes
{
public Axes(double xmin, double xmax, double ymin, double ymax,
double xzero, double yzero, double xunit, double yunit)
{
xMin = xmin;
xMax = xmax;
yMin = ymin;
yMax = ymax;
xZero = xzero;
yZero = yzero;
xUnit = xunit;
yUnit = yunit;
}
public void drawAxes(Graphics2D g2)
{
Line2D.Double xAxis = new Line2D.Double(xMin, yZero, xMax, yZero);
Line2D.Double yAxis = new Line2D.Double(xZero, yMin, xZero, yMax);
g2.draw(xAxis);
g2.draw(yAxis);
}
public void drawTicks(Graphics2D g2)
{
for(double i = xZero + xUnit; i <= xMax; i = i + xUnit)
{
Line2D.Double tick = new Line2D.Double(i, yZero + TICK_LENGTH, i, yZero - TICK_LENGTH);
g2.draw(tick);
}
for(double i = xZero - xUnit; i >= xMin; i = i - xUnit)
{
Line2D.Double tick = new Line2D.Double(i, yZero + TICK_LENGTH, i, yZero - TICK_LENGTH);
g2.draw(tick);
}
for(double i = yZero + yUnit; i <= yMin; i = i + yUnit)
{
Line2D.Double tick = new Line2D.Double(xZero + TICK_LENGTH, i, xZero - TICK_LENGTH, i);
g2.draw(tick);
}
for(double i = yZero - yUnit; i >= yMax; i = i - yUnit)
{
Line2D.Double tick = new Line2D.Double(xZero + TICK_LENGTH, i, xZero - TICK_LENGTH, i);
g2.draw(tick);
}
}
private double xMin;
private double xMax;
private double yMin;
private double yMax;
private double xZero;
private double yZero;
private double xUnit;
private double yUnit;
private static final double TICK_LENGTH = 3;
}
Start by changing ScriptEngine engine = mgr.getEngineByName("Java"); to ScriptEngine engine = mgr.getEngineByName("javascript");, despite what some people think, Java isn't a scripting language and there is a difference between Java and JavaScript
Also, you shouldn't ignoring the exceptions thrown by the engine...
try {
System.out.println(equa);
funcVal = ((Number) engine.eval(equa)).doubleValue();
} catch (ScriptException e) {
e.printStackTrace();
}
You'll also find it faster if you don't re-create the ScriptEngine each time you call functionVal
public static class Function {
private final ScriptEngine engine;
public Function() {
ScriptEngineManager mgr = new ScriptEngineManager();
engine = mgr.getEngineByName("javascript");
}
public void plot(Graphics2D g2, double x1, double y1, double x2, double y2) {
Line2D.Double seg = new Line2D.Double(x1, y1, x2, y2);
g2.draw(seg);
}
public double functionVal(double x, String equa) {
try {
System.out.println(equa);
funcVal = ((Number) engine.eval(equa)).doubleValue();
} catch (ScriptException e) {
}
return funcVal;
}
private double funcVal;
}
Updated
So, assuming you want to solve the equation 3x^2 + x + 8, you need to give x some value...
engine.put("x", 10);
Then you can use 3*2^+x+8 as you equation...
funcVal = ((Number) engine.eval("3*2^+x+8")).doubleValue();
For example...
public double functionVal(double x, String equa) {
try {
System.out.println(equa);
engine.put("x", 10);
funcVal = ((Number) engine.eval(equa)).doubleValue();
System.out.println(equa + " = " + funcVal);
} catch (ScriptException e) {
e.printStackTrace();
}
return funcVal;
}
Related
I am trying to make a program where object1 movement is warped by the gravity of object2. It should get flung. However, for some reason the velocity never decreases or goes negative. I expect for the velocity to change in both directions, but this does not happen. Can anyone tell me why? Here is the code.
Sim.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sim extends JPanel {
private static final long serialVersionUID = -2669101810074157675L;
public static final int PREF_W = 800, PREF_H = 600;
private Mass object1, object2;
private Sim() {
this.setFocusable(true);
this.setBackground(Color.WHITE);
double[] vect1 = {1, 0}, vect2 = {0, 0};
object1 = new Mass(new Point(PREF_W / 2 - 100, PREF_H / 2 - 100), vect1, 10);
object2 = new Mass(new Point(PREF_W / 2 + 100, PREF_H / 2 + 100), vect2, 30);
object2.lock();
gameTimer.start();
}
private Timer gameTimer = new Timer(1000 / 30, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
double[] v = Mass.vector(object1, object2);
object1.dx += v[0];
object1.dy += v[1];
Point p = new Point(
(int) (object1.center.x + object1.dx),
(int) (object1.center.y + object1.dy)
);
object1.center = p;
System.out.println("[" + object1.center.x + "," + object1.dy + "]");
}
});
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fillOval(
(int) object1.center.x - (int) object1.radius,
(int) object1.center.y - (int) object1.radius,
(int) object1.radius,
(int) object1.radius
);
g2.fillOval(
(int) object2.center.x - (int) object2.radius,
(int) object2.center.y - (int) object2.radius,
(int) object2.radius,
(int) object2.radius
);
g2.drawLine(object1.center.x, object1.center.y, object2.center.x, object2.center.y);
repaint();
}
/* METHODS FOR CREATING JFRAME AND JPANEL */
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Gravity Simulation");
JPanel gamePanel = new Sim();
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
Mass.java
import java.awt.Point;
public class Mass {
public static double G = 1f;
public static double deltaTime = 1;
public Point center;
public double mass;
public double radius;
public double dx = 0;
public double dy = 0;
public boolean locked = false;
public Mass(Point center, double[] vect, double mass) {
this.center = center;
this.dx = vect[0];
this.dy = vect[1];
this.mass = mass;
this.radius = mass;
}
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public static double distance(Mass obj1, Mass obj2) {
double dX = obj1.center.x - obj2.center.x;
double dY = obj1.center.y - obj2.center.y;
double ans = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));
return (double) ans;
}
public static double force(Mass obj1, Mass obj2) {
double ans = ((obj1.mass * obj2.mass) / Math.pow(distance(obj1, obj2), 2)) * G;
return (double) ans;
}
public static double[] vector(Mass obj1, Mass obj2) {
double force = force(obj1, obj2);
double dX = Math.abs(obj1.center.x - obj2.center.x);
double dY = Math.abs(obj1.center.y - obj2.center.y);
double udX = dX / distance(obj1, obj2);
double udY = dY / distance(obj1, obj2);
double fx = -(udX * force);
double fy = -(udY * force);
double x = obj1.dx + fx / obj1.mass * deltaTime;
double y = obj1.dy + fy / obj1.mass * deltaTime;
double[] v = {x, y};
return v;
}
}
Thank you in advance. I derived my formula from F = G(m1*m2)/r^2.
I am wring the bouncing ball program in java. And I Now have one bouncing ball, I would like to have at least five bouncing balls. I have tried a few ways to do it, however, I only end up with one ball or error.
Do you have any suggestions on how to proceed? This in the piece of code used for the one ball, is it possible to rewrite this piece of code to get multiple balls in a neat way?
import javafx.scene.shape.Rectangle;
public class World {
private final double width, height;
private Ball[] balls;
private final Rectangle pad;
public World(double width, double height) {
this.width = width;
this.height = height;
balls = new Ball[1];
balls[0] = new Ball(10, 10);
balls[0].setVelocity(75.0, 100.0);
pad = new Rectangle(width / 2, 0.9 * height,
width / 8, height / 32);
}
public void move(long elapsedTimeNs) {
balls[0].move(elapsedTimeNs);
constrainBall(balls[0]);
checkForCollisionWithPad(balls[0]);
}
public Ball[] getBalls() {
return (Ball[]) balls.clone();
}
public Rectangle getPad() {
return pad;
}
public void setPadX(double x) {
if (x > width) {
x = width;
}
if (x < 0) {
x = 0;
}
pad.setX(x);
}
private void constrainBall(Ball ball) {
double x = ball.getX(), y = ball.getY();
double dx = ball.getDx(), dy = ball.getDy();
double radius = ball.getRadius();
if (x < radius) {
dx = Math.abs(dx);
} else if (x > width - radius) {
dx = -Math.abs(dx);
}
if (y < radius) {
dy = Math.abs(dy);
} else if (y > height - radius) {
dy = -Math.abs(dy);
}
ball.setVelocity(dx, dy);
}
private void checkForCollisionWithPad(Ball ball) {
if (ball.intersectsArea(
pad.getX(), pad.getY(), pad.getWidth(), pad.getHeight())) {
double dx = ball.getDx();
// set dy negative, i.e. moving "up"
double newDy = -Math.abs(ball.getDy());
ball.setVelocity(dx, newDy);
}
}
}
Main
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Alert;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Bounce extends Application {
private World world;
private Canvas canvas;
private AnimationTimer timer;
protected class BounceTimer extends AnimationTimer {
private long previousNs = 0;
#Override
public void handle(long nowNs) {
if (previousNs == 0) {
previousNs = nowNs;
}
world.move(nowNs - previousNs);
previousNs = nowNs;
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.WHITESMOKE);
gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
Rectangle pad = world.getPad();
gc.setFill(Color.BLACK);
double x = pad.getX(), y = pad.getY(),
w = pad.getWidth(), h = pad.getHeight();
gc.fillRoundRect(x, y, w, h, h, h);
for (Ball b : world.getBalls()) {
b.paint(gc);
}
}
}
#Override
public void start(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root, 300, 300, Color.WHITESMOKE);
canvas = new Canvas(scene.getWidth(), scene.getHeight());
root.getChildren().add(canvas);
stage.setTitle("Bounce");
stage.setScene(scene);
stage.setResizable(false);
stage.sizeToScene();
stage.show();
world = new World(canvas.getWidth(), canvas.getHeight());
timer = new BounceTimer();
timer.start();
canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED,
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
world.setPadX(me.getX());
}
});
}
public static void main(String[] args) {
launch(args);
}
private void showAlert(String message) {
alert.setHeaderText("");
alert.setTitle("Alert!");
alert.setContentText(message);
alert.show();
}
private final Alert alert = new Alert(Alert.AlertType.INFORMATION);
}
Ball
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
public class Ball {
public static final double BILLION = 1_000_000_000.0;
private double x, y; // position of the balls center
private double dx, dy; // velocity measured in pixels/second
private double radius;
private Color color;
public Ball(double x0, double y0) {
x = x0;
y = y0;
radius = 10;
color = Color.MAGENTA;
}
public Ball(double x0, double y0, double rad, Color col) {
x = x0;
y = y0;
radius = rad;
color = col;
}
Ball(int i, int i0, Color BLUEVIOLET) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void setColor(Color col) { // setColor
color = col; }
public double getX() {
return x;
}
public double getY() {
return y;
}
public void setX(double newX) {
x = newX;
}
public void setY(double newY) {
y = newY;
}
public double getRadius() {
return radius;
}
public double getDx() {
return dx;
}
public double getDy() {
return dy;
}
public void setVelocity(double newDx, double newDy) {
dx = newDx;
dy = newDy;
}
public void moveTo(double newX, double newY) {
x = newX;
y = newY;
}
public void move(long elapsedTimeNs) {
x += dx * elapsedTimeNs / BILLION;
y += dy * elapsedTimeNs / BILLION;
}
public void paint(GraphicsContext gc) {
gc.setFill(color);
// arguments to fillOval: see the javadoc for GraphicsContext
gc.fillOval(x - radius, y - radius, radius * 2, radius * 2);
}
public boolean intersectsArea(
double rectX, double rectY,
double rectWidth, double rectHeight) {
double closestX = clamp(x, rectX, rectX + rectWidth);
double closestY = clamp(y, rectY, rectY + rectHeight);
double distanceX = x - closestX;
double distanceY = y - closestY;
return (distanceX * distanceX) + (distanceY * distanceY)
< (radius * radius);
}
private double clamp(double value, double lower, double upper) {
if (value < lower) {
return lower;
}
if (value > upper) {
return upper;
}
return value;
}
}
As Stormblessed said, you are only targeting one ball in your move method.
You should do:
public void move(Ball ball, long elapsedTimeNs) {
ball.move(elapsedTimeNs);
constrainBall(ball);
checkForCollisionWithPad(ball);
}
Edit: Since you want the handler method to accept only the elapsedTimeNs argument, do:
public void move(long elapsedTimeNs) {
for (Ball ball : balls) {
ball.move(elapsedTimeNs);
constrainBall(ball);
checkForCollisionWithPad(ball);
}
}
Edit 2: You should probably have a method that creates a new ball, for convenience:
public Ball newBall(double x, double y, double velocity1, double velocity2) {
Ball tmp = new Ball(x, y);
tmp.setVelocity(velocity1, velocity2);
balls.add(tmp);
return tmp;
}
Edit 3: The reason it throws an error is that you designated balls to have only one index position by using balls = new Ball[1]. You should use an ArrayList (java.util.ArrayList) instead, like so:
import java.util.ArrayList;
ArrayList<Ball> balls = new ArrayList<>;
You should now use balls.add and balls.get instead of = and []. References have been updated accordingly.
I am trying to make a Mandelbrot Set renderer with a GUI where you can click and drag to zoom into a specific area. When run, it will do the initial calculations and rendering fine, but when you try to click and drag to zoom in, the console says it is doing the calculations, but the content of the JFrame is not updated.
However, I'm not even positive that it is recalculating, because the initial calculation takes about 8 seconds but when you click/drag to zoom it takes about 6 ms.
I have posted my code below.
Complex Numbers Class
public class Complex {
private double real, imag;
// Constructors
public Complex(){
real=0.0;
imag=0.0;
}
public Complex(double real, double imag) {
this.real=real;
this.imag=imag;
}
// add given complex number to this one, returning the Complex result
public Complex add(Complex other) {
return new Complex(this.real+other.real, this.imag+other.imag);
}
// multiply given complex number by this one, returning the Complex result
public Complex multiply(Complex other) {
return new Complex((this.real*other.real)-(this.imag*other.imag), (this.imag*other.real)+(this.real*other.imag));
}
// get the magnitude of this complex number
public double getMagnitude() {
return Math.sqrt((real*real)+(imag*imag));
}
}
Runnable MandelbrotTask Class
public class MandelbrotTask implements Runnable {
private double x1, y1, x2, y2;
private int startCol, endCol, startRow, endRow, maxIters;
private int[][] iterCounts;
public MandelbrotTask(int maxIters, double x1, double y1, double x2, double y2, int startCol, int endCol, int startRow, int endRow, int[][] iterCounts) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.startCol = startCol;
this.endCol = endCol;
this.startRow = startRow;
this.endRow = endRow;
this.iterCounts = iterCounts;
this.maxIters=maxIters;
}
#Override
public void run() {
for (int i = startRow; i < endRow; i++) {
for (int j = startCol; j < endCol; j++) {
Complex c = getComplex(i, j);
int iterCount = countIters(c);
iterCounts[i][j] = iterCount;
}
}
}
public Complex getComplex(int i, int j){
//output image is 600 X 600 pixels
double incrementX;
double incrementY;
if(x2!=x1){
incrementX=(Math.abs(x2-x1)/600);
}
else{
throw new ArithmeticException("Error: area=0");
}
if(y2!=y1){
incrementY=(Math.abs(y2-y1)/600);
}
else{
throw new ArithmeticException("Error: area=0");
}
return new Complex(x1+((double)i*incrementX), y1+((double)j*incrementY));
}
public int countIters(Complex c){
Complex z=new Complex(0, 0);
int iters=0;
while(z.getMagnitude()<2 && iters<=maxIters){
z=z.multiply(z).add(c);
iters++;
}
return iters;
}
}
Main Mandelbrot Class
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Scanner;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Mandelbrot {
private static final int HEIGHT = 600;
private static final int WIDTH = 600;
private static final int maxIters=50000;
private static Rectangle zoomBox;
private static Point initialClick;
private static JLabel content; //bufferedImage will be put into this JLabel
private static int[][] iterCounts;
private static BufferedImage bufferedImage; //rendering will be written to this bufferedImage
private static JFrame frame;
public static void main(String[] args) throws IOException {
zoomBox=null;
Scanner keyboard = new Scanner(System.in);
double x1 = -2;
double y1 = -2;
double x2 = 2;
double y2 = 2;
/*System.out.print("Max iterations (16,581,375 supported): ");
int maxIters=50000;
if(maxIters>16581375){
throw new UnsupportedOperationException("Error: Max Iterations: Overflow.");
}
System.out.print("Output filename: ");
String fileName = keyboard.next();
if(!fileName.endsWith(".png") && !fileName.endsWith(".PNG")){
fileName=fileName + ".png";
}*/
// TODO: create the rendering, save it to a file
iterCounts=new int[WIDTH][HEIGHT];
recalculate(x1, y1, x2, y2, iterCounts);
bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
MouseAdapter listener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
handleMousePressed(e);
}
#Override
public void mouseDragged(MouseEvent e) {
handleMouseDragged(e);
}
#Override
public void mouseReleased(MouseEvent e) {
handleMouseReleased(e);
}
};
content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, true)));
content.addMouseListener(listener);
content.addMouseMotionListener(listener);
/*OutputStream os = new BufferedOutputStream(new FileOutputStream(fileName));
try {
ImageIO.write(bufferedImage, "PNG", os);
} finally {
os.close();
}*/
frame = new JFrame("Mandelbrot Viewer");
frame.getContentPane().add(content);
frame.pack();
frame.setSize(new Dimension(WIDTH, HEIGHT));
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static BufferedImage render(int[][] iterCounts, BufferedImage bufferedImage, Rectangle zoomBox, boolean updated){
bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics g = bufferedImage.getGraphics();
Graphics2D g2=(Graphics2D) g;
if(updated){
for(int i=0; i<WIDTH; i++){
for(int j=0; j<HEIGHT; j++){
if(iterCounts[i][j]<maxIters){
String hexCode= String.format("#%06x", (0xFFFFFF & (32*iterCounts[i][j])));
g.setColor(Color.decode(hexCode));
}
else{
g.setColor(Color.CYAN);
}
g.drawLine(i, j, i, j);
}
}
}
else{
if(zoomBox!=null){
g2.setStroke(new BasicStroke(7));
g2.draw(zoomBox);
}
}
return bufferedImage;
}
public static int[][] recalculate(double x1, double y1, double x2, double y2, int[][] iterCounts){
MandelbrotTask[] tasks=new MandelbrotTask[4];
tasks[0]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 0, HEIGHT/4, iterCounts);
tasks[1]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, HEIGHT/4, 2*(HEIGHT/4), iterCounts);
tasks[2]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 2*(HEIGHT/4), 3*(HEIGHT/4), iterCounts);
tasks[3]=new MandelbrotTask(maxIters, x1, y1, x2, y2, 0, WIDTH, 3*(HEIGHT/4), 4*(HEIGHT/4), iterCounts);
//parallelize computation
Thread[] threads=new Thread[4];
for(int i=0; i<4; i++){
threads[i]=new Thread(tasks[i]);
}
System.out.println("Working...");
//start timer, start computation
long start=System.currentTimeMillis();
for(int i=0; i<4; i++){
threads[i].start();
}
for(int i=0; i<4; i++){
try {
threads[i].join();
} catch (InterruptedException e) {
System.err.println("A thread was interrupted.");
}
}
//end timer
long end=System.currentTimeMillis();
long elapsed=end-start;
System.out.println("Done.");
System.out.println("Took " + elapsed + " ms.");
return iterCounts;
}
protected static void handleMousePressed(MouseEvent e) {
initialClick=e.getPoint();
}
protected static void handleMouseDragged(MouseEvent e) {
if(e.getX()>e.getY()){
zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getX()), (int)(e.getY()-initialClick.getX()));
}
else if(e.getY()>e.getX()){
zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getY()), (int)(e.getY()-initialClick.getY()));
}
else{
zoomBox=new Rectangle((int)initialClick.getX(), (int)initialClick.getY(), (int)(e.getX()-initialClick.getX()), (int)(e.getY()-initialClick.getY()));
}
content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false)));
content.repaint();
}
protected static void handleMouseReleased(MouseEvent e) {
recalculate(initialClick.getX(), initialClick.getY(), e.getX(), e.getY(), iterCounts);
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
zoomBox=null;
content=new JLabel(new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false)));
content.repaint();
}
});
}
}
For one, you're creating a new JLabel with each re-iteration, and this JLabel is being added to nothing.
Instead use the same JLabel but rather create a new ImageIcon and set the viewed JLabel's Icon.
ImageIcon icon = new ImageIcon(render(iterCounts, bufferedImage, zoomBox, false));
content.setIcon(icon);
You also don't seem to be doing anything with the int arrays returned from recalculate.
Shouldn't your handleMouseReleased method have:
iterCounts = recalculate(initialClick.getX(), initialClick.getY(),
e.getX(), e.getY(), iterCounts);
Also, you still have bad threading -- you're calling join on your Threads from within the Swing event thread, something almost sure to freeze your GUI. Use a SwingWorker and then after getting notified from the worker when it's done, use its data. Also you're grossly over using static variables in your GUI. Make your GUI components instance fields, not static fields.
There are more logical errors that I've yet to find I'm afraid...
Second iteration of program -- With the Mandelbrot calculations:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class Mandel2 extends JPanel {
private static final int GUI_HEIGHT = 600;
private static final int GUI_WIDTH = 600;
private static final int MAX_ITERS = 50000;
private BufferedImage image = new BufferedImage(GUI_WIDTH, GUI_HEIGHT,
BufferedImage.TYPE_INT_ARGB);
private Rectangle zoomRect;
private double myX0 = -2.5;
private double myY0 = -2.0;
private double myX1 = 1.5;
private double myY1 = 2.0;
private JDialog waitDialog;
public Mandel2() {
final MyMouse myMouse = new MyMouse();
int delayStartingCalc = 2 * 1000; // 2 second delay
Timer timer = new Timer(delayStartingCalc, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
Rectangle myRect = new Rectangle(0, 0, GUI_WIDTH, GUI_HEIGHT);
createMandel(myRect);
}
});
timer.setRepeats(false);
timer.start();
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(GUI_WIDTH, GUI_HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, this);
}
Graphics2D g2 = (Graphics2D) g;
if (zoomRect == null) {
return;
}
g2.setXORMode(Color.gray);
g2.draw(zoomRect);
}
private double screenToLogicalX(double screenX) {
return myX0 + (screenX * (myX1 - myX0)) / GUI_WIDTH;
}
private double screenToLogicalY(double screenY) {
return myY0 + ((GUI_HEIGHT - screenY) * (myY1 - myY0)) / GUI_HEIGHT;
}
private void createMandel(Rectangle myRect) {
double x0 = screenToLogicalX(myRect.x);
double y0 = screenToLogicalY(myRect.y + myRect.height);
double x1 = screenToLogicalX(myRect.x + myRect.width);
double y1 = screenToLogicalY(myRect.y);
myX0 = x0;
myY0 = y0;
myX1 = x1;
myY1 = y1;
MandelWorker mandelWorker = new MandelWorker(MAX_ITERS, x0, y0, x1, y1);
mandelWorker.addPropertyChangeListener(new MandelWorkerListener());
mandelWorker.execute();
if (waitDialog == null) {
Window win = SwingUtilities.getWindowAncestor(Mandel2.this);
JProgressBar jProgressBar = new JProgressBar();
jProgressBar.setIndeterminate(true);
waitDialog = new JDialog(win, "Please Wait", ModalityType.APPLICATION_MODAL);
waitDialog.add(jProgressBar);
waitDialog.pack();
waitDialog.setLocationRelativeTo(win);
}
waitDialog.setVisible(true);
}
private class MyMouse extends MouseAdapter {
private Point p;
#Override
public void mousePressed(MouseEvent e) {
p = e.getPoint();
}
public void mouseDragged(MouseEvent e) {
zoomRect = createRect(e);
repaint();
};
#Override
public void mouseReleased(MouseEvent e) {
zoomRect = createRect(e);
repaint();
createMandel(zoomRect);
}
private Rectangle createRect(MouseEvent e) {
int x = Math.min(p.x, e.getX());
int y = Math.min(p.y, e.getY());
int width = Math.abs(p.x - e.getX());
int height = Math.abs(p.y - e.getY());
return new Rectangle(x, y, width, height);
}
}
private class MandelWorkerListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
waitDialog.setVisible(false);
waitDialog.dispose();
MandelWorker worker = (MandelWorker) evt.getSource();
try {
image = worker.get();
zoomRect = null;
repaint();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
private class MandelWorker extends SwingWorker<BufferedImage, Void> {
private int maxIters;
private double x1;
private double y1;
private double x2;
private double y2;
public MandelWorker(int maxIters, double x1, double y1, double x2, double y2) {
this.maxIters = maxIters;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
#Override
protected BufferedImage doInBackground() throws Exception {
int[][] iterGrid = new int[GUI_HEIGHT][GUI_WIDTH];
for (int i = 0; i < GUI_HEIGHT; i++) {
double y = y1 + i * (y2 - y1) / GUI_HEIGHT;
for (int j = 0; j < GUI_WIDTH; j++) {
double x = x1 + j * (x2 - x1) / GUI_WIDTH;
int iIndex = GUI_HEIGHT - i - 1;
iterGrid[iIndex][j] = calcMandel(x, y);
}
}
return render(iterGrid);
}
private BufferedImage render(int[][] iterGrid) {
int w = GUI_WIDTH;
int h = GUI_HEIGHT;
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
if (iterGrid[i][j] < maxIters) {
String hexCode = String.format("#%06x", (0xFFFFFF & (32 * iterGrid[i][j])));
g2.setColor(Color.decode(hexCode));
} else {
g2.setColor(Color.CYAN);
}
g2.drawLine(j, i, j, i);
}
}
g2.dispose();
return img;
}
private int calcMandel(double x, double y) {
Complex c = new Complex(x, y);
Complex z = new Complex();
int iters = 0;
while (z.getMagnitude() < 2 && iters <= maxIters) {
z = z.multiply(z).add(c);
iters++;
}
return iters;
}
}
private class Complex {
private double real, imag;
// Constructors
public Complex() {
real = 0.0;
imag = 0.0;
}
public Complex(double real, double imag) {
this.real = real;
this.imag = imag;
}
// add given complex number to this one, returning the Complex result
public Complex add(Complex other) {
return new Complex(this.real + other.real, this.imag + other.imag);
}
// multiply given complex number by this one, returning the Complex
// result
public Complex multiply(Complex other) {
return new Complex((this.real * other.real) - (this.imag * other.imag),
(this.imag * other.real) + (this.real * other.imag));
}
// get the magnitude of this complex number
public double getMagnitude() {
return Math.sqrt((real * real) + (imag * imag));
}
}
private static void createAndShowGui() {
Mandel2 mainPanel = new Mandel2();
JFrame frame = new JFrame("Mandel2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.setResizable(false);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Second iteration -- it does the calculations, but is not very efficient at it.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I draw a (co)sinusoid signal in the time domain using a JPanel and overriding its paintComponent(Graphics g).
When i do a resize my application starts flickering and appears a black background until the resizing is not finished.
Is there a way to prevent this sort of flickering?
Thanks in advance.
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Dimension;
public class AnotherSignalGraph extends JPanel {
private double offset = 0.0;
private double scaleX = 0.0;
private double scaleY = 0.0;
protected double maxX = 0.0;
private double minX = 0.0;
private double maxY = 0.0;
private double minY = 0.0;
private Point[] points = null;
private Signal s = null;
private void initPoints(int pointsToGenerate) {
this.points = new Point[pointsToGenerate];
double t = 0;
double dt = this.maxX / pointsToGenerate;
for (int i=0; i<pointsToGenerate; i++, t += dt)
points[i] = new Point(t, this.s.v(t));
}
public AnotherSignalGraph(int pointsToGenerate, int width, int height) {
super.setPreferredSize(new Dimension(width, height));
this.s = new Sinusoid(1000, 10, 0);
this.maxX = (1 / this.s.frequency) * 2;
this.maxY = this.s.amplitude;
this.minY = -this.maxY;
this.offset = super.getHeight() / 2.0;
this.initPoints(pointsToGenerate);
this.updateScale();
}
private void updateScale() {
this.scaleX = super.getWidth() / (this.maxX - this.minX);
this.scaleY = super.getHeight() / (this.maxY - this.minY);
this.offset = super.getHeight() / 2.0;
}
private void drawSignal(Graphics2D g2D) {
int x1, x2, y1, y2;
g2D.setColor(Color.blue);
g2D.setClip(0, (int)(this.offset - (this.s.amplitude * this.scaleY)), (int)(super.getWidth()), (int)((this.s.amplitude * 2 * this.scaleY) + 2));
for (int i=0; i<this.points.length - 1; i++) {
x1 = (int)(this.scaleX * this.points[i].getX());
y1 = (int)(this.offset - (this.scaleY * this.points[i].getY()));
x2 = (int)(scaleX * this.points[i + 1].getX());
y2 = (int)(this.offset - (this.scaleY * this.points[i + 1].getY()));
g2D.drawLine(x1, y1, x2, y2);
}
}
private void graphIt(Graphics2D g2D) {
this.updateScale();
this.drawSignal(g2D);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
this.graphIt(g2D);
}
private class Point {
private double x;
private double y;
public Point(double x, double y){
this.x = x;
this.y = y;
}
public double getX(){
return this.x;
}
public double getY(){
return this.y;
}
}
private abstract class Signal {
double frequency; //Signal frequency in Hz
double amplitude; //Signal amplitude in Volt
double initPhase; //Signal initial phase in radians
public Signal(double frequency, double amplitude, double degInitPhase) throws IllegalArgumentException{ //degInitPhase parameter specifies the initial angle phase in degrees
this.frequency = frequency;
this.amplitude = amplitude;
this.initPhase = degInitPhase;
}
public abstract double v(double t);
}
private class Sinusoid extends Signal {
public Sinusoid(double frequency, double amplitude, double degInitPhase){ //degInitPhase parameter specifies the initial angle phase in degrees
super(frequency, amplitude, degInitPhase);
}
#Override
public double v(double t){ //t parameter indicates the specific time frame in seconds
return super.amplitude * Math.sin((super.frequency * Math.PI * t) + super.initPhase);
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.add(new AnotherSignalGraph(50000, 600, 600));
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I'm looking for a java code to create a oriented minimum bounding box with points, which have a lat/lon value. I've already created a minimum bounding box, like this:
public Mbb boundingBox() {
Point ll, ur;
Mbb bBox;
int id =1;
ll = new Point(id, Double.MAX_VALUE, Double.MAX_VALUE);
ur= new Point(id,-1*Double.MAX_VALUE, -1*Double.MAX_VALUE);
if (this.pg.size() <=1)
return null;
double minLat = Double.MAX_VALUE;
double minLong = Double.MAX_VALUE;
double maxLat = Double.MAX_VALUE*-1;
double maxLong = Double.MAX_VALUE*-1;
for (Point testPoint: this.pg) {
double lat = testPoint.getLat();
double lon = testPoint.getLon();
if(minLat>lat)
minLat=lat;
if(minLong>lon)
minLong=lon;
if(maxLat<lat)
maxLat=lat;
if(maxLong<lon)
maxLong=lon;
}
ll.setLat(minLat);
ll.setLon(minLong);
ur.setLat(maxLat);
ur.setLon(maxLong);
bBox= new Mbb(id, ll, ur);
return bBox;
}
But this is not an oriented one. Has anybody an idea how to orient my bounding box?
The computation if the minimum oriented bounding box is not so trivial. But one approach is described in an answer to https://gis.stackexchange.com/q/22895 . The answer links to a Java implementation, but this is obviously part of a larger framework. However, I implemented the approach here as an example:
The computation of the convex hull is made with a code snippet taken from convex-hull at Google code - it's not the nicest implementation, but was the first one that I found, and does the job.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MinOrientedBoundingBoxTest
{
public static void main(String[] args) throws IOException
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new MinOrientedBoundingBoxTestPanel());
f.setSize(500,500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class MinOrientedBoundingBoxTestPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private final List<Point2D> points;
private Point2D draggedPoint = null;
MinOrientedBoundingBoxTestPanel()
{
points = new ArrayList<Point2D>();
Random r = new Random(0);
for (int i=0; i<8; i++)
{
double x = 200 + r.nextDouble() * 200;
double y = 200 + r.nextDouble() * 200;
points.add(new Point2D.Double(x,y));
}
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
drawPoints(g, points);
boolean showConvexHull = false;
showConvexHull = true;
if (showConvexHull)
{
List<Point2D> convexHullPoints =
MinOrientedBoundingBoxComputer.computeConvexHullPoints(
points);
Path2D convexHullPath =
MinOrientedBoundingBoxComputer.createPath(
convexHullPoints);
g.setColor(Color.GRAY);
g.draw(convexHullPath);
}
List<Point2D> minObbCorners =
MinOrientedBoundingBoxComputer.computeCorners(points);
Path2D p = MinOrientedBoundingBoxComputer.createPath(minObbCorners);
g.setColor(Color.BLUE);
g.draw(p);
}
static void drawPoints(Graphics2D g, List<Point2D> points)
{
double r = 3;
for (Point2D point : points)
{
double x = point.getX();
double y = point.getY();
g.fill(new Ellipse2D.Double(
x-r, y-r, r+r, r+r));
}
}
#Override
public void mouseDragged(MouseEvent e)
{
if (draggedPoint != null)
{
draggedPoint.setLocation(e.getPoint());
repaint();
}
}
#Override
public void mouseMoved(MouseEvent e)
{
}
#Override
public void mouseClicked(MouseEvent e)
{
}
#Override
public void mousePressed(MouseEvent e)
{
draggedPoint = null;
double thresholdSquared = 10*10;
double minDs = Double.MAX_VALUE;
for (Point2D point : points)
{
double ds = point.distanceSq(e.getPoint());
if (ds < thresholdSquared && ds < minDs)
{
minDs = ds;
draggedPoint = point;
}
}
}
#Override
public void mouseReleased(MouseEvent e)
{
draggedPoint = null;
}
#Override
public void mouseEntered(MouseEvent e)
{
}
#Override
public void mouseExited(MouseEvent e)
{
}
}
class MinOrientedBoundingBoxComputer
{
static List<Point2D> computeCorners(List<Point2D> points)
{
List<Point2D> convexHullPoints =
computeConvexHullPoints(points);
int alignmentPointIndex =
computeAlignmentPointIndex(convexHullPoints);
Rectangle2D r = computeAlignedBounds(
convexHullPoints, alignmentPointIndex);
List<Point2D> alignedCorners = new ArrayList<Point2D>();
alignedCorners.add(new Point2D.Double(r.getMinX(), r.getMinY()));
alignedCorners.add(new Point2D.Double(r.getMaxX(), r.getMinY()));
alignedCorners.add(new Point2D.Double(r.getMaxX(), r.getMaxY()));
alignedCorners.add(new Point2D.Double(r.getMinX(), r.getMaxY()));
Point2D center = convexHullPoints.get(alignmentPointIndex);
double angleRad = computeEdgeAngleRad(
convexHullPoints, alignmentPointIndex);
AffineTransform at = new AffineTransform();
at.concatenate(
AffineTransform.getTranslateInstance(
center.getX(), center.getY()));
at.concatenate(
AffineTransform.getRotateInstance(angleRad));
List<Point2D> corners = transform(alignedCorners, at);
return corners;
}
private static int computeAlignmentPointIndex(
List<Point2D> points)
{
double minArea = Double.MAX_VALUE;
int minAreaIndex = -1;
for (int i=0; i<points.size(); i++)
{
Rectangle2D r = computeAlignedBounds(points, i);
double area = r.getWidth() * r.getHeight();
if (area < minArea)
{
minArea = area;
minAreaIndex = i;
}
}
return minAreaIndex;
}
private static double computeEdgeAngleRad(
List<Point2D> points, int index)
{
int i0 = index;
int i1 = (i0+1)%points.size();
Point2D p0 = points.get(i0);
Point2D p1 = points.get(i1);
double dx = p1.getX() - p0.getX();
double dy = p1.getY() - p0.getY();
double angleRad = Math.atan2(dy, dx);
return angleRad;
}
private static Rectangle2D computeAlignedBounds(
List<Point2D> points, int index)
{
Point2D p0 = points.get(index);
double angleRad = computeEdgeAngleRad(points, index);
AffineTransform at = createTransform(-angleRad, p0);
List<Point2D> transformedPoints = transform(points, at);
Rectangle2D bounds = computeBounds(transformedPoints);
return bounds;
}
private static AffineTransform createTransform(
double angleRad, Point2D center)
{
AffineTransform at = new AffineTransform();
at.concatenate(
AffineTransform.getRotateInstance(angleRad));
at.concatenate(
AffineTransform.getTranslateInstance(
-center.getX(), -center.getY()));
return at;
}
private static List<Point2D> transform(
List<Point2D> points, AffineTransform at)
{
List<Point2D> result = new ArrayList<Point2D>();
for (Point2D p : points)
{
Point2D tp = at.transform(p, null);
result.add(tp);
}
return result;
}
private static Rectangle2D computeBounds(
List<Point2D> points)
{
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = -Double.MAX_VALUE;
double maxY = -Double.MAX_VALUE;
for (Point2D p : points)
{
double x = p.getX();
double y = p.getY();
minX = Math.min(minX, x);
minY = Math.min(minY, y);
maxX = Math.max(maxX, x);
maxY = Math.max(maxY, y);
}
return new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY);
}
static Path2D createPath(List<Point2D> points)
{
Path2D path = new Path2D.Double();
for (int i=0; i<points.size(); i++)
{
Point2D p = points.get(i);
double x = p.getX();
double y = p.getY();
if (i == 0)
{
path.moveTo(x, y);
}
else
{
path.lineTo(x, y);
}
}
path.closePath();
return path;
}
static List<Point2D> computeConvexHullPoints(List<Point2D> points)
{
// NOTE: Converting from Point2D to Point here
// because the FastConvexHull class expects
// the points with integer coordinates.
// This should be generalized to Point2D!
ArrayList<Point> ps = new ArrayList<Point>();
for (Point2D p : points)
{
ps.add(new Point((int)p.getX(), (int)p.getY()));
}
List<Point> convexHull = FastConvexHull.execute(ps);
List<Point2D> result = new ArrayList<Point2D>();
for (Point p : convexHull)
{
double x = p.getX();
double y = p.getY();
result.add(new Point2D.Double(x,y));
}
return result;
}
}
// From https://code.google.com/p/convex-hull/source/browse/
// Convex+Hull/src/algorithms/FastConvexHull.java?r=4
// Under GPL2 license
// (Not a "nice" implementation, but the first one that
// I found with a websearch. Maybe, when I'm bored, I'll
// replace it with another one...)
class FastConvexHull
{
public static ArrayList<Point> execute(ArrayList<Point> points)
{
ArrayList<Point> xSorted = (ArrayList<Point>) points.clone();
Collections.sort(xSorted, new XCompare());
int n = xSorted.size();
Point[] lUpper = new Point[n];
lUpper[0] = xSorted.get(0);
lUpper[1] = xSorted.get(1);
int lUpperSize = 2;
for (int i = 2; i < n; i++)
{
lUpper[lUpperSize] = xSorted.get(i);
lUpperSize++;
while (lUpperSize > 2 &&
!rightTurn(lUpper[lUpperSize - 3], lUpper[lUpperSize - 2],
lUpper[lUpperSize - 1]))
{
// Remove the middle point of the three last
lUpper[lUpperSize - 2] = lUpper[lUpperSize - 1];
lUpperSize--;
}
}
Point[] lLower = new Point[n];
lLower[0] = xSorted.get(n - 1);
lLower[1] = xSorted.get(n - 2);
int lLowerSize = 2;
for (int i = n - 3; i >= 0; i--)
{
lLower[lLowerSize] = xSorted.get(i);
lLowerSize++;
while (lLowerSize > 2 &&
!rightTurn(lLower[lLowerSize - 3], lLower[lLowerSize - 2],
lLower[lLowerSize - 1]))
{
// Remove the middle point of the three last
lLower[lLowerSize - 2] = lLower[lLowerSize - 1];
lLowerSize--;
}
}
ArrayList<Point> result = new ArrayList<Point>();
for (int i = 0; i < lUpperSize; i++)
{
result.add(lUpper[i]);
}
for (int i = 1; i < lLowerSize - 1; i++)
{
result.add(lLower[i]);
}
return result;
}
private static boolean rightTurn(Point a, Point b, Point c)
{
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) > 0;
}
private static class XCompare implements Comparator<Point>
{
#Override
public int compare(Point o1, Point o2)
{
return (new Integer(o1.x)).compareTo(new Integer(o2.x));
}
}
}