package ddddbb.comb;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Vector;
import ddddbb.game.Main;
import ddddbb.math.Camera4d;
import ddddbb.math.HalfSpace;
import ddddbb.math.Point;
public abstract class ACell {
abstract public List<? extends BCell> facets();
abstract public Point normal();
// abstract public Vector<HalfSpace> halfSpaces();
abstract public ALocation location();
public int dim() { return location().dim(); }
public int spaceDim() { return location().spaceDim(); }
public Point o() { return location().o(); }
public final Equality<ACell> relaxed = new Equality<ACell>() {
@Override
public boolean equal(ACell a, ACell b) {
if ( a.dim() != b.dim() ) { return false; }
if (a instanceof OCell) {
OCell oa = (OCell) a;
OCell ob = (OCell) b;
return oa.cell() == ob.cell();
};
return a.location() == b.location();
//if ( a.location().equals(b.location())) { return true; }
// if ( a.dim() == 0 ) {
// return a.location().o().minus(b.location().o()).len() < Param.ERR;
// }
// return relaxed.setEqual(a.facets(),b.facets());
}
};
public final Equality<ACell> strict = new Equality<ACell>() {
@Override
public boolean equal(ACell a, ACell b) {
if ( a.dim() != b.dim() ) { return false; }
if ( a.location() == b.location() ) { return true; }
return strict.setEqual(a.facets(),b.facets());
}
};
public final Equality<ACell> locationStrict = new Equality<ACell>() {
@Override
public boolean equal(ACell a, ACell b) {
return a.location() == b.location();
}
};
/* Checks whether this cell is closed */
public boolean checkClosed() {
if ( ! Main.debug.isSelected() ) { return true; }
if ( dim() == 0 ) { return true; }
if ( dim() == 1 ) { return facets().size() == 2; }
for (ACell fm1: facets()) {
if (!fm1.checkClosed()) { assert false; }
for (ACell fm2: fm1.facets()) {
//For each facet of a facet there must be exactly
//one other facet of a facet with the same location
int refcount = 1;
for (ACell gm1: facets()) {
if (gm1 == fm1) { continue; }
for (ACell gm2: gm1.facets()) {
if (relaxed.equal(gm2,fm2)) { refcount++; }
}
}
if (refcount != 2) {
assert false :
"Did not find adjacent facet for " + fm1 +
" at " + fm2 +
" in " + this;
}
}
}
return true;
}
/* checks that each facets is contained in at most one parent
*/
public boolean checkFaces() {
for ( BCell f : facets() ) {
assert f!=null;
assert f.parent() != null;
//if (f.parent() == null) { f.parent = this; }
assert f.parent() == this;
}
return true;
}
public static boolean checkFaces(Collection<? extends ACell> tops) {
for ( ACell t : tops ) {
assert t.checkFaces();
}
return true;
}
/**
* Is a decoration to temporarily store visibility information
* Indicates whether the outer direction of the facet is directed to the camera
*/
public boolean facing = true;
public static class CompareByOcclusion implements Comparator<ACell> {
/** Returns >0 if d1 is in front of (or greater than) d2,
* returns 0 if neither is in front of the other
* returns <0 if d2 is in front of (or greater than) d1
*/
public int compare(ACell d1, ACell d2) {
assert d1.facing;
assert d2.facing;
//TODO creating a new HalfSpace for every compare is not performant.
HalfSpace h2 = new HalfSpace(d2.o(),d2.normal());
HalfSpace h1 = new HalfSpace(d1.o(),d1.normal());
int sideof1 = h2.side(d1);
int sideof2 = h1.side(d2);
if (sideof1 == Cell.CONTAINED) {
assert sideof2 == Cell.CONTAINED;
//System.out.println((DLocation)d1.location() + "?" + (DLocation)d2.location() + ":CONTAINED,CONTAINED:0");
return 0;
}
//OUTER is 1, SPLITTED is 0, INNER is -1
//returns in the range of -2 ... 2
//System.out.println(d1 + "?" + d2 + ":" + sideof1 + "," + sideof2 + ":" + (-sideof1+sideof2));
return sideof1 - sideof2;
}
}
public static <S,T extends S> void posort(Vector<T> l,Comparator<S> c) {
if (l.size()==0 || l.size() ==1) { return; }
int last = l.size()-1;
T em = l.get(last);
l.remove(last);
//use l instead of Vector<T> left = new Vector<T>();
Vector<T> right = new Vector<T>();
Vector<T> mid = new Vector<T>();
int i=0;
for (T e : l) {
int cmp = c.compare(e,em);
if (cmp==0) mid.add(e);
else if (cmp>0) right.add(e);
else if (cmp<0) {
l.set(i,e);
i++;
}
else assert false;
}
l.setSize(i);
//System.out.println(left.size() + "," + mid.size() + "," + right.size());
posort(l,c);
posort(right,c);
posort(mid,c);
for (T e: mid) l.add(e);
l.add(em);
for (T e: right) l.add(e);
}
public static <S,T extends S> void tsort(Vector<T> l,Comparator<S> c) {
HashMap<T,HashSet<T>> predecessors = new HashMap<T,HashSet<T>>();
for (T t: l) predecessors.put(t, new HashSet<T>());
int n = l.size();
for (int i=0;i<n;i++) {
T t1 = l.get(i);
for (int j=i+1;j<n;j++) {
T t2 = l.get(j);
int cmp = c.compare(t1, t2);
if (cmp<0) {//t1<t2
predecessors.get(t2).add(t1);
}
if (cmp>0) {//t1>t2
predecessors.get(t1).add(t2);
}
}
}
for (T t1: predecessors.keySet()) {
for (T t2: predecessors.get(t1)) {
assert c.compare(t2, t1) < 0;
}
}
l.clear();
while (l.size()<n) {
boolean isCyclic = true;
for (T t: predecessors.keySet()) {
if (predecessors.get(t).size() == 0) {
isCyclic = false;
l.add(t);
}
}
if (isCyclic) {
System.out.println("Cycle detected:");
T t = predecessors.keySet().iterator().next();
Vector<T> prev = new Vector<T>();
while (!prev.contains(t)) {
prev.add(t);
t = predecessors.get(t).iterator().next();
}
int i=prev.indexOf(t);
prev.add(t);
for (;i<prev.size()-1;i++) {
t = prev.get(i);
T tn = prev.get(i+1);
System.out.println(t + ", " + t.hashCode() + ", this > next: " + (c.compare(t, tn)>0) + "(" + c.compare(t,tn) + ")");
ACell cell = (ACell) t;
ACell celln = (ACell) tn;
System.out.println(cell.o() + ":" + cell.normal() + " > " + celln.o() + ":" + celln.normal());
}
assert false;
}
for (T t: predecessors.keySet()) {
for (T s:l) predecessors.get(t).remove(s);
}
for (T s: l) predecessors.remove(s);
}
}
// private static class CompareByOcclusion implements Comparator<OCell> {
// public int compare(OCell o1, OCell o2) {
// assert o1.visible;
// assert o2.visible;
// assert o1.cell().location.space != null;
// assert o2.cell().location.space != null;
// assert o1.orientation() != 0;
// assert o2.orientation() != 0;
// if ( o1.cell().location.space.side(o2.cell(),o1.orientation) == Cell.OUTER ) {
// return -1;
// }
// if ( o2.cell().location.space.side(o1.cell(),o2.orientation) == Cell.OUTER ) {
// return 1;
// }
// return 0;
// }
// }
public static void sortByOcclusion(Vector<? extends ACell> v) {
tsort(v,new CompareByOcclusion());
// for (ACell dc: v) {
// System.out.println(dc);
// }
}
public boolean isFacing(Camera4d c4) {
return c4.facedBy(this);
}
public void setFacing(Camera4d c4) {
facing = c4.facedBy(this);
}
public Vector<ALocation> getPointLocations() {
return getPointLocations(true);
}
public Vector<ALocation> getPointLocations(boolean inclOfInternal) {
Vector<ALocation> locations = new Vector<ALocation>();
addPointLocationsToList(locations,inclOfInternal);
return locations;
}
public void addPointLocationsToList(Collection<ALocation> locations, boolean inclOfInternal) {
if ( dim() == 0 ) {
for ( ALocation l : locations ) {
if ( l == location() ) { return; }
}
locations.add(location());
}
else {
for ( ACell f : facets() ) {
f.addPointLocationsToList(locations,inclOfInternal);
}
}
}
// public String toString() {
// if (dim()==0) {
// return location().o().toString();
// }
// return facets().toString();
// }
}