/*
* Copyright 2014 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.math.delaunay;
import java.util.ArrayList;
import java.util.List;
import org.terasology.math.geom.Vector2f;
final class HalfedgePriorityQueue {
private List<Halfedge> hash;
private int count;
private int minBucket;
private int hashsize;
private float ymin;
private float deltay;
public HalfedgePriorityQueue(float ymin, float deltay, int sqrtNumSites) {
this.ymin = ymin;
this.deltay = deltay;
hashsize = 4 * sqrtNumSites;
count = 0;
minBucket = 0;
hash = new ArrayList<Halfedge>(hashsize);
// dummy Halfedge at the top of each hash
for (int i = 0; i < hashsize; ++i) {
hash.add(Halfedge.createDummy());
hash.get(i).nextInPriorityQueue = null;
}
}
public void dispose() {
// get rid of dummies
for (int i = 0; i < hashsize; ++i) {
hash.get(i).dispose();
}
hash.clear();
hash = null;
}
public void insert(Halfedge halfEdge) {
Halfedge previous;
Halfedge next;
int insertionBucket = bucket(halfEdge);
if (insertionBucket < minBucket) {
minBucket = insertionBucket;
}
previous = hash.get(insertionBucket);
next = previous.nextInPriorityQueue;
while (next != null && (halfEdge.ystar > next.ystar || (halfEdge.ystar == next.ystar && halfEdge.vertex.getX() > next.vertex.getX()))) {
previous = next;
next = previous.nextInPriorityQueue;
}
halfEdge.nextInPriorityQueue = previous.nextInPriorityQueue;
previous.nextInPriorityQueue = halfEdge;
++count;
}
public void remove(Halfedge halfEdge) {
Halfedge previous;
int removalBucket = bucket(halfEdge);
if (halfEdge.vertex != null) {
previous = hash.get(removalBucket);
while (previous.nextInPriorityQueue != halfEdge) {
previous = previous.nextInPriorityQueue;
}
previous.nextInPriorityQueue = halfEdge.nextInPriorityQueue;
count--;
halfEdge.vertex = null;
halfEdge.nextInPriorityQueue = null;
halfEdge.dispose();
}
}
private int bucket(Halfedge halfEdge) {
int theBucket = (int) ((halfEdge.ystar - ymin) / deltay * hashsize);
if (theBucket < 0) {
theBucket = 0;
}
if (theBucket >= hashsize) {
theBucket = hashsize - 1;
}
return theBucket;
}
private boolean isEmpty(int bucket) {
return (hash.get(bucket).nextInPriorityQueue == null);
}
/**
* move _minBucket until it contains an actual Halfedge (not just the dummy
* at the top);
*
*/
private void adjustMinBucket() {
while (minBucket < hashsize - 1 && isEmpty(minBucket)) {
++minBucket;
}
}
public boolean empty() {
return count == 0;
}
/**
* @return coordinates of the Halfedge's vertex in V*, the transformed
* Voronoi diagram
*
*/
public Vector2f min() {
adjustMinBucket();
Halfedge answer = hash.get(minBucket).nextInPriorityQueue;
return new Vector2f(answer.vertex.getX(), answer.ystar);
}
/**
* remove and return the min Halfedge
*
* @return
*
*/
public Halfedge extractMin() {
Halfedge answer;
// get the first real Halfedge in _minBucket
answer = hash.get(minBucket).nextInPriorityQueue;
hash.get(minBucket).nextInPriorityQueue = answer.nextInPriorityQueue;
count--;
answer.nextInPriorityQueue = null;
return answer;
}
}