/* ZZUtil.java * * 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 Tuomas Lukka */ package org.gzigzag; import java.awt.*; import java.awt.datatransfer.*; import java.util.*; /** Some general routines that don't fit anywhere else. */ public class ZZUtil { public static final String rcsid = "$Id: ZZUtil.java,v 1.35.4.3 2002/03/07 19:36:53 bfallenstein Exp $"; 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); } /** Set a number of strings onto a rank. * Overwrites existing cells and creates new for the rest. */ static public void putStrings(ZZCell start, String dim, boolean incStart, String[] strs) { for(int i=0; i= nmin) break; throw new ZZError("Not enough strings: "+i); } } p("GetStrs: "+start.getID()); r[i] = start.getText(obs); } if(i == nmax) return r; String[] r2 = new String[i]; System.arraycopy(r, 0, r2, 0, i); return r2; } static public String[] getStrings(ZZCell start, String dim, boolean incStart, int nmin, int nmax) { return getStrings(start,dim,incStart,nmin,nmax,null); } static public int[] getInts(ZZCell start, String dim, boolean incStart, int nmin, int nmax,ZZObs obs) { String[] s = getStrings(start, dim, incStart, nmin, nmax, obs); int[] r = new int[s.length]; for(int i=0; i 256) fract = 256; int p = 256-fract; int m = fract; return new Color( (c1.getRed()*p + c2.getRed()*m) / 256, (c1.getGreen()*p + c2.getGreen()*m) / 256, (c1.getBlue()*p + c2.getBlue()*m) / 256); } /** Give a subtly randomly shaded version of the given color. */ static public Color perturb(Color c1) { return new Color( pff(c1.getRed()), pff(c1.getGreen()), pff(c1.getBlue()) ); } /** Perturb an integer in the range 0..255. */ static int pff(int f) { int offs = (int)(50 * (Math.random() - 0.5)); f += offs; if(f < 0) f = 0; if(f > 255) f = 255; return f; } /** Find the index of the given string that is closest to * the given X coordinate using FontMetrics. */ static public int findStringHit(String txt, int x, FontMetrics fm, boolean alwaysmin) { if(x <= 0) return 0; int min = 0; int max = txt.length(); int lmin = 0; int lmax = fm.stringWidth(txt); while(max-min > 1) { int cur = (max+min) / 2; int curlen = fm.stringWidth(txt.substring(0, cur)); p("Iter: "+cur+" "+curlen+" "+max+" "+lmax+" "+min+" "+lmin); if(curlen > x) { max = cur; lmax = curlen; } else { min = cur; lmin = curlen; } // XXX potential for infinite loop for weird fontmetrics? } // now we should have it... int mx = lmax - x; int mn = x - lmin; if(mn < mx || alwaysmin) return min; else return max; } static public int findStringHit(String txt, int x, FontMetrics fm) { return findStringHit(txt, x, fm, false); } /** Append a command-type thing. Strings are put in cells, cells are * cloned. */ static public ZZCell appendCommand(ZZCell cur, String cdim, String pdim, Object[] objs) { ZZCell tmp = null; if(objs[0] instanceof String) (cur = cur.N(cdim, 1)).setText((String)objs[0]); else { tmp = ((ZZCell)objs[0]).N("d.clone", 1); cur.connect(cdim, 1, tmp); cur = tmp; } ZZCell prev = cur; for(int i=1; i * If a dimension is given, then contents of this rank are concatenated. */ static public void puiCopy(ZZCell from, String dim) { StringBuffer cont = new StringBuffer(from.getText()); if(dim != null) { ZZCell cur = from.s(dim, 1); while(cur != null) { // XXX Use LoopDetector String s = cur.getText(); if(s.equals("")) cont.append('\n'); else cont.append(' ').append(s); cur = cur.s(dim, 1); } } puiCopy(cont.toString()); } static public void puiCopyRank(ZZCell from, String dim) { StringBuffer cont = new StringBuffer(from.getText()+"\n\n"); if(dim != null) { ZZCell cur = from.s(dim, 1); while(cur != null) { // XXX Use LoopDetector cont.append(cur.getText() + "\n\n"); cur = cur.s(dim, 1); } } puiCopy(cont.toString()); } static public void puiCopy(ZZCell from) { puiCopy(from.getText()); } static public String puiGetClipText() { Clipboard clipboard = getPUIClipboard(); Transferable content = clipboard.getContents(new Object()); String s = ""; if(content != null) { try { s = (String) content.getTransferData(DataFlavor.stringFlavor); } catch (Exception e) { ZZLogger.exc(e); return null; } } return s; } /** Paste from PUI clipboard. * Change a cell's contents to the (text) contents in the clipboard * of the surrounding PUI. Return true if action succeeded; return * false if not (because the clipboard didn't contain text). *

* XXX does nothing if cell is span (but what *should* be done?) */ static public boolean puiPaste(ZZCell to) { if(to.getSpan() != null) return false; p("PuiPASTE"); String s = puiGetClipText(); if(s == null) return false; to.setText(s); return true; } /** An interface which establishes an ordering (partial or full, * depending on context) between cells. */ public interface Comparator { /** Compare the things represented by the two cells to each other. * @return 0 if equal, 1 if c1 greater, -1 if c2 greater. */ int compare(ZZCell c1, ZZCell c2); } /* after sorting */ static private void sameToClone(ZZCell c1, String dim, int dir, ZZCell c2, Comparator comp){ //pa("CLONING\n"); ZZCell cur = c1; for ( cur = c1.s(dim,dir); cur != null;){ if ( c1 == c2 ) break; ZZCell next = cur.s(dim, dir); if ( comp.compare(c1,cur) == 0 ){ c1.insert( "d.clone", 1, cur ); cur.excise(dim); //pa("CLONED\n"); } else { c1 = cur; } cur = next; } } static private int partition(ZZCell[] v, int l, int r, Comparator s){ int i = l-1; int j = r; int len = v.length; ZZCell p = v[r]; //if ( p == null ) return 0; ZZCell tmp; while( true ){ while(s.compare(v[++i], p) < 0 ){ if ( i >= len ) break; } while(s.compare(p, v[--j]) < 0 ){ if ( j <=0 ) break; } if ( i >= j ) break; tmp = v[i]; v[i] = v[j]; v[j] = tmp; } tmp = v[i]; v[i] = v[r]; v[r] = tmp; return i; } static private void qs(ZZCell[] table, int l, int r,Comparator comp){ if ( l < r ) { int i = partition(table,l,r,comp); if ( i == r ) { i--; } qs(table,l,i,comp); qs(table,i+1,r,comp); } } // yet another sorting algorithm // this isn't too good, feel free to make a better one static private void qsRange(ZZCell c1, String dim, int dir, ZZCell c2, Comparator comp) { if(c1 == c2) return; if ( c1 == null || c2 == null ) return; // calculate the size of the table ZZCell first = c1; int len = 1, i = 0; for( ; c1 != c2; len++){ c1 = c1.s(dim,dir); if ( c1 == null ) return; } if ( len < 1 ) return; // read to table ZZCell[] table = new ZZCell[len]; c1 = first; for( i = 0; i < len; i++){ table[i] = c1; c1 = c1.s(dim,dir); } qs( table,0,len-1, comp ); c1 = first; if( c1 != table[0]) c1.insert(dim, -dir, table[0]); for(i = 1 ; i < len; i++){ c1.insert(dim, dir, table[i]); c1 = table[i]; } } static private void sortRange(ZZCell c1, String dim, int dir, ZZCell c2, Comparator comp) { // p("SortRange: "+c1.getText()+" "+c2.getText()); if(c1 == c2) return; ZZCell first = c1; ZZCell last = c2; ZZCell cur; for(cur = c1.s(dim, dir); cur != null;) { // p("cur: "+cur.getText()); ZZCell next = cur.s(dim, dir); int cp = comp.compare(c1, cur); if(cp > 0) { // p("move"); if(cur == c2) last = c2.s(dim, -dir); c1.insert(dim, -dir, cur); if(first == c1) first = cur; } if(cur == c2) break; cur = next; } if(first != c1) sortRange(first, dim, dir, c1.s(dim, -dir), comp); if(c1 != last) sortRange(c1.s(dim, dir), dim, dir, last, comp); } /** Sort a rank with a given comparator. */ static public void sortRank(ZZCell c, String dim, int dir, Comparator comp, boolean includeThis) { sortRank( c, dim, dir, comp, includeThis, false); } static public void sortRank(ZZCell c, String dim, int dir, Comparator comp, boolean includeThis, boolean clone) { ZZCell c1; if(!includeThis) c1 = c.s(dim, dir); else c1 = c; if(c==null) return; //long t0 = System.currentTimeMillis(); //sortRange(c1, dim, dir, c.h(dim, dir), comp); qsRange(c1, dim, dir, c.h(dim, dir), comp); //pa("TIME USED SORTING = " + (System.currentTimeMillis() - t0) ); if(!includeThis) c1 = c.s(dim, dir); else c1 = c; if ( clone ) sameToClone(c1, dim, dir, c.h(dim, dir), comp); } /** Show the dimension cross of a flob view. */ static public void showFlobDims(FlobSet into, FlobFactory fact, ZZCell view, int n) { Dimension s = fact.getSize(null, 1); ZZCell dim = view.s("d.dims", 1); ZZCell[] cdims = new ZZCell[3]; int i=0; for(; dim != null && i < n; i ++) { cdims[i] = ZZCursorReal.get(dim); if(cdims[i] == null) throw new ZZError("No dimension"); dim = dim.s("d.dims", 1); } Flob fl = fact.makeFlob(into, cdims[0], cdims[0], 1, s.width, 0, 1, s.width, s.height); fl.flobPath = "dim"; fl = fact.makeFlob(into, cdims[1], cdims[1], 1, 0, s.height, 1, s.width, s.height); fl.flobPath = "dim"; if(n > 2) { fl = fact.makeFlob(into, cdims[2], cdims[2], 1, s.width, s.height, 1, s.width, s.height); fl.flobPath = "dim"; } int wh = s.width / 2; int hh = s.height / 2; into.add(new LineDecor( new int[] { wh, hh, wh, s.height, wh, hh, s.width, s.height, wh, hh, s.width, hh }, 12, Color.red, 1 )); } /** Create a natural-looking spline between two vectors. * Returns an array of alternating x and y coordinates of * a polyline. * XXX Contains many constants that are just fixed. * XXX Needs to be prettified a bit. */ static public int[] bulgeCurve( int x0, int y0, int dx0, int dy0, int x1, int y1, int dx1, int dy1, int NPTS) { // Formula: x = t*(x0 + c*t*v_0) + (1-t) * (x1 + c*(1-t)*v_1) p("bulge: "+x0+" "+y0+" "+dx0+" "+dy0+" "+x1+" "+y1+" "+ dx1+" "+dy1+" "); int dx = (dx0 - dx1) / 2; int dy = (dy0 - dy1) / 2; int dotp = dx * (x1-x0) + dy * (y1-y0); int det = dx * (y1-y0) - dy * (x1-x0); // if(dotp < 0 && int[] res = new int[NPTS*2]; // Polynomial for factor. // a*x + b*x^3 is the polynomial through (-1,-1), (0,0) and (1,1) // whose derivative at -1 and 1 is zero. // Thus, it is suitable for curving. float a = (float)1.5; float b = (float)-0.5; float tense = (float)20.0/NPTS; for(int i=0; i