Problem with Multi-ArrayList

Hey,

I’m working on a slicer for a CNC-Project.
The aim is to convert selected 3D-Surefaces to G-Code to milling 2D contours with my CNC-Robot.

Till yet I started with the Slicer, and made the 3D Viewer and Editor.

The contour is built up by 2 ArrayList’s and an one Vector2f.
(ArrayList<ArrayList>)

The Vector2f is used for Points.
The ArrayList above is for single contours (connected lines).
The ArrayList above this ArrayList is to store a lot of single contours.

Now I have 3 classes:
ContourSlicer3DViewer (3D Viewer to convert 3D surfaces to 2D Contours; return contour)
ContourEditor (Editor to edit contours (move, scale, rotate, mirror); return contour)
ContourSlicer (The Slicer to convert contours to G-Code; return G-Code)

I tested it, and my 3D Viewer and the Editor works, if I directly handover the Contour List from 3D Viewer to Editor.
But if I first handover the contour to ContourSlicer, and then to Editor, it won’t work with a lot of errors.

“Exception in thread “main” java.lang.NullPointerException
at contourSlicer.Window.createContour(Window.java:79)
at contourSlicer.Window.(Window.java:67)
at contourSlicer.ContourSlicer.main(ContourSlicer.java:15)”

Or

“Uncaught exception thrown in Thread[jME3 Main,5,main]
IllegalStateException: Scene graph is not properly updated for rendering.
State was changed after rootNode.updateGeometricState() call.
Make sure you do not modify the scene from another thread!
Problem spatial name: Gui Node”

I tried a lot of things, but nothing works…
Here is the code:

ContourEditor:

public boolean isRunning() {
    return isRunning;
}

public ContourEditor() {
    super("Contour Editor");
}
public void start(ArrayList<ArrayList<Vector2f>> v) {
    
    isRunning = true;
    
    this.setLayout(new BorderLayout());
    this.setLocation(0,0);
    this.getContentPane().setPreferredSize(new Dimension(640,480));
    
    this.add(canvas,BorderLayout.CENTER);
    this.add(bar,BorderLayout.SOUTH);

3DViewer:

public ArrayList<ArrayList<Vector2f>> getPoints() {
        if (convertedPoints.size() <= 0) {
            return null;
        } else {
            return convertedPoints;
        } 
}

ContourSlicer: (Window.java)

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package contourSlicer;

import ContourEditor.ContourEditor;
import ContourFunctions.ContourFunctions;
import com.jme3.math.Vector2f;
import contourslicer3dviewer.ContourSlicer3DViewer;
import contourslicer3dviewer.FileChooser;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Polygon;
import java.io.File;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 *
 * @author Admin
 */
public class Window extends JFrame {
    
    private ContourFunctions contourFunctions;
    private Window window = this;
    private ContourSlicer3DViewer viewer = new ContourSlicer3DViewer();
    private ContourEditor editor = new ContourEditor();
    private Canvas canvas = new Canvas();
    
    private ArrayList<ArrayList<ArrayList<Vector2f>>> contours = new ArrayList<>();
    private ArrayList<ArrayList<Vector2f>> editableContour;
    
    private Thread viewerThread = new Thread(){
        public void run(){
            System.out.println("Start viewer");
            viewer.start();
        }
    };
    private Thread editorThread = new Thread(){
        public void run(){
            System.out.println("Start editor");
            editor.start(editableContour);
        }
    };
    
    
    public Window() {
        super("Contour Slicer");
        this.setLocation(0,0);
        this.getContentPane().setPreferredSize(new Dimension(640,480));
        
        this.add(canvas);
        
        canvas.setVisible(true);
        
        this.pack();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        createContour();
    }
    
    private void createContour(){
        this.setVisible(false);
        viewerThread.start();
        while(viewer.isRunning()) try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            Logger.getLogger(ContourSlicer.class.getName()).log(Level.SEVERE, null, ex);
        }
        ArrayList<ArrayList<Vector2f>> contour = contourFunctions.copyContourListWithoutArray(viewer.getPoints());

        if (viewer.getPoints().size() <= 0) {
            viewer.destroy();
        } else {
            editContour(contour);   
            viewerThread.interrupt();
            viewer.stop();
            viewer.destroy();     
        }
        
    }
    
    private void editContour(ArrayList<ArrayList<Vector2f>> contour){
        this.setVisible(false);
        editableContour = contour;
        editorThread.start();
        while(editor.isRunning()) try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            Logger.getLogger(ContourSlicer.class.getName()).log(Level.SEVERE, null, ex);
        }
        editorThread.interrupt();
        if (editor.getContour() == null); 
        else {
            contours.add((ArrayList<ArrayList<Vector2f>>) editor.getContour().clone());
        }
        editableContour = new ArrayList<>();
        editor.dispose();
        this.setVisible(true);
    }
    
    
    
    private class Canvas extends JPanel {

        private Paint paint;
        
        private static final int TOTAL_RECTANGLES = 5;
        private static final int WIDTH = 400;
        private static final int HEIGHT = 300;
        private static final int RADIUS = 50;
        private static final int X = 50;
        private static final int Y = 50;
        private int counter;
        private int moveXBy;
        private boolean isActive;
        private int count;

        public Canvas () {
            setOpaque (true);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(window.getPreferredSize().width, window.getPreferredSize().height);
        }

        @Override
        protected void paintComponent(Graphics g2) {
            Graphics2D g = (Graphics2D) g2;
            super.paintComponent(g);
            g.setPaint(paint);
            g.setColor(Color.black);
            g.setStroke(new BasicStroke(1));
            
            Polygon p = new Polygon();
            for (int i = 0; i < contours.size();i++) {
                for (int i2 = 0; i2 < contours.get(i).size();i2++) {
                    for (int i3 = 0; i3 < contours.get(i).get(i2).size();i3++) {
                        p.addPoint((int) Math.round(contours.get(i).get(i2).get(i3).x), (int) Math.round(contours.get(i).get(i2).get(i3).y));
                    }
                }
            }
            g.drawPolygon(p);
        }
    }
}

ContourFunctions:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ContourFunctions;

import com.jme3.math.Vector2f;
import java.util.ArrayList;

/**
 *
 * @author Admin
 */
public class ContourFunctions {
    
    /*
       Copy some contour
    */
    public ArrayList<ArrayList<Vector2f[]>> copyContourListWithArray(ArrayList<ArrayList<Vector2f[]>> listToCopy) {
        ArrayList<ArrayList<Vector2f[]>> newList = new ArrayList<>();
        for (int i = 0; i < listToCopy.size();i++) {
            ArrayList<Vector2f[]> list = new ArrayList<>();
            for (int i2 = 0; i2 < listToCopy.get(i).size();i2++) {
                Vector2f vArray[] = new Vector2f[2];
                vArray[0] = listToCopy.get(i).get(i2)[0].clone();
                vArray[1] = listToCopy.get(i).get(i2)[1].clone();
                list.add(vArray);
            }
            newList.add(list);
        }
        return newList;
    }
    public ArrayList<ArrayList<Vector2f>> copyContourListWithoutArray(ArrayList<ArrayList<Vector2f>> listToCopy) {
        ArrayList<ArrayList<Vector2f>> newList = new ArrayList<>();
        for (int i = 0; i < listToCopy.size();i++) {
            ArrayList<Vector2f> list = new ArrayList<>();
            for (int i2 = 0; i2 < listToCopy.get(i).size();i2++) {
                list.add(listToCopy.get(i).get(i2).clone());
            }
            newList.add(list);
        }
        return newList;
    }
    
    /*
    Copy some line
    */
    public ArrayList<Vector2f[]> copyLineListWithArray(ArrayList<Vector2f[]> listToCopy) {
        ArrayList<Vector2f[]> list = new ArrayList<>();
        for (int i = 0; i < listToCopy.size();i++) {
            Vector2f vArray[] = new Vector2f[2];
            vArray[0] = listToCopy.get(i)[0].clone();
            vArray[1] = listToCopy.get(i)[1].clone();
            list.add(vArray);
        }
        return list;
    }   
    public ArrayList<Vector2f> copyLineListWithoutArray(ArrayList<Vector2f> listToCopy) {
        ArrayList<Vector2f> list = new ArrayList<>();
        for (int i = 0; i < listToCopy.size();i++) {
            Vector2f v = listToCopy.get(i).clone();
            list.add(v);
        }
        return list;
    }   
    
    /*
    Convert contour to string
    */
    public String contourToStringWithArray(ArrayList<ArrayList<Vector2f[]>> contour) {
        String s = "";
        for (int i = 0; i < contour.size();i++) {
            if (i > 0)
                s += ";";
            ArrayList<Vector2f[]> lines = contour.get(i);
            for (int i2 = 0; i2 < lines.size();i2++) {
                if (i2 > 0)
                    s += ":";
                    s += Float.toString(lines.get(i2)[0].x);
                    s += ",";
                    s += Float.toString(lines.get(i2)[0].y);
                
            }
        }
        return s;
    }
    public String contourToStringWithoutArray(ArrayList<ArrayList<Vector2f>> contour) {
        String s = "";
        for (int i = 0; i < contour.size();i++) {
            if (i > 0)
                s += ";";
            ArrayList<Vector2f> lines = contour.get(i);
            for (int i2 = 0; i2 < lines.size();i2++) {
                if (i2 > 0)
                    s += ":";
                    s += Float.toString(lines.get(i2).x);
                    s += ",";
                    s += Float.toString(lines.get(i2).y);
                
            }
        }
        return s;
    }
    
    /*
    Convert string to contour
    */
    public ArrayList<ArrayList<Vector2f[]>> stringToContourWithArray(String s) {
        ArrayList<ArrayList<Vector2f[]>> contourReturn = new ArrayList<>();
        String contours[] = s.split(";");
        for (int i = 0; i < contours.length;i++) {
            ArrayList<Vector2f[]> lineList = new ArrayList<>();
            String contour = contours[i];
            String[] points = contour.split(":");
            for (int i2 = 0; i2 < points.length;i2++) {
                String[] point1;
                String[] point2;
                if (i2 < points.length-1) {
                    point1 = points[i2].split(",");
                    point2 = points[i2+1].split(",");
                } else {
                    point1 = points[i2].split(",");
                    point2 = points[0].split(",");
                }
                Vector2f[] v = {new Vector2f(Float.parseFloat(point1[0]),Float.parseFloat(point1[1])),new Vector2f(Float.parseFloat(point2[0]),Float.parseFloat(point2[1]))};
                lineList.add(v);
            }
            contourReturn.add(lineList);
        }
        return contourReturn;
    }
    public ArrayList<ArrayList<Vector2f>> stringToContourWithoutArray(String s) {
        ArrayList<ArrayList<Vector2f>> contourReturn = new ArrayList<>();
        String contours[] = s.split(";");
        for (int i = 0; i < contours.length;i++) {
            ArrayList<Vector2f> lineList = new ArrayList<>();
            String contour = contours[i];
            String[] points = contour.split(":");
            for (int i2 = 0; i2 < points.length;i2++) {
                String[] point1;
                point1 = points[i2].split(",");
                Vector2f v = new Vector2f(Float.parseFloat(point1[0]),Float.parseFloat(point1[1]));
                lineList.add(v);
            }
            contourReturn.add(lineList);
        }
        return contourReturn;
    }
}

Sorry, if it’s complicated, but I don’t know, how to make it easier. :frowning:

Thanks a lot for all help! :slight_smile:

Are you familiar with multithreading? Your code is incomplete. Several classes are missing that are required to diagnose your cause, but you are attempting to modify the scene on a thread that did not create it. All(?) Scene graphs as far as i know are single threaded. There are no out of the box solutions other than understanding what multithreading is - if you don’t already know.

I’m not going to suggest app.enqueue(callable) because it is prone to abuse, though likely to work regardless…

On line 79, you hadn’t set the value yet and didn’t check for null.

You were modifying scene graph structures from another thread.

It seems clear from the code that you are very new to threading. I think your whole approach is probably wrong.

If you want background threads to do work then you need to submit work to them. Then they will submit the results back to you when they are done. Nothing ever shares anything related to the scene graph in the mean time.

You’re modifying scene graph outside the main thread. I can’t see anything in your code rendering though, did you share every part of your application?

So thats all you need, the other code is not really needed…
The classes ContourSlicer3DViewer and ContourEditor have more than 3000 rows, but only input und output are really needed for this problem.

Yes, so it is…

I don’t know a lot about multithreading, and that’s the problem.
I tried to work with it, but it would be better, if I don’t use it.
And it’s not really needed, there may be a better solution, but I don’t know, how to realize it.

Is it possible to open a second window in the main class and wait for an answer?
Like the error windows, or JFileChooser…
That would be a better solution, but I don’t know, how to make it.

So open for example 3D Viewer or Editor and lock the main window, till I get the contour, and all in a single thread…

Is it possible?

Not really.

Just enqueue() your results back on the application when you need to send stuff over there.

And if you ever find yourself doing something like the above, you are already way down the wrong path.

I think, I found one solution…
Just use JDialog instead of JFrame in ContourEditor.
And for the 3D viewer I will try enqueue().

Thanks for help! :smiley: