/* 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 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