signalprocesser.voronoi.statusstructure.binarysearchtreeimpl.BSTStatusStructure Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mia-algorithms Show documentation
Show all versions of mia-algorithms Show documentation
ModularImageAnalysis (MIA) is an ImageJ plugin which provides a modular framework for assembling image and object analysis workflows. Detected objects can be transformed, filtered, measured and related. Analysis workflows are batch-enabled by default, allowing easy processing of high-content datasets.
/*
* "Concave" hulls by Glenn Hudson and Matt Duckham
*
* Source code downloaded from https://archive.md/l3Un5#selection-571.0-587.218 on 3rd November 2021.
*
* - This software is Copyright (C) 2008 Glenn Hudson released under Gnu Public License (GPL). Under
* GPL you are free to use, modify, and redistribute the software. Please acknowledge Glenn Hudson
* and Matt Duckham as the source of this software if you do use or adapt the code in further research
* or other work. For full details of GPL see http://www.gnu.org/licenses/gpl-3.0.txt.
* - This software comes with no warranty of any kind, expressed or implied.
*
* A paper with full details of the characteristic hulls algorithm is published in Pattern Recognition.
* Duckham, M., Kulik, L., Worboys, M.F., Galton, A. (2008) Efficient generation of simple polygons for
* characterizing the shape of a set of points in the plane. Pattern Recognition v41, 3224-3236
*
* The software was developed by Glenn Hudson while working with me as an RA. The characteristic shapes
* algorithm is collaborative work between Matt Duckham, Lars Kulik, Antony Galton, and Mike Worboys.
*
*/
package signalprocesser.voronoi.statusstructure.binarysearchtreeimpl;
import signalprocesser.voronoi.VoronoiShared;
import signalprocesser.voronoi.eventqueue.EventQueue;
import signalprocesser.voronoi.eventqueue.VSiteEvent;
import signalprocesser.voronoi.statusstructure.AbstractStatusStructure;
import signalprocesser.voronoi.statusstructure.VLinkedNode;
public class BSTStatusStructure extends AbstractStatusStructure {
/* ***************************************************** */
// Variables
public static int uniqueid = 1;
private VNode rootnode = null;
/* ***************************************************** */
// Core Methods
public boolean isStatusStructureEmpty() {
return ( rootnode==null );
}
public VNode getRootNode() { return rootnode; }
public void setRootNode( VSiteEvent siteevent ) {
setRootNode( new VLeafNode(siteevent) );
}
protected void setRootNode( VNode node ) {
rootnode = node;
rootnode.setParent(null);
if ( rootnode instanceof VInternalNode ) {
((VInternalNode)rootnode).setDepthForRootNode();
}
}
public VLinkedNode insertNode(VLinkedNode _nodetosplit, VSiteEvent siteevent) {
VLeafNode nodetosplit = (VLeafNode) _nodetosplit;
// Create new leaf and two new internal nows
VLeafNode newleaf = new VLeafNode(siteevent);
VInternalNode top = new VInternalNode();
VInternalNode bottom = new VInternalNode();
// Link top to parent node
if ( nodetosplit.getParent()==null ) {
setRootNode( top );
} else {
if ( nodetosplit.getParent().getLeft()==nodetosplit ) {
nodetosplit.getParent().setLeft( top );
} else if ( nodetosplit.getParent().getRight()==nodetosplit ) {
nodetosplit.getParent().setRight( top );
} else {
throw new RuntimeException("Neither child matched suggested parent for attaching new branch - linking error");
}
}
// Link Tree
top.setLeft( bottom );
top.setRight( nodetosplit.cloneLeafNode() );
bottom.setLeft( nodetosplit );
bottom.setRight( newleaf );
// Set internal values
top .setSiteEvents(siteevent, nodetosplit.siteevent);
bottom.setSiteEvents(nodetosplit.siteevent, siteevent);
// Double-linked List
VLeafNode leaf1 = (VLeafNode)bottom.getLeft();
VLeafNode leaf3 = (VLeafNode)top.getRight();
VLeafNode tmp = (VLeafNode)nodetosplit.getNext();
leaf1.setNext( newleaf );
newleaf.setNext( leaf3 );
leaf3.setNext( tmp );
return newleaf;
}
public void removeNode(EventQueue eventqueue, VLinkedNode _toremove) {
VLeafNode toremove = (VLeafNode) _toremove;
VInternalNode parent = toremove.getParent();
// Unlink Double-Linked List Structure
if ( toremove.getPrev()==null ) {
toremove.setNext(null);
} else {
toremove.getPrev().setNext( toremove.getNext() );
}
// Determine which branch we're keeping, also update v1/v2 nodes
VNode tosave;
if ( parent.getLeft()==toremove ) {
tosave = parent.getRight();
// Update v2 node
VInternalNode ces = getPredecessor( parent );
ces.v2 = parent.v2;
} else if ( parent.getRight()==toremove ) {
tosave = parent.getLeft();
// Update v2 node
VInternalNode ces = getSuccessor( parent );
ces.v1 = parent.v1;
} else {
throw new RuntimeException("Neither child matched suggested parent - linking error");
}
// Re-link branch so that parent isn't involved anymore
if ( parent.getParent()==null ) {
throw new RuntimeException("Parent is null - error; parent=#" + parent.id);
} else if ( parent.getParent().getLeft()==parent ) {
parent.getParent().setLeft( tosave );
} else if ( parent.getParent().getRight()==parent ) {
parent.getParent().setRight( tosave );
} else {
throw new RuntimeException("Neither child matched suggested parent's parent - linking error");
}
}
private VInternalNode getSuccessor(VInternalNode x){
VInternalNode y = x.getParent();
while (( y!=null )&&( x==y.getRight() )) {
x = y;
y = y.getParent();
}
return y;
}
private VInternalNode getPredecessor(VInternalNode x){
VInternalNode y = x.getParent();
while (( y!=null )&&( x==y.getLeft() )) {
x = y;
y = y.getParent();
}
return y;
}
public VLinkedNode getNodeAboveSiteEvent( int siteevent_x , int sweepline ) {
if ( rootnode==null ) {
return null;
} else if ( rootnode.isLeafNode() ) {
return (VLeafNode)rootnode;
} else {
VInternalNode internalnode = (VInternalNode) rootnode;
do {
VSiteEvent v1 = internalnode.v1;
VSiteEvent v2 = internalnode.v2;
// Calculate a, b and c of the parabola
v1.calcParabolaConstants(sweepline);
v2.calcParabolaConstants(sweepline);
// Determine where two parabola meet
double intersects[] = VoronoiShared.solveQuadratic(v2.a-v1.a, v2.b-v1.b, v2.c-v1.c);
// Determine whether to go left or right
VNode currnode;
if ( siteevent_x<=intersects[0] ) {
currnode = internalnode.getLeft();
} else {
currnode = internalnode.getRight();
}
// Determine if at a leaf yet
if ( currnode.isLeafNode() ) {
return (VLeafNode) currnode;
}
// Otherwise, continue
internalnode = (VInternalNode)currnode;
} while ( true );
}
}
// Function only used by test functions
public VLinkedNode getHeadNode() {
if ( rootnode==null ) {
return null;
} else if ( rootnode.isLeafNode() ) {
return (VLinkedNode) rootnode;
} else {
VInternalNode internalnode = (VInternalNode) rootnode;
VNode currnode;
do {
currnode = internalnode.getLeft();
// Determine if at a leaf yet
if ( currnode.isLeafNode() ) {
break;
}
// Otherwise, continue
internalnode = (VInternalNode)currnode;
} while ( true );
// Check leaf is the very left
VLeafNode leafnode = (VLeafNode) currnode;
if ( leafnode.getPrev()!=null ) {
throw new RuntimeException("Leftmost element of tree is not leftmost element of doubly-linked list - linking error");
}
// Return value if correct
return leafnode;
}
}
/* ***************************************************** */
// Debug toString() Method
public String toString() {
return "| " + strDoublyLinkedList(-1) + "\n* " + strTreeStructure(rootnode, 1);
}
public String strDoublyLinkedList(int sweepline) {
VLeafNode leafnode = (VLeafNode) getHeadNode();
if ( leafnode==null ) {
return "Doubly-linked list is empty";
} else {
StringBuffer buffer = new StringBuffer();
boolean isfirst = true;
do {
if ( isfirst ) {
isfirst = false;
} else {
buffer.append(" ");
if ( sweepline>0 ) {
VSiteEvent v1 = leafnode.getPrev().siteevent;
VSiteEvent v2 = leafnode.siteevent;
v1.calcParabolaConstants(sweepline);
v2.calcParabolaConstants(sweepline);
// Determine where two parabola meet
double intersects[] = VoronoiShared.solveQuadratic(v2.a-v1.a, v2.b-v1.b, v2.c-v1.c);
buffer.append("[" + ((int)intersects[0]) + "]");
}
buffer.append("-> ");
}
buffer.append("Leaf (" + leafnode.siteevent.getX() + "," + leafnode.siteevent.getY() + ") #" + leafnode.id + "/" + leafnode.siteevent.getID());
} while ( (leafnode=(VLeafNode)leafnode.getNext())!=null );
return buffer.toString();
}
}
private String strTreeStructure(VNode node, int depth) {
if ( node==null ) {
return "Tree is empty (null root)";
} else if ( node instanceof VLeafNode ) {
VLeafNode leafnode = (VLeafNode) node;
return "Leaf #" + leafnode.id + "/" + leafnode.siteevent.getID() + " (" + leafnode.siteevent.getX() + "," + leafnode.siteevent.getY() + ") (parent=" + (leafnode.parent==null?"null":leafnode.parent.id) + ",prev=" + (leafnode.getPrev()==null?"null":((VLeafNode)leafnode.getPrev()).id+"/"+leafnode.getPrev().siteevent.getID()) + ",next=" + (leafnode.getNext()==null?"null":((VLeafNode)leafnode.getNext()).id+"/"+leafnode.getNext().siteevent.getID()) + ")";
} else if ( node instanceof VInternalNode ) {
VInternalNode internalnode = (VInternalNode) node;
return "Node #" + internalnode.id + "(v1=" + internalnode.v1.getID() + ",v2=" + internalnode.v2.getID() + ") (parent=" + (internalnode.parent==null?"null":internalnode.parent.id) + "):\n"
+ printGap(depth) + "* Left " + strTreeStructure(internalnode.getLeft(), depth+1) + "\n"
+ printGap(depth) + "* Right " + strTreeStructure(internalnode.getRight(), depth+1);
} else {
throw new RuntimeException("Unknown node type; " + node.getClass().getName());
}
}
private String printGap(int gap) {
String tmp = "";
for ( int x=0 ; x