Commit 8176593d authored by Andrey Filippov's avatar Andrey Filippov

Refactored to have multiple world (needed when scenes can not be

matched)
parent 7e53b24d
......@@ -25,11 +25,11 @@
package com.elphel.imagej.tileprocessor.lwoc;
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicInteger;
///import java.util.concurrent.atomic.AtomicInteger;
public class LwocMesh implements Serializable {
private static final long serialVersionUID = 1L;
static AtomicInteger MESH_ID = new AtomicInteger();
/// static AtomicInteger MESH_ID = new AtomicInteger();
// static ArrayList<LwocMesh> LWOC_MESHES;
int id; // assign unique ID
String stimestamp; // mesh is always referenced to a single scene
......@@ -37,7 +37,7 @@ public class LwocMesh implements Serializable {
double [] hdims_xyz; // world bounding box half-dimensions along x,y,z
public static void resetMeshes() {
MESH_ID.set(0);
/// MESH_ID.set(0);
// LWOC_MESHES = new ArrayList<LwocMesh>();
}
// maybe use mesh properties instead of id?
......@@ -48,7 +48,7 @@ public class LwocMesh implements Serializable {
String stimestamp
) {
this.stimestamp = stimestamp;
id = MESH_ID.getAndIncrement();
/// id = MESH_ID.getAndIncrement();
// LWOC_MESHES.add(this);
}
......
......@@ -24,11 +24,11 @@
package com.elphel.imagej.tileprocessor.lwoc;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
//import java.io.FileInputStream;
//import java.io.FileOutputStream;
//import java.io.IOException;
//import java.io.ObjectInputStream;
//import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
......@@ -37,99 +37,21 @@ import com.elphel.imagej.common.MultiThreading;
public class LwocOctree implements Serializable {
private static final long serialVersionUID = 1L;
public static final int NUM_CHILDREN = 8;
public static double MIN_HSIZE = 0.3; // meter - do not subdivide more
// can not be used as intersecting meshes' bb will not decrease number after splitting
public static int MAX_MESH_CENTERS = 10; // maximal number of meshes in a leaf;
public static int MAX_CAMERAS = 10; // maximal number of cameras in a leaf;
public static LwocOctree lwoc_root = null;
// static ArrayList<LwocOctree> LWOK_OCTREE = new ArrayList<LwocOctree>();
static AtomicInteger OCTREE_ID = new AtomicInteger();
// static AtomicInteger OCTREE_ID = new AtomicInteger();
static List<LwocScene> pendingScenes; // synchronized - add not-yet-added scenes pending growing world
static List<LwocMesh> pendingMeshes; // synchronized - add not-yet-added meshes pending growing world
static List<LwocOctree> pendingLeafNodes;// synchronized - leaf nodes needed to be split
// static List<LwocWorld> lwoc_worlds; //
int id; // assign unique ID
LwocOctree parent;
transient LwocWorld world;
transient LwocOctree parent;
LwocOctree [] children;
LwocLeaf leaf; //
double [] center; // x-center, y-center, z-center
double hsize;
/**
* Serialize and write world to an output stream
* @param oos ObjectOutputStream to write to.
* @throws IOException
*/
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
if (parent == null) { // write static members
oos.writeObject(MIN_HSIZE);
oos.writeObject(MAX_MESH_CENTERS);
oos.writeObject(MAX_CAMERAS);
oos.writeObject(OCTREE_ID.get());
oos.writeObject(LwocMesh.MESH_ID.get());
}
}
/**
* Read input stream and deserialize it to world octree structure.
* @param ois ObjectInputStream to read from
* @throws ClassNotFoundException
* @throws IOException
*/
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
if (parent == null) { // read static members
MIN_HSIZE = (Double) ois.readObject();
MAX_MESH_CENTERS = (Integer) ois.readObject();
MAX_CAMERAS = (Integer) ois.readObject();
OCTREE_ID.set( (Integer) ois.readObject());
LwocMesh.MESH_ID.set((Integer) ois.readObject());
}
}
/**
*
* @param path
*/
public static void saveWorld(String path) {
try {
FileOutputStream fileOut = new FileOutputStream(path);
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(lwoc_root); // will cause all world to be saved
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in "+path);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void restoreWorld(String path) {
try {
FileInputStream fileIn = new FileInputStream(path);
ObjectInputStream in = new ObjectInputStream(fileIn);
lwoc_root = (LwocOctree) in.readObject();
in.close();
fileIn.close();
} catch (IOException e) {
e.printStackTrace();
return;
} catch (ClassNotFoundException c) {
System.out.println(path+" not found");
c.printStackTrace();
return;
}
}
public static void addPendingScene(LwocScene scene) {
synchronized(pendingScenes) {
pendingScenes.add(scene);
......@@ -148,22 +70,6 @@ public class LwocOctree implements Serializable {
}
}
/**
* Initialize Octree data and set initial world node. It is possible to grow
* world as needed later.
* @param center world center: x,y,z in meters. Usually {0,0,0}
* @param world_hsize Half linear dimension of the world
*/
public static void initOctree(double [] center, double world_hsize) {
LwocMesh.resetMeshes();
LwocScene.resetScenes();
resetPending();
// LWOK_OCTREE = new ArrayList<LwocOctree>();
// Add a single leaf node
lwoc_root = new LwocOctree(null, center, world_hsize);
lwoc_root.leaf = new LwocLeaf();
}
/**
* Synchronized lists of pending scenes, meshes and nodes are used to delay modification of
* the world octree and its elements from the multithreaded environment. These lists should
......@@ -202,18 +108,19 @@ public class LwocOctree implements Serializable {
/**
* LwocOctree constructor
* @param lwocWorld World global parameters
* @param parent Parent node
* @param xyz position of the node center in meters
* @param xyz Position of the node center in meters
* @param hsize Node half size - each of the X,Y,Z coordinates of the internal
* points are limited within +/-hsize from the node center
*/
public LwocOctree (LwocOctree parent,
public LwocOctree (
LwocWorld lwocWorld,
LwocOctree parent,
double [] xyz,
double hsize) {
id = OCTREE_ID.getAndIncrement();
this.parent = parent;
this.hsize = hsize;
// LWOK_OCTREE.add(this);
}
/**
......@@ -222,14 +129,15 @@ public class LwocOctree implements Serializable {
* @return Octree node that includes the point or null if the point
* is outside the root node (the whole world)
*/
public static LwocOctree getLeafNode( // if null - needs growing world
//FIXME: obey border (on the edges of max hsize of the root) nodes
public LwocOctree getLeafNode( // if null - needs growing world
double [] xyz) { // thread safe
if (!lwoc_root.contains(xyz)) return null; // needs growing world
LwocOctree node = lwoc_root;
while (lwoc_root.children != null) {
if (!world.getLwocRoot().contains(xyz)) return null; // needs growing world
LwocOctree node = world.getLwocRoot();
while (world.getLwocRoot().children != null) {
int indx = 0;
for (int dm = 0; dm < 3; dm++) {
if (xyz[dm] >= lwoc_root.center[dm] ) {
if (xyz[dm] >= world.getLwocRoot().center[dm] ) {
indx += 1 << dm;
}
}
......@@ -245,7 +153,7 @@ public class LwocOctree implements Serializable {
* @param check_existed - do not add duplicate scenes and nodes
* @return An octree node where scene is added or null if the world needs growing
*/
public static LwocOctree addScene(
public LwocOctree addScene(
LwocScene scene,
boolean check_existed
) {// returns null and adds to pendingScenes if needs growing
......@@ -259,8 +167,8 @@ public class LwocOctree implements Serializable {
} else {
node.leaf.addScene(scene,check_existed);
// Check if leaf node requires splitting
if (node.leaf.getScenes().size() > MAX_CAMERAS) {
if (node.hsize > MIN_HSIZE) {
if (node.leaf.getScenes().size() > world.getMaxCameras()) {
if (node.hsize > world.getMinHsize()) {
if (!check_existed || !pendingLeafNodes.contains(node)) {
addPendingLeafNode(node);
}
......@@ -274,7 +182,7 @@ public class LwocOctree implements Serializable {
* Tend to pending scenes list. Not thread-safe, should be run in a single-thread mode.
* @param check_existed - do not add duplicate scenes and nodes
*/
public static void tendPendingScenes(
public void tendPendingScenes(
boolean check_existed
) {
if (pendingScenes.isEmpty()) {
......@@ -295,7 +203,7 @@ public class LwocOctree implements Serializable {
* Only adds mesh centers and grows the world if needed.
* @param check_existed do not add duplicate meshes
*/
public static void tendPendingMeshCentersOnly(
public void tendPendingMeshCentersOnly(
boolean check_existed
) {
if (pendingMeshes.isEmpty()) {
......@@ -326,14 +234,14 @@ public class LwocOctree implements Serializable {
* Should be run after tendPendingMeshCentersOnly().
* @param check_existed do not add duplicate meshes
*/
public static void tendPendingMeshes(
public void tendPendingMeshes(
boolean check_existed ) {
if (pendingMeshes.isEmpty()) {
return; // nothing to do
} else {
for (LwocMesh mesh: pendingMeshes) {
// int num_added =
lwoc_root.addMesh(
world.lwoc_root.addMesh(
mesh,
check_existed);
}
......@@ -417,6 +325,7 @@ public class LwocOctree implements Serializable {
xyz[dm] +=new_hsize * ((((nchild >> dm) & 1) > 0)? 1 : -1);
}
children[nchild] = new LwocOctree(
world,
this,
xyz,
new_hsize);
......@@ -481,41 +390,44 @@ public class LwocOctree implements Serializable {
* until the specified point gets inside. Not thread safe, should run in single-thread mode
* @param xyz The point to be included in the world.
*/
public static void growXYZ(double [] xyz) {
while (!lwoc_root.contains(xyz)) {// already fits, do not grow
public void growXYZ(double [] xyz) {
while (!world.lwoc_root.contains(xyz)) {// already fits, do not grow
// grow once
int indx=0; //direction to grow
double [] new_center = new double[3];
double [] root_center = world.lwoc_root.center;
double root_hsize = world.lwoc_root.hsize;
for (int dm = 0; dm < new_center.length; dm++) {
if (xyz[dm] >= lwoc_root.center[dm] ) {
if (xyz[dm] >= root_center[dm] ) {
indx += 1 << dm;
new_center[dm]= lwoc_root.center[dm] + lwoc_root.hsize;
new_center[dm]= root_center[dm] + root_hsize;
} else {
new_center[dm]= lwoc_root.center[dm] - lwoc_root.hsize;
new_center[dm]= root_center[dm] - root_hsize;
}
}
LwocOctree new_root = new LwocOctree(null, new_center, lwoc_root.hsize * 2);
LwocOctree new_root = new LwocOctree(world,null, new_center, world.lwoc_root.hsize * 2);
new_root.children = new LwocOctree[NUM_CHILDREN];
int child = (~indx ) & (NUM_CHILDREN -1); // opposite direction, from new root to old root
for (int ichild = 0; ichild < NUM_CHILDREN; ichild++) {
if (ichild == child) {
lwoc_root.parent = new_root;
new_root.children[ichild] = lwoc_root;
world.lwoc_root.parent = new_root;
new_root.children[ichild] = world.lwoc_root;
} else { // create empty leaf nodes
new_root.children[ichild] = new LwocOctree(
world,
new_root,
new double [] {
new_center[0] + lwoc_root.hsize * ((((child >> 0) & 1) > 0)? 1 : -1),
new_center[1] + lwoc_root.hsize * ((((child >> 1) & 1) > 0)? 1 : -1),
new_center[2] + lwoc_root.hsize * ((((child >> 2) & 1) > 0)? 1 : -1)
new_center[0] + root_hsize * ((((child >> 0) & 1) > 0)? 1 : -1),
new_center[1] + root_hsize * ((((child >> 1) & 1) > 0)? 1 : -1),
new_center[2] + root_hsize * ((((child >> 2) & 1) > 0)? 1 : -1)
},
lwoc_root.hsize);
root_hsize);
// add leaf
new_root.children[ichild].leaf = new LwocLeaf();
}
}
lwoc_root = new_root;
world.lwoc_root = new_root;
}
}
......@@ -525,6 +437,7 @@ public class LwocOctree implements Serializable {
* @param half_whd half width(x), height(y) and depth(z)
* @return True if it intersects
*/
//FIXME: obey border (on the edges of max hsize of the root) nodes
public boolean intersects (
double [] xyz,
double [] half_whd) {
......@@ -544,6 +457,7 @@ public class LwocOctree implements Serializable {
* @param xyz point
* @return True if it intersects
*/
//FIXME: obey border (on the edges of max hsize of the root) nodes
public boolean contains (
double [] xyz) {
for (int dm = 0; dm < center.length; dm++) {
......@@ -563,6 +477,8 @@ public class LwocOctree implements Serializable {
* @param half_whd half width(x), height(y) and depth(z)
* @return True if it intersects
*/
//FIXME: obey border (on the edges of max hsize of the root) nodes
public boolean contains (
double [] xyz,
double [] half_whd) {
......@@ -584,7 +500,7 @@ public class LwocOctree implements Serializable {
* @param check_existed Add only if the same mesh did not exist
* @return number of nodes it was added to (intersecting)
*/
public static boolean addMeshCenter( // returns 0 and adds to pendingMeshes if needs growing
public boolean addMeshCenter( // returns 0 and adds to pendingMeshes if needs growing
LwocMesh mesh,
boolean check_existed
) {
......@@ -594,7 +510,7 @@ public class LwocOctree implements Serializable {
double [] corner_xyz = center.clone();
for (int dm = 0; dm < center.length; dm++) {
corner_xyz[dm] += dims[dm];
if (!lwoc_root.contains(corner_xyz)){
if (!world.lwoc_root.contains(corner_xyz)){
if (pendingMeshes != null) {
if (!check_existed || !pendingMeshes.contains(mesh)) {
addPendingMesh(mesh);
......@@ -603,7 +519,7 @@ public class LwocOctree implements Serializable {
return false;
}
corner_xyz[dm] -= 2*dims[dm];
if (!lwoc_root.contains(corner_xyz)){
if (!world.lwoc_root.contains(corner_xyz)){
if (pendingMeshes != null) {
if (!check_existed || !pendingMeshes.contains(mesh)) {
addPendingMesh(mesh);
......@@ -617,8 +533,8 @@ public class LwocOctree implements Serializable {
LwocOctree node = getLeafNode(center);
node.leaf.addMeshCenter(mesh, check_existed);
// Check if leaf node requires splitting
if (node.leaf.getMeshCenters().size() > MAX_MESH_CENTERS) {
if (node.hsize > MIN_HSIZE) {
if (node.leaf.getMeshCenters().size() > world.getMaxMeshCenters()) {
if (node.hsize > world.getMinHsize()) {
if (!check_existed || !pendingLeafNodes.contains(node)) {
addPendingLeafNode(node);
}
......@@ -659,7 +575,7 @@ public class LwocOctree implements Serializable {
* Should fit in a root node (as it was earlier added)
* @param mesh new mesh to add
*/
public static void removeMeshCenter(
public void removeMeshCenter(
LwocMesh mesh) { // check and remove duplicates
LwocOctree node = getLeafNode(mesh.getCenter());
node.leaf.removeMeshCenter(mesh);
......@@ -688,6 +604,26 @@ public class LwocOctree implements Serializable {
return removed;
}
/**
* Restore pointers to .parent and .world
* @param world
* @param parent
*/
public void restoreWorldParent(
LwocWorld world,
LwocOctree parent) {
this.parent = parent;
this.world = world;
if (!isLeaf()) {
for (LwocOctree child:children) {
child.restoreWorldParent(
world, // LwocWorld world,
this); // LwocOctree parent)
}
}
}
/**
* Recursively create a list of all leave nodes in the world.
* @return A list of all leaf nodes
......@@ -708,9 +644,9 @@ public class LwocOctree implements Serializable {
* Rebuild meshes lists from mesh_centers for all leaf nodes after restoring the world.
* Used after restoring world from serialized form
*/
public static void rebuildMeshLists(
public void rebuildMeshLists(
final boolean check_existed) {
final List<LwocOctree> leaves = lwoc_root.getAllLeafNodes();
final List<LwocOctree> leaves = world.lwoc_root.getAllLeafNodes();
final List<LwocMesh> meshes = new ArrayList<LwocMesh>();
for (LwocOctree node : leaves) {
node.leaf.initMeshes();
......@@ -726,7 +662,7 @@ public class LwocOctree implements Serializable {
public void run() {
for (int indx_mesh = ai.getAndIncrement(); indx_mesh < meshes.size(); indx_mesh = ai.getAndIncrement()) {
LwocMesh mesh = meshes.get(indx_mesh);
lwoc_root.addMesh(mesh, check_existed);
world.lwoc_root.addMesh(mesh, check_existed);
}
}
};
......
/**
**
** LwocWorld.java - Octree world parameters (previous static)
**
** Copyright (C) 2022 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** LwocWorld.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.tileprocessor.lwoc;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class LwocWorld implements Serializable {
private static final long serialVersionUID = 1L;
static List<LwocWorld> lwoc_worlds; //
public double min_hsize; // = 0.3; // meter - do not subdivide more
public double max_hsize; // = 10000.0; // meter - do not grow more
public int max_mesh_centers; // = 10; // maximal number of meshes in a leaf;
public int max_cameras; // = 10; // maximal number of cameras in a leaf;
// public transient LwocOctree lwoc_root; // = null;
public LwocOctree lwoc_root; // = null;
public double [] atr; // = null; // Azimuth, tilt, roll - may be used to merge
public double [] xyz; // = null; // this world offset (before rotation) or
// null if not known.
public LwocWorld (
double min_hsize,
double max_hsize,
int max_mesh_centers,
int max_cameras,
LwocOctree lwoc_root,
double [] atr,
double [] xyz) {
setMinHsize (min_hsize);
setMaxHsize (max_hsize);
setMaxMeshCenters(max_mesh_centers);
setMaxCameras (max_cameras);
setLwocRoot (lwoc_root);
setATR (atr);
setXYZ (xyz);
}
public double getMinHsize() {return min_hsize;}
public double getMaxHsize() {return max_hsize;}
public int getMaxMeshCenters() {return max_mesh_centers;}
public int getMaxCameras() {return max_cameras;}
public LwocOctree getLwocRoot() {return lwoc_root;}
public double [] getATR() {return atr;}
public double [] getXYZ() {return xyz;}
public void setMinHsize (double min_hsize) {this.min_hsize = min_hsize;}
public void setMaxHsize (double max_hsize) {this.max_hsize = max_hsize;}
public void setMaxMeshCenters(int max_mesh_centers) {this.max_mesh_centers = max_mesh_centers;}
public void setMaxCameras (int max_cameras) {this.max_cameras = max_cameras;}
public void setLwocRoot (LwocOctree lwoc_root) {this.lwoc_root = lwoc_root;}
public void setATR (double [] atr) {this.atr = atr;}
public void setXYZ (double [] xyz) {this.xyz = xyz;}
/**
* Serialize and write world to an output stream
* @param oos ObjectOutputStream to write to.
* @throws IOException
*/
private void writeObject(ObjectOutputStream oos)
throws IOException {
oos.defaultWriteObject();
// as of now - nothing else, custom for reads only
// if (parent == null) { // write world members
/*
oos.writeObject(world.getMinHsize());
oos.writeObject(world.getMaxHsize());
oos.writeObject(world.getMaxMeshCenters());
oos.writeObject(world.getMaxCameras());
oos.writeObject(world.getATR());
oos.writeObject(world.getXYZ());
*/
// oos.writeObject(world);
// Remove those
// oos.writeObject(OCTREE_ID.get());
// oos.writeObject(LwocMesh.MESH_ID.get());
// }
}
/**
* Read input stream and deserialize it to world octree structure.
* @param ois ObjectInputStream to read from
* @throws ClassNotFoundException
* @throws IOException
*/
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
// now rebuild transient .parent and .world field for the whole tree
lwoc_root.restoreWorldParent(
this, // LwocWorld world,
null); //LwocOctree parent)
lwoc_root.rebuildMeshLists(
true); // final boolean check_existed)
}
/**
* Init worlds and their list. Multiple worlds may be needed if some world initially can not
* be matched. They will grow independently and possibly merged later
*/
public static void initWorlds() {
LwocMesh.resetMeshes();
LwocScene.resetScenes();
LwocOctree.resetPending();
lwoc_worlds = new ArrayList<LwocWorld>();
}
/**
* Create a new empty world and add it to the list of worlds in the universe.
* @param center X,Y,Z of the new world
* @param world_hsize new world half-size
* @param min_hsize do not subdivide nodes if half-size is smaller than this
* @param max_hsize do not grow world if its half-size it >= this
* @param max_mesh_centers maximal number of mesh centers in a node to trigger subdivision
* @param max_cameras maximal number of scenes (cameras) in a node to trigger subdivision
* @param atr world rotation relative to the universe (non-null when defined)
* @param xyz world offset in the universe (non-null when defined)
* @return new world instance
*/
public static LwocWorld newWorld(
double [] center,
double world_hsize,
double min_hsize,
double max_hsize,
int max_mesh_centers,
int max_cameras,
double [] atr,
double [] xyz) {
LwocWorld new_world = new LwocWorld (
min_hsize, // double min_hsize,
max_hsize, // double max_hsize,
max_mesh_centers, // int max_mesh_centers,
max_cameras, // int max_cameras,
null, // LwocOctree lwoc_root,
null, //double [] atr,
null); //double [] xyz);
LwocOctree root_node = new LwocOctree (
new_world, // LwocWorld lwocWorld,
null, // LwocOctree parent,
center, // double [] xyz,
world_hsize); //double hsize)
root_node.leaf = new LwocLeaf();
new_world.setLwocRoot(root_node);
lwoc_worlds.add(new_world);
return new_world;
}
/**
* Delete world from the universe (if existed)
* @param world world instance to remove
* @return True if removed, false if did not exist.
*/
public static boolean deleteWorld (LwocWorld world) {
return lwoc_worlds.remove(world);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment