Yolo detector in Java - tensorflow

I'm trying to implement Yolo detector in Java (not Android, but desktops - Windows/Ubuntu)
There is already Yolo detector for Android: https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/android
I copied some java classes from that project, added them to IntelliJ IDEA and edited them
I even copied and edited TensorFlowInferenceInterface.java which is from jar file (tensorflow library - libandroid_tensorflow_inference_java.jar) for Android
I'm almost succeeded to get it working.
Result
Console output (class title, confidence, x, y, width, height):
car 0.8836523, 148 166 270 267
car 0.51286024, 147 174 268 274
car 0.05002968, 174 164 275 262
So seems it correctly detected car, determined correct x, y
but something wrong with width and height
What could be wrong?
Here's the full code from my project
Main
public class Main implements Classifier {
private static final int BLOCK_SIZE = 32;
private static final int MAX_RESULTS = 3;
private static final int NUM_CLASSES = 20;
private static final int NUM_BOXES_PER_BLOCK = 5;
private static final int INPUT_SIZE = 416;
private static final String inputName = "input";
private static final String outputName = "output";
// Pre-allocated buffers.
private static int[] intValues;
private static float[] floatValues;
private static String[] outputNames;
// yolo 2
private static final double[] ANCHORS = { 1.3221, 1.73145, 3.19275, 4.00944, 5.05587, 8.09892, 9.47112, 4.84053, 11.2364, 10.0071 };
// tiny yolo
//private static final double[] ANCHORS = { 1.08, 1.19, 3.42, 4.41, 6.63, 11.38, 9.42, 5.11, 16.62, 10.52 };
private static final String[] LABELS = {
"aeroplane",
"bicycle",
"bird",
"boat",
"bottle",
"bus",
"car",
"cat",
"chair",
"cow",
"diningtable",
"dog",
"horse",
"motorbike",
"person",
"pottedplant",
"sheep",
"sofa",
"train",
"tvmonitor"
};
private static TensorFlowInferenceInterface inferenceInterface;
public static void main(String[] args) {
//String modelDir = "/home/user/JavaProjects/TensorFlowJavaProject"; // Ubuntu
String modelAndTestImagesDir = "D:\\JavaProjects\\TensorFlowJavaProject"; // Windows
String imageFile = modelAndTestImagesDir + File.separator + "0.png"; // 416x416 test image
outputNames = outputName.split(",");
floatValues = new float[INPUT_SIZE * INPUT_SIZE * 3];
// yolo 2 voc
inferenceInterface = new TensorFlowInferenceInterface(Paths.get(modelAndTestImagesDir, "yolo-voc.pb"));
// tiny yolo voc
//inferenceInterface = new TensorFlowInferenceInterface(Paths.get(modelAndTestImagesDir, "graph-tiny-yolo-voc.pb"));
BufferedImage img;
try {
img = ImageIO.read(new File(imageFile));
BufferedImage convertedImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
convertedImg.getGraphics().drawImage(img, 0, 0, null);
intValues = ((DataBufferInt) convertedImg.getRaster().getDataBuffer()).getData() ;
List<Classifier.Recognition> recognitions = recognizeImage();
System.out.println("Result length " + recognitions.size());
Graphics2D graphics = convertedImg.createGraphics();
for (Recognition recognition : recognitions) {
RectF rectF = recognition.getLocation();
System.out.println(recognition.getTitle() + " " + recognition.getConfidence() + ", " +
(int) rectF.x + " " + (int) rectF.y + " " + (int) rectF.width + " " + ((int) rectF.height));
Stroke stroke = graphics.getStroke();
graphics.setStroke(new BasicStroke(3));
graphics.setColor(Color.green);
graphics.drawRoundRect((int) rectF.x, (int) rectF.y, (int) rectF.width, (int) rectF.height, 5, 5);
graphics.setStroke(stroke);
}
graphics.dispose();
ImageIcon icon=new ImageIcon(convertedImg);
JFrame frame=new JFrame();
frame.setLayout(new FlowLayout());
frame.setSize(convertedImg.getWidth(),convertedImg.getHeight());
JLabel lbl=new JLabel();
frame.setTitle("Java (Win/Ubuntu), Tensorflow & Yolo");
lbl.setIcon(icon);
frame.add(lbl);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
} catch (IOException e) {
e.printStackTrace();
}
}
private static List<Classifier.Recognition> recognizeImage() {
for (int i = 0; i < intValues.length; ++i) {
floatValues[i * 3 + 0] = ((intValues[i] >> 16) & 0xFF) / 255.0f;
floatValues[i * 3 + 1] = ((intValues[i] >> 8) & 0xFF) / 255.0f;
floatValues[i * 3 + 2] = (intValues[i] & 0xFF) / 255.0f;
}
inferenceInterface.feed(inputName, floatValues, 1, INPUT_SIZE, INPUT_SIZE, 3);
inferenceInterface.run(outputNames, false);
final int gridWidth = INPUT_SIZE / BLOCK_SIZE;
final int gridHeight = INPUT_SIZE / BLOCK_SIZE;
final float[] output = new float[gridWidth * gridHeight * (NUM_CLASSES + 5) * NUM_BOXES_PER_BLOCK];
inferenceInterface.fetch(outputNames[0], output);
// Find the best detections.
final PriorityQueue<Classifier.Recognition> pq =
new PriorityQueue<>(
1,
new Comparator<Classifier.Recognition>() {
#Override
public int compare(final Classifier.Recognition lhs, final Classifier.Recognition rhs) {
// Intentionally reversed to put high confidence at the head of the queue.
return Float.compare(rhs.getConfidence(), lhs.getConfidence());
}
});
for (int y = 0; y < gridHeight; ++y) {
for (int x = 0; x < gridWidth; ++x) {
for (int b = 0; b < NUM_BOXES_PER_BLOCK; ++b) {
final int offset =
(gridWidth * (NUM_BOXES_PER_BLOCK * (NUM_CLASSES + 5))) * y
+ (NUM_BOXES_PER_BLOCK * (NUM_CLASSES + 5)) * x
+ (NUM_CLASSES + 5) * b;
final float xPos = (x + expit(output[offset + 0])) * BLOCK_SIZE;
final float yPos = (y + expit(output[offset + 1])) * BLOCK_SIZE;
final float w = (float) (Math.exp(output[offset + 2]) * ANCHORS[2 * b + 0]) * BLOCK_SIZE;
final float h = (float) (Math.exp(output[offset + 3]) * ANCHORS[2 * b + 1]) * BLOCK_SIZE;
final RectF rect =
new RectF(
Math.max(0, xPos - w / 2),
Math.max(0, yPos - h / 2),
Math.min(INPUT_SIZE - 1, xPos + w / 2),
Math.min(INPUT_SIZE - 1, yPos + h / 2));
final float confidence = expit(output[offset + 4]);
int detectedClass = -1;
float maxClass = 0;
final float[] classes = new float[NUM_CLASSES];
for (int c = 0; c < NUM_CLASSES; ++c) {
classes[c] = output[offset + 5 + c];
}
softmax(classes);
for (int c = 0; c < NUM_CLASSES; ++c) {
if (classes[c] > maxClass) {
detectedClass = c;
maxClass = classes[c];
}
}
final float confidenceInClass = maxClass * confidence;
if (confidenceInClass > 0.01) {
pq.add(new Classifier.Recognition(detectedClass, LABELS[detectedClass], confidenceInClass, rect));
}
}
}
}
final ArrayList<Classifier.Recognition> recognitions = new ArrayList<>();
for (int i = 0; i < Math.min(pq.size(), MAX_RESULTS); ++i) {
recognitions.add(pq.poll());
}
return recognitions;
}
private static float expit(final float x) {
return (float) (1. / (1. + Math.exp(-x)));
}
private static void softmax(final float[] vals) {
float max = Float.NEGATIVE_INFINITY;
for (final float val : vals) {
max = Math.max(max, val);
}
float sum = 0.0f;
for (int i = 0; i < vals.length; ++i) {
vals[i] = (float) Math.exp(vals[i] - max);
sum += vals[i];
}
for (int i = 0; i < vals.length; ++i) {
vals[i] = vals[i] / sum;
}
}
public void close() {
inferenceInterface.close();
}
}
TensorFlowInferenceInterface
public class TensorFlowInferenceInterface {
private static final String TAG = "TensorFlowInferenceInterface";
private final Graph g;
private final Session sess;
private Runner runner;
private List<String> feedNames = new ArrayList();
private List<Tensor> feedTensors = new ArrayList();
private List<String> fetchNames = new ArrayList();
private List<Tensor> fetchTensors = new ArrayList();
private RunStats runStats;
public TensorFlowInferenceInterface(Path path) {
this.prepareNativeRuntime();
this.g = new Graph();
this.sess = new Session(this.g);
this.runner = this.sess.runner();
try {
this.loadGraph(readAllBytesOrExit(path), this.g);
} catch (IOException e) {
e.printStackTrace();
}
}
private static byte[] readAllBytesOrExit(Path path) {
try {
return Files.readAllBytes(path);
} catch (IOException e) {
System.err.println("Failed to read [" + path + "]: " + e.getMessage());
System.exit(1);
}
return null;
}
public void run(String[] var1) {
this.run(var1, false);
}
public void run(String[] var1, boolean var2) {
this.closeFetches();
String[] var3 = var1;
int var4 = var1.length;
for (int var5 = 0; var5 < var4; ++var5) {
String var6 = var3[var5];
this.fetchNames.add(var6);
TensorFlowInferenceInterface.TensorId var7 = TensorFlowInferenceInterface.TensorId.parse(var6);
this.runner.fetch(var7.name, var7.outputIndex);
}
try {
if (var2) {
Run var13 = this.runner.setOptions(RunStats.runOptions()).runAndFetchMetadata();
this.fetchTensors = var13.outputs;
if (this.runStats == null) {
this.runStats = new RunStats();
}
this.runStats.add(var13.metadata);
} else {
this.fetchTensors = this.runner.run();
}
} catch (RuntimeException var11) {
throw var11;
} finally {
this.closeFeeds();
this.runner = this.sess.runner();
}
}
public Graph graph() {
return this.g;
}
public Operation graphOperation(String var1) {
Operation var2 = this.g.operation(var1);
if (var2 == null) {
throw new RuntimeException("Node '" + var1 + "' does not exist in model '");
} else {
return var2;
}
}
public String getStatString() {
return this.runStats == null ? "" : this.runStats.summary();
}
public void close() {
this.closeFeeds();
this.closeFetches();
this.sess.close();
this.g.close();
if (this.runStats != null) {
this.runStats.close();
}
this.runStats = null;
}
protected void finalize() throws Throwable {
try {
this.close();
} finally {
super.finalize();
}
}
public void feed(String var1, float[] var2, long... var3) {
this.addFeed(var1, Tensor.create(var3, FloatBuffer.wrap(var2)));
}
public void fetch(String var1, float[] var2) {
this.fetch(var1, FloatBuffer.wrap(var2));
}
public void fetch(String var1, FloatBuffer var2) {
this.getTensor(var1).writeTo(var2);
}
private void prepareNativeRuntime() {
System.out.println("TensorFlowInferenceInterface Checking to see if TensorFlow native methods are already loaded");
try {
new RunStats();
System.out.println("TensorFlowInferenceInterface TensorFlow native methods already loaded");
} catch (UnsatisfiedLinkError var4) {
System.out.println("TensorFlowInferenceInterface TensorFlow native methods not found, attempting to load via tensorflow_inference");
}
}
private void loadGraph(byte[] var1, Graph var2) throws IOException {
try {
var2.importGraphDef(var1);
} catch (IllegalArgumentException var7) {
throw new IOException("Not a valid TensorFlow Graph serialization: " + var7.getMessage());
}
}
private void addFeed(String var1, Tensor var2) {
TensorFlowInferenceInterface.TensorId var3 = TensorFlowInferenceInterface.TensorId.parse(var1);
this.runner.feed(var3.name, var3.outputIndex, var2);
this.feedNames.add(var1);
this.feedTensors.add(var2);
}
private Tensor getTensor(String var1) {
int var2 = 0;
for (Iterator var3 = this.fetchNames.iterator(); var3.hasNext(); ++var2) {
String var4 = (String) var3.next();
if (var4.equals(var1)) {
return this.fetchTensors.get(var2);
}
}
throw new RuntimeException("Node '" + var1 + "' was not provided to run(), so it cannot be read");
}
private void closeFeeds() {
Iterator var1 = this.feedTensors.iterator();
while (var1.hasNext()) {
Tensor var2 = (Tensor) var1.next();
var2.close();
}
this.feedTensors.clear();
this.feedNames.clear();
}
private void closeFetches() {
Iterator var1 = this.fetchTensors.iterator();
while (var1.hasNext()) {
Tensor var2 = (Tensor) var1.next();
var2.close();
}
this.fetchTensors.clear();
this.fetchNames.clear();
}
private static class TensorId {
String name;
int outputIndex;
private TensorId() {
}
public static TensorFlowInferenceInterface.TensorId parse(String var0) {
TensorFlowInferenceInterface.TensorId var1 = new TensorFlowInferenceInterface.TensorId();
int var2 = var0.lastIndexOf(58);
if (var2 < 0) {
var1.outputIndex = 0;
var1.name = var0;
return var1;
} else {
try {
var1.outputIndex = Integer.parseInt(var0.substring(var2 + 1));
var1.name = var0.substring(0, var2);
} catch (NumberFormatException var4) {
var1.outputIndex = 0;
var1.name = var0;
}
return var1;
}
}
}
}
Classifier
public interface Classifier {
public class Recognition {
private final int id;
private final String title;
private final Float confidence;
private RectF location;
public Recognition(
final int id, final String title, final Float confidence, final RectF location) {
this.id = id;
this.title = title;
this.confidence = confidence;
this.location = location;
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public Float getConfidence() {
return confidence;
}
public RectF getLocation() {
return new RectF(location);
}
public void setLocation(RectF location) {
this.location = location;
}
}
void close();
}
RunStats
public class RunStats implements AutoCloseable {
private long nativeHandle = allocate();
private static byte[] fullTraceRunOptions = new byte[]{8, 3};
public static byte[] runOptions() {
return fullTraceRunOptions;
}
public RunStats() {
}
public void close() {
if(this.nativeHandle != 0L) {
delete(this.nativeHandle);
}
this.nativeHandle = 0L;
}
public synchronized void add(byte[] var1) {
add(this.nativeHandle, var1);
}
public synchronized String summary() {
return summary(this.nativeHandle);
}
private static native long allocate();
private static native void delete(long var0);
private static native void add(long var0, byte[] var2);
private static native String summary(long var0);
}
RectF
public class RectF {
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public float getWidth() {
return width;
}
public void setWidth(float width) {
this.width = width;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
public float x = 0f;
public float y = 0f;
public float width = 0f;
public float height = 0f;
RectF(RectF rectF) {
this.x = rectF.x;
this.y = rectF.y;
this.width = rectF.width;
this.height = rectF.height;
}
RectF(float x, float y, float w, float h) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
}
}

solved (I mixed up with x,y,width,height and left,top,right,bottom)
here's an updated RectF:
public class RectF {
public float left;
public float top;
public float right;
public float bottom;
public RectF() {}
public RectF(float left, float top, float right, float bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public RectF(RectF r) {
if (r == null) {
left = top = right = bottom = 0.0f;
} else {
left = r.left;
top = r.top;
right = r.right;
bottom = r.bottom;
}
}
public String toString() {
return "RectF(" + left + ", " + top + ", "
+ right + ", " + bottom + ")";
}
public final float width() {
return right - left;
}
public final float height() {
return bottom - top;
}
public final float centerX() {
return (left + right) * 0.5f;
}
public final float centerY() {
return (top + bottom) * 0.5f;
}
}
and then
graphics.drawRoundRect((int) rectF.left, (int) rectF.top, (int) rectF.width(), (int) rectF.height(), 5, 5);
p.s. for TensorFlow 1.4.0 here's an updated TensorFlowInferenceInterface class:
public class TensorFlowInferenceInterface {
private final Graph g;
private final Session sess;
private Runner runner;
private List<String> feedNames = new ArrayList();
private List<Tensor<?>> feedTensors = new ArrayList();
private List<String> fetchNames = new ArrayList();
private List<Tensor<?>> fetchTensors = new ArrayList();
private RunStats runStats;
public TensorFlowInferenceInterface(Path path) {
this.prepareNativeRuntime();
this.g = new Graph();
this.sess = new Session(this.g);
this.runner = this.sess.runner();
try {
this.loadGraph(readAllBytesOrExit(path), this.g);
} catch (IOException e) {
e.printStackTrace();
}
}
private static byte[] readAllBytesOrExit(Path path) {
try {
return Files.readAllBytes(path);
} catch (IOException e) {
System.err.println("Failed to read [" + path + "]: " + e.getMessage());
System.exit(1);
}
return null;
}
public void run(String[] var1) {
this.run(var1, false);
}
public void run(String[] var1, boolean var2) {
this.closeFetches();
String[] var3 = var1;
int var4 = var1.length;
for(int var5 = 0; var5 < var4; ++var5) {
String var6 = var3[var5];
this.fetchNames.add(var6);
TensorFlowInferenceInterface.TensorId var7 = TensorFlowInferenceInterface.TensorId.parse(var6);
this.runner.fetch(var7.name, var7.outputIndex);
}
try {
if(var2) {
Run var13 = this.runner.setOptions(RunStats.runOptions()).runAndFetchMetadata();
this.fetchTensors = var13.outputs;
if(this.runStats == null) {
this.runStats = new RunStats();
}
this.runStats.add(var13.metadata);
} else {
this.fetchTensors = this.runner.run();
}
} catch (RuntimeException var11) {
System.out.println("Failed to run TensorFlow inference with inputs:["
+ String.join(", ", this.feedNames)
+ "], outputs:[" + String.join(", ", this.fetchNames) + "]");
throw var11;
} finally {
this.closeFeeds();
this.runner = this.sess.runner();
}
}
public Graph graph() {
return this.g;
}
public Operation graphOperation(String var1) {
Operation var2 = this.g.operation(var1);
if(var2 == null) {
throw new RuntimeException("Node '" + var1 + "' does not exist in model '");
} else {
return var2;
}
}
public String getStatString() {
return this.runStats == null?"":this.runStats.summary();
}
public void close() {
this.closeFeeds();
this.closeFetches();
this.sess.close();
this.g.close();
if(this.runStats != null) {
this.runStats.close();
}
this.runStats = null;
}
protected void finalize() throws Throwable {
try {
this.close();
} finally {
super.finalize();
}
}
public void feed(String var1, float[] var2, long... var3) {
this.addFeed(var1, Tensor.create(var3, FloatBuffer.wrap(var2)));
}
public void feed(String var1, int[] var2, long... var3) {
this.addFeed(var1, Tensor.create(var3, IntBuffer.wrap(var2)));
}
public void feed(String var1, long[] var2, long... var3) {
this.addFeed(var1, Tensor.create(var3, LongBuffer.wrap(var2)));
}
public void feed(String var1, double[] var2, long... var3) {
this.addFeed(var1, Tensor.create(var3, DoubleBuffer.wrap(var2)));
}
public void feed(String var1, byte[] var2, long... var3) {
this.addFeed(var1, Tensor.create(UInt8.class, var3, ByteBuffer.wrap(var2)));
}
public void feedString(String var1, byte[] var2) {
this.addFeed(var1, Tensors.create(var2));
}
public void feedString(String var1, byte[][] var2) {
this.addFeed(var1, Tensors.create(var2));
}
public void feed(String var1, FloatBuffer var2, long... var3) {
this.addFeed(var1, Tensor.create(var3, var2));
}
public void feed(String var1, IntBuffer var2, long... var3) {
this.addFeed(var1, Tensor.create(var3, var2));
}
public void feed(String var1, LongBuffer var2, long... var3) {
this.addFeed(var1, Tensor.create(var3, var2));
}
public void feed(String var1, DoubleBuffer var2, long... var3) {
this.addFeed(var1, Tensor.create(var3, var2));
}
public void feed(String var1, ByteBuffer var2, long... var3) {
this.addFeed(var1, Tensor.create(UInt8.class, var3, var2));
}
public void fetch(String var1, float[] var2) {
this.fetch(var1, FloatBuffer.wrap(var2));
}
public void fetch(String var1, int[] var2) {
this.fetch(var1, IntBuffer.wrap(var2));
}
public void fetch(String var1, long[] var2) {
this.fetch(var1, LongBuffer.wrap(var2));
}
public void fetch(String var1, double[] var2) {
this.fetch(var1, DoubleBuffer.wrap(var2));
}
public void fetch(String var1, byte[] var2) {
this.fetch(var1, ByteBuffer.wrap(var2));
}
public void fetch(String var1, FloatBuffer var2) {
this.getTensor(var1).writeTo(var2);
}
public void fetch(String var1, IntBuffer var2) {
this.getTensor(var1).writeTo(var2);
}
public void fetch(String var1, LongBuffer var2) {
this.getTensor(var1).writeTo(var2);
}
public void fetch(String var1, DoubleBuffer var2) {
this.getTensor(var1).writeTo(var2);
}
public void fetch(String var1, ByteBuffer var2) {
this.getTensor(var1).writeTo(var2);
}
private void prepareNativeRuntime() {
System.out.println("Checking to see if TensorFlow native methods are already loaded");
try {
new RunStats();
System.out.println("TensorFlow native methods already loaded");
} catch (UnsatisfiedLinkError var4) {
System.out.println("TensorFlow native methods not found, attempting to load via tensorflow_inference");
/* try {
System.loadLibrary("tensorflow_inference");
System.out.println("Successfully loaded TensorFlow native methods (RunStats error may be ignored)");
} catch (UnsatisfiedLinkError var3) {
throw new RuntimeException("Native TF methods not found; check that the correct native libraries are present in the APK.");
}*/
}
}
private void loadGraph(byte[] var1, Graph var2) throws IOException {
long var3 = System.currentTimeMillis();
try {
var2.importGraphDef(var1);
} catch (IllegalArgumentException var7) {
throw new IOException("Not a valid TensorFlow Graph serialization: " + var7.getMessage());
}
long var5 = System.currentTimeMillis();
System.out.println("Model load took " + (var5 - var3) + "ms, TensorFlow version: " + TensorFlow.version());
}
private void addFeed(String var1, Tensor<?> var2) {
TensorFlowInferenceInterface.TensorId var3 = TensorFlowInferenceInterface.TensorId.parse(var1);
this.runner.feed(var3.name, var3.outputIndex, var2);
this.feedNames.add(var1);
this.feedTensors.add(var2);
}
private Tensor<?> getTensor(String var1) {
int var2 = 0;
for(Iterator var3 = this.fetchNames.iterator(); var3.hasNext(); ++var2) {
String var4 = (String)var3.next();
if(var4.equals(var1)) {
return (Tensor)this.fetchTensors.get(var2);
}
}
throw new RuntimeException("Node '" + var1 + "' was not provided to run(), so it cannot be read");
}
private void closeFeeds() {
Iterator var1 = this.feedTensors.iterator();
while(var1.hasNext()) {
Tensor var2 = (Tensor)var1.next();
var2.close();
}
this.feedTensors.clear();
this.feedNames.clear();
}
private void closeFetches() {
Iterator var1 = this.fetchTensors.iterator();
while(var1.hasNext()) {
Tensor var2 = (Tensor)var1.next();
var2.close();
}
this.fetchTensors.clear();
this.fetchNames.clear();
}
private static class TensorId {
String name;
int outputIndex;
private TensorId() {
}
public static TensorFlowInferenceInterface.TensorId parse(String var0) {
TensorFlowInferenceInterface.TensorId var1 = new TensorFlowInferenceInterface.TensorId();
int var2 = var0.lastIndexOf(58);
if(var2 < 0) {
var1.outputIndex = 0;
var1.name = var0;
return var1;
} else {
try {
var1.outputIndex = Integer.parseInt(var0.substring(var2 + 1));
var1.name = var0.substring(0, var2);
} catch (NumberFormatException var4) {
var1.outputIndex = 0;
var1.name = var0;
}
return var1;
}
}
}
}

Related

jvm condition and locksupport which is faster?

An experimental example of synchronous call is made. Each thread task waits for a task callback with its own value of + 1. The performance difference between condition and locksupport is compared. The result is unexpected. The two times are the same, but the difference on the flame diagram is very big. Does it mean that the JVM has not optimized locksupport
enter image description here
public class LockerTest {
static int max = 100000;
static boolean usePark = true;
static Map<Long, Long> msg = new ConcurrentHashMap<>();
static ExecutorService producer = Executors.newFixedThreadPool(4);
static ExecutorService consumer = Executors.newFixedThreadPool(16);
static AtomicLong record = new AtomicLong(0);
static CountDownLatch latch = new CountDownLatch(max);
static ReentrantLock lock = new ReentrantLock();
static Condition cond = lock.newCondition();
static Map<Long, Thread> parkMap = new ConcurrentHashMap<>();
static AtomicLong cN = new AtomicLong(0);
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
for (int num = 0; num < max; num++) {
consumer.execute(() -> {
long id = record.incrementAndGet();
msg.put(id, -1L);
call(id);
if (usePark) {
Thread thread = Thread.currentThread();
parkMap.put(id, thread);
while (msg.get(id) == -1) {
cN.incrementAndGet();
LockSupport.park(thread);
}
} else {
lock.lock();
try {
while (msg.get(id) == -1) {
cN.incrementAndGet();
cond.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
latch.countDown();
});
}
latch.await();
consumer.shutdown();
producer.shutdown();
System.out.printf("park %s suc %s cost %s cn %s"
, usePark
, msg.entrySet().stream().noneMatch(entry -> entry.getKey() + 1 != entry.getValue())
, System.currentTimeMillis() - start
, cN.get()
);
}
private static void call(long id) {
producer.execute(() -> {
try {
Thread.sleep((id * 13) % 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (usePark) {
msg.put(id, id + 1);
LockSupport.unpark(parkMap.remove(id));
} else {
lock.lock();
try {
msg.put(id, id + 1);
cond.signalAll();
} finally {
lock.unlock();
}
}
});
}
}

How to put different limit for multi chekbox in recyclerview item

I can choose 2 items from the first group and choose 1 item from the second group.
Now there should be a certain limit for each group and the whole choice as well.
How can I apply it?
#Override
public void onBindViewHolder(#NonNull Holder holder, int position) {
holder.txt_ingredient.setText(ingredients.get(position).getName());
ArrayList<HomeList.Ingredient> sizeArrayList = new ArrayList<>();
sizeArrayList.addAll(ingredients);
for (int i = 0; i < ingredients.get(position).getIngredients().size(); i++) {
indi.add(ingredients.get(position).getIngredients().get(i).getId());
}
for (int i = 0; i < sizeArrayList.get(position).getIngredients().size(); i++) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View rowView = inflater.inflate(R.layout.checkbox_feild, null);
holder.parentLinearSize.addView(rowView, holder.parentLinearSize.getChildCount() - 1);
}
for (int itemPos = 0; itemPos < holder.parentLinearSize.getChildCount(); itemPos++) {
View view1 = holder.parentLinearSize.getChildAt(itemPos);
CheckBox ch = (CheckBox) view1.findViewById(R.id.chkSpe);
TextView cat_value = (TextView) view1.findViewById(R.id.cat_value);
String c = String.valueOf(sizeArrayList.get(position).getIngredients().size());
ch.setText(sizeArrayList.get(position).getIngredients().get(itemPos).getTitle());
ch.setOnCheckedChangeListener(null);
int finalItemPos = itemPos;
ch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int limit = Integer.parseInt(sizeArrayList.get(position).getSelectionLimit());
int globalInc = 0;
if (globalInc == limit && isChecked) {
ch.setChecked(false);
Toast.makeText(context,
"Limit reached!!!", Toast.LENGTH_SHORT).show();
} else if (isChecked) {
globalInc++;
Toast.makeText(context,
globalInc + " checked!",
Toast.LENGTH_SHORT)
.show();
ch.setSelected(isChecked);
if (ch.isChecked()) {
for (int i = 0; i < ingredients.get(position).getIngredients().size(); i++) {
if (i == finalItemPos) {
ingredients.get(position).getIngredients().get(i).setQty("1");
} else
ingredients.get(position).getIngredients().get(i).setQty("0");
}
ingredients.get(position).setIsOpenSection("1");
onItemCheckListener.onItemCheck(sizeArrayList.get(position).getIngredients(), sizeArrayList.get(position));
Toast.makeText(context, sizeArrayList.get(position).getIngredients().get(finalItemPos).getPrice(), Toast.LENGTH_SHORT).show();
onRecycleItemClickListenerExtra.onItemClickListener(holder, position, "selectExtra", sizeArrayList.get(position).getIngredients().get(finalItemPos).getPrice(), "trextra");
} else {
onItemCheckListener.onItemUncheck(sizeArrayList.get(position).getIngredients().get(position));
onRecycleItemClickListenerExtra.onItemClickListener(holder, position, "DeselectExtra", sizeArrayList.get(position).getIngredients().get(finalItemPos).getPrice(), "trextra");
sizeArrayList.get(position).getIngredients().get(finalItemPos).setQty("0");
}
} else if (!isChecked) {
globalInc--;
}
}
});
cat_value.setText(sizeArrayList.get(position).getIngredients().get(itemPos).getPrice());
holder.maximum_amt.setText(sizeArrayList.get(position).getMaximumAmount());
holder.select_limit.setText(sizeArrayList.get(position).getSelectionLimit());
String count = String.valueOf(getItemCount());
holder.total_itm_count.setText(c);
String eql_les_grt = sizeArrayList.get(position).getEqualGraterLess();
if (eql_les_grt.equalsIgnoreCase("1")) {
if (lan.equals("English")) {
String eql_ls_status = "Greater";
holder.euql_grt_les.setText(eql_ls_status);
}
if (lan.equals("Español")) {
String eql_ls_status = "Máximo";
holder.euql_grt_les.setText(eql_ls_status);
}
}
if (eql_les_grt.equalsIgnoreCase("2")) {
if (lan.equals("English")) {
String eql_ls_status = "Exactly";
holder.euql_grt_les.setText(eql_ls_status);
}
if (lan.equals("Español")) {
String eql_ls_status = "solo";
holder.euql_grt_les.setText(eql_ls_status);
}
}
if (eql_les_grt.equalsIgnoreCase("3")) {
if (lan.equals("English")) {
String eql_ls_status = "Less";
holder.euql_grt_les.setText(eql_ls_status);
}
if (lan.equals("Español")) {
String eql_ls_status = "Mínimo";
holder.euql_grt_les.setText(eql_ls_status);
}
}
holder.img_arrow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.rl_details.setVisibility(View.VISIBLE);
holder.img_dwn_arrow.setVisibility(View.VISIBLE);
holder.img_dwn_arrow.setVisibility(View.VISIBLE);
holder.img_arrow.setVisibility(View.GONE);
holder.parentLinearSize.setVisibility(View.VISIBLE);
}
});
holder.img_dwn_arrow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
holder.rl_details.setVisibility(View.GONE);
holder.img_dwn_arrow.setVisibility(View.GONE);
holder.img_dwn_arrow.setVisibility(View.GONE);
holder.img_arrow.setVisibility(View.VISIBLE);
holder.parentLinearSize.setVisibility(View.GONE);
}
});
}

error is occuring while creating custom tokenizer in lucene 7.3

I m trying to create new tokenizer by refering book tamingtext (which uses lucene 3.+ api) using new lucene api 7.3, but it is giving me error as mentioned below
java.lang.IllegalStateException: TokenStream contract violation: reset()/close() call missing, reset() called multiple times, or subclass does not call super.reset(). Please see Javadocs of TokenStream class for more information about the correct consuming workflow.
at org.apache.lucene.analysis.Tokenizer$1.read(Tokenizer.java:109)
at java.io.Reader.read(Reader.java:140)
at solr.SentenceTokenizer.fillSentences(SentenceTokenizer.java:43)
at solr.SentenceTokenizer.incrementToken(SentenceTokenizer.java:55)
at solr.NameFilter.fillSpans(NameFilter.java:56)
at solr.NameFilter.incrementToken(NameFilter.java:88)
at spec.solr.NameFilterTest.testNameFilter(NameFilterTest.java:81)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
Here is my SentenceTokenizer class
Initializing method, in older api there was super(Reader); but in current api there is no access to Reader class
public SentenceTokenizer(SentenceDetector detector) {
super();
setReader(reader);
this.sentenceDetector = detector;
}
Here is my reset method
#Override
public void reset() throws IOException {
super.reset();
sentenceDetector = null;
}
When i tried to access this method from custom TokenFilter, I m getting above error
public void fillSentences() throws IOException {
char[] c = new char[256];
int size = 0;
StringBuilder stringBuilder = new StringBuilder();
while ((size = input.read(c)) >= 0) {
stringBuilder.append(c, 0, size);
}
String temp = stringBuilder.toString();
inputSentence = temp.toCharArray();
sentenceSpans = sentenceDetector.sentPosDetect(temp);
tokenOffset = 0;
}
#Override
public final boolean incrementToken() throws IOException {
if (sentenceSpans == null) {
//invoking following method
fillSentences();
}
if (tokenOffset == sentenceSpans.length) {
return false;
}
Span sentenceSpan = sentenceSpans[tokenOffset];
clearAttributes();
int start = sentenceSpan.getStart();
int end = sentenceSpan.getEnd();
charTermAttribute.copyBuffer(inputSentence, start, end - start);
positionIncrementAttribute.setPositionIncrement(1);
offsetAttribute.setOffset(start, end);
tokenOffset++;
return true;
}
Here is my custom TokenFilter class
public final class NameFilter extends TokenFilter {
public static final String NE_PREFIX = "NE_";
private final Tokenizer tokenizer;
private final String[] tokenTypeNames;
private final NameFinderME[] nameFinderME;
private final KeywordAttribute keywordAttribute = addAttribute(KeywordAttribute.class);
private final PositionIncrementAttribute positionIncrementAttribute = addAttribute(PositionIncrementAttribute.class);
private final CharTermAttribute charTermAttribute = addAttribute(CharTermAttribute.class);
private final OffsetAttribute offsetAttribute = addAttribute(OffsetAttribute.class);
private String text;
private int baseOffset;
private Span[] spans;
private String[] tokens;
private Span[][] foundNames;
private boolean[][] tokenTypes;
private int spanOffsets = 0;
private final Queue<AttributeSource.State> tokenQueue =
new LinkedList<>();
public NameFilter(TokenStream in, String[] modelNames, NameFinderME[] nameFinderME) {
super(in);
this.tokenizer = SimpleTokenizer.INSTANCE;
this.nameFinderME = nameFinderME;
this.tokenTypeNames = new String[modelNames.length];
for (int i = 0; i < modelNames.length; i++) {
this.tokenTypeNames[i] = NE_PREFIX + modelNames[i];
}
}
//consumes tokens from the upstream tokenizer and buffer them in a
//StringBuilder whose contents will be passed to opennlp
protected boolean fillSpans() throws IOException {
if (!this.input.incrementToken()) return false;
//process the next sentence from the upstream tokenizer
this.text = input.getAttribute(CharTermAttribute.class).toString();
this.baseOffset = this.input.getAttribute(OffsetAttribute.class).startOffset();
this.spans = this.tokenizer.tokenizePos(text);
this.tokens = Span.spansToStrings(spans, text);
this.foundNames = new Span[this.nameFinderME.length][];
for (int i = 0; i < nameFinderME.length; i++) {
this.foundNames[i] = nameFinderME[i].find(tokens);
}
//insize
this.tokenTypes = new boolean[this.tokens.length][this.nameFinderME.length];
for (int i = 0; i < nameFinderME.length; i++) {
Span[] spans = foundNames[i];
for (int j = 0; j < spans.length; j++) {
int start = spans[j].getStart();
int end = spans[j].getEnd();
for (int k = start; k < end; k++) {
this.tokenTypes[k][i] = true;
}
}
}
spanOffsets = 0;
return true;
}
#Override
public boolean incrementToken() throws IOException {
//if there's nothing in the queue
if(tokenQueue.peek()==null){
//no span or spans consumed
if (spans==null||spanOffsets>=spans.length){
if (!fillSpans())return false;
}
if (spanOffsets>=spans.length)return false;
//copy the token and any types
clearAttributes();
keywordAttribute.setKeyword(false);
positionIncrementAttribute.setPositionIncrement(1);
int startOffset = baseOffset +spans[spanOffsets].getStart();
int endOffset = baseOffset+spans[spanOffsets].getEnd();
offsetAttribute.setOffset(startOffset,endOffset);
charTermAttribute.setEmpty()
.append(tokens[spanOffsets]);
//determine of the current token is of a named entity type, if so
//push the current state into the queue and add a token reflecting
// any matching entity types.
boolean [] types = tokenTypes[spanOffsets];
for (int i = 0; i < nameFinderME.length; i++) {
if (types[i]){
keywordAttribute.setKeyword(true);
positionIncrementAttribute.setPositionIncrement(0);
tokenQueue.add(captureState());
positionIncrementAttribute.setPositionIncrement(1);
charTermAttribute.setEmpty().append(tokenTypeNames[i]);
}
}
}
spanOffsets++;
return true;
}
#Override
public void close() throws IOException {
super.close();
reset();
}
#Override
public void reset() throws IOException {
super.reset();
this.spanOffsets = 0;
this.spans = null;
}
#Override
public void end() throws IOException {
super.end();
reset();
}
}
here is my test case for following class
#Test
public void testNameFilter() throws IOException {
Reader in = new StringReader(input);
Tokenizer tokenizer = new SentenceTokenizer( detector);
tokenizer.reset();
NameFilter nameFilter = new NameFilter(tokenizer, modelName, nameFinderMES);
nameFilter.reset();
CharTermAttribute charTermAttribute;
PositionIncrementAttribute positionIncrementAttribute;
OffsetAttribute offsetAttribute;
int pass = 0;
while (pass < 2) {
int pos = 0;
int lastStart = 0;
int lastEnd = 0;
//error occur on below invoke
while (nameFilter.incrementToken()) {
}
}
I have added following changes in my code and it work fine but i m now sure it is correct answer
public SentenceTokenizer(Reader reader,SentenceDetector sentenceDetector) {
super();
this.input =reader;
this.sentenceDetector = sentenceDetector;
}

How to set both axis's scales to be always the same?

MPAndroidChart allows you to zoom in the X axis, Y axis and both. I would like to rescale the remaining axis (or both) to match the one(s) being scaled.
For that I've created a OnChartGestureListener:
public class ZoomNotDistorting implements OnChartGestureListener {
private Chart chart;
private ViewPortHandler viewPortHandler;
private float startDist = 1f;
private float scaleX, scaleY;
public ZoomNotDistorting(Chart chart) {
this.chart = chart;
this.viewPortHandler = chart.getViewPortHandler();
}
#Override
public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
int action = me.getAction() & MotionEvent.ACTION_MASK;
if(action == MotionEvent.ACTION_POINTER_DOWN && me.getPointerCount() >= 2) {
startDist = spacing(me);
}
}
#Override
public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
switch (lastPerformedGesture) {
case PINCH_ZOOM:
float scale = spacing(me) / startDist; // total scale
boolean isZoomingOut = (scale < 1);
if(isZoomingOut) {
if(scaleX < scaleY) {
viewPortHandler.zoom(scaleX, scaleX);
} else {
viewPortHandler.zoom(scaleY, scaleY);
}
} else {
if(scaleX > scaleY) {
viewPortHandler.zoom(scaleX, scaleX);
} else {
viewPortHandler.zoom(scaleY, scaleY);
}
}
break;
case X_ZOOM:
viewPortHandler.zoom(scaleX, scaleX);
break;
case Y_ZOOM:
viewPortHandler.zoom(scaleY, scaleY);
break;
}
chart.invalidate();
}
#Override
public void onChartLongPressed(MotionEvent me) {}
#Override
public void onChartDoubleTapped(MotionEvent me) {}
#Override
public void onChartSingleTapped(MotionEvent me) {}
#Override
public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) {}
#Override
public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
this.scaleX = scaleX;
this.scaleY = scaleY;
}
#Override
public void onChartTranslate(MotionEvent me, float dX, float dY) {}
/**
* returns the distance between two pointer touch points
*/
private static float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.sqrt(x * x + y * y);
}
}
That class doesn't seem to be doing anything,How to set both axis's scales to be always the same?
Also, here is my chart class:
public class MathGraph extends LineChart {
public static final int BOUNDARIES = 100;
public static final int DATA_POINTS = 200;
private static final int LINE_WIDTH = 2;
private LineData data;
public MathGraph(Context context, AttributeSet attrs) {
super(context, attrs);
super.setDescription(null);
//Misc
getLegend().setEnabled(false);
setRenderer(new LineChatRendererNoData(this, mAnimator, mViewPortHandler));
//Lines encasing the chart
getXAxis().setAxisLineWidth(LINE_WIDTH);
getAxisLeft().setAxisLineWidth(LINE_WIDTH);
getAxisRight().setEnabled(false);
//Line for (x; 0)
getAxisLeft().setDrawZeroLine(true);
getAxisLeft().setZeroLineWidth(LINE_WIDTH);
getAxisRight().setDrawZeroLine(true);
getAxisRight().setZeroLineWidth(LINE_WIDTH);
//Line for (0; y)
LimitLine limitLine = new LimitLine(0f);
limitLine.setLineColor(Color.GRAY);
limitLine.setLineWidth(LINE_WIDTH);
getXAxis().addLimitLine(limitLine);
setOnChartGestureListener(new ZoomNotDistorting(this));
}
public void setFunction(Function f) {
if(!f.checkSyntax()) throw new IllegalStateException("Error in function: " + f.toString() + "!");
setDescription(f);
data = null;
LoadFunctionAsyncTask l = new LoadFunctionAsyncTask(f, -BOUNDARIES, BOUNDARIES, DATA_POINTS,
(List<Entry> pointList) -> {
if(data == null) {
ILineDataSet dataSet = new LineDataSet(new ArrayList<>(), null);
dataSet.setValueFormatter(new PointValueFormatter());
dataSet.setHighlightEnabled(true);
for (Entry dataPoint : pointList) {
dataSet.addEntry(dataPoint);
}
data = new LineData(dataSet);
setData(data);
} else {
for (Entry dataPoint : pointList) {
data.addEntry(dataPoint, 0);// 0 is the only dataset
}
data.notifyDataChanged();
notifyDataSetChanged();
invalidate();
}
});
l.execute();
}
private void setDescription(Function f) {
Description desc = new Description();
desc.setText(f.getDescription());
desc.setPosition(16, getBottom() - 16);
setDescription(desc);
}
private class PointValueFormatter implements IValueFormatter {
#Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
return "(" + entry.getX() + ", " + entry.getY() + ")";
}
}
}
OK, apparently chart.invalidate() isn't enough, the Matrix needs to be refreshed:
Matrix matrix = null;
//...
matrix = viewPortHandler.zoom(scaleX, scaleX);
//...
if(matrix != null) {
viewPortHandler.refresh(matrix, chart, true);
}
As a bonus, the last true in refresh() is for invalidate, so no need for chart.invalidate();.

java.lang.NullPointerException in JApplet game

new guy to stack and to java.
to start off, here's my code,
AsteroidsGame.java
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;
import java.util.*;
public class AsteroidsGame extends JApplet implements Runnable, KeyListener {
Thread thread;
Dimension dim;
Image img;
Graphics g;
long endTime, startTime, framePeriod;
Ship ship;
boolean paused;
// True if the game is paused. Enter is the pause key
Shot[] shots;
int numShots;
boolean shooting;
Asteroid[] asteroids;
int numAsteroids;
double astRadius;
double minAstVel;
double maxAstVel;
int astNumHits;
int astNumSplit;
int level;
private AudioClip audioClip;
public void init() {
resize(500, 500);
shots = new Shot[41];
numAsteroids = 0;
level = 0;
astRadius = 60;
minAstVel = .5;
maxAstVel = 5;
astNumHits = 3;
astNumSplit = 2;
endTime = 0;
startTime = 0;
framePeriod = 25;
addKeyListener(this);
setFocusable(true);
requestFocusInWindow();
dim = getSize();
img = createImage(dim.width, dim.height);
g = img.getGraphics();
thread = new Thread(this);
thread.start();
URL urlAudioClip = getClass().getResource("audio/minorcircuit.wav");
audioClip = Applet.newAudioClip(urlAudioClip);
audioClip.loop();
}
public void start() {
audioClip.loop();
}
public void stop() {
audioClip.stop();
}
public void setUpNextLevel() {
level++;
ship = new Ship(250, 250, 0, .35, .98, .1, 12);
numShots = 0;
paused = false;
shooting = false;
asteroids = new Asteroid[level * (int)Math.pow(astNumSplit, astNumHits - 1) + 1];
numAsteroids = level;
for(int i = 0; i < numAsteroids; i++) {
asteroids[i] = new Asteroid(Math.random() * dim.width, Math.random() * dim.height, astRadius, minAstVel, maxAstVel, astNumHits, astNumSplit);
}
}
public void paint(Graphics gfx) {
g.setColor(Color.black);
g.fillRect(0, 0, 500, 500);
for(int i = 0; i < numShots; i++) {
shots[i].draw(g);
}
for(int i = 0; i < numAsteroids; i++) {
asteroids[i].draw(g);
}
ship.draw(g);
//draw the ship
g.setColor(Color.cyan);
g.drawString("Level " + level, 20, 20);
gfx.drawImage(img, 0, 0, this);
}
public void update(Graphics gfx) {
paint(gfx);
}
public void run() {
for(;;) {
startTime = System.currentTimeMillis();
//start next level when all asteroids are destroyed
if(numAsteroids <= 0) {
setUpNextLevel();
}
if(!paused) {
ship.move(dim.width, dim.height);
//move the ship
for(int i = 0; i < numShots; i++) {
shots[i].move(dim.width, dim.height);
if(shots[i].getLifeLeft() <= 0) {
deleteShot(i);
i--;
}
}
updateAsteroids();
if(shooting && ship.canShoot()) {
//add a shot on to the array
shots[numShots] = ship.shoot();
numShots++;
}
}
repaint();
try {
endTime = System.currentTimeMillis();
if(framePeriod - (endTime - startTime) > 0) {
Thread.sleep(framePeriod - (endTime - startTime));
}
}
catch(InterruptedException e) {
}
}
}
private void deleteShot(int index) {
//delete shot and move all shots after it up in the array
numShots--;
for(int i = index; i < numShots; i++) {
shots[i] = shots[i + 1];
shots[numShots] = null;
}
}
private void deleteAsteroid(int index) {
//delete asteroid and shift ones after it up in the array
numAsteroids--;
for(int i = index; i < numAsteroids; i++) {
asteroids[i] = asteroids[i + 1];
asteroids[numAsteroids] = null;
}
}
private void addAsteroid(Asteroid ast) {
//adds asteroid in at end of array
asteroids[numAsteroids] = ast;
numAsteroids++;
}
private void updateAsteroids() {
for(int i = 0; i < numAsteroids; i++) {
// move each asteroid
asteroids[i].move(dim.width, dim.height);
//check for collisions with the ship
if(asteroids[i].shipCollision(ship)) {
level--;
//restart this level
numAsteroids = 1;
return;
}
//check for collisions with any of the shots
for(int j = 0; j < numShots; j++) {
if(asteroids[i].shotCollision(shots[j])) {
//if the shot hit an asteroid, delete the shot
deleteShot(j);
//split the asteroid up if needed
if(asteroids[i].getHitsLeft() > 1) {
for(int k = 0; k < asteroids[i].getNumSplit(); k++) {
addAsteroid(
asteroids[i].createSplitAsteroid(
minAstVel, maxAstVel));
}
}
//delete the original asteroid
deleteAsteroid(i);
j=numShots;
i--;
}
}
}
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER) {
if(!ship.isActive() && !paused) {
ship.setActive(true);
}
else {
paused = !paused;
//enter is the pause button
if(paused) {
//grays out the ship if paused
ship.setActive(false);
}
else {
ship.setActive(true);
}
}
}
else if(paused || !ship.isActive()) {
return;
}
else if(e.getKeyCode() == KeyEvent.VK_SPACE) {
ship.setAccelerating(true);
}
else if(e.getKeyCode() == KeyEvent.VK_LEFT) {
ship.setTurningLeft(true);
}
else if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
ship.setTurningRight(true);
}
else if(e.getKeyCode() == KeyEvent.VK_CONTROL) {
shooting=true;
}
else if(e.getKeyCode() == KeyEvent.VK_M) {
audioClip.stop();
}
else if(e.getKeyCode() == KeyEvent.VK_S) {
audioClip.loop();
}
}
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_UP) {
ship.setAccelerating(false);
}
else if(e.getKeyCode() == KeyEvent.VK_LEFT) {
ship.setTurningLeft(false);
}
else if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
ship.setTurningRight(false);
}
else if(e.getKeyCode() == KeyEvent.VK_CONTROL) {
shooting=false;
}
}
public void keyTyped(KeyEvent e) {
}
}
this is a "clone" of Asteroids. Some of you who have been around maybe a little longer than myself have probably played it in an arcade. Anyways, I wrote this for a final project in my Java course, but could never get to work completely. It starts up just fine, you can turn and shoot, but not move forward. I'm not worried about moving right now though. What happens after applet starts is music plays and an "asteroid" travels across the screen. If you shoot the asteroid or shoot 3 times in any direction, a thread exception occurs.
Exception in thread "Thread-3" java.lang.NullPointerException
at AsteroidsGame.updateAsteroids(AseroidsGame.java:161)
at AsteroidsGame.run(AsteroidsGame.java:115)
at java.lang.Thread.run(Thread.java:722)
Having looked around online has benefited me very little. Most of what I have learned was self taught and not as easy to wrap my head around. I'm not exactly sure what to do here to resolve this exception. In another thread on stack, it was mentioned to use an ArrayList because the array was not formerly declared with a size. Tinkered with that; never got it to work.
I need ideas/suggestions/criticism. I want to get this to work just so I can say I finished it.
other classes,
Asteroid.java
import java.awt.*;
public class Asteroid {
double x, y, xVelocity, yVelocity, radius;
int hitsLeft, numSplit;
public Asteroid(double x,double y,double radius,double minVelocity, double maxVelocity,int hitsLeft,int numSplit) {
this.x = x;
this.y = y;
this.radius = radius;
this.hitsLeft = hitsLeft;
this.numSplit = numSplit;
double vel = minVelocity + Math.random() * (maxVelocity - minVelocity);
double dir = 2 * Math.PI * Math.random();
xVelocity = vel * Math.cos(dir);
yVelocity = vel * Math.sin(dir);
}
public void move(int scrnWidth, int scrnHeight) {
x += xVelocity;
y += yVelocity;
if(x < 0 - radius) {
x += scrnWidth + 2 * radius;
}
else if(x > scrnWidth + radius) {
x -= scrnWidth + 2 * radius;
}
if(y < 0 - radius) {
y += scrnHeight + 2 * radius;
}
else if(y > scrnHeight + radius) {
y -= scrnHeight + 2 * radius;
}
}
public void draw(Graphics g) {
g.setColor(Color.gray);
g.fillOval((int)(x - radius + .5), (int)(y - radius + .5), (int)(2 * radius), (int)(2 * radius));
}
public Asteroid createSplitAsteroid(double minVelocity,double maxVelocity) {
return new Asteroid(x, y, radius / Math.sqrt(numSplit), minVelocity, maxVelocity, hitsLeft - 1, numSplit);
}
public boolean shipCollision(Ship ship) {
if(Math.pow(radius + ship.getRadius(), 2) > Math.pow(ship.getX() - x, 2) + Math.pow(ship.getY() - y, 2) && ship.isActive()) {
return true;
}
return false;
}
public boolean shotCollision(Shot shot) {
if(Math.pow(radius, 2) > Math.pow(shot.getX() - x, 2) + Math.pow(shot.getY() - y, 2)) {
return true;
}
return false;
}
public int getHitsLeft() {
return hitsLeft;
}
public int getNumSplit() {
return numSplit;
}
}
Ship.java
import java.awt.*;
public class Ship {
final double[] origXPts = {14,-10,-6,-10}, origYPts = {0,-8,0,8}, origFlameXPts = {-6,-23,-6}, origFlameYPts = {-3,0,3};
final int radius = 6;
double x, y, angle, xVelocity, yVelocity, acceleration, velocityDecay, rotationalSpeed;
//used for movement
boolean turningLeft, turningRight, accelerating, active;
int [] xPts, yPts, flameXPts, flameYPts;
//current location of ship
int shotDelay, shotDelayLeft;
//rate of fire
public Ship(double x, double y, double angle, double acceleration, double velocityDecay, double rotationalSpeed, int shotDelay) {
this.x = x;
this.y = y;
this.angle = angle;
this.acceleration = acceleration;
this.velocityDecay = velocityDecay;
this.rotationalSpeed = rotationalSpeed;
xVelocity = 0;
yVelocity = 0;
turningLeft = false;
turningRight = false;
accelerating = false;
active = false;
xPts = new int[4];
yPts = new int[4];
flameXPts = new int[3];
flameYPts = new int[3];
this.shotDelay = shotDelay;
shotDelayLeft = 0;
}
public void draw(Graphics g) {
if(accelerating && active) {
for(int i = 0; i < 3; i++) {
flameXPts[i] = (int)(origFlameXPts[i] * Math.cos(angle) - origFlameYPts[i] * Math.sin(angle) + x + .5);
flameYPts[i] = (int)(origFlameXPts[i] * Math.sin(angle) + origFlameYPts[i] * Math.cos(angle) + y + .5);
}
g.setColor(Color.red);
//color of flame
g.fillPolygon(flameXPts, flameYPts, 3);
}
for(int i = 0; i < 4; i++) {
xPts[i] = (int)(origXPts[i] * Math.cos(angle) - origYPts[i] * Math.sin(angle) + x + .5);
yPts[i] = (int)(origXPts[i] * Math.sin(angle) + origYPts[i] * Math.cos(angle) + y + .5);
}
if(active) {
g.setColor(Color.white);
}
else {
g.setColor(Color.darkGray);
}
g.fillPolygon(xPts, yPts, 4);
}
public void move(int scrnWidth, int scrnHeight) {
if(shotDelayLeft > 0) {
shotDelayLeft--;
}
if(turningLeft) {
angle -= rotationalSpeed;
}
if(turningRight) {
angle += rotationalSpeed;
}
if(angle > (2 * Math.PI)) {
angle -= (2 * Math.PI);
}
else if(angle < 0) {
angle += (2 * Math.PI);
}
if(accelerating) {
xVelocity += acceleration * Math.cos(angle);
yVelocity += acceleration * Math.sin(angle);
}
x += xVelocity;
y += yVelocity;
xVelocity *= velocityDecay;
yVelocity *= velocityDecay;
if(x<0) {
x += scrnWidth;
}
else if(x > scrnWidth) {
x -= scrnWidth;
}
if(y < 0) {
y += scrnHeight;
}
else if(y > scrnHeight) {
y -= scrnHeight;
}
}
public void setAccelerating(boolean accelerating) {
this.accelerating = accelerating;
//start or stop accelerating the ship
}
public void setTurningLeft(boolean turningLeft) {
this.turningLeft = turningLeft;
//start or stop turning the ship
}
public void setTurningRight(boolean turningRight) {
this.turningRight = turningRight;
}
public double getX() {
return x;
//returns the ship’s x location
}
public double getY() {
return y;
//returns the ship's y location
}
public double getRadius() {
return radius;
//returns radius of circle that approximates the ship
}
public void setActive(boolean active) {
this.active = active;
//used when the game is paused or unpaused
}
public boolean isActive() {
return active;
}
public boolean canShoot() {
if(shotDelayLeft > 0) {
return false;
}
else {
return true;
}
}
public Shot shoot() {
shotDelayLeft=shotDelay;
//set delay till next shot can be fired
//a life of 40 makes the shot travel about the width of the
//screen before disappearing
return new Shot(x, y, angle, xVelocity, yVelocity, 40);
}
}
Shot.java
import java.awt.*;
public class Shot {
final double shotSpeed = 12;
double x, y, xVelocity, yVelocity;
int lifeLeft;
public Shot(double x, double y, double angle, double shipXVel, double shipYVel, int lifeLeft) {
this.x = x;
this.y = y;
xVelocity = shotSpeed * Math.cos(angle) + shipXVel;
yVelocity = shotSpeed * Math.sin(angle) + shipYVel;
this.lifeLeft = lifeLeft;
}
public void move(int scrnWidth, int scrnHeight) {
lifeLeft--;
x += xVelocity;
y += yVelocity;
if(x < 0) {
x += scrnWidth;
}
else if(x > scrnWidth) {
x -= scrnWidth;
}
if(y < 0) {
y += scrnHeight;
}
else if(y > scrnHeight) {
y -= scrnHeight;
}
}
public void draw(Graphics g) {
g.setColor(Color.yellow);
g.fillOval((int)(x - .5), (int)(y - .5), 3, 3);
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public int getLifeLeft() {
return lifeLeft;
}
}
thanks for looking.
---------------------Edit 12/9/2012------------------------
Tried debugging with NetBeans IDE 7.2.1. It improperly depicts the turning of ship and gives a completely different error:
Exception in thread "Thread-3" java.lang.NullPointerException
at AsteroidsGame.run(AsteroidsGame.java:122)
at java.lang.Thread.run(Thread.java:722)
It is directing attention to the Shot[] shots array inside the run method.
This could be expected as the array for Asteroid[] and Shot[] are set up the same.