created mirror
This commit is contained in:
commit
6764738d92
600 files changed
+87187
No files matched your search
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
LevelRaster.zob
|
||||
*
|
||||
* Copyright (c) 2000, Ted Nelson and Tuomas Lukka
|
||||
*
|
||||
* You may use and distribute under the terms of either the GNU Lesser
|
||||
* General Public License, either version 2 of the license or,
|
||||
* at your choice, any later version. Alternatively, you may use and
|
||||
* distribute under the terms of the XPL.
|
||||
*
|
||||
* See the LICENSE.lgpl and LICENSE.xpl files for the specific terms of
|
||||
* the licenses.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the README
|
||||
* file for more details.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Written by Benjamin Fallenstein
|
||||
*/
|
||||
|
||||
package org.gzigzag;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
|
||||
/** A raster for multiple-level structures.
|
||||
* Interprets two dimensions as many-to-many relationship. Also interprets
|
||||
* third dimension as one-to-one relationships which are shown the same way
|
||||
* as the many-to-many relationships. For many-to-many relationships with
|
||||
* link cells, shows the link cells.
|
||||
*/
|
||||
|
||||
/* Some XXX'es:
|
||||
- fix the damn occupiedbreadth bug!!!
|
||||
- should move the parentlinks in the right place
|
||||
- primitive actions for leveled visualization (including move)
|
||||
- loose linking so that new links can be created? or: new link creates
|
||||
new target, too? or maybe: in loose mode, links w/o target are shown as
|
||||
that, links w/o target? yeah, way to go. (anyways, should new link also
|
||||
create new target or not? pbly yes, eh?)
|
||||
- gaps that shrink; think depthwise gapping through again
|
||||
*/
|
||||
|
||||
public class LevelView implements FlobView, ZOb {
|
||||
public static final String rcsid = "$Id$";
|
||||
public static boolean dbg = false ;
|
||||
static final void p(String s) { if(dbg) System.out.println(s); }
|
||||
static final void pa(String s) { System.out.println(s); }
|
||||
|
||||
STRUCTPARAMS {
|
||||
/** The size factor applied to cells. */
|
||||
float initmul
|
||||
= (float)1.6;
|
||||
|
||||
/** How many recursion levels shall we render (on both sides of the
|
||||
* root)? */
|
||||
int maxdepth
|
||||
= 4;
|
||||
|
||||
/** The breadth distance between neigbouring cells. */
|
||||
int bgap
|
||||
= 15;
|
||||
|
||||
/** The depth distance between a cell and a link */
|
||||
int dgap
|
||||
= 10;
|
||||
|
||||
/** The factor of enlargement of cells as they move away
|
||||
depthwise from the accursed cell. Element 0 is
|
||||
horizontal, element 1 is vertical. */
|
||||
float shrink
|
||||
= (float)0.7;
|
||||
|
||||
/** The enlargement factor applied to link cells. Think linkshrink! */
|
||||
float linkshrink
|
||||
= (float)0.25;
|
||||
|
||||
/** Should the tree be shown "depthhorizontal", ie. depth rightwards? */
|
||||
boolean depthhorizontal
|
||||
= false;
|
||||
|
||||
/** Should the headcell be used on both dimensions?
|
||||
* Normally on target dim, tailcell is used. */
|
||||
boolean bothhead
|
||||
= false;
|
||||
|
||||
/** Are links without targets interpreted as targets?
|
||||
* If two elements are given, the first is used for upstream- and the
|
||||
* second for downstream connections. */
|
||||
boolean[] loose // 1..2
|
||||
= new boolean[] { false, false };
|
||||
}
|
||||
|
||||
INITIALIZE {
|
||||
if(loose.length == 1)
|
||||
loose = new boolean[] { loose[0], loose[0] };
|
||||
}
|
||||
|
||||
DimVector[] dims;
|
||||
Coordinates treegap; // gap converted to a Coordinates instance
|
||||
Coordinates linkgap; // same with linkshrink applied
|
||||
Coordinates[] cellsizes;
|
||||
boolean looseup, loosedown; // XXX ???
|
||||
|
||||
|
||||
// A "direction" in the ZZ space
|
||||
static class DimVector {
|
||||
String dim;
|
||||
int dir;
|
||||
DimVector rev; // The reverse vector
|
||||
|
||||
DimVector(String dim, int dir) {
|
||||
this.dim = dim; this.dir = dir; rev = new DimVector(this);
|
||||
}
|
||||
DimVector(DimVector rev) { dim=rev.dim; dir=-rev.dir; this.rev=rev; }
|
||||
|
||||
ZZCell get(ZZCell c) { return c.s(dim, dir); }
|
||||
ZZCell end(ZZCell c) { return c.h(dim, dir); }
|
||||
ZZCell end(ZZCell c, boolean ensuremove) {
|
||||
return c.h(dim, dir, ensuremove);
|
||||
}
|
||||
ZZCell[] rank(ZZCell c, boolean includeThis) {
|
||||
return c.readRank(dim, dir, includeThis);
|
||||
}
|
||||
}
|
||||
|
||||
class Coordinates {
|
||||
public int breadth;
|
||||
public int depth;
|
||||
|
||||
public int x() { return depthhorizontal ? depth : breadth; }
|
||||
public int y() { return depthhorizontal ? breadth : depth; }
|
||||
|
||||
public void set(int setx, int sety) {
|
||||
if(depthhorizontal) {
|
||||
depth = setx; breadth = sety;
|
||||
} else {
|
||||
breadth = setx; depth = sety;
|
||||
}
|
||||
}
|
||||
|
||||
Coordinates(Dimension d) { set(d.width, d.height); }
|
||||
Coordinates(int b, int d) { breadth = b; depth = d; }
|
||||
Coordinates(int i, int j, boolean treecoords) {
|
||||
if(treecoords) { breadth = i; depth = j; }
|
||||
else set(i, j);
|
||||
}
|
||||
Coordinates(Coordinates c, float fract) {
|
||||
breadth = (int) (c.breadth * fract);
|
||||
depth = (int) (c.depth * fract);
|
||||
}
|
||||
}
|
||||
|
||||
DimVector[] directeddims(boolean upwards) {
|
||||
return new DimVector[] {
|
||||
upwards ? dims[1].rev : dims[0],
|
||||
upwards ? dims[0].rev : dims[1],
|
||||
upwards ? dims[2].rev : dims[2]
|
||||
};
|
||||
}
|
||||
|
||||
// STEP 1: Tree generation. This may be overridden
|
||||
// Note that "children" are cells away from root, and "parents" are
|
||||
// cells towards root. Only the links to the parents are drawn.
|
||||
CellInfo connectCell(ZZCell c, ZZCell linkc, ZZCell parentc,
|
||||
int steps, boolean upwards) {
|
||||
CellInfo r = new CellInfo(c, linkc, parentc, steps, upwards);
|
||||
boolean more = steps < maxdepth - 1;
|
||||
|
||||
DimVector[] ds = directeddims(upwards);
|
||||
DimVector linkdim = ds[0];
|
||||
DimVector targetdim = ds[1];
|
||||
DimVector tunneldim = ds[2];
|
||||
|
||||
ZZCell target, parent;
|
||||
if((target=tunneldim.get(c)) != null && more)
|
||||
r.childinfo.addElement(connectCell(target, null, c,
|
||||
steps+1, upwards));
|
||||
if((parent=tunneldim.rev.get(c)) != null)
|
||||
r.parentlinks.addElement(parent);
|
||||
|
||||
ZZCell[] links = linkdim.rank(c, true);
|
||||
for(int i=0; more && i<links.length; i++) {
|
||||
target = targetdim.end(links[i], !loose[upwards ? 0 : 1]);
|
||||
if(target == null) continue;
|
||||
if(target == links[i])
|
||||
r.childinfo.addElement(connectCell(target, null, c,
|
||||
steps+1, upwards));
|
||||
else
|
||||
r.childinfo.addElement(connectCell(target, links[i], c,
|
||||
steps+1, upwards));
|
||||
}
|
||||
|
||||
if((parent=linkdim.rev.end(c)) != null && parent != c)
|
||||
r.parentlinks.addElement(parent);
|
||||
|
||||
ZZCell[] parentlinks = targetdim.rev.rank(c, false);
|
||||
for(int i=0; i<parentlinks.length; i++)
|
||||
if(loose[upwards?1:0] || linkdim.rev.get(parentlinks[i]) != null)
|
||||
r.parentlinks.addElement(parentlinks[i]);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
class CellInfo {
|
||||
// Stuff set in step 1 (tree generation)
|
||||
ZZCell c, linkc, parentc; // Linkcell CAN be null
|
||||
Vector childinfo = new Vector();
|
||||
Vector parentlinks = new Vector(); // can include linkless parents
|
||||
int steps; // from root
|
||||
boolean upwards;
|
||||
|
||||
// Stuff set in step 2 (size & breadth computing)
|
||||
Coordinates size;
|
||||
int occupiedbreadth = 0;
|
||||
|
||||
CellInfo(ZZCell c, ZZCell linkc, ZZCell parentc, int steps,
|
||||
boolean upwards) {
|
||||
this.c = c; this.linkc = linkc; this.parentc = parentc;
|
||||
this.steps = steps; this.upwards = upwards;
|
||||
}
|
||||
|
||||
// STEP 2: Compute the size of cell and breadth of tree part
|
||||
public int computesize() {
|
||||
size = cellsizes[steps];
|
||||
for(Enumeration e = childinfo.elements(); e.hasMoreElements();)
|
||||
occupiedbreadth += ((CellInfo) e.nextElement()).computesize()
|
||||
+ treegap.breadth;
|
||||
if(occupiedbreadth < size.breadth)
|
||||
occupiedbreadth = size.breadth;
|
||||
return occupiedbreadth;
|
||||
}
|
||||
|
||||
// STEP 3: Put the cell and its parentlinks into a continuum
|
||||
public void putall(Continuum co, int bstart, int dstart) {
|
||||
int bcoord=bstart, dcoord=dstart;
|
||||
if(upwards) dcoord -= size.depth;
|
||||
Coordinates pos = new Coordinates(
|
||||
bcoord + occupiedbreadth/2 - size.breadth/2,
|
||||
dcoord);
|
||||
boolean newlypositioned = co.put(c, pos, size, steps, false);
|
||||
if(parentc != null) {
|
||||
if(upwards) co.putlink(c, linkc, parentc);
|
||||
else co.putlink(parentc, linkc, c);
|
||||
}
|
||||
|
||||
if(!newlypositioned && parentc != null) return;
|
||||
|
||||
int childdcoord = dcoord + treegap.depth * (upwards ? -1 : 1);
|
||||
if(!upwards) childdcoord += size.depth;
|
||||
bcoord += treegap.breadth / 2;
|
||||
for(Enumeration e = childinfo.elements(); e.hasMoreElements();) {
|
||||
CellInfo ci = (CellInfo)e.nextElement();
|
||||
ci.putall(co, bcoord, childdcoord);
|
||||
bcoord += ci.occupiedbreadth + treegap.breadth;
|
||||
}
|
||||
|
||||
if(parentc == null) return;
|
||||
|
||||
int bmiddle = pos.breadth + size.breadth/2;
|
||||
Coordinates linksize = new Coordinates(size, linkshrink);
|
||||
int plnkbreadth = linkgap.breadth + linksize.breadth;
|
||||
bcoord = bmiddle - (int)(plnkbreadth * parentlinks.size() / 2);
|
||||
bcoord += linkgap.breadth / 2;
|
||||
dcoord += linkgap.depth * (upwards ? 1 : -2);
|
||||
dcoord += size.depth * (upwards ? 1 : 0);
|
||||
|
||||
for(Enumeration e = parentlinks.elements(); e.hasMoreElements();) {
|
||||
Coordinates lpos = new Coordinates(bcoord, dcoord);
|
||||
ZZCell plinkc = (ZZCell)e.nextElement();
|
||||
if(co.put(plinkc, lpos, linksize, steps, true))
|
||||
co.putsmalllink(c, plinkc, upwards);
|
||||
bcoord += linksize.breadth + linkgap.breadth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Continuum {
|
||||
class Link { ZZCell fromc, linkc, toc; }
|
||||
|
||||
// Information used for rastering
|
||||
Hashtable posns = new Hashtable(); // POSitionNS
|
||||
Hashtable sizes = new Hashtable();
|
||||
Hashtable depths = new Hashtable();
|
||||
Vector links = new Vector();
|
||||
Hashtable linksmalls = new Hashtable();
|
||||
|
||||
// Information used only while generating the continuum
|
||||
Hashtable smalls = new Hashtable();
|
||||
Hashtable linkbigs = new Hashtable();
|
||||
|
||||
Coordinates getpos(ZZCell c) { return (Coordinates)posns.get(c); }
|
||||
Coordinates getsize(ZZCell c) { return (Coordinates)sizes.get(c); }
|
||||
int getdepth(ZZCell c) { return ((Integer)depths.get(c)).intValue(); }
|
||||
|
||||
// Return false if the cell has already been positioned
|
||||
public boolean put(ZZCell c, Coordinates pos, Coordinates size,
|
||||
int steps, boolean small) {
|
||||
if(posns.containsKey(c) && (small || !smalls.containsKey(c)))
|
||||
return false;
|
||||
posns.put(c, pos);
|
||||
sizes.put(c, size);
|
||||
depths.put(c, new Integer(steps+1));
|
||||
if(small) smalls.put(c, c);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void putsmalllink(ZZCell fromc, ZZCell linkc, boolean up) {
|
||||
if(linkbigs.containsKey(linkc)) return;
|
||||
Link link = new Link(); link.linkc = linkc;
|
||||
if(up) { link.fromc = fromc; link.toc = linkc; }
|
||||
else { link.fromc = linkc; link.toc = fromc; }
|
||||
linksmalls.put(linkc, link);
|
||||
}
|
||||
|
||||
public void putlink(ZZCell fromc, ZZCell linkc, ZZCell toc) {
|
||||
Link link = new Link();
|
||||
link.fromc = fromc; link.linkc = linkc; link.toc = toc;
|
||||
links.addElement(link);
|
||||
if(linkc != null && linksmalls.containsKey(linkc))
|
||||
linksmalls.remove(linkc);
|
||||
if(linkc != null) linkbigs.put(linkc, link);
|
||||
}
|
||||
|
||||
// STEP 4: Position link cells
|
||||
public void positionlinkcells() {
|
||||
for(Enumeration e = links.elements(); e.hasMoreElements();) {
|
||||
Link link = (Link)e.nextElement();
|
||||
|
||||
if(link.linkc == null) continue; // only draw a line
|
||||
|
||||
int fdepth = getdepth(link.fromc);
|
||||
int tdepth = getdepth(link.toc);
|
||||
int depth = fdepth > tdepth ? fdepth : tdepth;
|
||||
Coordinates size = new Coordinates(cellsizes[depth-1],
|
||||
linkshrink);
|
||||
Coordinates fpos = getpos(link.fromc);
|
||||
Coordinates fsize = getsize(link.fromc);
|
||||
Coordinates tpos = getpos(link.toc);
|
||||
Coordinates tsize = getsize(link.toc);
|
||||
Coordinates pos = new Coordinates(
|
||||
(fpos.breadth + (fsize.breadth/2) +
|
||||
tpos.breadth + (tsize.breadth/2) - size.breadth) / 2,
|
||||
(fpos.depth + fsize.depth + tpos.depth - size.depth) / 2);
|
||||
boolean succ = put(link.linkc, pos, size, depth, false);
|
||||
p((succ ? "S" : "Uns")+"uccessfully positioned link cell: "+
|
||||
link.linkc.getText());
|
||||
}
|
||||
}
|
||||
|
||||
// STEP 5: Raster the continuum into a flob set
|
||||
public void raster(FlobSet into, FlobFactory fact, ZZCell view) {
|
||||
for(Enumeration e = posns.keys(); e.hasMoreElements();) {
|
||||
ZZCell c = (ZZCell)e.nextElement();
|
||||
Coordinates pos = getpos(c);
|
||||
Coordinates size = getsize(c);
|
||||
int depth = getdepth(c);
|
||||
fact.makeFlob(into, c, c, 1, pos.x(), pos.y(), depth,
|
||||
size.x(), size.y());
|
||||
}
|
||||
for(Enumeration e = linksmalls.elements(); e.hasMoreElements();) {
|
||||
Link link = (Link)e.nextElement();
|
||||
putlink(link.fromc, null, link.toc);
|
||||
}
|
||||
for(Enumeration e = links.elements(); e.hasMoreElements();) {
|
||||
Link link = (Link)e.nextElement();
|
||||
int fdepth = getdepth(link.fromc);
|
||||
int tdepth = getdepth(link.toc);
|
||||
int depth = fdepth > tdepth ? fdepth : tdepth;
|
||||
Coordinates fpos = getpos(link.fromc);
|
||||
Coordinates fsize = getsize(link.fromc);
|
||||
Coordinates tpos = getpos(link.toc);
|
||||
Coordinates tsize = getsize(link.toc);
|
||||
|
||||
Coordinates from = new Coordinates(
|
||||
fpos.breadth + fsize.breadth/2, fpos.depth + fsize.depth);
|
||||
Coordinates to = new Coordinates(
|
||||
tpos.breadth + tsize.breadth/2, tpos.depth);
|
||||
|
||||
into.add(new LineDecor(from.x(), from.y(), to.x(), to.y(),
|
||||
Color.red, depth));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void initdims(String[] dims) {
|
||||
this.dims = new DimVector[] {
|
||||
new DimVector(dims[0], 1),
|
||||
new DimVector(dims[1], bothhead ? -1 : 1),
|
||||
new DimVector(dims[2], 1),
|
||||
};
|
||||
}
|
||||
|
||||
public ZZCell look(ZZCell c, int x, int y) {
|
||||
Coordinates co = new Coordinates(x, y, false);
|
||||
DimVector d=null, t=null;
|
||||
if(co.depth == 0)
|
||||
return null;
|
||||
else if(co.depth < 0) {
|
||||
d = dims[0];
|
||||
t = dims[3];
|
||||
} else {
|
||||
d = dims[1].rev;
|
||||
t = dims[3].rev;
|
||||
}
|
||||
|
||||
if(co.breadth < 0)
|
||||
return d.get(c);
|
||||
else if(co.breadth > 0)
|
||||
return d.end(c, true);
|
||||
else {
|
||||
if(t.get(c) != null) return t.get(c);
|
||||
ZZCell[] rank = d.rank(c, false);
|
||||
if(rank.length == 0) return null;
|
||||
return rank[rank.length / 2];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void raster(FlobSet into, FlobFactory fact,
|
||||
ZZCell view, String[] dims, ZZCell c) {
|
||||
Dimension cs = fact.getSize(null, initmul);
|
||||
cellsizes = new Coordinates[maxdepth];
|
||||
cellsizes[0] = new Coordinates(cs);
|
||||
for(int i=1; i<maxdepth; i++)
|
||||
cellsizes[i] = new Coordinates(cellsizes[i-1], shrink);
|
||||
treegap = new Coordinates(bgap, cellsizes[0].depth + dgap*4);
|
||||
linkgap = new Coordinates(bgap/4, dgap);
|
||||
|
||||
Coordinates fs = new Coordinates(into.getSize());
|
||||
p("Prepared canvas size");
|
||||
|
||||
initdims(dims);
|
||||
DimVector linkdim = this.dims[0], targetdim = this.dims[1],
|
||||
tunneldim = this.dims[2];
|
||||
p("Prepared DimVectors");
|
||||
|
||||
CellInfo uproot, downroot;
|
||||
boolean islink;
|
||||
|
||||
// CALL 1: Tree generation. This may be overridden
|
||||
p("Starting LevelRaster call one");
|
||||
|
||||
if(linkdim.rev.get(c) != null && targetdim.get(c) != null) {
|
||||
islink = true;
|
||||
uproot = connectCell(linkdim.rev.end(c), null, c, 1, true);
|
||||
downroot = connectCell(targetdim.end(c), null, c, 1, false);
|
||||
} else {
|
||||
islink = false;
|
||||
uproot = connectCell(c, null, null, 0, true);
|
||||
downroot = connectCell(c, null, null, 0, false);
|
||||
}
|
||||
|
||||
// CALL 2: Compute the size of cell and breadth of tree part
|
||||
p("Starting LevelRaster call two");
|
||||
|
||||
uproot.computesize(); downroot.computesize();
|
||||
|
||||
// CALL 3: Put the cell and its parentlinks into a continuum
|
||||
p("Starting LevelRaster call three");
|
||||
|
||||
Continuum co = new Continuum();
|
||||
int upbstart = (fs.breadth - uproot.occupiedbreadth) / 2;
|
||||
int downbstart = (fs.breadth - downroot.occupiedbreadth) / 2;
|
||||
int updstart = (fs.depth + cellsizes[0].depth) / 2;
|
||||
int downdstart = (fs.depth - cellsizes[0].depth) / 2;
|
||||
if(islink) {
|
||||
co.put(c, new Coordinates((fs.breadth - cellsizes[0].breadth) / 2,
|
||||
downdstart), cellsizes[0],
|
||||
0, false);
|
||||
updstart -= treegap.depth/4*3; //+ cellsizes[0].depth;
|
||||
downdstart += treegap.depth/4*3;
|
||||
}
|
||||
downroot.putall(co, downbstart, downdstart);
|
||||
uproot.putall(co, upbstart, updstart);
|
||||
|
||||
// CALL 4: Position link cells
|
||||
p("Starting LevelRaster call four");
|
||||
|
||||
co.positionlinkcells();
|
||||
|
||||
// CALL 5: Raster the continuum into a flob set
|
||||
p("Starting LevelRaster call five");
|
||||
|
||||
co.raster(into, fact, view);
|
||||
}
|
||||
}
|
||||
|
||||
// vim: set syntax=java :
|
||||
Reference in new issue
Block a user