Commit d35ccbb6 authored by Andrey Filippov's avatar Andrey Filippov

completed methods for multithreaded operation

parent 1aa3cc9e
...@@ -36,17 +36,23 @@ public class LwocLeaf { ...@@ -36,17 +36,23 @@ public class LwocLeaf {
scenes = new ArrayList <LwocScene>(); // cameras located in this node scenes = new ArrayList <LwocScene>(); // cameras located in this node
} }
public void addScene(LwocScene scene) { public void addScene(LwocScene scene,
scenes.add(scene); boolean check_existed) {
if (!check_existed || !scenes.contains(scene)) {
scenes.add(scene);
}
} }
public void addMeshCenter(LwocMesh mesh) { public void addMeshCenter(LwocMesh mesh,
mesh_centers.add(mesh); boolean check_existed) {
if (!check_existed || !mesh_centers.contains(mesh)) {
mesh_centers.add(mesh);
}
} }
public void addMesh(LwocMesh mesh, public void addMesh(LwocMesh mesh,
boolean check_existed) { boolean check_existed) {
if (!check_existed || meshes.contains(mesh)) { if (!check_existed || !meshes.contains(mesh)) {
meshes.add(mesh); meshes.add(mesh);
} }
} }
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
package com.elphel.imagej.tileprocessor.lwoc; package com.elphel.imagej.tileprocessor.lwoc;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import com.elphel.imagej.common.MultiThreading; import com.elphel.imagej.common.MultiThreading;
public class LwocOctree { public class LwocOctree {
...@@ -98,7 +99,6 @@ public class LwocOctree { ...@@ -98,7 +99,6 @@ public class LwocOctree {
* @return if world needs growing. * @return if world needs growing.
*/ */
public static boolean pendingGrow() { public static boolean pendingGrow() {
// return !pendingScenes.isEmpty() || !pendingMeshes.isEmpty();
return (numPendingScenes.get() + numPendingMeshes.get()) > 0; return (numPendingScenes.get() + numPendingMeshes.get()) > 0;
} }
...@@ -154,24 +154,32 @@ public class LwocOctree { ...@@ -154,24 +154,32 @@ public class LwocOctree {
* @param scene Scene to add. * @param scene Scene to add.
* @param pendingScenes per thread list of scenes to be combined after merging threads * @param pendingScenes per thread list of scenes to be combined after merging threads
* @param pendingLeafNodes per thread list of nodes to be combined after merging threads * @param pendingLeafNodes per thread list of nodes to be combined after merging threads
* @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 * @return An octree node where scene is added or null if the world needs growing
*/ */
public static LwocOctree addScene( public static LwocOctree addScene(
LwocScene scene, LwocScene scene,
ArrayList<LwocScene> pendingScenes, ArrayList<LwocScene> pendingScenes,
ArrayList<LwocOctree> pendingLeafNodes ArrayList<LwocOctree> pendingLeafNodes,
boolean check_existed
) {// returns null and adds to pendingScenes if needs growing ) {// returns null and adds to pendingScenes if needs growing
LwocOctree node = getLeafNode(scene.getCameraXYZ()); LwocOctree node = getLeafNode(scene.getCameraXYZ());
if (node == null) { if (node == null) {
if (pendingScenes != null) { if (pendingScenes != null) {
pendingScenes.add(scene); if (!check_existed || !pendingScenes.contains(scene)) {
pendingScenes.add(scene);
numPendingScenes.getAndIncrement();
}
} }
} else { } else {
node.leaf.addScene(scene); node.leaf.addScene(scene,check_existed);
// Check if leaf node requires splitting // Check if leaf node requires splitting
if (node.leaf.getScenes().size() > MAX_CAMERAS) { if (node.leaf.getScenes().size() > MAX_CAMERAS) {
if (node.hsize > MIN_HSIZE) { if (node.hsize > MIN_HSIZE) {
pendingLeafNodes.add(node); if (!check_existed || !pendingLeafNodes.contains(node)) {
pendingLeafNodes.add(node);
numPendingLeafNodes.getAndIncrement();
}
} }
} }
} }
...@@ -182,10 +190,12 @@ public class LwocOctree { ...@@ -182,10 +190,12 @@ public class LwocOctree {
* Tend to pending scenes list. Not thread-safe, should be run in a single-thread mode. * Tend to pending scenes list. Not thread-safe, should be run in a single-thread mode.
* @param pendingScenes combined list of scenes to be added growing world (merged from per-thread ones) * @param pendingScenes combined list of scenes to be added growing world (merged from per-thread ones)
* @param pendingLeafNodes a list of nodes that will need splitting (here in single-thread mode - single one) * @param pendingLeafNodes a list of nodes that will need splitting (here in single-thread mode - single one)
* @param check_existed - do not add duplicate scenes and nodes
*/ */
public static void tendPendingScenes( public static void tendPendingScenes(
ArrayList<LwocScene> pendingScenes, ArrayList<LwocScene> pendingScenes,
ArrayList<LwocOctree> pendingLeafNodes ArrayList<LwocOctree> pendingLeafNodes,
boolean check_existed
) { ) {
if (pendingScenes.isEmpty()) { if (pendingScenes.isEmpty()) {
return; // nothing to do return; // nothing to do
...@@ -195,7 +205,8 @@ public class LwocOctree { ...@@ -195,7 +205,8 @@ public class LwocOctree {
LwocOctree node= addScene( // should not be null LwocOctree node= addScene( // should not be null
scene, scene,
pendingScenes, pendingScenes,
pendingLeafNodes); pendingLeafNodes,
check_existed);
assert node != null : "addScene() should not fail after growing world"; assert node != null : "addScene() should not fail after growing world";
} }
} }
...@@ -203,11 +214,12 @@ public class LwocOctree { ...@@ -203,11 +214,12 @@ public class LwocOctree {
/** /**
* Tend to pending meshes list. Not thread-safe, should be run in a single-thread mode. * Tend to pending meshes list. Not thread-safe, should be run in a single-thread mode.
* Only adds mesh centers and grows the world if needed.
* @param check_existed do not add duplicate meshes * @param check_existed do not add duplicate meshes
* @param pendingMeshes combined list of meshes to be added (merged from per-thread ones). Will only be read. * @param pendingMeshes combined list of meshes to be added (merged from per-thread ones). Will only be read.
* @param pendingLeafNodes a list of nodes that will need splitting (here in single-thread mode - single one). May grow. * @param pendingLeafNodes a list of nodes that will need splitting (here in single-thread mode - single one). May grow.
*/ */
public static void tendPendingMeshCenters( public static void tendPendingMeshCentersOnly(
boolean check_existed, boolean check_existed,
ArrayList<LwocMesh> pendingMeshes, // will read ArrayList<LwocMesh> pendingMeshes, // will read
ArrayList<LwocOctree> pendingLeafNodes // may add to ArrayList<LwocOctree> pendingLeafNodes // may add to
...@@ -232,6 +244,26 @@ public class LwocOctree { ...@@ -232,6 +244,26 @@ public class LwocOctree {
null, // pendingMeshes, null, // pendingMeshes,
pendingLeafNodes); pendingLeafNodes);
assert ok : "addMeshCenter() should not fail after growing world"; assert ok : "addMeshCenter() should not fail after growing world";
}
}
}
/**
* Tend to pending meshes list after thge world is grown and related mesh centers are added.
* Only adds mesh themselves that do not trigger node splits.
* Should be run after tendPendingMeshCentersOnly().
* @param check_existed do not add duplicate meshes
* @param pendingMeshes combined list of meshes to be added (merged from per-thread ones).
* Will only be read.
*/
public static void tendPendingMeshes(
boolean check_existed,
ArrayList<LwocMesh> pendingMeshes // will read
) {
if (pendingMeshes.isEmpty()) {
return; // nothing to do
} else {
for (LwocMesh mesh: pendingMeshes) {
// int num_added = // int num_added =
lwoc_root.addMesh( lwoc_root.addMesh(
mesh, mesh,
...@@ -240,13 +272,31 @@ public class LwocOctree { ...@@ -240,13 +272,31 @@ public class LwocOctree {
} }
} }
/**
* Recursively (if needed) splits octree nodes to reduce number of scenes/cameras
* and mesh centers below specified thresholds (limited by the minimal node size)
* @param pendingLeafNodesIn array list of nodes to be split. Will be filtered to
* remove any duplicates and non-leaf nodes
* @param min_hsize Minimal half-size of the node (in meters). Smaller nodes will not be split
* @param max_mesh_centers Maximal number of mesh centers in a node. Larger number
* triggers node split.
* @param max_cameras Maximasl number of scenes (camera positions) in a node. Larger
* number triggers node split.
* @param check_existed
*/
public static void tendPendingLeafNodes( public static void tendPendingLeafNodes(
ArrayList<LwocOctree> pendingLeafNodesIn) { ArrayList<LwocOctree> pendingLeafNodesIn,
final double min_hsize,
final int max_mesh_centers,
final int max_cameras, // scenes
final boolean check_existed) {
// remove any possible duplicates // remove any possible duplicates
final ArrayList<LwocOctree> pendingLeafNodes = new ArrayList<LwocOctree>(); final ArrayList<LwocOctree> pendingLeafNodes = new ArrayList<LwocOctree>();
for (LwocOctree node:pendingLeafNodesIn) { for (LwocOctree node:pendingLeafNodesIn) {
if (!pendingLeafNodes.contains(node)) { if (!pendingLeafNodes.contains(node) && node.isLeaf()) {// filter already split nodes
pendingLeafNodes.add(node); pendingLeafNodes.add(node);
numPendingLeafNodes.getAndIncrement();
} }
} }
// run splitting multithreaded // run splitting multithreaded
...@@ -259,6 +309,11 @@ public class LwocOctree { ...@@ -259,6 +309,11 @@ public class LwocOctree {
for (int indx_node = ai.getAndIncrement(); indx_node < pendingLeafNodes.size(); indx_node = ai.getAndIncrement()) { for (int indx_node = ai.getAndIncrement(); indx_node < pendingLeafNodes.size(); indx_node = ai.getAndIncrement()) {
LwocOctree node = pendingLeafNodes.get(indx_node); LwocOctree node = pendingLeafNodes.get(indx_node);
// Split recursively until satisfied conditions // Split recursively until satisfied conditions
node.splitNode(
min_hsize, // double min_hsize,
max_mesh_centers, // int max_mesh_centers,
max_cameras, // int max_cameras,
check_existed); // boolean check_existed)
} // end of tile } // end of tile
} }
}; };
...@@ -269,9 +324,10 @@ public class LwocOctree { ...@@ -269,9 +324,10 @@ public class LwocOctree {
// Split recursively until satisfied conditions // Split recursively until satisfied conditions
public int splitNode( public int splitNode(
double min_hsize, double min_hsize,
int max_mesh_centers, int max_mesh_centers,
int max_cameras int max_cameras,
boolean check_existed
) { ) {
if ((hsize <= min_hsize) || (leaf == null)) { if ((hsize <= min_hsize) || (leaf == null)) {
return 0; return 0;
...@@ -298,17 +354,53 @@ public class LwocOctree { ...@@ -298,17 +354,53 @@ public class LwocOctree {
} }
ArrayList <LwocMesh> meshes = leaf.getMeshes(); ArrayList <LwocMesh> meshes = leaf.getMeshes();
// distribute meshes between 8 children // distribute meshes between 8 children
for (LwocMesh mesh: meshes) { // may go to any number >=1 of the children
double [] mesh_center = mesh.getCenter();
double [] mesh_dims = mesh.getDims();
for (LwocOctree node:children) {
if (node.intersects(
mesh_center,
mesh_dims)) {
node.leaf.addMesh(
mesh,
check_existed);
}
}
}
ArrayList <LwocMesh> mesh_centers = leaf.getMeshCenters(); ArrayList <LwocMesh> mesh_centers = leaf.getMeshCenters();
// distribute mesh_centers between 8 children // distribute mesh_centers between 8 children
for (LwocMesh mesh: mesh_centers) { // goes to a single child only
double [] mesh_center = mesh.getCenter();
for (LwocOctree node:children) {
if (node.contains(mesh_center)) {
node.leaf.addMeshCenter(
mesh,
check_existed);
break; // single point - goes to one child only
}
}
}
ArrayList <LwocScene> scenes = leaf.getScenes(); ArrayList <LwocScene> scenes = leaf.getScenes();
// distribute scnes (cameras) between 8 children // distribute scenes (cameras) between 8 children
for (LwocScene scene: scenes) { // goes to a single child only
double [] camera_xyz = scene. getCameraXYZ();
for (LwocOctree node:children) {
if (node.contains(camera_xyz)) {
node.leaf.addScene(
scene,
check_existed);
break; // single point - goes to one child only
}
}
}
// delete old leaf (if it is in a list) and its lists - no need // delete old leaf (if it is in a list) and its lists - no need
leaf = null; leaf = null;
for (int nchild = 0; nchild < children.length; nchild++) { for (int nchild = 0; nchild < children.length; nchild++) {
num_added += children[nchild].splitNode( num_added += children[nchild].splitNode(
min_hsize, min_hsize,
max_mesh_centers, max_mesh_centers,
max_cameras); max_cameras,
check_existed);
} }
return num_added; return num_added;
} }
...@@ -445,14 +537,20 @@ public class LwocOctree { ...@@ -445,14 +537,20 @@ public class LwocOctree {
corner_xyz[dm] += dims[dm]; corner_xyz[dm] += dims[dm];
if (!lwoc_root.contains(corner_xyz)){ if (!lwoc_root.contains(corner_xyz)){
if (pendingMeshes != null) { if (pendingMeshes != null) {
pendingMeshes.add(mesh); if (!check_existed || !pendingMeshes.contains(mesh)) {
pendingMeshes.add(mesh);
numPendingMeshes.getAndIncrement();
}
} }
return false; return false;
} }
corner_xyz[dm] -= 2*dims[dm]; corner_xyz[dm] -= 2*dims[dm];
if (!lwoc_root.contains(corner_xyz)){ if (!lwoc_root.contains(corner_xyz)){
if (pendingMeshes != null) { if (pendingMeshes != null) {
pendingMeshes.add(mesh); if (!check_existed || !pendingMeshes.contains(mesh)) {
pendingMeshes.add(mesh);
numPendingMeshes.getAndIncrement();
}
} }
return false; return false;
} }
...@@ -460,18 +558,22 @@ public class LwocOctree { ...@@ -460,18 +558,22 @@ public class LwocOctree {
} }
// all corners fit - add center // all corners fit - add center
LwocOctree node = getLeafNode(center); LwocOctree node = getLeafNode(center);
node.leaf.addMeshCenter(mesh); node.leaf.addMeshCenter(mesh, check_existed);
// Check if leaf node requires splitting // Check if leaf node requires splitting
if (node.leaf.getMeshCenters().size() > MAX_MESH_CENTERS) { if (node.leaf.getMeshCenters().size() > MAX_MESH_CENTERS) {
if (node.hsize > MIN_HSIZE) { if (node.hsize > MIN_HSIZE) {
pendingLeafNodes.add(node); if (!check_existed || !pendingLeafNodes.contains(node)) {
pendingLeafNodes.add(node);
numPendingLeafNodes.getAndIncrement();
}
} }
} }
// Now recursively add mesh to all nodes intersected by this mesh bounding box // Not here: Now recursively add mesh to all nodes intersected by this mesh bounding box
return true; return true;
} }
public int addMesh( // returns 0 and adds to pendingMeshes if needs growing // recursive
public int addMesh(
LwocMesh mesh, LwocMesh mesh,
boolean check_existed) { boolean check_existed) {
if (!intersects( if (!intersects(
...@@ -504,4 +606,89 @@ public class LwocOctree { ...@@ -504,4 +606,89 @@ public class LwocOctree {
return 0; return 0;
} }
/**
* Prepare list of lists to add scenes in multithreaded environment, one inner
* list for each thread.
* @param threads Array of threads, only length is used
* @return list of lists to provide to threads
*/
public static List<List<LwocScene>> getMultiPendingScenes(Thread [] threads){
List<List<LwocScene>> multiPendingScenes = new ArrayList<List<LwocScene>>(threads.length);
for (int i = 0; i < threads.length; i++) {
multiPendingScenes.add(i, new ArrayList<LwocScene>());
}
return multiPendingScenes;
}
/**
* Combine a List of list of scenes (created in multithreaded method getMultiPendingScenes())
* into a single list.
* @param multiPendingScenes list of list of scenes
* @return flattened single list of scenes
*/
public static List<LwocScene> mergeMultiPendingScenes(List<List<LwocScene>> multiPendingScenes){
List<LwocScene> pendingScenes = new ArrayList<LwocScene>();
for (List<LwocScene> scenes:multiPendingScenes) {
pendingScenes.addAll(scenes);
}
return pendingScenes;
}
/**
* Prepare list of lists to add meshes in multithreaded environment, one inner
* list for each thread.
* @param threads Array of threads, only length is used
* @return list of lists to provide to threads
*/
public static List<List<LwocMesh>> getMultiPendingMeshes(Thread [] threads){
List<List<LwocMesh>> multiPendingMeshes = new ArrayList<List<LwocMesh>>(threads.length);
for (int i = 0; i < threads.length; i++) {
multiPendingMeshes.add(i, new ArrayList<LwocMesh>());
}
return multiPendingMeshes;
}
/**
* Combine a List of list of meshes (created in multithreaded method getMultiPendingMeshes())
* into a single list.
* @param multiPendingMeshes list of list of meshes
* @return flattened single list of meshes
*/
public static List<LwocMesh> mergeMultiPendingMeshes(List<List<LwocMesh>> multiPendingMeshes){
List<LwocMesh> pendingMeshes = new ArrayList<LwocMesh>();
for (List<LwocMesh> meshes:multiPendingMeshes) {
pendingMeshes.addAll(meshes);
}
return pendingMeshes;
}
/**
* Prepare list of lists to add scenes in multithreaded environment, one inner
* list for each thread.
* @param threads Array of threads, only length is used
* @return list of lists to provide to threads
*/
public static List<List<LwocOctree>> getMultiPendingLeafNodes(Thread [] threads){
List<List<LwocOctree>> multiPendingLeafNodes = new ArrayList<List<LwocOctree>>(threads.length);
for (int i = 0; i < threads.length; i++) {
multiPendingLeafNodes.add(i, new ArrayList<LwocOctree>());
}
return multiPendingLeafNodes;
}
/**
* Combine a List of list of scenes (created in multithreaded method getMultiPendingScenes())
* into a single list.
* @param multiPendingLeafNodes list of list of scenes
* @return flattened single list of scenes
*/
public static List<LwocOctree> mergeMultiPendingLeafNodes(List<List<LwocOctree>> multiPendingLeafNodes){
List<LwocOctree> pendingLeafNodes = new ArrayList<LwocOctree>();
for (List<LwocOctree> nodes:multiPendingLeafNodes) {
pendingLeafNodes.addAll(nodes);
}
return pendingLeafNodes;
}
} }
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