I have a group of triangles which move around based on a flocking algorithm. I have used a path transition from JavaFX so that they move along a set pathway while retaining the flocking behavior within that pathway. However, when I try to slow down the path transition by setting the duration in milliseconds, the triangles either move very quickly or remain stuck in one place. I have tried looking for other solutions to this but haven't been able to find any and so I am posing this here.
package model;
import utility.Vector2d;
import java.util.Random;
import javafx.animation.PathTransition;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.util.Duration;
public class BusinessPersonBoid extends Circle{
public Vector2d position;
protected Vector2d position2;
protected Vector2d position3;
protected Vector2d position4;
protected Vector2d position5;
protected Vector2d velocity;
protected final int speedLimit = 2;
protected String name;
protected ImageView display;
protected Circle circle;
public LineTo line1;
public LineTo line2;
public LineTo line3;
public LineTo line4;
public BusinessPersonBoid(int x, int y, String n, Circle c) {
this.display = new ImageView();
this.display.setX(x);
this.display.setY(y);
this.display.setSmooth(true);
this.display.setCache(true);
this.display.setImage(new Image("images2/boid-green.png"));
this.circle = new Circle(15,Color.BLUE);
this.position = new Vector2d(x, y);
this.velocity = new Vector2d(0,0);
if( n != null) {
this.name = n;
}
}
public Vector2d getPosition() {
return new Vector2d(position.xPos, position.yPos);
}
public Vector2d getVelocity() {
return velocity;
}
public void setVelocity(Vector2d velocity) {
if ( velocity.xPos > speedLimit) {
velocity.xPos = speedLimit;
}
if ( velocity.yPos > speedLimit) {
velocity.yPos = speedLimit;
}
this.velocity = velocity;
}
public void setPosition(Vector2d pos) {
setAngle(this.position, pos);
this.display.setX(pos.xPos);
this.display.setY(pos.yPos);
this.position = pos;
}
public void setPosition2(Vector2d pos, Vector2d pos2) {
setAngle(this.position, pos);
this.display.setX(pos.xPos);
this.display.setY(pos.yPos);
this.display.setX(pos.xPos);
this.display.setY(pos.yPos);
this.position = pos;
}
public void setPathway() {
Random randNum = new Random();
Path path = new Path();
MoveTo moveTo = new MoveTo(108, 71);
LineTo line1 = new LineTo(150, 140);
LineTo line2 = new LineTo(550, 140);
LineTo line3 = new LineTo(750, 140);
LineTo line4 = new LineTo(910, -40);
path.getElements().add(moveTo);
path.getElements().addAll(line1, line2, line3, line4);
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(100));
pathTransition.setNode(display);
pathTransition.setPath(path);
pathTransition.play();
}
public void setAngle(Vector2d current, Vector2d next) {
double delta_x = next.xPos - current.xPos;
double delta_y = next.yPos - current.yPos;
Double theta = Math.atan2(delta_y, delta_x);
double angle = theta*180/Math.PI;
this.display.setRotate(angle);
}
public String getName() {
return this.name;
}
public ImageView getDisplay() {
return this.display;
}
}
Here is the flocking class:
package model;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import utility.Vector2d;
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
public class Flock2 {
public ArrayList<BusinessPersonBoid> boids2;
private double movementFactor = 1000;
private double boundingFactor = 10;
private int seperationDistance = 43;
private double seperationFactor = 50;
public BusinessPersonBoid cBoid;
public Vector2d Checkin;
public Vector2d CurrencyExchange;
public Vector2d Lounge;
public Vector2d Shop;
public Vector2d BoardingGate;
Circle c = new Circle();
Circle c2 = new Circle();
public Flock2() {
boids2 = new ArrayList<BusinessPersonBoid>();
Random randNum = new Random();
RandomName randName = new RandomName();
for(int i = 0; i < 10; i++) {
int x = randNum.nextInt(50) + 1;
int y = randNum.nextInt(140) + 1;
boids2.add(new BusinessPersonBoid(x,y, randName.getName(), c ));
}
}
public void updateBoidsPostion() {
Vector2d v1, v2, v3, v4, v5 = new Vector2d();
for (BusinessPersonBoid cBoid : boids2) {
v1 = groupFlock(cBoid);
v2 = collisionAvoidance(cBoid);
v3 = matchFlockVelocity(cBoid);
v4 = bounding(cBoid);
v5 = positionTend(cBoid);
Vector2d v12 = new Vector2d(-50,140);
Vector2d sum = new Vector2d();
sum = sum.add(v1);
sum = sum.add(v2);
sum = sum.add(v3);
sum = sum.add(v4);
sum = sum.add(v5);
cBoid.setPathway();
cBoid.setVelocity(cBoid.getVelocity().add(sum));
Vector2d next = new Vector2d( cBoid.getPosition().add(cBoid.getVelocity()) );
cBoid.setPosition(next);
}
}
private Vector2d groupFlock(BusinessPersonBoid cBoid) {
Vector2d center = new Vector2d();
for (BusinessPersonBoid aBoid : boids2) {
if(!aBoid.equals(cBoid)) {
center = center.add(aBoid.getPosition());
}
}
center = center.division(boids2.size() - 1 );
center = center.subtract(cBoid.getPosition());
center = center.division(movementFactor);
return center;
}
private Vector2d collisionAvoidance(BusinessPersonBoid cBoid) {
Vector2d correction = new Vector2d();
Vector2d cPosition = new Vector2d(cBoid.getPosition());
for (BusinessPersonBoid aBoid : boids2) {
if (!aBoid.equals(cBoid)) {
Vector2d aPosition = aBoid.getPosition();
Vector2d xD = new Vector2d(aPosition.xPos - cPosition.xPos, aPosition.yPos - cPosition.yPos);
if(Math.abs(xD.xPos) < seperationDistance && Math.abs(xD.yPos) < seperationDistance) {
correction = correction.subtract(xD).division(seperationFactor);
}
}
}
return correction;
}
private Vector2d matchFlockVelocity(BusinessPersonBoid cBoid) {
Vector2d perceivedVelocity = new Vector2d();
for(BusinessPersonBoid aBoid : boids2) {
if(!aBoid.equals(cBoid)) {
perceivedVelocity = perceivedVelocity.add(aBoid.getVelocity());
}
}
perceivedVelocity = perceivedVelocity.division(boids2.size() - 1);
perceivedVelocity = perceivedVelocity.subtract(cBoid.getVelocity());
perceivedVelocity = perceivedVelocity.division(8);
return perceivedVelocity;
}
private Vector2d bounding(BusinessPersonBoid cBoid) {
Vector2d bound = new Vector2d();
int xMin = 0, xMax = 1400, yMin = 0, yMax = 320;
Vector2d cPos = cBoid.getPosition();
if (cPos.xPos < xMin) {
bound.xPos += 1;
cPos.xPos = bound.xPos;
} else if (cPos.xPos > xMax){
bound.xPos += -100;
cPos.xPos = bound.xPos;
}
if (cPos.yPos < yMin) {
bound.yPos += 1;
cPos.yPos = bound.yPos;
} else if (cPos.yPos > yMax){
bound.yPos += -100;
cPos.yPos = bound.yPos;
}
bound = bound.division(boundingFactor);
return bound;
}
private Vector2d positionTend(BusinessPersonBoid cBoid) {
Vector2d place = new Vector2d(900,600);
Vector2d tend = new Vector2d();
tend = new Vector2d(place.subtract(cBoid.getPosition()));
tend.division(6000);
return tend;
}
public List<ImageView> getBoidsRepresentation() {
List<ImageView> circles = new ArrayList<ImageView>();
for(BusinessPersonBoid aBoid : boids2) {
circles.add( aBoid.getDisplay() );
}
return circles;
}
private Vector2d bounding2(BusinessPersonBoid cBoid) {
Vector2d bound = new Vector2d();
int xMin = 0, xMax = 600, yMin = 0, yMax = 600;
Vector2d cPos = cBoid.getPosition();
bound.xPos = 100;
cPos.xPos = bound.xPos;
bound.yPos = 100;
cPos.yPos = bound.yPos;
bound = bound.division(boundingFactor);
return bound;
}
private Vector2d pathway(BusinessPersonBoid cBoid) {
Vector2d pathway = new Vector2d();
Path path = new Path();
path.getElements().add(new MoveTo(20,20));
path.getElements().add(new CubicCurveTo(380, 0, 380, 120, 200, 120));
path.getElements().add(new CubicCurveTo(0, 120, 0, 240, 380, 240));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(4000));
pathTransition.setPath(path);
pathTransition.setNode(cBoid);
return pathway;
}
public int tendToPlace(BusinessPersonBoid cBoid) {
Vector2d place = new Vector2d(-50,140);
return place.minus(cBoid.position)/100;
}
My question is how do I slow the triangles down to a speed where you can actually see them moving properly?
RandomName class:
package model;
import java.util.ArrayList;
import java.util.Random;
public class RandomName {
private Random randomGenerator;
private ArrayList<Integer> usedList;
private String[] nameBank = { "Alpha",
"Bravo",
"Charlie",
"Delta",
"Echo",
"Foxtrot",
"Golf",
"Hotel",
"India",
"Juliet",
"Kilo",
"Lima",
"Mike",
"November",
"Oscar",
"Papa",
"Quebec",
"Romeo",
"Sierra",
"Tango",
"Uniform",
"Victor",
"Whiskey",
"X-ray",
"Yankee",
"Zulu" };
public RandomName() {
randomGenerator = new Random();
usedList = new ArrayList<Integer>();
}
public String getName() {
return nameBank[getIndex()];
}
private int getIndex() {
int i = randomGenerator.nextInt(nameBank.length);
while(usedList.contains(i)) {
if(usedList.size() >= nameBank.length ) {
usedList.clear();
}
i = randomGenerator.nextInt(nameBank.length);
}
usedList.add(i);
return i;
}
public void addName(String add) {
nameBank[nameBank.length+1+1] = add;
}
public void addName(String[] add) {
for(int i = 0; i < add.length; i++) {
nameBank[nameBank.length+1] = add[i];
}
}
}
Vector2d class:
package utility;
public class Vector2d {
public double xPos;
public double yPos;
public Vector2d(){
this.xPos = 0;
this.yPos = 0;
}
public Vector2d(Vector2d v){
this.xPos = v.xPos;
this.yPos = v.yPos;
}
public Vector2d(double x, double y){
this.xPos = x;
this.yPos = y;
}
public Vector2d add(Vector2d addVector){
return new Vector2d(this.xPos += addVector.xPos, this.yPos += addVector.yPos );
}
public Vector2d add(double x, double y){
return new Vector2d(this.xPos += x, this.yPos += y );
}
public Vector2d subtract(double x, double y){
return new Vector2d(this.xPos += x, this.yPos += y );
}
public Vector2d subtract(Vector2d subVector){
return new Vector2d(this.xPos = (this.xPos - subVector.xPos), this.yPos = (this.yPos - subVector.yPos));
}
public Vector2d division(double divider) {
return new Vector2d(this.xPos =( xPos / divider), this.yPos =( yPos / divider));
}
public double absX() {
return Math.sqrt(Math.pow(this.xPos, 2));
}
public double absY() {
return Math.sqrt(Math.pow(this.yPos, 2));
}
public double abs() {
return Math.sqrt(Math.pow(this.xPos, 2) + Math.pow(this.yPos, 2));
}
public int minus(Vector2d position) {
// TODO Auto-generated method stub
return 0;
}
}
Related
I am a new Android game developer. I want to force my game object which moves to projectile trajectory like in Angry birds.
Related example here and my example You can see image.
I use jbox2d little bit code, but I don't know how to make it work.
private static class ProjectileEquation {
public float gravity;
public Vector2 startVelocity = new Vector2();
public Vector2 startPoint = new Vector2();
public float yogx(float t) {
return startPoint.x + t * startVelocity.x;
}
public float yogy(float t) {
float u = (float) -4.97741939970554;
float v = (float) 30.88079;
float tt = 1/6f;
float n = (u+v)/2*tt;
return startPoint.y + n * t *startVelocity.y + 0.5f *(n*n+n)
* t * t * gravity;
}
}
private static class Controller {
public float power = -66f;
public float angle = 585f;
}
}
public float P2M(float xPixels) {
return xPixels * metersPerPixel;
}
public float M2P(float xMetres) {
return xMetres * pixelsPerMeter;
}
public float MaxHeight(){
if (projectileEquation.startVelocity.y > 0){
return projectileEquation.startPoint.y;
}
return projectileEquation.startPoint.y;
}
public void Update() {
projectileEquation.startVelocity.set(controller.power,controller.power);
projectileEquation.startVelocity.rotate(controller.angle);
// bd = bodies;
}
#Override
protected void onDraw(Canvas c) {
super.onDraw(c);
MaxHeight();
Update();
float t = 0f;
x = c.getWidth()/2;
y = c.getHeight()-20;
float timeSeparation = this.timeSeparation;
for (int i = 0; i < trajectoryPointCount; i++) {
float x = this.x + projectileEquation.yogx(t);
float y = this.y + -projectileEquation.yogy(t);
t += timeSeparation;
if(A) {
c.drawBitmap(bmp, x, y, null);
}else if(B) {
float bX = M2P(body.getPosition().x);
float by = c.getHeight() - M2P(body.getPosition().y);
body.setActive(true);
c.drawCircle(bX, by, M2P(body.m_fixtureList.m_shape.m_radius), circlePaint);
}
}
}
public class jbox2d {
public float targetFps = 30.0f;
public float timeStep = (1.0f / targetFps);
private int interations = 2;
public Body body;
private World world;
private BodyDef bodydef;
public void Create(){
Vec2 gravity = new Vec2(0,-10f);
boolean doSleep = true;
world = new World(gravity, doSleep);
}
public void addboll() {
bodydef = new BodyDef();
bodydef.type = BodyType.DYNAMIC;
bodydef.allowSleep = true;
bodydef.position.set(8.24f,10f);
body = world.createBody(bodydef);
body.setActive(false);
float tt = 0f;
Vec2 stx = new Vec2();
Vec2 sty =new Vec2();
for (int it = 0; it < 20; it++) {
stx = M2P(projectileEquation.startVelocity.x);
sty = M2P(projectileEquation.startVelocity.y);
stx = M2P(projectileEquation.yogx(tt));
sty = -M2P(projectileEquation.yogy(tt));
body.setLinearVelocity(new Vec2(stx,sty));
body.applyLinearImpulse(body.getLinearVelocity(), body.getWorldCenter());
tt += timeSeparation;
}
CircleShape circle = new CircleShape();
circle.m_radius = (float).42;
FixtureDef fixturedef = new FixtureDef();
fixturedef.density = 1.0f;
fixturedef.restitution = 0.9f;
fixturedef.friction = 0.2f;
fixturedef.shape = circle;
body.createFixture(fixturedef);
}
public void update(){
world.step(timeStep, interations, interations);
}
}
I have a model of planet (below) and paint the Sun in center of screen. Then I draw the planets using a new thread. Planets move is too fast and not means that it's a move on a circle. I tried to change the thread's sleep time and planet's velocity and does not matter - planets move too fast. Velocity > 3 - too fast speed.
I need result: Planets move slow and I can manage a planet's speed with her velocity (1, 3, 5, 10). Angle (position of planet) changes 1 time in second on small amount (1, 3, 5 degrees - velocity)
public class Planet
{
private String name;
private int id;
private double radius = 1.0;
private double radiusOrbit = 5.0;
private double velocity = 1;
private Color color;
private int angle = 0;
private String parent;
public Planet(String name, int id, double rad, double radOrbit, double velocity, Color color)
{
this.name = name;
this.id = id;
this.radius = rad;
this.radiusOrbit = radOrbit;
this.velocity = velocity;
this.color = color;
}
...getters and setters
}
Main Class
public class ShowCosmos2 {
public static void main(String[] args)
{
JFrame frame = new PlanetsFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class PlanetsFrame extends JFrame
{
private int width = Toolkit.getDefaultToolkit().getScreenSize().width;
private int height = Toolkit.getDefaultToolkit().getScreenSize().height;
public PlanetsFrame()
{
setSize(width, height);
setTitle("Planets");
setContentPane(new PlanetsCanvas(width, height));
Container contentPane = getContentPane();
contentPane.setBackground(Color.BLACK);
}
}
class PlanetsCanvas extends JPanel
{
private int width, height;
private int centerX = 0;
private int centerY = 0;
private Thread runner;
private boolean running = false;
Planet[] planets = {
new Planet("Venera", 1, 5.0, 50.0, 1, Color.GREEN),
new Planet("Mercury", 1, 3.0, 75.0, 1.5, Color.ORANGE),
new Planet("Earth", 1, 6.0, 100.0, 2, Color.BLUE),
new Planet("Jupiter", 1, 12.0, 150.0, 1, Color.RED)
};
public PlanetsCanvas(int w, int h)
{
width = w;
height = h;
centerX = (int)(w/2);
centerY = (int)(h/2);
}
protected void drawFrame(Graphics g)
{
//Sun
g.setColor(Color.YELLOW);
g.fillOval(width/2 - 25, height/2 - 25, 50, 50);
for (int i = 0; i < planets.length; i++)
{
Planet p = planets[i];
g.setColor(p.getColor());
int newX = (int)(centerX + Math.cos(p.getAngle())*p.getRadiusOrbit());
int newY = (int)(centerY - Math.sin(p.getAngle())*p.getRadiusOrbit());
g.fillOval((int)(newX-p.getRadius()),
(int)(newY-p.getRadius()),
(int)p.getRadius()*2, (int)p.getRadius()*2);
//int angle = (int)(p.getAngle() + p.getVelocity());
//if (angle >= 360) angle = 0;
//p.setAngle(angle);
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
drawFrame(g);
startAnimation();
}
public void startAnimation() {
runner = new Thread() {
public void run() {
try {
while (!Thread.interrupted()) {
repaint();
for(int i=0; i<planets.length; i++)
{
Planet p = planets[i];
int angle = (int)(p.getAngle() + p.getVelocity());
if (angle >= 360) angle = 0;
p.setAngle(angle);
}
Thread.sleep(500);
}
} catch (Exception e) {
}
}
};
runner.start();
running = true;
}
}
Most important -- don't start your animation from within paintComponent. The paintcomponent method will keep being called over and over again, meaning you're going to be creating more and more animation threads unnecessarily, when only one is what's called for. What's worse, you do not have complete control over when or even if paintComponent is called. So instead start your animation thread once and likely in your class's constructor.
Consider following points
Your startAnimation() method should be called once only and not in paintComponent() method which will instantiate a new thread on every repaint()
Apart from that keep the angle a double type as this will allow you to make arbitrarily small increments and decrements to it.
Thead.sleep() interval should be the single frame time.
Maintain a DAMPING_COFFICIENT to multiply to velocity when calculating new angle to slow down or speed up.
Here's modified slowed down code.
import java.awt.Color;
public class Planet {
private String name;
private int id;
private double radius = 1.0;
private double radiusOrbit = 5.0;
private double velocity = 1;
private Color color;
private double angle = 0;
private String parent;
public Planet(String name, int id, double rad, double radOrbit,
double velocity, Color color) {
this.name = name;
this.id = id;
this.radius = rad;
this.radiusOrbit = radOrbit;
this.velocity = velocity;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double getRadiusOrbit() {
return radiusOrbit;
}
public void setRadiusOrbit(double radiusOrbit) {
this.radiusOrbit = radiusOrbit;
}
public double getVelocity() {
return velocity;
}
public void setVelocity(double velocity) {
this.velocity = velocity;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
public double getAngle() {
return angle;
}
public void setAngle(double angle) {
this.angle = angle;
}
}
Rest of the classes
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ShowCosmos2 {
public static void main(String[] args) {
JFrame frame = new PlanetsFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class PlanetsFrame extends JFrame {
private int width = Toolkit.getDefaultToolkit().getScreenSize().width;
private int height = Toolkit.getDefaultToolkit().getScreenSize().height;
public PlanetsFrame() {
setSize(width, height);
setTitle("Planets");
setContentPane(new PlanetsCanvas(width, height));
Container contentPane = getContentPane();
contentPane.setBackground(Color.BLACK);
}
}
class PlanetsCanvas extends JPanel {
private static final double DAMPING_COFFICIENT = 0.01;
private static final int FRAMES_PER_SECOND = 60;
private static final long FRAME_DURATION = (1000 / FRAMES_PER_SECOND);
private int width, height;
private int centerX = 0;
private int centerY = 0;
private Thread runner;
private boolean running = false;
Planet[] planets = { new Planet("Venera", 1, 5.0, 50.0, 1, Color.GREEN),
new Planet("Mercury", 1, 3.0, 75.0, 1.5, Color.ORANGE),
new Planet("Earth", 1, 6.0, 100.0, 2, Color.BLUE),
new Planet("Jupiter", 1, 12.0, 150.0, 1, Color.RED) };
public PlanetsCanvas(int w, int h) {
width = w;
height = h;
centerX = (int) (w / 2);
centerY = (int) (h / 2);
startAnimation();
}
protected void drawFrame(Graphics g) {
// Sun
g.setColor(Color.YELLOW);
g.fillOval(width / 2 - 25, height / 2 - 25, 50, 50);
for (int i = 0; i < planets.length; i++) {
Planet p = planets[i];
g.setColor(p.getColor());
int newX = (int) (centerX + Math.cos(p.getAngle())
* p.getRadiusOrbit());
int newY = (int) (centerY - Math.sin(p.getAngle())
* p.getRadiusOrbit());
g.fillOval((int) (newX - p.getRadius()),
(int) (newY - p.getRadius()), (int) p.getRadius() * 2,
(int) p.getRadius() * 2);
// int angle = (int)(p.getAngle() + p.getVelocity());
// if (angle >= 360) angle = 0;
// p.setAngle(angle);
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawFrame(g);
}
public void startAnimation() {
runner = new Thread() {
public void run() {
try {
while (!Thread.interrupted()) {
repaint();
for (Planet p : planets) {
double angle = (p.getAngle() + p.getVelocity() * DAMPING_COFFICIENT);
//System.out.println(p.getName() + " : " + angle);
if (angle >= 360)
angle = 0;
p.setAngle(angle);
}
Thread.sleep(FRAME_DURATION);
}
} catch (Exception e) {
}
}
};
runner.start();
running = true;
}
}
.
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));
}
}
}
I would like to know, how to make scrollview swipe effect when mouse is dragged up and down. I'm developing JavaFX application which has ListView & I need to scroll the list when user drags the mouse up and down (like iOS contact list).
Also how to add the user's dragging velocity and speed to the scrolling value?
UPDATED : I got some code here but It doesn't work properly...
import java.util.ArrayList;
import java.util.Arrays;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.animation.TimelineBuilder;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.Event;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.FlowPane;
import javafx.util.Duration;
public class Swipe extends ScrollPane {
private static final int INERTIA_DURATION = 2400;
private static final double CLICK_THRESHOLD = 20;
private static final double CLICK_TIME_THRESHOLD = Integer.parseInt(System.getProperty("click", "400"));
private final double width;
private final double height;
private long startDrag;
private long lastDrag;
private long lastDragDelta;
private int startDragX;
private int startDragY;
private int lastDragX;
private int lastDragY;
private int lastDragStepX;
private int lastDragStepY;
private double dragVelocityX;
private double dragVelocityY;
private boolean clickThresholdBroken;
private Timeline inertiaTimeline = null;
private long lastClickTime = -1;
private final boolean isFiredByMe = false;
public Swipe(double width, double height) {
this.width = width;
this.height = height;
init();
}
private void init() {
setPrefSize(width, height);
setPannable(true);
setHbarPolicy(ScrollBarPolicy.NEVER);
setVbarPolicy(ScrollBarPolicy.NEVER);
setEventHandlers();
ContentPane cp = new ContentPane();
setContent(cp);
}
private void setEventHandlers() {
setOnMousePressed((MouseEvent event) -> {
lastDragX = startDragX = (int) event.getX();
lastDragY = startDragY = (int) event.getY();
lastDrag = startDrag = System.currentTimeMillis();
lastDragDelta = 0;
if (inertiaTimeline != null) {
inertiaTimeline.stop();
}
clickThresholdBroken = false;
});
setOnDragDetected((MouseEvent event) -> {
// Delta of this drag vs. last drag location (or start)
lastDragStepX = (int) event.getX() - lastDragX;
lastDragStepY = (int) event.getY() - lastDragY;
// Duration of this drag step.
lastDragDelta = System.currentTimeMillis() - lastDrag;
// Velocity of last drag increment.
dragVelocityX = (double) lastDragStepX / (double) lastDragDelta;
dragVelocityY = (double) lastDragStepY / (double) lastDragDelta;
// Snapshot of this drag event.
lastDragX = (int) event.getX();
lastDragY = (int) event.getY();
lastDrag = System.currentTimeMillis();
// Calculate distance so far -- have we dragged enough to scroll?
final int dragX = (int) event.getX() - startDragX;
final int dragY = (int) event.getY() - startDragY;
double distance = Math.abs(Math.sqrt((dragX * dragX) + (dragY * dragY)));
int scrollDistX = lastDragStepX;
int scrollDistY = lastDragStepY;
if (!clickThresholdBroken && distance > CLICK_THRESHOLD) {
clickThresholdBroken = true;
scrollDistX = dragX;
scrollDistY = dragY;
}
if (clickThresholdBroken) {
Event.fireEvent(event.getTarget(), new ScrollEvent(
ScrollEvent.SCROLL,
scrollDistX, scrollDistY,
scrollDistX, scrollDistY,
event.isShiftDown(), event.isControlDown(), event.isAltDown(), event.isMetaDown(),
true, false,
event.getX(), event.getY(),
event.getSceneX(), event.getSceneY(),
ScrollEvent.HorizontalTextScrollUnits.NONE, 0,
ScrollEvent.VerticalTextScrollUnits.NONE, 0, 0, null));
}
});
setOnMouseReleased((MouseEvent event) -> {
handleRelease(event);
});
setOnMouseClicked((MouseEvent event) -> {
final long time = System.currentTimeMillis();
if (clickThresholdBroken || (lastClickTime != -1 && (time - lastClickTime) < CLICK_TIME_THRESHOLD)) {
event.consume();
}
lastClickTime = time;
});
}
private void handleRelease(final MouseEvent me) {
if (clickThresholdBroken) {
// Calculate last instantaneous velocity. User may have stopped moving
// before they let go of the mouse.
final long time = System.currentTimeMillis() - lastDrag;
dragVelocityX = (double) lastDragStepX / (time + lastDragDelta);
dragVelocityY = (double) lastDragStepY / (time + lastDragDelta);
// determin if click or drag/flick
final int dragX = (int) me.getX() - startDragX;
final int dragY = (int) me.getY() - startDragY;
// calculate complete time from start to end of drag
final long totalTime = System.currentTimeMillis() - startDrag;
// if time is less than 300ms then considered a quick flick and whole time is used
final boolean quick = totalTime < 300;
// calculate velocity
double velocityX = quick ? (double) dragX / (double) totalTime : dragVelocityX; // pixels/ms
double velocityY = quick ? (double) dragY / (double) totalTime : dragVelocityY; // pixels/ms
final int distanceX = (int) (velocityX * INERTIA_DURATION); // distance
final int distanceY = (int) (velocityY * INERTIA_DURATION); // distance
//
DoubleProperty animatePosition = new SimpleDoubleProperty() {
double lastMouseX = me.getX();
double lastMouseY = me.getY();
#Override
protected void invalidated() {
final double mouseX = me.getX() + (distanceX * get());
final double mouseY = me.getY() + (distanceY * get());
final double dragStepX = mouseX - lastMouseX;
final double dragStepY = mouseY - lastMouseY;
if (Math.abs(dragStepX) >= 1.0 || Math.abs(dragStepY) >= 1.0) {
Event.fireEvent(me.getTarget(), new ScrollEvent(
ScrollEvent.SCROLL,
dragStepX, dragStepY,
(distanceX * get()), (distanceY * get()),
me.isShiftDown(), me.isControlDown(), me.isAltDown(), me.isMetaDown(),
true, true,
me.getX(), me.getY(),
me.getSceneX(), me.getSceneY(),
ScrollEvent.HorizontalTextScrollUnits.NONE, 0,
ScrollEvent.VerticalTextScrollUnits.NONE, 0,
0, null));
}
lastMouseX = mouseX;
lastMouseY = mouseY;
}
};
// animate a slow down from current velocity to zero
inertiaTimeline = TimelineBuilder.create()
.keyFrames(
new KeyFrame(Duration.ZERO, new KeyValue(animatePosition, 0)),
new KeyFrame(Duration.millis(INERTIA_DURATION), new KeyValue(animatePosition, 1d, Interpolator.SPLINE(0.0513, 0.1131, 0.1368, 1.0000)))
).build();
inertiaTimeline.play();
}
}
private class ContentPane extends FlowPane {
private ArrayList getList() {
String[] list = {
"Kerrie Batts", "Raina Huffstutler", "Kip Kukowski", "Trish Sullivan", "Kyla Hollingsworth", "Gearldine Leavy", "Major Langdon", "Avery Rusin", "Hedy Messina", "Audry Felps", "Tianna Robbins", "Marian Tranmer", "Lashaunda Bivona", "Leighann Schwab", "Emanuel Volpe", "Neida Geist", "Edda Placencia", "Olevia Hippe", "Fernando Cohen", "Danette Dorsett"};
ArrayList<String> nameList = new ArrayList();
nameList.addAll(Arrays.asList(list));
return nameList;
}
public ContentPane() {
setPrefSize(215, 271);
ArrayList<String> nameList = getList();
Element[] element = new Element[nameList.size()];
for (int i = 0; i < nameList.size(); i++) {
String name = nameList.get(i);
Element el = element[i] = new Element(210, 25, name);
getChildren().add(el);
}
}
}
private class Element extends AnchorPane {
private double width;
private double height;
private String name;
public Element(double width, double height, String name) {
this.width = width;
this.height = height;
this.name = name;
init();
}
private Label createName() {
Label label = new Label(name);
label.setPrefSize(width, height);
label.setAlignment(Pos.CENTER_LEFT);
AnchorPane.setLeftAnchor(label, 5.0);
label.setStyle("-fx-font-family: Calibri; -fx-font-size: 14;");
return label;
}
private void init() {
setPrefSize(width, height);
getChildren().add(createName());
}
public void setPrefSize(double width, double height) {
this.width = width;
this.height = height;
}
public void setName(String name) {
this.name = name;
}
}
}
I figured out myself. There is two classes. 1st one is for calculate the velocity of the movement.
VelocityTracker.java
import javafx.scene.input.MouseEvent;
public final class VelocityTracker {
private static final boolean DEBUG = false;
private static final boolean localLOGV = DEBUG;
private static final int NUM_PAST = 10;
private static final int MAX_AGE_MILLISECONDS = 200;
private static final int POINTER_POOL_CAPACITY = 20;
private static Pointer sRecycledPointerListHead;
private static int sRecycledPointerCount;
private static final class Pointer {
public Pointer next;
public int id;
public float xVelocity;
public float yVelocity;
public final float[] pastX = new float[NUM_PAST];
public final float[] pastY = new float[NUM_PAST];
public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel
}
private Pointer mPointerListHead; // sorted by id in increasing order
private int mLastTouchIndex;
public VelocityTracker() {
clear();
}
public void clear() {
releasePointerList(mPointerListHead);
mPointerListHead = null;
mLastTouchIndex = 0;
}
public void addMovement(MouseEvent ev) {
final int historySize = 0;
final int lastTouchIndex = mLastTouchIndex;
final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST;
final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST;
mLastTouchIndex = finalTouchIndex;
if (mPointerListHead == null) {
mPointerListHead = obtainPointer();
}
final float[] pastX = mPointerListHead.pastX;
final float[] pastY = mPointerListHead.pastY;
final long[] pastTime = mPointerListHead.pastTime;
pastX[finalTouchIndex] = (float) ev.getX();
pastY[finalTouchIndex] = (float) ev.getY();
pastTime[finalTouchIndex] = System.currentTimeMillis();
}
public void computeCurrentVelocity(int units) {
computeCurrentVelocity(units, Float.MAX_VALUE);
}
public void computeCurrentVelocity(int units, float maxVelocity) {
final int lastTouchIndex = mLastTouchIndex;
for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
final long[] pastTime = pointer.pastTime;
int oldestTouchIndex = lastTouchIndex;
int numTouches = 1;
final long minTime = pastTime[lastTouchIndex] - MAX_AGE_MILLISECONDS;
while (numTouches < NUM_PAST) {
final int nextOldestTouchIndex = (oldestTouchIndex + NUM_PAST - 1) % NUM_PAST;
final long nextOldestTime = pastTime[nextOldestTouchIndex];
if (nextOldestTime < minTime) { // also handles end of trace sentinel
break;
}
oldestTouchIndex = nextOldestTouchIndex;
numTouches += 1;
}
if (numTouches > 3) {
numTouches -= 1;
}
final float[] pastX = pointer.pastX;
final float[] pastY = pointer.pastY;
final float oldestX = pastX[oldestTouchIndex];
final float oldestY = pastY[oldestTouchIndex];
final long oldestTime = pastTime[oldestTouchIndex];
float accumX = 0;
float accumY = 0;
for (int i = 1; i < numTouches; i++) {
final int touchIndex = (oldestTouchIndex + i) % NUM_PAST;
final int duration = (int) (pastTime[touchIndex] - oldestTime);
if (duration == 0) {
continue;
}
float delta = pastX[touchIndex] - oldestX;
float velocity = (delta / duration) * units; // pixels/frame.
accumX = (accumX == 0) ? velocity : (accumX + velocity) * .5f;
delta = pastY[touchIndex] - oldestY;
velocity = (delta / duration) * units; // pixels/frame.
accumY = (accumY == 0) ? velocity : (accumY + velocity) * .5f;
}
if (accumX < -maxVelocity) {
accumX = -maxVelocity;
} else if (accumX > maxVelocity) {
accumX = maxVelocity;
}
if (accumY < -maxVelocity) {
accumY = -maxVelocity;
} else if (accumY > maxVelocity) {
accumY = maxVelocity;
}
pointer.xVelocity = accumX;
pointer.yVelocity = accumY;
}
}
public float getXVelocity() {
Pointer pointer = getPointer(0);
return pointer != null ? pointer.xVelocity : 0;
}
public float getYVelocity() {
Pointer pointer = getPointer(0);
return pointer != null ? pointer.yVelocity : 0;
}
public float getXVelocity(int id) {
Pointer pointer = getPointer(id);
return pointer != null ? pointer.xVelocity : 0;
}
public float getYVelocity(int id) {
Pointer pointer = getPointer(id);
return pointer != null ? pointer.yVelocity : 0;
}
private Pointer getPointer(int id) {
for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) {
if (pointer.id == id) {
return pointer;
}
}
return null;
}
private static Pointer obtainPointer() {
if (sRecycledPointerCount != 0) {
Pointer element = sRecycledPointerListHead;
sRecycledPointerCount -= 1;
sRecycledPointerListHead = element.next;
element.next = null;
return element;
}
return new Pointer();
}
private static void releasePointer(Pointer pointer) {
if (sRecycledPointerCount < POINTER_POOL_CAPACITY) {
pointer.next = sRecycledPointerListHead;
sRecycledPointerCount += 1;
sRecycledPointerListHead = pointer;
}
}
private static void releasePointerList(Pointer pointer) {
if (pointer != null) {
int count = sRecycledPointerCount;
if (count >= POINTER_POOL_CAPACITY) {
return;
}
Pointer tail = pointer;
for (;;) {
count += 1;
if (count >= POINTER_POOL_CAPACITY) {
break;
}
Pointer next = tail.next;
if (next == null) {
break;
}
tail = next;
}
tail.next = sRecycledPointerListHead;
sRecycledPointerCount = count;
sRecycledPointerListHead = pointer;
}
}
}
And this is second class
import java.util.concurrent.atomic.AtomicReference;
import javafx.animation.FadeTransition;
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Bounds;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
public class VerticlePane extends Region {
private ObjectProperty<Node> content;
private final VelocityTracker tracker = new VelocityTracker();
private final Rectangle clipRect = new Rectangle();
private Rectangle scrollNode;
private boolean magnet, autoTranslate = true;
private DoubleProperty targetY = new SimpleDoubleProperty(), controlHeight = new SimpleDoubleProperty(), viewHeight = new SimpleDoubleProperty();
private double startValue, endValue;
private HBox indicator;
private StringProperty indicatorValue = new SimpleStringProperty("Pull Up to Update...");
public VerticlePane() {
setFocusTraversable(false);
setClip(clipRect);
contentProperty().addListener((ObservableValue<? extends Node> observable, Node oldValue, Node newValue) -> {
getChildren().clear();
if (newValue != null) {
getChildren().addAll(newValue, createRectangle());
}
layout();
});
final AtomicReference<MouseEvent> deltaEvent = new AtomicReference<>();
final AtomicReference<TranslateTransition> currentTransition1 = new AtomicReference<>();
final AtomicReference<TranslateTransition> currentTransition2 = new AtomicReference<>();
final AtomicReference<Timeline> timeline = new AtomicReference<>();
setOnMousePressed((me) -> {
tracker.addMovement(me);
deltaEvent.set(me);
startValue = me.getY();
if (currentTransition1.get() != null) {
currentTransition1.get().stop();
}
if (currentTransition2.get() != null) {
currentTransition2.get().stop();
}
if (timeline.get() != null) {
timeline.get().stop();
}
});
setOnMouseDragged((event) -> {
tracker.addMovement(event);
double delta = event.getY() - deltaEvent.get().getY();
targetY.set(content.get().getTranslateY() + delta);
content.get().setTranslateY(targetY.get());
controlHeight.set(content.get().getLayoutBounds().getHeight());
viewHeight.set(getHeight());
double scrollTargetY = targetY.divide(controlHeight.divide(viewHeight.get()).get()).get();
double lastInstanceHeight = controlHeight.subtract(viewHeight.get()).multiply(-1).get();
scrollNode.relocate(1.0, (getHeight() - 5.0));
scrollNode.setArcHeight(5.0);
scrollNode.setArcWidth(5.0);
scrollNode.setHeight(viewHeight.divide(controlHeight.divide(viewHeight.get()).get()).get());
scrollNode.setVisible(true);
scrollNode.setOpacity(0.25);
if (targetY.lessThan(0).and(targetY.greaterThan(lastInstanceHeight)).get()) {
scrollNode.setTranslateY(Math.abs(scrollTargetY));
} else if (targetY.greaterThanOrEqualTo(0).get()) {
scrollNode.setHeight(scrollNode.getHeight() - targetY.get());
}
if (targetY.get() < lastInstanceHeight) {
double scrollNodeHeight = scrollNode.getHeight() - (lastInstanceHeight - targetY.get());
double scrollNodeTranslateY = viewHeight.subtract(viewHeight.divide(controlHeight.divide(viewHeight.get()).get()).get()).add(lastInstanceHeight - targetY.get()).get();
scrollNode.setHeight(scrollNodeHeight);
scrollNode.setTranslateY(scrollNodeTranslateY);
}
deltaEvent.set(event);
});
setOnMouseReleased((me) -> {
FadeTransition ft = new FadeTransition(Duration.millis(300), scrollNode);
ft.setFromValue(0.25);
ft.setToValue(0.00);
tracker.addMovement(me);
tracker.computeCurrentVelocity(500);
endValue = me.getY();
controlHeight.set(content.get().getLayoutBounds().getHeight());
viewHeight.set(getHeight());
float velocityY = tracker.getYVelocity();
targetY.set(scrollNode(velocityY, currentTransition1, content.get()));
TranslateTransition tt = bb(currentTransition2, scrollNode, Math.abs((targetY.divide(controlHeight.divide(viewHeight.get()).get()).get())));
tt.setOnFinished((ae) -> {
ft.play();
});
});
}
public double getStartValue() {
return startValue;
}
public double getEndValue() {
return endValue;
}
public double getControlHeight() {
return controlHeight.get();
}
public double getViewHeight() {
return viewHeight.get();
}
public VerticlePane(double prefWidth, double prefHeight) {
this();
setPrefSize(prefWidth, prefHeight);
}
public VerticlePane(double prefWidth, double prefHeight, boolean magnet) {
this(prefWidth, prefHeight);
setMagnet(magnet);
}
public final void setMagnet(boolean magnet) {
this.magnet = magnet;
}
private TranslateTransition bb(AtomicReference<TranslateTransition> currentTransition, Node node, Double targetY) {
TranslateTransition translate = new TranslateTransition(new Duration(1000), node);
if (node != null) {
translate.setInterpolator(Interpolator.SPLINE(0.0513, 0.1131, 0.1368, 1.0000));
if (targetY != null) {
translate.setFromY(node.getTranslateY());
translate.setToY(targetY);
}
translate.play();
currentTransition.set(translate);
}
return translate;
}
private Double scrollNode(float velocityY, AtomicReference<TranslateTransition> currentTransition, Node node) {
final Bounds b = node.getLayoutBounds();
Double backBouncingY = null;
Double targetY = null;
if (node != null) {
TranslateTransition translate = new TranslateTransition(new Duration(1000), node);
translate.setInterpolator(Interpolator.SPLINE(0.0513, 0.1131, 0.1368, 1.0000));
if (Math.abs(velocityY) < 10) {
velocityY = 0;
}
targetY = node.getTranslateY();
double controlHeight = b.getHeight() - indicator.heightProperty().add(10).get();
double viewHeight = getHeight();
if (controlHeight < viewHeight && targetY < controlHeight) {
targetY = 0.0;
} else if (targetY > 0) {
targetY = 0.0;
} else if ((targetY < (controlHeight - viewHeight) * -1)) {
targetY = (controlHeight - viewHeight) * -1;
} else {
targetY += velocityY;
if (controlHeight < viewHeight && targetY < controlHeight) {
targetY = -25.0;
backBouncingY = 0.0;
} else if (targetY > 0) {
targetY = 25.0;
backBouncingY = 0.0;
} else if (targetY < (controlHeight - viewHeight) * -1) {
targetY = (controlHeight - viewHeight) * -1 - 25;
backBouncingY = (controlHeight - viewHeight) * -1;
}
}
//Magnet
if (magnet) {
double dragRegion = (viewHeight / 2) * -1;
double instances = controlHeight / viewHeight;
double lastInstance = ((controlHeight - viewHeight) * -1);
double newInstanceValue = dragRegion;
if (targetY > dragRegion) {
targetY = 0.0;
}
if (instances > 1) {
for (int i = 1; i < instances - 1; i++) {
double instanceValue = (viewHeight * i) * -1;
if (targetY < newInstanceValue && targetY > instanceValue || targetY < instanceValue && targetY > (instanceValue + dragRegion)) {
targetY = instanceValue;
}
newInstanceValue += (viewHeight * -1);
}
}
if (targetY < (lastInstance - dragRegion) && targetY > lastInstance) {
targetY = lastInstance;
}
}
//
if (targetY != null) {
translate.setFromY(node.getTranslateY());
translate.setToY(targetY);
}
if (backBouncingY != null) {
final Double fbackFlingY = backBouncingY;
translate.setOnFinished((ae) -> {
currentTransition.set(null);
TranslateTransition translate1 = new TranslateTransition(new Duration(300), node);
if (fbackFlingY != null) {
translate1.setFromY(node.getTranslateY());
translate1.setToY(fbackFlingY);
}
translate1.play();
currentTransition.set(translate1);
});
} else {
translate.setOnFinished((ae) -> {
currentTransition.set(null);
});
}
translate.play();
currentTransition.set(translate);
}
return targetY;
}
private Rectangle createRectangle() {
if (scrollNode == null) {
scrollNode = new Rectangle(4.0, 4.0, Color.BLACK);
}
scrollNode.setVisible(false);
return scrollNode;
}
public ObjectProperty<Node> contentProperty() {
if (content == null) {
content = new SimpleObjectProperty<>(this, "content");
}
return content;
}
public final void setContent(Node value) {
contentProperty().set(
new VBox(10) {
{
getChildren().addAll(value, indicator());
}
}
);
contentProperty().get().layoutBoundsProperty().addListener((ObservableValue<? extends Bounds> obs, Bounds ov, Bounds nv) -> {
Double containerHeight = nv.getHeight();
Double flingHeight = prefHeightProperty().get();
if (autoTranslate) {
if (flingHeight <= containerHeight) {
contentProperty().get().setTranslateY((containerHeight - flingHeight - 34) * -1);
}
}
});
}
private HBox indicator() {
return indicator = new HBox(10) {
{
setVisible(false);
setAlignment(Pos.CENTER);
getChildren().addAll(
new ImageView(new Image(getClass().getResourceAsStream("Indicator.gif"))),
new Label() {
{
textProperty().bind(indicatorValue);
setStyle("-fx-font-family: Roboto; -fx-font-size: 14px;");
}
}
);
}
};
}
public void setAutoTranslate(Boolean autoTranslate) {
this.autoTranslate = autoTranslate;
}
public void setTranslateZero() {
this.contentProperty().get().setTranslateY(0);
}
public HBox getIndicator() {
return indicator;
}
public void showIndicator() {
indicator.setVisible(true);
}
public void hideIndicator() {
indicator.setVisible(false);
}
public void setIndicatorValue(String value) {
indicatorValue.setValue(value);
}
public final Node getContent() {
return content == null ? null : content.get();
}
#Override
protected void layoutChildren() {
super.layoutChildren();
clipRect.setWidth(getWidth());
clipRect.setHeight(getHeight());
}
}
Scroll Bar disabled why?
It should not extend:
public class VerticlePane extends Control
I'm writing an android program and i just ran into a problem.
My program serves to create graphs, run some specific algorithms on them (Dijsktra, BelmanFord, etc.), save the graphs to SD Card and loading them back.
The problem: If i save a little bigger, more complex graph i get a stackoverflow error..
Serialization:
public void createExternalStoragePublicGraph(String filename) {
File dir = Environment.getExternalStoragePublicDirectory("grapher");
try {
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, filename);
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(graphDrawerView.getGraph());
objectOutputStream.close();
} catch (IOException e) {
Log.w("ExternalStorage", "Error Writing" + filename, e);
}
}
Deserialization:
public Graph loadExternalStoragePublicGraph(String filename) {
File file = new File(Environment.getExternalStoragePublicDirectory("grapher"), filename);
Graph graph = null;
try {
FileInputStream fint = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fint);
graph = (Graph) ois.readObject();
ois.close();
} catch (Exception e) {
Log.e("Deserialization error:", e.getMessage());
e.printStackTrace();
}
return graph;
}
Graph class:
package com.cslqaai.grapher;
import android.util.Log;
import java.io.Serializable;
import java.util.LinkedList;
public class Graph implements Serializable {
private String name;
private boolean directed = false;
private boolean weighted = false;
private LinkedList<Vertex> vertexes = new LinkedList<Vertex>();
private LinkedList<Edge> edges = new LinkedList<Edge>();
// --------------------------------------------------------------------------------------------------------------------------------------
public Graph(boolean weighted, boolean directed) {
this.directed = directed;
this.weighted = weighted;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void add(AbstractGraphObject ago) {
if (ago instanceof Vertex) {
this.vertexes.add((Vertex) ago);
} else if (ago instanceof Edge) {
this.edges.add((Edge) ago);
} else {
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isWeighted() {
return this.weighted;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isDirected() {
return this.directed;
}
// --------------------------------------------------------------------------------------------------------------------------------------\
public LinkedList<Edge> getEdges() {
return this.edges;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public LinkedList<Vertex> getVertexes() {
return this.vertexes;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void reset() {
for (int i = 0; i < this.edges.size(); i++) {
this.edges.get(i).setColorToDefault();
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
void remove(AbstractGraphObject ago) {
if (ago instanceof Vertex) {
Log.d("Graph", "Remove Vertex from graph");
this.vertexes.remove((Vertex) ago);
} else if (ago instanceof Edge) {
Log.d("Graph", "Remove Edge to graph");
this.edges.remove((Edge) ago);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setWeighted(boolean weighted) {
this.weighted = weighted;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setDirected(boolean directed) {
this.directed = directed;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public String getName() {
return this.name;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setName(String name) {
this.name = name;
}
}
// ----------------------------------------------------------------------------------------------------------------------------------------
Vertex class:
package com.cslqaai.grapher;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
public class Vertex extends AbstractGraphObject {
public static final int RADIUS_SIZE = 20;
public static final int SURROUNDING_RADIUS_SIZE = 30;
private static Layer defaultVertexLayer = AbstractGraphObject.defaultLayer;
private static int no = 0;
private static Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint paintColored = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final int id;
private String name = null;
private Coord coord;
private Coord cachedCoord;
private ArrayList<Edge> edges = new ArrayList<Edge>();
private boolean colored = false;
// --------------------------------------------------------------------------------------------------------------------------------------
static {
Vertex.paint.setColor(0xFF0FF5F5);
Vertex.paintColored.setColor(Color.RED);
Vertex.textPaint.setStyle(Paint.Style.FILL);
Vertex.textPaint.setColor(Color.BLACK);
Vertex.textPaint.setTextAlign(Paint.Align.CENTER);
Vertex.textPaint.setTextSize(20);
Vertex.textPaint.setTypeface(Typeface.create("Helvetica", Typeface.BOLD));
Vertex.textBgPaint.setStyle(Paint.Style.FILL);
Vertex.textBgPaint.setColor(0xFF0FF5F5);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex(Coord coord) {
super(Vertex.defaultVertexLayer);
this.id = Vertex.no++;
this.coord = coord;
this.recalculate();
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex(int x, int y) {
this(new Coord(x, y));
}
// --------------------------------------------------------------------------------------------------------------------------------------
#Override
public void recalculate() {
this.cachedCoord = new Coord(Math.round((Vertex.baseX + this.coord.getX()) * Vertex.scaleFactor), Math.round((Vertex.baseY + this.coord.getY()) * Vertex.scaleFactor));
this.onScreen = this.cachedCoord.getX() + Vertex.RADIUS_SIZE > 0 && this.cachedCoord.getY() + Vertex.RADIUS_SIZE > 0
&& this.cachedCoord.getX() - Vertex.RADIUS_SIZE < Vertex.screenWidth && this.cachedCoord.getY() - Vertex.RADIUS_SIZE < this.cachedCoord.getY();
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Coord getCoord() {
return this.coord;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Coord getCachedCoord() {
return this.cachedCoord;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getRadiusSize() {
return Vertex.RADIUS_SIZE;
}
// --------------------------------------------------------------------------------------------------------------------------------------
#Override
public void draw(Canvas canvas) {
this.recalculate();
if (!this.onScreen) {
return;
}
canvas.drawCircle(this.cachedCoord.getX(), this.cachedCoord.getY(), Vertex.RADIUS_SIZE * Vertex.scaleFactor, Vertex.paint);
if (this.name != null) {
float width = Vertex.textPaint.measureText(this.name) + 10;
float height = Vertex.textPaint.getTextSize() + 5;
canvas.drawRect(this.cachedCoord.getX() - width / 2, this.cachedCoord.getY() - height / 2, this.cachedCoord.getX() + width / 2, this.cachedCoord.getY() + height / 2, Vertex.textBgPaint);
canvas.drawText(this.name, this.cachedCoord.getX(), this.cachedCoord.getY() + height * 0.25f, Vertex.textPaint);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
private boolean searchingCoordOn(float radius, float pX, float pY) {
return this.onScreen && ((Math.pow(pX - this.cachedCoord.getX(), 2) + Math.pow(pY - this.cachedCoord.getY(), 2)) < (Math.pow(radius * Vertex.scaleFactor, 2)));
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isOnCoord(float pX, float pY) {
return this.searchingCoordOn(Vertex.RADIUS_SIZE, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isInCoordSurroundings(float pX, float pY) {
return this.searchingCoordOn(Vertex.SURROUNDING_RADIUS_SIZE, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void addEdge(Edge edge) {
if (!this.edges.contains(edge)) {
this.edges.add(edge);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public ArrayList<Edge> getEdges() {
return this.edges;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void removeEdge(Edge edge) {
if (this.edges.contains(edge)) {
this.edges.remove(edge);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean hasEdge(Edge edge) {
return this.edges.contains(edge);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void setDefaultLayer(Layer layer) {
Vertex.defaultVertexLayer = layer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static Layer getDefaultLayer() {
return Vertex.defaultVertexLayer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void remove(Vertex vertex) {
Iterator<Edge> edges = vertex.getEdges().iterator();
while (edges.hasNext()) {
Edge e = edges.next();
edges.remove();
Edge.remove(e);
}
Vertex.defaultVertexLayer.remove(vertex);
if (Vertex.graph != null) {
Vertex.graph.remove(vertex);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean hasName() {
return this.name != null;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setName(String name) {
this.name = name;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public String getName() {
return this.name;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getId() {
return this.id;
}
// --------------------------------------------------------------------------------------------------------------------------------------
}
Edge class:
package com.cslqaai.grapher;
import android.graphics.*;
import java.util.ArrayList;
import java.util.LinkedList;
public class Edge extends AbstractGraphObject {
public static final float STROKE_WIDTH = 5f;
public static final float SENSOR_WIDTH = 50f;
public static final float SURROUNDING_SENSOR_WIDTH = 15f;
public static final float TRIANGLE_SIZE = 8f;
private static Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint paintColored = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Paint textBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static Layer defaultEdgeLayer = AbstractGraphObject.defaultLayer;
private static int no = 0;
private final int id;
private int weight = 1;
private Coord cachedSourceCoord;
private Coord cachedTargetCoord;
private Vertex sourceVertex;
private Vertex targetVertex;
private Coord weightCoord;
private boolean colored = false;
// --------------------------------------------------------------------------------------------------------------------------------------
static {
Edge.paint.setColor(0xFFFFFFFF);
Edge.paint.setStrokeWidth(Edge.STROKE_WIDTH * Edge.scaleFactor);
Edge.paint.setStrokeCap(Paint.Cap.ROUND);
Edge.paint.setStyle(Paint.Style.FILL_AND_STROKE);
Edge.paintColored.setColor(0xFFFF0000);
Edge.paintColored.setStrokeWidth(Edge.STROKE_WIDTH * Edge.scaleFactor);
Edge.paintColored.setStrokeCap(Paint.Cap.ROUND);
Edge.paintColored.setStyle(Paint.Style.FILL_AND_STROKE);
Edge.textPaint.setStyle(Paint.Style.FILL);
Edge.textPaint.setColor(Color.BLACK);
Edge.textPaint.setTextAlign(Paint.Align.CENTER);
Edge.textPaint.setTextSize(20);
Edge.textPaint.setTypeface(Typeface.create("Helvetica", Typeface.BOLD));
Edge.textBgPaint.setStyle(Paint.Style.FILL);
Edge.textBgPaint.setColor(Color.WHITE);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Edge(Vertex sourceVertex, Vertex targetVertex) {
super(Edge.defaultEdgeLayer);
this.id = Edge.no++;
this.sourceVertex = sourceVertex;
this.targetVertex = targetVertex;
this.sourceVertex.addEdge(this);
this.targetVertex.addEdge(this);
this.recalculate();
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void recalculate() {
this.cachedSourceCoord = new Coord(Math.round((Edge.baseX + this.sourceVertex.getCoord().getX()) * Edge.scaleFactor), Math.round((Edge.baseY + this.sourceVertex.getCoord().getY()) * Edge.scaleFactor));
this.cachedTargetCoord = new Coord(Math.round((Edge.baseX + this.targetVertex.getCoord().getX()) * Edge.scaleFactor), Math.round((Edge.baseY + this.targetVertex.getCoord().getY()) * Edge.scaleFactor));
Line line = new Line(this.cachedSourceCoord, this.cachedTargetCoord);
this.weightCoord = line.getMiddle();
this.onScreen = Edge.screenBottomLine.hasIntersection(line)
|| Edge.screenLeftLine.hasIntersection(line)
|| Edge.screenRightLine.hasIntersection(line)
|| Edge.screenTopLine.hasIntersection(line)
|| this.cachedSourceCoord.getX() > 0 && this.cachedSourceCoord.getX() < Edge.screenWidth && this.cachedSourceCoord.getY() > 0 && this.cachedSourceCoord.getY() < Edge.screenHeight
|| this.cachedTargetCoord.getX() > 0 && this.cachedTargetCoord.getX() < Edge.screenWidth && this.cachedTargetCoord.getY() > 0 && this.cachedTargetCoord.getY() < Edge.screenHeight;
}
// --------------------------------------------------------------------------------------------------------------------------------------
#Override
public void draw(Canvas canvas) {
this.recalculate();
if (!this.onScreen) {
return;
}
canvas.drawLine(this.cachedSourceCoord.getX(), this.cachedSourceCoord.getY(), this.cachedTargetCoord.getX(), this.cachedTargetCoord.getY(), this.colored ? Edge.paintColored : Edge.paint);
if (Edge.graph != null && Edge.graph.isDirected()) {
Line line = new Line(this.cachedSourceCoord, this.cachedTargetCoord);
Coord v = line.getVector();
float t = (float) ((Vertex.RADIUS_SIZE + 5) / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord t1 = new Coord((int) (this.cachedTargetCoord.getX() - t * v.getX()), (int) (this.cachedTargetCoord.getY() - t * v.getY()));
if (!line.isOnLine(t1)) {
t1 = new Coord((int) (this.cachedTargetCoord.getX() + t * v.getX()), (int) (this.cachedTargetCoord.getY() + t * v.getY()));
}
t = (float) ((Vertex.RADIUS_SIZE + 5 + Edge.TRIANGLE_SIZE) / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord p = new Coord((int) (this.cachedTargetCoord.getX() - t * v.getX()), (int) (this.cachedTargetCoord.getY() - t * v.getY()));
if (!line.isOnLine(p)) {
p = new Coord((int) (this.cachedTargetCoord.getX() + t * v.getX()), (int) (this.cachedTargetCoord.getY() + t * v.getY()));
}
v = line.getNormalVector().getVector();
t = (float) ((Edge.TRIANGLE_SIZE) / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord t2 = new Coord((int) (p.getX() - t * v.getX()), (int) (p.getY() - t * v.getY()));
Coord t3 = new Coord((int) (p.getX() + t * v.getX()), (int) (p.getY() + t * v.getY()));
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.moveTo(t1.getX(), t1.getY());
path.lineTo(t2.getX(), t2.getY());
path.lineTo(t3.getX(), t3.getY());
path.lineTo(t1.getX(), t1.getY());
path.close();
canvas.drawPath(path, Edge.paint);
}
if (Edge.graph != null && Edge.graph.isWeighted()) {
float width = Edge.textPaint.measureText(Integer.toString(this.weight)) + 10;
float height = Edge.textPaint.getTextSize() + 5;
canvas.drawRect(this.weightCoord.getX() - width / 2, this.weightCoord.getY() - height / 2, this.weightCoord.getX() + width / 2, this.weightCoord.getY() + height / 2, Edge.textBgPaint);
canvas.drawText(Integer.toString(this.weight), this.weightCoord.getX(), this.weightCoord.getY() + height * 0.25f, Edge.textPaint);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
private boolean searchingCoordOn(float distance, float pX, float pY) {
Coord p = new Coord((int) pX, (int) pY);
Coord v = (new Line(this.cachedSourceCoord, this.cachedTargetCoord)).getNormalVector().getVector();
float t = (float) (distance / Math.sqrt(Math.pow(v.getX(), 2) + Math.pow(v.getY(), 2)));
Coord c1 = new Coord((int) (this.cachedSourceCoord.getX() - t * v.getX()), (int) (this.cachedSourceCoord.getY() - t * v.getY()));
Coord c2 = new Coord((int) (this.cachedSourceCoord.getX() + t * v.getX()), (int) (this.cachedSourceCoord.getY() + t * v.getY()));
Coord c3 = new Coord((int) (this.cachedTargetCoord.getX() - t * v.getX()), (int) (this.cachedTargetCoord.getY() - t * v.getY()));
Coord v1 = new Coord(c2.getX() - c1.getX(), c2.getY() - c1.getY());
Coord v2 = new Coord(c3.getX() - c1.getX(), c3.getY() - c1.getY());
v = Coord.minus(p, c1);
return this.onScreen
&& 0 <= Coord.dotProduct(v, v1) && Coord.dotProduct(v, v1) <= Coord.dotProduct(v1, v1)
&& 0 <= Coord.dotProduct(v, v2) && Coord.dotProduct(v, v2) <= Coord.dotProduct(v2, v2);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isOnCoord(float pX, float pY) {
return this.searchingCoordOn(Edge.SENSOR_WIDTH / 2, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public boolean isInCoordSurroundings(float pX, float pY) {
return this.searchingCoordOn(Edge.SURROUNDING_SENSOR_WIDTH / 2, pX, pY);
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex getSourceVertex() {
return this.sourceVertex;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public Vertex getTargetVertex() {
return this.targetVertex;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void setDefaultLayer(Layer layer) {
Edge.defaultEdgeLayer = layer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static Layer getDefaultLayer() {
return Edge.defaultEdgeLayer;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static void remove(Edge edge) {
edge.getSourceVertex().removeEdge(edge);
edge.getTargetVertex().removeEdge(edge);
Edge.defaultEdgeLayer.remove(edge);
if (Edge.graph != null) {
Edge.graph.remove(edge);
}
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setWeight(int weight) {
this.weight = weight;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getWeight() {
return this.weight;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public int getId() {
return this.id;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setColored() {
this.colored = true;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public void setColorToDefault() {
this.colored = false;
}
// --------------------------------------------------------------------------------------------------------------------------------------
public static ArrayList<Edge> getEdgesBetween(Vertex v1, Vertex v2) {
ArrayList<Edge> edges = v1.getEdges();
ArrayList<Edge> edgesRet = new ArrayList<Edge>();
for (Edge edge : edges) {
if (v2.hasEdge(edge)) {
edgesRet.add(edge);
}
}
return edges;
}
// --------------------------------------------------------------------------------------------------------------------------------------
}
The reason for this is that the "normal" serialization would for each entry recursively invoke another writeObject(e), and for long lists this would give a StackOverflowError. The iterative serialization avoids this.
I found this on stackoverflow. I think my problem is related to my long LinkedLists..
Any ideas, advices how to properly serialize and deserialize my Graph object?
Thank You In Advance!
As a fast fix, you could try substuting the LinkedList List implemetnations with ArrayLists. You didn't give enough details (the exact exception and the implementations of the vertexes and edges would be helpful), but what you quoted might be right: we run out of memory because of the recursive calls. I didn't try, but the ArrayList default Java serialization would avoid this issue.
For a really good solution, the serialization methods of the graph objects could be re-implemented, or a tested and more memory efficient library could be used (eg. kryo).
This question would also be helpful.
Try runnning your program with more RAM. StackOverFlowError means, that the stack is filled and cant take any other method. What you could also do, is run the deserialization in another Thread; They have a seperate stack. There are several ways to do so, I recommend using the classes from java.util.concurrent.