Unity - Infinite terrain gaps betwean chunks? - multithreading

So I am creating an endless terrain.
I can create the terrain but my chunks have gaps betwean them and they don't align properly.
I think the problem might be caused by my Noise Generation script, but I am not sure.
This is my Noise Generation script
public static class Noise_GENERATOR
{
public static float[,] GenerateNoise(int chunkSize, int octaves, int seed, float noiseScale, float persistence, float lacunarity, Vector2 offset)
{
float[,] noiseMap = new float[chunkSize, chunkSize];
System.Random prng = new System.Random(seed);
Vector2[] octaveOffsets = new Vector2[octaves];
float maxPossibleHeight = 0;
float amplitude = 1;
float frequency = 1;
for (int i = 0; i < octaves; i++)
{
float offsetX = prng.Next(-100000, 100000) + offset.x;
float offsetY = prng.Next(-100000, 100000) + offset.y;
octaveOffsets[i] = new Vector2(offsetX, offsetY);
maxPossibleHeight += amplitude;
amplitude *= persistence;
}
if (noiseScale <= 0)
{
noiseScale = 0.0001f;
}
float maxLocalNoiseHeight = float.MinValue;
float minLocalNoiseHeight = float.MaxValue;
float halfWidth = chunkSize / 2f;
float halfHeight = chunkSize / 2f;
for (int y = 0; y < chunkSize; y++)
{
for (int x = 0; x < chunkSize; x++)
{
amplitude = 1;
frequency = 1;
float noiseHeight = 0;
for (int i = 0; i < octaves; i++)
{
float sampleX = (x-halfWidth + octaveOffsets[i].x) / noiseScale * frequency;
float sampleY = (y-halfHeight + octaveOffsets[i].y) / noiseScale * frequency;
float perlinValue = Mathf.PerlinNoise(sampleX, sampleY) * 2 - 1;
noiseHeight += perlinValue * amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
if (noiseHeight > maxLocalNoiseHeight)
{
maxLocalNoiseHeight = noiseHeight;
}
else if (noiseHeight < minLocalNoiseHeight)
{
minLocalNoiseHeight = noiseHeight;
}
noiseMap[x, y] = noiseHeight;
float normalizedHeight = (noiseMap[x, y] + 1) / (maxPossibleHeight / 0.9f);
noiseMap[x, y] = Mathf.Clamp(normalizedHeight, 0, int.MaxValue);
}
}
return noiseMap;
}
}
To Generate the height of a mesh, I am using Animation Curve and multiplying it by elevationScale variable.
float height = heightCurve.Evaluate(noiseMap[x, y]) * elevationScale;
I thought about accesing each Terrain chunk and getting the height of the edges and matching them together but that would look really weird and I don't know how to do it properly.
EDIT: Here just in case my Mesh generator script and how I am creating the Terrain chunk
public static class Mesh_GENERATOR
{
public static MeshData GenerateChunkMesh(int chunkSize,float[,] noiseMapData,float elevationScale,AnimationCurve terrainCurve,int LODlevel )
{
float[,] noiseMap = noiseMapData;
AnimationCurve heightCurve = new AnimationCurve(terrainCurve.keys);
//Setup variables
Vector3[] vertices = new Vector3[chunkSize * chunkSize];
int[] triangles = new int[(chunkSize - 1) * (chunkSize - 1) * 6];
Vector2[] uvs = new Vector2[chunkSize * chunkSize];
int triangle = 0;
int levelOfDetailIncrement = (LODlevel == 0) ? 1 : LODlevel * 2;
int numberOfVerticesPerRow = (chunkSize) / levelOfDetailIncrement + 1;
for (int y = 0; y < chunkSize; y++)
{
for (int x = 0; x < chunkSize; x++)
{
int i = y * chunkSize + x;
//Create vertices at position and center mesh
float height = heightCurve.Evaluate(noiseMap[x, y]) * elevationScale;
Vector2 percentPosition = new Vector2(x / (chunkSize - 1f), y / (chunkSize -1f ));
Vector3 vertPosition = new Vector3(percentPosition.x * 2 - 1, 0, percentPosition.y * 2 - 1) * chunkSize/2;
vertPosition.y = height;
vertices[i] = vertPosition;
uvs[i] = new Vector2((float)x / chunkSize, (float)y / chunkSize);
//Construct triangles
if (x != chunkSize - 1 && y != chunkSize - 1)
{
triangles[triangle + 0] = i + chunkSize;
triangles[triangle + 1] = i + chunkSize + 1;
triangles[triangle + 2] = i;
triangles[triangle + 3] = i + chunkSize + 1;
triangles[triangle + 4] = i + 1;
triangles[triangle + 5] = i;
triangle += 6;
}
}
}
MeshData meshData = new MeshData(chunkSize, vertices, triangles, uvs);
return meshData;
}
}
public class MeshData
{
public int chunkSize;
public Vector3[] vertices;
public int[] triangles;
public Vector2[] uvs;
public Mesh mesh;
public MeshData(int chunkSize,Vector3[] vertices,int[] triangles, Vector2[] uvs)
{
this.chunkSize = chunkSize;
this.vertices = vertices;
this.triangles = triangles;
this.uvs = uvs;
}
public Mesh CreateMesh()
{
if(mesh == null) { mesh = new Mesh(); } else { mesh.Clear(); }
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
return mesh;
}
}
And here is my TerrainChunk
public class TerrainChunk
{
GameObject meshObject;
Vector2 position;
Bounds bounds;
MeshRenderer meshRenderer;
MeshFilter meshFilter;
public TerrainChunk(Vector2 coord, int chunkSize, Transform parent,Material terrainMaterial)
{
position = coord * chunkSize;
bounds = new Bounds(position, Vector2.one * chunkSize);
Vector3 positionV3 = new Vector3(position.x , 0, position.y );
Debug.Log("CHUNK: COORD" + coord + "POSITION" + position + "POSITION3V" + positionV3);
meshObject = new GameObject("Terrain Chunk");
meshFilter = meshObject.AddComponent<MeshFilter>();
meshRenderer = meshObject.AddComponent<MeshRenderer>();
meshRenderer.material = terrainMaterial;
meshObject.transform.position = positionV3;
meshObject.transform.parent = parent;
SetVisible(false);
worldGenerator.RequestMapData(position,OnNoiseDataReceived);
}
void OnNoiseDataReceived(MapData mapData)
{
worldGenerator.RequestMeshData(mapData, OnMeshDataReceived);
}
void OnMeshDataReceived(MeshData meshData)
{
meshFilter.mesh = meshData.CreateMesh();
}
public void UpdateTerrainChunk(Vector2 viewerPosition, int maxRenderDistance)
{
float viewerDstFromNearestEdge = Mathf.Sqrt(bounds.SqrDistance(viewerPosition));
bool visible = viewerDstFromNearestEdge <= maxRenderDistance;
SetVisible(visible);
}
public void SetVisible(bool visible)
{
meshObject.SetActive(visible);
}
public bool IsVisible()
{
return meshObject.activeSelf;
}
}
}

If I undestand all your values and variables correctly.
The problem might lay in the Noise Generator.
You need to create the chunkSize to be bigger by 1 so if you are passing 250 you will need to pass 251, as the for loop in the Noise Generator stops at 249 and not 250. (I might be wrong about this ), If you do this the mesh generator will now have the right values for calculation.
So your chunksize variable should look like this
chunkSize = chunkSize + 1;
Now there will still be smaller gaps and the mesh will clip through each other, so to fix this you will need to position the Chunk and you do it this way ->
(If your coord serves as a direction in which the chunk will be created from your World generator object -> for example chunks pointing North will be with values x:0 y:1, chunks pointing West will be x:-1 y:0, NorthWest chunks x:-1 y:-1 and so on), you may need to change the 0.5f to your values so the chunks align properly.
Vector3 positionV3 = new Vector3(position.x + (coord.x + 0.5f), 0, position.y + (coord.y + 0.5f) );
There still may be some smaller gaps visible in the terrain, but this can be fixed by playing with the values, or you can try and access each Chunk and get the edge vertices and their heights and connect the chunks together this way.

Related

Processing, simple "raytracing" engine "target VM failed to initialize"

I've been trying to fix this thing for a while now but it doesn't seem to work; "Could not run the sketch (Target VM failed to initialize)."
I'll post the full code down below.
In the draw(), there are three for loops.
for(int i = 0; i<objectAmount; i++) {
circles[i].drawObj();
}
The first one creates the circles, while the second nested ones take care of collision and drawing the lines;
for(int i = 0; i<rayAmount; i++) {
rays[i].update();
for(int j = 0; j<objectAmount; j++) {
rays[i].collide(circles[j]);
}
line(rays[i].xPos, rays[i].yPos, rays[i].xEnd, rays[i].yEnd);
}
the .collide takes point on the 'ray' and moves closer to the circle until it reaches some value, where it marks the line's end, which is then used by the line() function to draw it to the circle.
For some reason, when I implemented the .collide function, everything stopped working unless I set the amount of rays to one, in which case no rays would appear but the circle generation would follow along just fine.
int rayAmount = 45;
int angleCorrect = 360/rayAmount;
int objectAmount = 10;
Ray[] rays = new Ray[rayAmount];
Object[] circles = new Object[objectAmount];
void setup() {
size(600, 400, P2D);
for(int i = 0; i<rayAmount; i++) {
rays[i] = new Ray(i*angleCorrect);
}
for(int i = 0; i<objectAmount; i++) {
circles[i] = new Object(random(0, 600), random(0, 400), random(20, 100));
}
}
void draw() {
background(255);
stroke(100);
for(int i = 0; i<objectAmount; i++) {
circles[i].drawObj();
}
for(int i = 0; i<rayAmount; i++) {
rays[i].update();
for(int j = 0; j<objectAmount; j++) {
rays[i].collide(circles[j]);
}
line(rays[i].xPos, rays[i].yPos, rays[i].xEnd, rays[i].yEnd);
}
}
class Ray {
float xPos, yPos, Angle, xEnd, yEnd;
Ray(float angle) {
xPos = mouseX;
yPos = mouseY;
Angle = angle;
}
void update() {
xPos = mouseX;
yPos = mouseY;
//xEnd = xPos + 100 * cos(radians(Angle));
//yEnd = yPos + 100 * sin(radians(Angle));
}
void collide(Object other) {
float newXEnd = this.xEnd;
float newYEnd = this.yEnd;
float distToObject = sqrt(pow(other.xPos-this.xPos, 2) + pow(other.yPos-this.yPos, 2));
while(distToObject > 1) {
newXEnd = newXEnd + distToObject * cos(radians(Angle));
newYEnd = newYEnd + distToObject * sin(radians(Angle));
distToObject = sqrt(pow(other.xPos-newXEnd, 2) + pow(other.yPos-newYEnd, 2));
}
this.xEnd = newXEnd;
this.yEnd = newYEnd;
}
}
class Object {
float xPos, yPos, radius;
Object(float x, float y, float r) {
xPos = x;
yPos = y;
radius = r;
}
void drawObj() {
stroke(100);
circle(xPos, yPos, radius);
}
}

What might cause a sprite to stop directional movement when colliding in a 2-D game (the sprite still animates)?

!! This is not my code - I am using this to learn !! However, I do not understand what might cause the sprite to stop it's directional movement when colliding with the "STONE" object. The entire 2-D Game will run and the sprite works properly, but when I move close to the end of the screen or too close to the stone, I get stuck in place. I could not find an explanation I understood completely. I apologize in advance if this is a trivial question to anyone.
Basic Solid Tile Class:
public class BasicSolidTile extends BasicTile {
public BasicSolidTile(int id, int x, int y, int tileColour) {
super(id, x, y, tileColour);
this.solid = true;
}
}
Tile Class:
public abstract class Tile {
public static final Tile[] tiles = new Tile[256];
public static final Tile VOID = new BasicSolidTile(0, 0, 0, Colours.get(000, -1, -1, -1));
public static final Tile STONE = new BasicSolidTile(1, 1, 0, Colours.get(-1, 333, -1, -1));
public static final Tile GRASS = new BasicTile(2, 2, 0, Colours.get(-1, 131, 141, -1));
protected byte id;
protected boolean solid;
protected boolean emitter;
public Tile(int id, boolean isSolid, boolean isEmitter) {
this.id = (byte) id;
if (tiles[id] != null) {
throw new RuntimeException("Duplicate tile id on" + id);
}
this.solid = isSolid;
this.emitter = isEmitter;
tiles[id] = this;
}
public byte getId() {
return id;
}
public boolean isSolid() {
return solid;
}
public boolean isEmitter() {
return emitter;
}
public abstract void render(Screen screen, Level level, int x, int y);
}
Mob Class:
public abstract class Mob extends Entity {
protected String name;
protected int speed;
protected int numSteps = 0;
protected boolean isMoving;
protected int movingDir = 1;
protected int scale = 1;
public Mob(Level level, String name, int x, int y, int speed) {
super(level);
this.name = name;
this.x = x;
this.y = y;
this.speed = speed;
}
public void move(int xa, int ya) {
// you want to check if they are not zero
if (xa != 0 && ya != 0) {
move(xa, 0);
move(0, ya);
numSteps--;
return;
}
numSteps++;
if (!hasCollided(x, y)) {
if (ya < 0)
movingDir = 0;
if (ya > 0)
movingDir = 1;
if (xa < 0)
movingDir = 2;
if (xa > 0)
movingDir = 3;
x += xa * speed;
y += ya * speed;
}
}
public abstract boolean hasCollided(int xa, int ya);
protected boolean isSolidTile(int xa, int ya, int x, int y) {
if (level == null) {
return false;
}
Tile lastTile = level.getTile((this.x + x) >> 3, (this.y + y) >> 3);
Tile newTile = level.getTile((this.x + x + xa) >> 3, (this.y + y + ya) >> 3);
if (!lastTile.equals(newTile) && newTile.isSolid()) {
return true;
}
return false;
}
public String getName() {
return name;
}
}
Level Class:
public class Level {
// array of id's
private byte[] tiles;
public int width;
public int height;
public List<Entity> entities = new ArrayList<Entity>();
public Level(int width, int height) {
tiles = new byte[width * height];
this.width = width;
this.height = height;
this.generateLevel();
}
public void generateLevel() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (x * y %10 < 7) {
tiles[x + y * width] = Tile.GRASS.getId();
} else {
tiles[x + y * width] = Tile.STONE.getId();
}
}
}
}
public void tick() {
// loop through all the vars if you dont need an index var
for (Entity e : entities) {
e.tick();
}
}
public void renderTiles(Screen screen, int xOffset, int yOffset) {
if (xOffset < 0)
xOffset = 0;
if (xOffset > ((width << 3) - screen.width))
xOffset = ((width << 3) - screen.width);
if (yOffset < 0)
yOffset = 0;
if (yOffset > ((height << 3) - screen.height))
yOffset = ((height << 3) - screen.height);
screen.setOffset(xOffset, yOffset);
for (int y = (yOffset >> 3); y < (yOffset + screen.height >> 3) + 1; y++) {
for (int x = (xOffset >> 3); x < (xOffset + screen.width >> 3) + 1; x++) {
getTile(x, y).render(screen, this, x << 3, y << 3);
}
}
}
public void renderEntities(Screen screen) {
for (Entity e : entities) {
e.render(screen);
}
}
public Tile getTile(int x, int y) {
if (0 > x || x >= width || 0 > y || y >= height)
return Tile.VOID;
return Tile.tiles[tiles[x + y * width]];
}
public void addEntity(Entity entity) {
this.entities.add(entity);
}
}
Player Class:
public class Player extends Mob {
private InputHandler input;
private int colour = Colours.get(-1, 111, 145, 543);
private int scale = 1;
public Player(Level level, int x, int y, InputHandler input) {
super(level, "Player", x, y, 1);
this.input = input;
}
public void tick() {
int xa = 0;
int ya = 0;
if (input.up.isPressed()) {
ya--;
}
if (input.down.isPressed()) {
ya++;
}
if (input.left.isPressed()) {
xa--;
}
if (input.right.isPressed()) {
xa++;
}
if (xa != 0 || ya != 0) {
move(xa, ya);
isMoving = true;
} else {
isMoving = false;
}
this.scale = 1;
}
public void render(Screen screen) {
int xTile = 0;
int yTile = 28;
int walkingSpeed = 4;
int flipTop = (numSteps >> walkingSpeed) & 1;
int flipBottom = (numSteps >> walkingSpeed) & 1;
if (movingDir == 1) {
xTile += 2;
} else if (movingDir > 1) {
xTile += 4 + ((numSteps >> walkingSpeed) & 1) * 2;
flipTop = (movingDir - 1) % 2;
}
int modifier = 8 * scale;
int xOffset = x - modifier / 2;
int yOffset = y - modifier / 2 - 4;
// upper body
screen.render(xOffset + (modifier * flipTop), yOffset, xTile + yTile * 32, colour, flipTop, scale);
screen.render(xOffset + modifier - (modifier * flipTop), yOffset, (xTile + 1) + yTile * 32, colour, flipTop, scale);
// lower body
screen.render(xOffset + (modifier * flipBottom), yOffset + modifier, xTile + (yTile + 1) * 32, colour, flipBottom, scale);
screen.render(xOffset + modifier - (modifier * flipBottom), yOffset + modifier, (xTile + 1) + (yTile + 1) * 32, colour, flipBottom,
scale);
}
public boolean hasCollided(int xa, int ya) {
int xMin = 0;
int xMax = 7;
int yMin = 3;
int yMax = 7;
for (int x = xMin; x < xMax; x++) {
if (isSolidTile(xa, ya, x, yMin)) {
return true;
}
}
for (int x = xMin; x < xMax; x++) {
if (isSolidTile(xa, ya, x, yMax)) {
return true;
}
}
for (int y = yMin; y < yMax; y++) {
if (isSolidTile(xa, ya, xMin, y)) {
return true;
}
}
for (int y = yMin; y < yMax; y++) {
if (isSolidTile(xa, ya, xMax, y)) {
return true;
}
}
return false;
}
}
Okay. It took me FOREVER, but it was a simple fix. All I had to do was change the "(x, y)" to "(xa, ya)"
if (!hasCollided(xa, ya)) {
if (ya < 0)
movingDir = 0;
if (ya > 0)
movingDir = 1;
if (xa < 0)
movingDir = 2;
if (xa > 0)
movingDir = 3;
x += xa * speed;
y += ya * speed;
}
}

How can you get a sprite to follow another sprite in Java?

Here is the code for the initial character:
public class Player extends Mob {
private InputHandler input;
private int colour = Colours.get(-1, 111, 145, 543);
private int scale = 1;
protected boolean isSwimming = false;
private int tickCount = 0;
public Player(Level level, int x, int y, InputHandler input) {
super(level, "Player", x, y, 1);
this.input = input;
}
public void tick() {
int xa = 0;
int ya = 0;
if (input.up.isPressed()) {
ya--;
}
if (input.down.isPressed()) {
ya++;
}
if (input.left.isPressed()) {
xa--;
}
if (input.right.isPressed()) {
xa++;
}
if (xa != 0 || ya != 0) {
move(xa, ya);
isMoving = true;
} else {
isMoving = false;
}
if (level.getTile(this.x >> 3, this.y >> 3).getId() == 3) {
isSwimming = true;
}
if (isSwimming && level.getTile(this.x >> 3, this.y >> 3).getId() != 3) {
isSwimming = false;
}
tickCount++;
this.scale = 1;
}
public void render(Screen screen) {
int xTile = 0;
int yTile = 28;
int walkingSpeed = 4;
int flipTop = (numSteps >> walkingSpeed) & 1;
int flipBottom = (numSteps >> walkingSpeed) & 1;
if (movingDir == 1) {
xTile += 2;
} else if (movingDir > 1) {
xTile += 4 + ((numSteps >> walkingSpeed) & 1) * 2;
flipTop = (movingDir - 1) % 2;
}
int modifier = 8 * scale;
int xOffset = x - modifier / 2;
int yOffset = y - modifier / 2 - 4;
if (isSwimming) {
int waterColour = 0;
yOffset += 4;
if (tickCount % 60 < 15) {
waterColour = Colours.get(-1, -1, 225, -1);
} else if (15 <= tickCount % 60 && tickCount % 60 < 30) {
yOffset -= 1;
waterColour = Colours.get(-1, 225, 115, -1);
} else if (30 <= tickCount % 60 && tickCount % 60 < 45) {
waterColour = Colours.get(-1, 115, -1, 225);
} else {
waterColour = Colours.get(-1, 225, 115, -1);
}
screen.render(xOffset, yOffset + 3, 0 + 27 * 32, waterColour, 0x00, 1);
screen.render(xOffset + 8, yOffset + 3, 0 + 27 * 32, waterColour, 0x01, 1);
}
// upper body
screen.render(xOffset + (modifier * flipTop), yOffset, xTile + yTile * 32, colour, flipTop, scale);
screen.render(xOffset + modifier - (modifier * flipTop), yOffset, (xTile + 1) + yTile * 32, colour, flipTop,
scale);
if (!isSwimming) {
// lower body
screen.render(xOffset + (modifier * flipBottom), yOffset + modifier, xTile + (yTile + 1) * 32, colour,
flipBottom, scale);
screen.render(xOffset + modifier - (modifier * flipBottom), yOffset + modifier,
(xTile + 1) + (yTile + 1) * 32, colour, flipBottom, scale);
}
}
public boolean hasCollided(int xa, int ya) {
int xMin = 0;
int xMax = 7;
int yMin = 1;
int yMax = 7;
for (int x = xMin; x < xMax; x++) {
if (isSolidTile(xa, ya, x, yMin)) {
return true;
}
}
for (int x = xMin; x < xMax; x++) {
if (isSolidTile(xa, ya, x, yMax)) {
return true;
}
}
for (int y = yMin; y < yMax; y++) {
if (isSolidTile(xa, ya, xMin, y)) {
return true;
}
}
for (int y = yMin; y < yMax; y++) {
if (isSolidTile(xa, ya, xMax, y)) {
return true;
}
}
return false;
}
}
can get another character to load at a given location; however, I cannot figure out an algorithm to have it follow the initial player. In other words the second one will follow these same collision and commands. Any help understanding is appreciated, thank you!

Counting Shapes/Objects Created Continuously within Processing

My code takes an image and creates a pointillist image through creating ellipses with a pixel's color.
After a while, the image is fully 'painted' and I want to automatically switch to another image in my sketch folder.
I would like to be able to count the number of ellipses generated. Once 'z' ellipses are generated I want to tell my code to erase all ellipses and start over with a new image.
CODE:
PImage img;
int smallPoint, largePoint;
void setup() {
size(1920, 1080);
img = loadImage("rio.jpg");
smallPoint = 12;
largePoint = 12;
imageMode(CENTER);
noStroke();
background(255);
}
void draw() {
for (int i = 0; i < 1000; i++)
{
drawADot();
}
}
void drawADot()
{
int imageWidth = img.width;
int imageHeight = img.height;
int ptSize = int(random(100)) + 4;
float pointillize = map(mouseX, 0, width, smallPoint, largePoint); //not used right now but for controlling ellipse size
int x = int(random(0, imageWidth/8));
int y = int(random(0, imageHeight/8));
color pix = img.get(x*8, y*8);
fill(pix, 255);
ellipse(x*8, y*8, pointillize, pointillize);
}
Store the images in an array, count the dots added, and conditionally (based in number of dots) change the image being used to next one in the array, you can pass the image to the drawADot() function as a parameter. Something like:
PImage img[] = new PImage[2];
int smallPoint, largePoint;
final int DOTSPERDRAW = 500;
int numberOfDots = 0;
final int MAXDOTS = DOTSPERDRAW * 100;
PImage workingImage ;
int index;
void setup() {
size(810, 455);
img[0] = loadImage("http://assets2.exame.abril.com.br/assets/images/2014/8/506584/size_810_16_9_rio.jpg");
img[1] = loadImage("http://upload.wikimedia.org/wikipedia/commons/1/1e/Pilcomayo_rio.jpg");
img[1].resize(810, 0);
smallPoint = 12;
largePoint = 12;
imageMode(CENTER);
noStroke();
background(255);
workingImage = img[0];
}
void draw() {
if (numberOfDots > MAXDOTS) {
index = (index + 1) % img.length;
workingImage = img[index];
numberOfDots = 0;
}
for (int i = 0; i < DOTSPERDRAW; i++)
{
drawADot(workingImage);
}
numberOfDots += DOTSPERDRAW;
}
void drawADot(PImage theImage)
{
int imageWidth = theImage.width;
int imageHeight = theImage.height;
int ptSize = int(random(100)) + 4;
float pointillize = map(mouseX, 0, width, smallPoint, largePoint); //not used right now but for controlling ellipse size
int x = int(random(0, imageWidth/8));
int y = int(random(0, imageHeight/8));
color pix = theImage.get(x*8, y*8);
fill(pix, 255);
ellipse(x*8, y*8, pointillize, pointillize);
}

Why are my curved edges not updating correctly?

I'm trying to customize a layout of JGraph. I want to create curved edges, but I have a problem. Every time I create a group of vertex on JGraph, and I am firing an event to update this graph, the edge is missing points compared to the previous state. Here is a example:
Can someone help me?
Here is my code:
class CurveGraphView extends mxGraphView {
public CurveGraphView(mxGraph graph) {
super(graph);
}
/* Only override this if you want the label to automatically position itself on the control point */
#Override
public mxPoint getPoint(mxCellState state, mxGeometry geometry) {
double x = state.getCenterX();
double y = state.getCenterY();
if (state.getAbsolutePointCount() == 3) {
mxPoint mid = state.getAbsolutePoint(1);
x = mid.getX();
y = mid.getY();
}
return new mxPoint(x, y);
}
// /* Makes sure that the full path of the curve is included in the bounding box */
#Override
public mxRectangle updateBoundingBox(mxCellState state) {
List<mxPoint> points = state.getAbsolutePoints();
mxRectangle bounds = super.updateBoundingBox(state);
Object style = state.getStyle().get("edgeStyle");
if (CurvedEdgeStyle.KEY.equals(style) && points != null && points.size() == 3) {
Rectangle pathBounds = CurvedShape.createPath(state.getAbsolutePoints()).getBounds();
Rectangle union = bounds.getRectangle().union(pathBounds);
bounds = new mxRectangle(union);
state.setBoundingBox(bounds);
}
return bounds;
}
}
class CurvedEdgeStyle implements mxEdgeStyle.mxEdgeStyleFunction {
public static final String KEY = "curvedEdgeStyle";
#Override
public void apply(mxCellState state, mxCellState source, mxCellState target, List<mxPoint> points, List<mxPoint> result) {
mxPoint pt = (points != null && points.size() > 0) ? points.get(0) : null;
if (source != null && target != null) {
double x = 0;
double y = 0;
if (pt != null) {
result.add(pt);
} else {
x = (target.getCenterX() + source.getCenterX()) / 2;
y = (target.getCenterY() + source.getCenterY()) / 2;
mxPoint point = new mxPoint(x, y);
result.add(point);
}
}
}
}
class CurvedShape extends mxConnectorShape {
public static final String KEY = "curvedEdge";
private GeneralPath path;
#Override
public void paintShape(mxGraphics2DCanvas canvas, mxCellState state) {
List<mxPoint> abs = state.getAbsolutePoints();
int n = state.getAbsolutePointCount();
mxCell aux = (mxCell)state.getCell();
if (n < 3) {
super.paintShape(canvas, state);
} else if (configureGraphics(canvas, state, false)) {
Graphics2D g = canvas.getGraphics();
path = createPath(abs);
g.draw(path);
paintMarker(canvas, state, false);
paintMarker(canvas, state, true);
}
}
/* Code borrowed from here: http://www.codeproject.com/Articles/31859/Draw-a-Smooth-Curve-through-a-Set-of-2D-Points-wit */
public static GeneralPath createPath(List<mxPoint> abs) {
mxPoint[] knots = abs.toArray(new mxPoint[abs.size()]);
int n = knots.length - 1;
mxPoint[] firstControlPoints = new mxPoint[n];
mxPoint[] secondControlPoints = new mxPoint[n]; // Calculate first Bezier control points // Right hand side vector
double[] rhs = new double[n]; // Set right hand side X values
for (int i = 1; i < n - 1; ++i) {
rhs[i] = 4 * knots[i].getX() + 2 * knots[i + 1].getX();
}
rhs[0] = knots[0].getX() + 2 * knots[1].getX();
rhs[n - 1] = (8 * knots[n - 1].getX() + knots[n].getX()) / 2.0; // Get first control points X-values
double[] x = getFirstControlPoints(rhs); // Set right hand side Y values
for (int i = 1; i < n - 1; ++i) {
rhs[i] = 4 * knots[i].getY() + 2 * knots[i + 1].getY();
}
rhs[0] = knots[0].getY() + 2 * knots[1].getY();
rhs[n - 1] = (8 * knots[n - 1].getY() + knots[n].getY()) / 2.0; // Get first control points Y-values
double[] y = getFirstControlPoints(rhs); // Fill output arrays.
for (int i = 0; i < n; ++i) { // First control point
firstControlPoints[i] = new mxPoint(x[i], y[i]); // Second control point
if (i < n - 1) {
secondControlPoints[i] = new mxPoint(2 * knots[i + 1].getX() - x[i + 1], 2 * knots[i + 1].getY() - y[i + 1]);
} else {
secondControlPoints[i] = new mxPoint((knots[n].getX() + x[n - 1]) / 2, (knots[n].getY() + y[n - 1]) / 2);
}
}
GeneralPath path = new GeneralPath();
path.moveTo(knots[0].getX(), knots[0].getY());
for (int i = 1; i < n + 1; i++) {
path.curveTo(firstControlPoints[i - 1].getX(), firstControlPoints[i - 1].getY(), secondControlPoints[i - 1].getX(), secondControlPoints[i - 1].getY(), knots[i].getX(), knots[i].getY());
}
return path;
}/// <summary>/// Solves a tridiagonal system for one of coordinates (x or y)/// of first Bezier control points./// </summary>/// <param name="rhs">Right hand side vector.</param>/// <returns>Solution vector.</returns>
private static double[] getFirstControlPoints(double[] rhs) {
int n = rhs.length;
double[] x = new double[n]; // Solution vector.
double[] tmp = new double[n]; // Temp workspace.
double b = 2.0;
x[0] = rhs[0] / b;
for (int i = 1; i < n; i++) // Decomposition and forward substitution.
{
tmp[i] = 1 / b;
b = (i < n - 1 ? 4.0 : 3.5) - tmp[i];
x[i] = (rhs[i] - x[i - 1]) / b;
}
for (int i = 1; i < n; i++) {
x[n - i - 1] -= tmp[n - i] * x[n - i]; // Backsubstitution.
}
return x;
}
#Override
protected mxLine getMarkerVector(List<mxPoint> points, boolean source, double markerSize) {
if (path == null || points.size() < 3) {
return super.getMarkerVector(points, source, markerSize);
}
double coords[] = new double[6];
double x0 = 0;
double y0 = 0;
double x1 = 0;
double y1 = 0;
PathIterator p = path.getPathIterator(null, 2.0);
if (source) {
p.currentSegment(coords);
x1 = coords[0];
y1 = coords[1];
p.next();
p.currentSegment(coords);
x0 = coords[0];
y0 = coords[1];
} else {
while (!p.isDone()) {
p.currentSegment(coords);
x0 = x1;
y0 = y1;
x1 = coords[0];
y1 = coords[1];
p.next();
}
}
return new mxLine(x0, y0, new mxPoint(x1, y1));
}
}
extends mxGraph and Override the constructor adding:
mxGraphics2DCanvas.putShape(CurvedShape.KEY, new CurvedShape());
mxStyleRegistry.putValue(CurvedEdgeStyle.KEY, new CurvedEdgeStyle());
getStylesheet().getDefaultEdgeStyle().put(mxConstants.STYLE_SHAPE, CurvedShape.KEY);
getStylesheet().getDefaultEdgeStyle().put(mxConstants.STYLE_EDGE, CurvedEdgeStyle.KEY);
and Override createGraphView() returning CurveGraphView.
Have told me it has to do something with absolute points.

Resources