diff options
Diffstat (limited to 'appl/wm/mand.b')
| -rw-r--r-- | appl/wm/mand.b | 839 |
1 files changed, 839 insertions, 0 deletions
diff --git a/appl/wm/mand.b b/appl/wm/mand.b new file mode 100644 index 00000000..7e060722 --- /dev/null +++ b/appl/wm/mand.b @@ -0,0 +1,839 @@ +implement Mand; + +# +# Copyright © 2000 Vita Nuova Limited. All rights reserved. +# + +# mandelbrot/julia fractal browser: +# button 1 - drag a rectangle to zoom into +# button 2 - (from mandel only) show julia at point +# button 3 - zoom out + +include "sys.m"; + sys : Sys; +include "draw.m"; + draw : Draw; + Point, Rect, Image, Context, Screen, Display : import draw; +include "tk.m"; + tk: Tk; +include "tkclient.m"; + tkclient: Tkclient; + +Mand : module +{ + init : fn(nil : ref Context, argv : list of string); +}; + +colours: array of ref Image; +stderr : ref Sys->FD; + +FIX: type big; + +Calc: adt { + xr, yr: array of FIX; + parx, pary: FIX; + # column order + dispbase: array of COL; # auxiliary display and border + imgch: chan of (ref Image, Rect); + img: ref Image; + maxx, maxy, supx, supy: int; + disp: int; # origin of auxiliary display + morj : int; + winr: Rect; + kdivisor: int; + pointsdone: int; +}; + +# BASE, LIMIT, MAXCOUNT, MINDELTA may be varied + +# +# calls with 256X128 on initial set +# --------------------------------- +# crawl 58 (5% of time) +# fillline 894 (6% of time) +# isblank 5012 (0% of time) +# mcount 6928 (55% of time) +# getcolour 52942 (11% of time) +# displayset 1 (15% of time) +# +WHITE : con 16r0; +BLACK : con 16rff; + +COL : type byte; + +BASE : con 60; # 28 +HBASE : con (BASE/2); +SCALE : con (big 1<<BASE); +TWO : con (big 1<<(BASE+1)); +FOUR : con (big 1<<(BASE+2)); +NEG : con (~((big 1<<(32-HBASE))-big 1)); +MINDELTA : con (big 1<<(HBASE-1)); # (1<<(HBASE-2)) + +SCHEDCOUNT: con 100; + +BLANK : con 0; # blank pixel +BORDER : con 255; # border pixel +LIMIT : con 4; # 4 or 5 + +# pointcolour() returns values in the range 1..MAXCOUNT+1 +# these must not clash with 0 or 255 +# hence 0 <= MAXCOUNT <= 253 +# +MAXCOUNT : con 253; # 92 64 + +# colour cube +R, G, B : int; + +# initial width and height +WIDTH: con 400; +HEIGHT: con 400; + +Fracpoint: adt { + x, y: real; +}; + +Fracrect: adt { + min, max: Fracpoint; +}; + +Params: adt { + r: Fracrect; + p: Fracpoint; + m: int; + kdivisor: int; + fill: int; +}; + +Usercmd: adt { + pick { + Zoomin => + r: Rect; + Julia => + p: Point; + Zoomout or + Restart => + # nothing + } +}; + +badmod(mod: string) +{ + sys->fprint(stderr, "mand: cannot load %s: %r\n", mod); + raise "fail:bad module"; +} + +win_config := array[] of { + "frame .f", + "label .f.dl -text Depth", + "entry .f.depth", + ".f.depth insert 0 1", + "checkbutton .f.fill -text {Fill} -command {send cmd fillchanged} -variable fill", + ".f.fill select", + "pack .f.dl -side left", + "pack .f.fill -side right", + "pack .f.depth -side top -fill x", + "frame .c -bd 3 -relief sunken -width " + string WIDTH + " -height " + string HEIGHT, + "pack .f -side top -fill x", + "pack .c -side bottom -fill both -expand 1", + "pack propagate . 0", + "bind .c <Button-1> {send cmd b1 %x %y}", + "bind .c <ButtonRelease-2> {send cmd b2 %x %y}", + "bind .c <ButtonRelease-1> {send cmd b1r %x %y}", + "bind .c <ButtonRelease-3> {send cmd b3 %x %y}", + + "bind .f.depth <Key-\n> {send cmd setkdivisor}", + "update", +}; + +mouseproc(win: ref Tk->Toplevel) +{ + for(;;) + tk->pointer(win, *<-win.ctxt.ptr); +} + +init(ctxt: ref Context, argv : list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + + draw = load Draw Draw->PATH; + tk = load Tk Tk->PATH; + + tkclient = load Tkclient Tkclient->PATH; + if (tkclient == nil) badmod(Tkclient->PATH); + + tkclient->init(); + if (ctxt == nil) + ctxt = tkclient->makedrawcontext(); + (win, wmcmd) := tkclient->toplevel(ctxt, "", "Fractals", Tkclient->Appl); + sys->pctl(Sys->NEWPGRP, nil); + + cmdch := chan of string; + tk->namechan(win, cmdch, "cmd"); + for (i := 0; i < len win_config; i++) + cmd(win, win_config[i]); + tkclient->onscreen(win, nil); + tkclient->startinput(win, "kbd"::"ptr"::nil); + fittoscreen(win); + cmd(win, "update"); + spawn mouseproc(win); + + R = G = B = 6; + argv = tl argv; + if (argv != nil) { (R, argv) = (int hd argv, tl argv); if (R <= 0) R = 1; } + if (argv != nil) { (G, argv) = (int hd argv, tl argv); if (G <= 0) G = 1; } + if (argv != nil) { (B, argv) = (int hd argv, tl argv); if (B <= 0) B = 1; } + colours = array[256] of ref Image; + for (i = 0; i < len colours; i++) + # colours[i] = ctxt.display.color(i); + colours[i] = ctxt.display.rgb(col(i/(G*B), R), + col(i/(1*B), G), + col(i/(1*1), B)); + specr := Fracrect((-2.0, -1.5), (1.0, 1.5)); + p := Params( + correctratio(specr, win), + (0.0, 0.0), + 1, # m + 1, # kdivisor + int cmd(win, "variable fill") + ); + pid := -1; + sync := chan of int; + imgch := chan of (ref Image, Rect); + canvr := canvposn(win); + spawn docalculate(sync, p, imgch); + pid = <-sync; + imgch <-= (win.image, canvr); + + stack: list of (Fracrect, Params); + for(;;){ + restart := 0; + alt { + s := <-win.ctxt.kbd => + tk->keyboard(win, s); + c := <-win.ctxt.ctl or + c = <-win.wreq or + c = <-wmcmd => + if(c[0] == '!'){ + if(pid != -1) + restart = winreq(win, c, imgch, sync); + else + restart = winreq(win, c, nil, nil); + }else{ + tkclient->wmctl(win, c); + if(c == "task" && pid != -1){ + kill(pid); + pid = -1; + } + } + press := <-cmdch => + (nil, toks) := sys->tokenize(press, " "); + ucmd: ref Usercmd = nil; + case hd toks { + "start" => + ucmd = ref Usercmd.Restart; + "b1" or "b2" or "b3" => + #cmd(win, "grab set .c"); + #fiximage(win); + ucmd = trackmouse(win, cmdch, hd toks, Point(int hd tl toks, int hd tl tl toks)); + #cmd(win, "grab release .c"); + "fillchanged" => + p.fill = int cmd(win, "variable fill"); + ucmd = ref Usercmd.Restart; + "setkdivisor" => + p.kdivisor = int cmd(win, ".f.depth get"); + if (p.kdivisor < 1) + p.kdivisor = 1; + ucmd = ref Usercmd.Restart; + } + if (ucmd != nil) { + pick u := ucmd { + Zoomin => + # sys->print("zoomin to %s\n", r2s(u.r)); + if (u.r.dx() > 0 && u.r.dy() > 0) { + stack = (specr, p) :: stack; + specr.min = pt2real(u.r.min, win, p.r); + specr.max = pt2real(u.r.max, win, p.r); + (specr.min.y, specr.max.y) = (specr.max.y, specr.min.y); # canonicalise + restart = 1; + } + Zoomout => + if (stack != nil) { + ((specr, p), stack) = (hd stack, tl stack); + cmd(win, ".f.depth delete 0 end"); + cmd(win, ".f.depth insert 0 " + string p.kdivisor); + if (p.fill) + cmd(win, ".f.fill select"); + else + cmd(win, ".f.fill deselect"); + cmd(win, "update"); + restart = 1; + } + Julia => + # pt := pt2real(u.p, win, p.r); + if (p.m) { + stack = (specr, p) :: stack; + p.p = pt2real(u.p, win, p.r); + specr = ((-2.0, -1.5), (1.0, 1.5)); + p.m = 0; + restart = 1; + } + Restart => + restart = 1; + } + } + <-sync => + win.image.flush(Draw->Flushon); + pid = -1; + } + if (restart) { + if (pid != -1) + kill(pid); + win.image.flush(Draw->Flushoff); + p.r = correctratio(specr, win); + sync = chan of int; + spawn docalculate(sync, p, imgch); + pid = <-sync; + imgch <-= (win.image, canvposn(win)); + } + } +} + +winreq(win: ref Tk->Toplevel, c: string, imgch: chan of (ref Image, Rect), terminated: chan of int): int +{ + oldimage := win.image; + if (imgch != nil) { + # halt calculation process + alt { + imgch <-= (nil, ((0,0), (0,0))) =>; + <-terminated => + imgch = nil; + } + } + tkclient->wmctl(win, c); + if(win.image != oldimage) + return 1; + if(imgch != nil) + imgch <-= (win.image, canvposn(win)); + return 0; +} + +correctratio(r: Fracrect, win: ref Tk->Toplevel): Fracrect +{ + # make sure calculation rectangle is in + # the same ratio as bitmap (also make sure that + # calculated area always includes desired area) + wr := canvposn(win); + (btall, atall) := (real wr.dy() / real wr.dx(), (r.max.y - r.min.y) / (r.max.x - r.min.x)); + if (btall > atall) { + # bitmap is taller than area, so expand area vertically + excess := (r.max.x - r.min.x) * btall - (r.max.y - r.min.y); + r.min.y -= excess / 2.0; + r.max.y += excess / 2.0; + } else { + # area is taller than bitmap, so expand area horizontally + excess := (r.max.y - r.min.y) / btall - (r.max.x - r.min.x); + r.min.x -= excess / 2.0; + r.max.x += excess / 2.0; + } + return r; +} + +pt2real(pt: Point, win: ref Tk->Toplevel, r: Fracrect): Fracpoint +{ + sz := Point(int cmd(win, ".c cget -actwidth"), int cmd(win, ".c cget -actheight")); + return (real pt.x / real sz.x * (r.max.x- r.min.x) + r.min.x, + real (sz.y - pt.y) / real sz.y * (r.max.y - r.min.y) + r.min.y); +} + +pt2s(pt: Point): string +{ + return string pt.x + " " + string pt.y; +} + +r2s(r: Rect): string +{ + return pt2s(r.min) + " " + pt2s(r.max); +} + +trackmouse(win: ref Tk->Toplevel, cmdch: chan of string, but: string, p: Point): ref Usercmd +{ + case but { + "b1" => + cr := canvposn(win); + display := win.image.display; + save := display.newimage(cr, win.image.chans, 0, Draw->Nofill); + save.draw(cr, win.image, nil, cr.min); + oclip := win.image.clipr; + win.image.clipr = cr; + + p = p.add(cr.min); + r := Rect(p, p); + win.image.border(r, 1, display.white, (0, 0)); + win.image.flush(Draw->Flushnow); + do { + but = <-cmdch; + (nil, toks) := sys->tokenize(but, " "); + but = hd toks; + if(but == "b1"){ + xr := r.canon(); + win.image.draw(xr, save, nil, xr.min); + (r.max.x, r.max.y) = (int hd tl toks + cr.min.x, int hd tl tl toks + cr.min.y); + win.image.border(r.canon(), 1, display.white, (0, 0)); + win.image.flush(Draw->Flushnow); + } + } while (but != "b1r"); + r = r.canon(); + win.image.draw(r, save, nil, r.min); + win.image.clipr = oclip; + r = r.subpt(cr.min); + return ref Usercmd.Zoomin(r); + "b2" => + return ref Usercmd.Julia(p); + "b3" => + return ref Usercmd.Zoomout; + } + return nil; +} + +poll(calc: ref Calc) +{ + calc.img.flush(Draw->Flushnow); + alt { + <-calc.imgch => + calc.img = nil; + (calc.img, calc.winr) = <-calc.imgch; + * =>; + } +} + +docalculate(sync: chan of int, p: Params, imgch: chan of (ref Image, Rect)) +{ + if (p.m) + ; # sys->print("mandel [[%g,%g],[%g,%g]]\n", r.min.x, r.min.y, r.max.x, r.max.y); + else + ; # sys->print("julia [[%g,%g],[%g,%g]] [%g,%g]\n", r.min.x, r.min.y, r.max.x, r.max.y, p.p.x, p.p.y); + sync <-= sys->pctl(0, nil); + calculate(p, imgch); + sync <-= 0; +} + +canvposn(win: ref Tk->Toplevel): Rect +{ + return tk->rect(win, ".c", Tk->Local); +} + +calculate(p: Params, imgch: chan of (ref Image, Rect)) +{ + calc := ref Calc; + (calc.img, calc.winr) = <-imgch; + r := calc.winr; + calc.maxx = r.dx(); + calc.maxy = r.dy(); + calc.supx = calc.maxx + 2; + calc.supy = calc.maxy + 2; + calc.imgch = imgch; + calc.xr = array[calc.maxx] of FIX; + calc.yr = array[calc.maxy] of FIX; + calc.morj = p.m; + initr(calc, p); + calc.img.drawop(r, calc.img.display.white, nil, (0,0), Draw->S); + + if (p.fill) { + calc.dispbase = array[calc.supx*calc.supy] of COL; # auxiliary display and border + calc.disp = calc.maxy + 3; + setdisp(calc); + displayset(calc); + } else { + for (x := 0; x < calc.maxx; x++) { + for (y := 0; y < calc.maxy; y++) + point(calc, calc.img, (x, y), pointcolour(calc, x, y)); + } + } +} + +setdisp(calc: ref Calc) +{ + d : int; + i : int; + + for (i = 0; i < calc.supx*calc.supy; i++) + calc.dispbase[i] = byte BLANK; + + i = 0; + for (d = 0; i < calc.supx; d += calc.supy) { + calc.dispbase[d] = byte BORDER; + i++; + } + i = 0; + for (d = 0; i < calc.supy; d++) { + calc.dispbase[d] = byte BORDER; + i++; + } + i = 0; + for (d = 0+calc.supx*calc.supy-1; i < calc.supx; d -= calc.supy) { + calc.dispbase[d] = byte BORDER; + i++; + } + i = 0; + for (d = 0+calc.supx*calc.supy-1; i < calc.supy; d--) { + calc.dispbase[d] = byte BORDER; + i++; + } +} + +initr(calc: ref Calc, p: Params): int +{ + r := p.r; + dp := real2fix((r.max.x-r.min.x)/(real calc.maxx)); + dq := real2fix((r.max.y-r.min.y)/(real calc.maxy)); + calc.xr[0] = real2fix(r.min.x)-(big calc.maxx*dp-(real2fix(r.max.x)-real2fix(r.min.x)))/big 2; + for (x := 1; x < calc.maxx; x++) + calc.xr[x] = calc.xr[x-1] + dp; + calc.yr[0] = real2fix(r.max.y)+(big calc.maxy*dq-(real2fix(r.max.y)-real2fix(r.min.y)))/big 2; + for (y := 1; y < calc.maxy; y++) + calc.yr[y] = calc.yr[y-1] - dq; + calc.parx = real2fix(p.p.x); + calc.pary = real2fix(p.p.y); + calc.kdivisor = p.kdivisor; + calc.pointsdone = 0; + return dp >= MINDELTA && dq >= MINDELTA; +} + +fillline(calc: ref Calc, x, y, d, dir, dird, col: int) +{ + x0 := x; + + while (calc.dispbase[d] == byte BLANK) { + calc.dispbase[d] = byte col; + x -= dir; + d -= dird; + } + if (0 && pointcolour(calc, (x0+x+dir)/2, y) != col) { # midpoint of line (island code) + # island - undo colouring or do properly + do { + d += dird; + x += dir; + # *d = BLANK; + calc.dispbase[d] = byte pointcolour(calc, x, y); + point(calc, calc.img, (x, y), int calc.dispbase[d]); + } while (x != x0); + return; # abort crawl ? + } + horizline(calc, calc.img, x0, x, y, col); +} + +crawlt(calc: ref Calc, x, y, d, col: int) +{ + yinc, dyinc : int; + + firstd := d; + xinc := 1; + dxinc := calc.supy; + + for (;;) { + if (getcolour(calc, x+xinc, y, d+dxinc) == col) { + x += xinc; + d += dxinc; + yinc = -xinc; + dyinc = -dxinc; + # if (isblank(x+xinc, y, d+dxinc)) + if (calc.dispbase[d+dxinc] == byte BLANK) + fillline(calc, x+xinc, y, d+dxinc, yinc, dyinc, col); + if (d == firstd) + break; + } + else { + yinc = xinc; + dyinc = dxinc; + } + if (getcolour(calc, x, y+yinc, d+yinc) == col) { + y += yinc; + d += yinc; + xinc = yinc; + dxinc = dyinc; + # if (isblank(x-xinc, y, d-dxinc)) + if (calc.dispbase[d-dxinc] == byte BLANK) + fillline(calc, x-xinc, y, d-dxinc, yinc, dyinc, col); + if (d == firstd) + break; + } + else { + xinc = -yinc; + dxinc = -dyinc; + } + } +} + +# spurious lines problem - disallow all acw paths +# +# 43---------> +# 12---------> +# +# 654------------> +# 7 3------------> +# 812------------> +# + +# Given a closed curve completely described by unit movements LRUD (left, +# right, up, and down), calculate the enclosed area. The description +# may be cw or acw and of arbitrary shape. +# +# Based on Green's Theorem :- area = integral ydx +# C +# area = 0; +# count = ARBITRARY_VALUE; +# while( moves_are_left() ){ +# move = next_move(); +# switch(move){ +# case L: +# area -= count; +# break; +# case R: +# area += count; +# break; +# case U: +# count++; +# break; +# case D: +# count--; +# break; +# } +# area = abs(area); + +crawlf(calc: ref Calc, x, y, d, col: int) +{ + xinc, yinc, dxinc, dyinc : int; + firstx, firsty : int; + firstd : int; + area := 0; + count := 0; + + firstx = x; + firsty = y; + firstd = d; + xinc = 1; + dxinc = calc.supy; + + # acw on success, cw on failure + for (;;) { + if (getcolour(calc, x+xinc, y, d+dxinc) == col) { + x += xinc; + d += dxinc; + yinc = -xinc; + dyinc = -dxinc; + area += xinc*count; + if (d == firstd) + break; + } else { + yinc = xinc; + dyinc = dxinc; + } + if (getcolour(calc, x, y+yinc, d+yinc) == col) { + y += yinc; + d += yinc; + xinc = yinc; + dxinc = dyinc; + count -= yinc; + if (d == firstd) + break; + } else { + xinc = -yinc; + dxinc = -dyinc; + } + } + if (area > 0) # cw + crawlt(calc, firstx, firsty, firstd, col); +} + +displayset(calc: ref Calc) +{ + edge : int; + last := BLANK; + d := calc.disp; + + for (x := 0; x < calc.maxx; x++) { + for (y := 0; y < calc.maxy; y++) { + col := calc.dispbase[d]; + if (col == byte BLANK) { + col = calc.dispbase[d] = byte pointcolour(calc, x, y); + point(calc, calc.img, (x, y), int col); + if (col == byte last) + edge++; + else { + last = int col; + edge = 0; + } + if (edge >= LIMIT) { + crawlf(calc, x, y-edge, d-edge, last); + # prevent further crawlf() + last = BLANK; + } + } + else { + if (col == byte last) + edge++; + else { + last = int col; + edge = 0; + } + } + d++; + } + last = BLANK; + d += 2; + } +} + +pointcolour(calc: ref Calc, x, y: int) : int +{ + if (++calc.pointsdone >= SCHEDCOUNT) { + calc.pointsdone = 0; + sys->sleep(0); + poll(calc); + } + if (calc.morj) + return mcount(calc, x, y) + 1; + else + return jcount(calc, x, y) + 1; +} + +mcount(calc: ref Calc, x_coord, y_coord: int): int +{ + (p, q) := (calc.xr[x_coord], calc.yr[y_coord]); + (x, y) := (calc.parx, calc.pary); + k := 0; + maxcount := MAXCOUNT * calc.kdivisor; + while (k < maxcount) { + if (x >= TWO || y >= TWO || x <= -TWO || y <= -TWO) + break; + + if (0) { + # x = (x < 0) ? (x>>HBASE)|NEG : x>>HBASE; + # y = (y < 0) ? (y>>HBASE)|NEG : y>>HBASE; + } + + x >>= HBASE; + y >>= HBASE; + t := y*y; + y = big 2*x*y+q; # possible unserious overflow when BASE == 28 + x *= x; + if (x+t >= FOUR) + break; + x -= t-p; + k++; + } + return k / calc.kdivisor; +} + +jcount(calc: ref Calc, x_coord, y_coord: int): int +{ + (x, y) := (calc.xr[x_coord], calc.yr[y_coord]); + (p, q) := (calc.parx, calc.pary); + k := 0; + maxcount := MAXCOUNT * calc.kdivisor; + while (k < maxcount) { + if (x >= TWO || y >= TWO || x <= -TWO || y <= -TWO) + break; + + if (0) { + # x = (x < 0) ? (x>>HBASE)|NEG : x>>HBASE; + # y = (y < 0) ? (y>>HBASE)|NEG : y>>HBASE; + } + + x >>= HBASE; + y >>= HBASE; + t := y*y; + y = big 2*x*y+q; # possible unserious overflow when BASE == 28 + x *= x; + if (x+t >= FOUR) + break; + x -= t-p; + k++; + } + return k / calc.kdivisor; +} + +getcolour(calc: ref Calc, x, y, d: int): int +{ + if (calc.dispbase[d] == byte BLANK) { + calc.dispbase[d] = byte pointcolour(calc, x, y); + point(calc, calc.img, (x, y), int calc.dispbase[d]); + } + return int calc.dispbase[d]; +} + +point(calc: ref Calc, d: ref Image, p: Point, col: int) +{ + d.draw(Rect(p, p.add((1,1))).addpt(calc.winr.min), colours[col], nil, (0,0)); +} + +horizline(calc: ref Calc, d: ref Image, x0, x1, y: int, col: int) +{ + if (x0 < x1) + r := Rect((x0, y), (x1, y+1)); + else + r = Rect((x1+1, y), (x0+1, y+1)); + d.draw(r.addpt(calc.winr.min), colours[col], nil, (0, 0)); + # r := Rect((x0, y), (x1, y)).canon(); + # r.max = r.max.add((1, 1)); +} + +real2fix(x: real): FIX +{ + return big (x * real SCALE); +} + +cmd(top: ref Tk->Toplevel, s: string): string +{ + e := tk->cmd(top, s); + if (e != nil && e[0] == '!') + sys->fprint(stderr, "mand: tk error on '%s': %s\n", s, e); + return e; +} + +kill(pid: int): int +{ + fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE); + if (fd == nil) + return -1; + if (sys->write(fd, array of byte "kill", 4) != 4) + return -1; + return 0; +} + +col(i, r : int) : int +{ + if (r == 1) + return 0; + return (255*(i%r))/(r-1); +} + +fittoscreen(win: ref Tk->Toplevel) +{ + Point: import draw; + if (win.image == nil || win.image.screen == nil) + return; + r := win.image.screen.image.r; + scrsize := Point((r.max.x - r.min.x), (r.max.y - r.min.y)); + bd := int cmd(win, ". cget -bd"); + winsize := Point(int cmd(win, ". cget -actwidth") + bd * 2, int cmd(win, ". cget -actheight") + bd * 2); + if (winsize.x > scrsize.x) + cmd(win, ". configure -width " + string (scrsize.x - bd * 2)); + if (winsize.y > scrsize.y) + cmd(win, ". configure -height " + string (scrsize.y - bd * 2)); + actr: Rect; + actr.min = Point(int cmd(win, ". cget -actx"), int cmd(win, ". cget -acty")); + actr.max = actr.min.add((int cmd(win, ". cget -actwidth") + bd*2, + int cmd(win, ". cget -actheight") + bd*2)); + (dx, dy) := (actr.dx(), actr.dy()); + if (actr.max.x > r.max.x) + (actr.min.x, actr.max.x) = (r.max.x - dx, r.max.x); + if (actr.max.y > r.max.y) + (actr.min.y, actr.max.y) = (r.max.y - dy, r.max.y); + if (actr.min.x < r.min.x) + (actr.min.x, actr.max.x) = (r.min.x, r.min.x + dx); + if (actr.min.y < r.min.y) + (actr.min.y, actr.max.y) = (r.min.y, r.min.y + dy); + cmd(win, ". configure -x " + string actr.min.x + " -y " + string actr.min.y); +} |
