package edu.stanford.rsl.conrad.geometry.shapes.compound;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import edu.stanford.rsl.conrad.geometry.AbstractCurve;
import edu.stanford.rsl.conrad.geometry.AbstractShape;
import edu.stanford.rsl.conrad.geometry.shapes.simple.PointND;
import edu.stanford.rsl.conrad.geometry.transforms.Transform;
import edu.stanford.rsl.conrad.numerics.SimpleOperators;
public class LinearOctree extends CompoundShape {
/**
*
*/
private static final long serialVersionUID = 8039686125389239903L;
protected CompoundShape octant1 = new CompoundShape();
protected CompoundShape octant2 = new CompoundShape();
protected CompoundShape octant3 = new CompoundShape();
protected CompoundShape octant4 = new CompoundShape();
protected CompoundShape octant5 = new CompoundShape();
protected CompoundShape octant6 = new CompoundShape();
protected CompoundShape octant7 = new CompoundShape();
protected CompoundShape octant8 = new CompoundShape();
private PointND center;
private int dimension = -1;
public LinearOctree(PointND center){
this.center = center;
}
public LinearOctree (PointND min, PointND max){
this(new PointND(SimpleOperators.add(min.getAbstractVector(), max.getAbstractVector()).dividedBy(2)));
this.min = min;
this.max = max;
}
public LinearOctree (PointND min, PointND max, double random){
this(new PointND(SimpleOperators.add(min.getAbstractVector(), SimpleOperators.subtract(min.getAbstractVector(), max.getAbstractVector()).multipliedBy(random))));
this.min = min;
this.max = max;
}
public LinearOctree (PointND min, PointND max, PointND center){
this(center);
this.min = min;
this.max = max;
}
public LinearOctree(LinearOctree lo){
super(lo);
dimension = lo.dimension;
center = (lo.center!=null) ? lo.center.clone() : null;
octant1 = (lo.octant1!=null) ? (CompoundShape)lo.octant1.clone() : null;
octant2 = (lo.octant2!=null) ? (CompoundShape)lo.octant2.clone() : null;
octant3 = (lo.octant3!=null) ? (CompoundShape)lo.octant3.clone() : null;
octant4 = (lo.octant4!=null) ? (CompoundShape)lo.octant4.clone() : null;
octant5 = (lo.octant5!=null) ? (CompoundShape)lo.octant5.clone() : null;
octant6 = (lo.octant6!=null) ? (CompoundShape)lo.octant6.clone() : null;
octant7 = (lo.octant7!=null) ? (CompoundShape)lo.octant7.clone() : null;
octant8 = (lo.octant8!=null) ? (CompoundShape)lo.octant8.clone() : null;
}
@Override
protected synchronized void init(){
if (dirty){
min = new PointND(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
max = new PointND(-Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE);
for (AbstractShape s: createToDoList()){
min.updateIfLower(s.getMin());
max.updateIfHigher(s.getMax());
}
super.generateBoundingPlanes();
dirty = false;
}
}
@Override
public boolean add(AbstractShape shape){
boolean revan = false;
if (shape.isBounded()){
if (dimension == -1) {
dimension = shape.getDimension();
}
if (shape.getDimension() != dimension){
throw new RuntimeException("Dimensions do not match. My dimension is " + dimension + ". You added a shape of dimension " + shape.getDimension() + "!");
}
int dim [] = new int[3];
for (int i = 0; i < 3; i++){
if (shape.getMin().get(i) > center.get(i)){
// completely right from the center
dim[i] = 2;
} else {
if ((shape.getMax().get(i) > center.get(i))){
// overlaps the center
dim[i] = 1;
} else {
// completely left from the center;
dim[i] = 0;
}
}
}
//System.out.println(center + " " + shape.getMin() + " " + shape.getMax() + " " + dim[0] + dim[1] + dim[2]);
if (testOctant(1, dim)) revan = octant1.add(shape);
else if (testOctant(2, dim)) revan = octant2.add(shape);
else if (testOctant(3, dim)) revan = octant3.add(shape);
else if (testOctant(4, dim)) revan = octant4.add(shape);
else if (testOctant(5, dim)) revan = octant5.add(shape);
else if (testOctant(6, dim)) revan = octant6.add(shape);
else if (testOctant(7, dim)) revan = octant7.add(shape);
else revan = octant8.add(shape);
dirty = true;
} else {
throw new RuntimeException("Cannot add an unbounded shape");
}
return revan;
}
@Override
public PointND getMin(){
if (dirty){
init();
}
return super.getMin();
}
@Override
public PointND getMax(){
if (dirty){
init();
}
return super.getMax();
}
/**
* Tests whether the dimension code is in the respective octant.<br><BR>
* The dimension code is a three dimensional int [] with the following coding for each dimension:<BR>
* <li>
* 0: object is completely left of the center axis, i.e. numerically smaller
* </li>
* <li> 1: object overlaps the center axis
* </li>
* <li> 2: object is completely right of the center axis, i.e. numerically greater
* </li><br><BR>
* This figure shows the eight octants in 3D:<BR><BR>
* <img src="http://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Octant_numbers.svg/565px-Octant_numbers.svg.png" width =480 height=480>
*
*
*
* @param i
* @param dim
* @return
*/
private boolean testOctant(int i, int ... dim){
boolean revan = false;
switch (i){
case 1:
revan = (dim[0]>0)&&(dim[1]>0)&&(dim[2]>0);
break;
case 2:
revan = (dim[0]>0)&&(dim[1]<2)&&(dim[2]>0);
break;
case 3:
revan = (dim[0]<2)&&(dim[1]<2)&&(dim[2]>0);
break;
case 4:
revan = (dim[0]<2)&&(dim[1]>0)&&(dim[2]>0);
break;
case 5:
revan = (dim[0]>0)&&(dim[1]>0)&&(dim[2]<2);
break;
case 6:
revan = (dim[0]>0)&&(dim[1]<2)&&(dim[2]<2);
break;
case 7:
revan = (dim[0]<2)&&(dim[1]<2)&&(dim[2]<2);
break;
case 8:
revan = (dim[0]<2)&&(dim[1]>0)&&(dim[2]<2);
break;
default:
revan = false;
}
return revan;
}
@Override
public void applyTransform(Transform t) {
min = t.transform(min);
max = t.transform(max);
center = t.transform(center);
ArrayList<AbstractShape> list = createToDoList();
for (AbstractShape s : list) {
s.applyTransform(t);
}
}
@Override
public PointND evaluate(PointND u) {
return null;
}
@Override
public int getDimension() {
return dimension;
}
@Override
public int getInternalDimension() {
return octant1.size() +
octant2.size() +
octant3.size() +
octant4.size() +
octant5.size() +
octant6.size() +
octant7.size() +
octant8.size()
;
}
@Override
public ArrayList<PointND> intersect(AbstractCurve other) {
if (dirty){
init();
}
ArrayList<PointND> hits = new ArrayList<PointND>();
if (octant1.size() > 0) {
if (octant1.getHitsOnBoundingBox(other).size() > 0){
for (AbstractShape s: octant1){
hits.addAll(s.intersect(other));
}
}
}
if (octant2.size() > 0) {
if (octant2.getHitsOnBoundingBox(other).size() > 0){
for (AbstractShape s: octant2){
hits.addAll(s.intersect(other));
}
}
}
if (octant3.size() > 0) {
if (octant3.getHitsOnBoundingBox(other).size() > 0){
for (AbstractShape s: octant3){
hits.addAll(s.intersect(other));
}
}
}
if (octant4.size() > 0) {
if (octant4.getHitsOnBoundingBox(other).size() > 0){
for (AbstractShape s: octant4){
hits.addAll(s.intersect(other));
}
}
}
if (octant5.size() > 0) {
if (octant5.getHitsOnBoundingBox(other).size() > 0){
for (AbstractShape s: octant5){
hits.addAll(s.intersect(other));
}
}
}
if (octant6.size() > 0) {
if (octant6.getHitsOnBoundingBox(other).size() > 0){
for (AbstractShape s: octant6){
hits.addAll(s.intersect(other));
}
}
}
if (octant7.size() > 0) {
if (octant7.getHitsOnBoundingBox(other).size() > 0){
for (AbstractShape s: octant7){
hits.addAll(s.intersect(other));
}
}
}
if (octant8.size() > 0) {
if (octant8.getHitsOnBoundingBox(other).size() > 0){
for (AbstractShape s: octant8){
hits.addAll(s.intersect(other));
}
}
}
return hits;
}
@Override
public boolean isBounded() {
return true;
}
/**
* Creates a list with each shape in this LineaerOctree appearing exactly once.
* @return
*/
private ArrayList<AbstractShape> createToDoList(){
ArrayList<AbstractShape> toDo = new ArrayList<AbstractShape>();
for (AbstractShape s: octant1) {
toDo.add(s);
}
for (AbstractShape s: octant2) {
toDo.add(s);
}
for (AbstractShape s: octant3) {
toDo.add(s);
}
for (AbstractShape s: octant4) {
toDo.add(s);
}
for (AbstractShape s: octant5) {
toDo.add(s);
}
for (AbstractShape s: octant6) {
toDo.add(s);
}
for (AbstractShape s: octant7) {
toDo.add(s);
}
for (AbstractShape s: octant8) {
toDo.add(s);
}
return toDo;
}
@Override
public PointND[] getRasterPoints(int number){
ArrayList<PointND[]> lists = new ArrayList<PointND[]>();
ArrayList<AbstractShape> toDo = createToDoList();
int sum = 0;
for (AbstractShape s: toDo) {
PointND [] pts = s.getRasterPoints(number/ toDo.size());
sum += pts.length;
lists.add(pts);
}
PointND [] points = new PointND[sum];
int increment = 0;
for (PointND [] pts : lists){
System.arraycopy(pts, 0, points, increment, pts.length);
increment += pts.length;
}
return points;
}
public String toString(){
return "Octree at " + center + "\n" +
"Octant 1: " + octant1.size() + " " + octant1.getMin() + " " + octant1.getMax()+ "\n" +
"Octant 2: " + octant2.size() + " " + octant2.getMin() + " " + octant2.getMax() + "\n" +
"Octant 3: " + octant3.size() + " " + octant3.getMin() + " " + octant3.getMax()+ "\n" +
"Octant 4: " + octant4.size() + " " + octant4.getMin() + " " + octant4.getMax()+ "\n" +
"Octant 5: " + octant5.size() + " " + octant5.getMin() + " " + octant5.getMax()+ "\n" +
"Octant 6: " + octant6.size() + " " + octant6.getMin() + " " + octant6.getMax()+ "\n" +
"Octant 7: " + octant7.size() + " " + octant7.getMin() + " " + octant7.getMax()+ "\n" +
"Octant 8: " + octant8.size() + " " + octant8.getMin() + " " + octant8.getMax()+ "\n" +
"Octant 1: " + octant1.toString() + "\n" +
"Octant 2: " + octant2.toString() + "\n" +
"Octant 3: " + octant3.toString() + "\n" +
"Octant 4: " + octant4.toString() + "\n" +
"Octant 5: " + octant5.toString() + "\n" +
"Octant 6: " + octant6.toString() + "\n" +
"Octant 7: " + octant7.toString() + "\n" +
"Octant 8: " + octant8.toString() + "\n";
}
public int size() {
return createToDoList().size();
}
@Override
public boolean addAll(Collection<? extends AbstractShape> arg0) {
boolean revan = false;
for (AbstractShape s: arg0){
if (add(s)){
revan = true;
}
}
dirty = true;
return revan;
}
@Override
public void clear() {
octant1.clear();
octant2.clear();
octant3.clear();
octant4.clear();
octant5.clear();
octant6.clear();
octant7.clear();
octant8.clear();
dirty = true;
}
@Override
public boolean contains(Object arg0) {
return createToDoList().contains(arg0);
}
@Override
public boolean containsAll(Collection<?> arg0) {
return createToDoList().containsAll(arg0);
}
@Override
public boolean isEmpty() {
return createToDoList().isEmpty();
}
@Override
public Iterator<AbstractShape> iterator() {
return createToDoList().iterator();
}
@Override
public boolean remove(Object arg0) {
boolean revan = false;
revan = revan || octant1.remove(arg0);
revan = revan || octant2.remove(arg0);
revan = revan || octant3.remove(arg0);
revan = revan || octant4.remove(arg0);
revan = revan || octant5.remove(arg0);
revan = revan || octant6.remove(arg0);
revan = revan || octant7.remove(arg0);
revan = revan || octant8.remove(arg0);
if (revan) dirty = true;
return revan;
}
@Override
public boolean removeAll(Collection<?> arg0) {
boolean revan = false;
for (Object o : arg0){
revan = revan || remove(o);
}
if (revan) dirty = true;
return revan;
}
@Override
public boolean retainAll(Collection<?> arg0) {
boolean revan = false;
for (AbstractShape s: this){
if (!arg0.contains(s)){
revan = revan || remove(s);
}
}
if (revan) dirty = true;
return revan;
}
@Override
public Object[] toArray() {
return createToDoList().toArray();
}
@Override
public <T> T[] toArray(T[] arg0) {
return createToDoList().toArray(arg0);
}
@Override
public AbstractShape clone() {
return new LinearOctree(this);
}
}
/*
* Copyright (C) 2010-2014 Andreas Maier
* CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
*/