+ /*
+ public int hashCode() {
+ throw new Error();
+ }
+ */
+ public void kdremove() {
+ if (!inserted) return;
+ inserted = false;
+ try { kd.delete(new double[]{p.x,p.y,p.z}); } catch (Exception e) { }
+ }
+ public void kdinsert() {
+ if (inserted) return;
+ inserted = true;
+ try { kd.insert(new double[]{p.x,p.y,p.z},this); } catch (Exception e) { throw new Error(e); }
+ }
+
+ public float score() { return oldscore; }
+ public void unscore() {
+ if (watch == null) return;
+ watch.watch_x -= x;
+ watch.watch_y -= y;
+ watch.watch_z -= z;
+ watch.watch_count--;
+ if (watch.watch_count==0) {
+ watch.watch_x = 0;
+ watch.watch_y = 0;
+ watch.watch_z = 0;
+ }
+ watch = null;
+ }
+ public V partner() { return watch==null ? this : watch; }
+ public V watchback() { return watch_count==0 ? partner() :
+ newP(watch_x/watch_count, watch_y/watch_count, watch_z/watch_count).register(); }
+ public void rescore() {
+ if (score_against == null) return;
+
+ score -= oldscore;
+ oldscore = 0;
+
+ if (watch != null) unscore();
+ V po = this;
+ if (watch == null) {
+ watch = (V)score_against.nearest(po);
+
+ // don't attract to vertices that face the other way
+ if (watch.norm().dot(norm()) < 0) {
+ watch = null;
+ } else {
+ watch.watch_x += po.x;
+ watch.watch_y += po.y;
+ watch.watch_z += po.z;
+ watch.watch_count++;
+ }
+ }
+
+ double s1, s2;
+ if (watch_count==0) s1 = 0;
+ else s1 = this.distance(watch_x/watch_count, watch_y/watch_count, watch_z/watch_count);
+ s2 = watch==null ? 0 : po.distance(watch);
+ oldscore = (float)(s1 + s2);
+ score += oldscore;
+ }
+
+
+ /** does NOT update bound pairs! */
+ public boolean transform(M m) {
+ // FIXME: screws up kdtree
+ // FIXME: screws up hashmap
+ unscore();
+ try {
+ if (ps.get(this.p)==null) throw new Error();
+ ps.remove(this.p);
+ float newx = m.a*p.x + m.b*p.y + m.c*p.z + m.d;
+ float newy = m.e*p.x + m.f*p.y + m.g*p.z + m.h;
+ float newz = m.i*p.x + m.j*p.y + m.k*p.z + m.l;
+ this.x = newx;
+ this.y = newy;
+ this.z = newz;
+ this.p = new P(newx, newy, newz);
+ // FIXME: what if we move onto exactly where another point is?
+ ps.put(this.p,(V)this);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ rescore();
+ boolean good = true;
+ /*
+ for(T t : ts) {
+ for(E e = this.e; ;) {
+ if (e.intersects(t)) { good = false; break; }
+ e = e.pair.next;
+ if (e == this.e) break;
+ }
+ }
+ */
+ /*
+ if (t==this.t) continue;
+ if (this.intersects(t)) good = false;
+ }
+ */
+ return good;
+ }
+ public boolean move(Vec v) {
+ M m = new M(v);
+ V p = this;
+ boolean good = true;
+ do {
+ good &= p.transform(m);
+ v = v.times(binding); // bleh wrong
+ p = p.bound_to;
+ } while (p != this);
+ return good;
+ }
+
+ public E makeE(V p2) {
+ E e = getE(p2);
+ if (e != null) return e;
+ e = p2.getE(this);
+ if (this.e == null && p2.e == null) return this.e = new E(this, p2);
+ if (this.e == null && p2.e != null) return p2.makeE(this).pair;
+ return new E(getFreeIncident(), p2);
+ }
+
+ public E getFreeIncident() {
+ E ret = getFreeIncident(e, e);
+ if (ret != null) return ret;
+ ret = getFreeIncident(e.pair.next, e.pair.next);
+ if (ret == null) throw new Error("unable to find free incident to " + this);
+ return ret;
+ }
+
+ public E getFreeIncident(E start, E before) {
+ E e = start;
+ do {
+ if (e.pair.p2 == this && e.pair.t == null && e.pair.next.t == null) return e.pair;
+ e = e.pair.next;
+ } while(e != before);
+ return null;
+ }
+
+ public E getE(V p2) {
+ p2 = p2.register();
+ E e = this.e;
+ do {
+ if (e==null) return null;
+ if (e.p1 == this && e.p2 == p2) return e;
+ e = e.pair.next;
+ } while (e!=this.e);
+ return null;
+ }
+
+ public boolean isBoundTo(V p) {
+ p = p.register();
+ V px = p;
+ do {
+ if (px==this) return true;
+ px = px.bound_to;
+ } while(px != p);
+ return false;
+ }
+
+ public void unbind() { bound_to = this; binding = new M(); }
+ public void bind(V p) { bind(p, new M()); }
+ public void bind(V p, M binding) {
+ p = p.register();
+ if (isBoundTo(p)) return;
+ V temp_bound_to = p.bound_to;
+ M temp_binding = p.binding;
+ p.bound_to = this.bound_to;
+ p.binding = binding.times(this.binding); // FIXME: may have order wrong here
+ this.bound_to = temp_bound_to;
+ this.binding = temp_binding.times(temp_binding); // FIXME: may have order wrong here
+ }
+ public Vec norm() {
+ Vec norm = new Vec(0, 0, 0);
+ E e = this.e;
+ do {
+ if (e.t != null) norm = norm.plus(e.t.norm().times((float)e.prev.angle()));
+ e = e.pair.next;
+ } while(e != this.e);
+ return norm.norm();
+ }
+ V bound_to = this;
+
+ int watch_count;
+ float watch_x;
+ float watch_y;
+ float watch_z;
+ V watch;
+ E e; // some edge *leaving* this point
+ M binding = new M();
+ float oldscore = 0;
+ boolean inserted = false;
+ }
+
+ public class P {
+ float x, y, z;
+ public P(float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
+ public float distance(P p) { return distance(p.x, p.y, p.z); }
+ public float distance(float ox, float oy, float oz) { return (float)Math.sqrt((x-ox)*(x-ox)+(y-oy)*(y-oy)+(z-oz)*(z-oz)); }
+ public V register() { V v = ps.get(this); return v==null ? new V(this) : v; }
+ public P times(M m) { return m.times(this); }
+ public Vec minus(P p) { return new Vec(x-p.x, y-p.y, z-p.z); }
+ public P plus(Vec v) { return newP(x+v.x, y+v.y, z+v.z); }
+ public boolean equals(Object o) { return o!=null && (o instanceof P) && ((P)o).x==x && ((P)o).y==y && ((P)o).z==z; }
+ public void glVertex(GL gl) { _glVertex(gl); }
+ private void _glVertex(GL gl) { gl.glVertex3f(x, y, z); }
+ public String toString() { return "("+x+","+y+","+z+")"; }
+ // FIXME: moving a point alters its hashCode