aboutsummaryrefslogtreecommitdiffstats
path: root/z15.c
diff options
context:
space:
mode:
authorJeffrey H. Kingston <jeff@it.usyd.edu.au>2010-09-14 19:21:41 +0000
committerJeffrey H. Kingston <jeff@it.usyd.edu.au>2010-09-14 19:21:41 +0000
commit71bdb35d52747e6d7d9f55df4524d57c2966be94 (patch)
tree480ee5eefccc40d5f3331cc52d66f722fd19bfb9 /z15.c
parentb41263ea7578fa9742486135c762803b52794105 (diff)
downloadlout-71bdb35d52747e6d7d9f55df4524d57c2966be94.tar.gz
Lout 3.17.
git-svn-id: http://svn.savannah.nongnu.org/svn/lout/trunk@2 9365b830-b601-4143-9ba8-b4a8e2c3339c
Diffstat (limited to 'z15.c')
-rw-r--r--z15.c838
1 files changed, 838 insertions, 0 deletions
diff --git a/z15.c b/z15.c
new file mode 100644
index 0000000..cf25102
--- /dev/null
+++ b/z15.c
@@ -0,0 +1,838 @@
+/*@z15.c:Size Constraints:MinConstraint(), EnlargeToConstraint()@*************/
+/* */
+/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.17) */
+/* COPYRIGHT (C) 1991, 1999 Jeffrey H. Kingston */
+/* */
+/* Jeffrey H. Kingston (jeff@cs.usyd.edu.au) */
+/* Basser Department of Computer Science */
+/* The University of Sydney 2006 */
+/* AUSTRALIA */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation; either Version 2, or (at your option) */
+/* any later version. */
+/* */
+/* This program 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 */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */
+/* */
+/* FILE: z15.c */
+/* MODULE: Size Constraints */
+/* EXTERNS: MinConstraint(), EnlargeToConstraint(), */
+/* ReflectConstraint(), SemiRotateConstraint(), */
+/* RotateConstraint(), InvScaleConstraint(), Constrained(), */
+/* EchoConstraint(), DebugConstrained() */
+/* */
+/*****************************************************************************/
+#include <math.h>
+#ifndef M_PI
+#define M_PI 3.1415926535897931160E0
+#endif
+#include "externs.h"
+
+
+/*****************************************************************************/
+/* */
+/* MinConstraint(xc, yc) */
+/* */
+/* Replace *xc by the minimum of the two constraints *xc and *yc. */
+/* */
+/*****************************************************************************/
+
+void MinConstraint(CONSTRAINT *xc, CONSTRAINT *yc)
+{ bc(*xc) = find_min(bc(*xc), bc(*yc));
+ bfc(*xc) = find_min(bfc(*xc), bfc(*yc));
+ fc(*xc) = find_min(fc(*xc), fc(*yc));
+} /* end MinConstraint */
+
+
+/*****************************************************************************/
+/* */
+/* SetSizeToMaxForwardConstraint(b, f, c) */
+/* */
+/* Set *b, *f to their largest possible value within constraint *c, such */
+/* that *f is as large as possible. */
+/* */
+/*****************************************************************************/
+
+void SetSizeToMaxForwardConstraint(FULL_LENGTH *b, FULL_LENGTH *f, CONSTRAINT *c)
+{
+ *f = find_min(bfc(*c), fc(*c));
+ *b = find_min(bc(*c), bfc(*c) - *f);
+} /* end EnlargeToConstraint */
+
+
+/*****************************************************************************/
+/* */
+/* EnlargeToConstraint(b, f, c) */
+/* */
+/* Enlarge *b,*f to its largest possible value within constraint *c. */
+/* */
+/*****************************************************************************/
+
+void EnlargeToConstraint(FULL_LENGTH *b, FULL_LENGTH *f, CONSTRAINT *c)
+{
+ *f = find_min(bfc(*c) - *b, fc(*c));
+} /* end EnlargeToConstraint */
+
+
+/*****************************************************************************/
+/* */
+/* ReflectConstraint(xc, yc) */
+/* */
+/* Set xc to the constraint which is yc with its back and forward reversed. */
+/* */
+/*****************************************************************************/
+
+#define ReflectConstraint(xc, yc) SetConstraint(xc, fc(yc), bfc(yc), bc(yc))
+
+
+/*@::ScaleToConstraint(), InvScaleConstraint(), etc@**************************/
+/* */
+/* int ScaleToConstraint(b, f, c) */
+/* */
+/* Return the scale factor needed to scale object of size b, f down so it */
+/* has a size which fits tightly into constraint c. */
+/* */
+/*****************************************************************************/
+
+int ScaleToConstraint(FULL_LENGTH b, FULL_LENGTH f, CONSTRAINT *c)
+{ float scale_factor; int res;
+ debug3(DSC, DD, "ScaleToConstraint(%s, %s, %s)", EchoLength(b),
+ EchoLength(f), EchoConstraint(c));
+ scale_factor = 1.0;
+ if( b > 0 ) scale_factor = find_min(scale_factor, (float) bc(*c)/b );
+ if( b + f > 0 ) scale_factor = find_min(scale_factor, (float) bfc(*c)/(b + f));
+ if( f > 0 ) scale_factor = find_min(scale_factor, (float) fc(*c)/f );
+ res = scale_factor * SF;
+ debug2(DSC, DD, "ScaleToConstraint returning %.2f (%d)", scale_factor, res);
+ return res;
+} /* end ScaleToConstraint */
+
+
+/*****************************************************************************/
+/* */
+/* InvScaleConstraint(yc, sf, xc) */
+/* */
+/* Scale constraint xc to the inverse of the scale factor sf. */
+/* */
+/*****************************************************************************/
+
+void InvScaleConstraint(CONSTRAINT *yc, FULL_LENGTH sf, CONSTRAINT *xc)
+{
+#if DEBUG_ON
+ char buff[10];
+#endif
+ ifdebug(DSC, DD, sprintf(buff, "%.3f", (float) sf / SF));
+ debug2(DSC, DD, "InvScaleConstraint(yc, %s, %s)", buff, EchoConstraint(xc));
+ assert( sf > 0, "InvScaleConstraint: sf <= 0!" );
+ bc(*yc) = bc(*xc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH :
+ find_min(MAX_FULL_LENGTH, bc(*xc) * SF / sf);
+ bfc(*yc) = bfc(*xc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH :
+ find_min(MAX_FULL_LENGTH, bfc(*xc)* SF / sf);
+ fc(*yc) = fc(*xc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH :
+ find_min(MAX_FULL_LENGTH, fc(*xc) * SF / sf);
+ debug1(DSC, DD, "InvScaleConstraint returning %s", EchoConstraint(yc));
+} /* end InvScaleConstraint */
+
+
+/*****************************************************************************/
+/* */
+/* static SemiRotateConstraint(xc, u, v, angle, yc) */
+/* */
+/* Used by RotateConstraint to calculate one rotated constraint. */
+/* */
+/*****************************************************************************/
+
+static void SemiRotateConstraint(CONSTRAINT *xc, FULL_LENGTH u, FULL_LENGTH v,
+float angle, CONSTRAINT *yc)
+{ float cs, sn;
+#if DEBUG_ON
+ char buff[20];
+#endif
+ ifdebug(DSC, DD, sprintf(buff, "%.1f", angle * 360.0 / (2 * M_PI)));
+ debug4(DSC, DD, "SemiRotateConstraint(xc, %s, %s, %sd, %s",
+ EchoLength(u), EchoLength(v), buff, EchoConstraint(yc));
+ cs = cos(angle); sn = sin(angle);
+ if( fabs(cs) < 1e-6 )
+ SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
+ else
+ SetConstraint(*xc,
+ find_min(MAX_FULL_LENGTH, (bc(*yc) - u * sn) / cs),
+ find_min(MAX_FULL_LENGTH, (bfc(*yc) - u * sn - v * sn) / cs),
+ find_min(MAX_FULL_LENGTH, (fc(*yc) - v * sn) / cs ));
+ debug1(DSC, DD, "SemiRotateConstraint returning %s", EchoConstraint(xc));
+} /* end SemiRotateConstraint */
+
+
+/*@::RotateConstraint()@******************************************************/
+/* */
+/* RotateConstraint(c, y, angle, hc, vc, dim) */
+/* */
+/* Take the object angle @Rotate y, which is supposed to be constrained */
+/* horizontally by hc and vertically by vc, and determine a constraint */
+/* (either horizontal or vertical, depending on dim) for y. */
+/* */
+/* The constraint returned is a trigonometric function of all these */
+/* parameters, including the present size of y in dimension 1-dim. */
+/* */
+/*****************************************************************************/
+
+void RotateConstraint(CONSTRAINT *c, OBJECT y, FULL_LENGTH angle,
+CONSTRAINT *hc, CONSTRAINT *vc, int dim)
+{ CONSTRAINT c1, c2, c3, dc; float theta, psi;
+#if DEBUG_ON
+ char buff[20];
+#endif
+ ifdebug(DSC, DD, sprintf(buff, "%.1f", (float) angle / DG ));
+ debug4(DSC, DD, "RotateConstraint(c, y, %sd, %s, %s, %s)",
+ buff, EchoConstraint(hc), EchoConstraint(vc), dimen(dim));
+
+ /* work out angle in radians between 0 and 2*PI */
+ theta = (float) angle * 2 * M_PI / (float) (DG * 360);
+ while( theta < 0 ) theta += 2 * M_PI;
+ while( theta >= 2 * M_PI ) theta -= 2 * M_PI;
+ assert( 0 <= theta && theta <= 2 * M_PI, "RotateConstraint: theta!" );
+
+ /* determine theta, c1, and c2 depending on which quadrant we are in */
+ if( theta <= M_PI / 2.0 ) /* first quadrant */
+ { theta = theta;
+ CopyConstraint(c1, *hc);
+ CopyConstraint(c2, *vc);
+ }
+ else if ( theta <= M_PI ) /* second quadrant */
+ { theta -= M_PI / 2.0;
+ ReflectConstraint(c1, *vc);
+ CopyConstraint(c2, *hc);
+ }
+ else if ( theta <= 3.0 * M_PI / 2.0 ) /* third quadrant */
+ { theta -= M_PI;
+ ReflectConstraint(c1, *hc);
+ ReflectConstraint(c2, *vc);
+ }
+ else /* fourth quadrant */
+ { theta -= 3.0 * M_PI / 2.0;
+ CopyConstraint(c1, *vc);
+ ReflectConstraint(c2, *hc);
+ }
+ psi = M_PI / 2.0 - theta;
+ debug2(DSC, DD, " c1: %s; c2: %s", EchoConstraint(&c1), EchoConstraint(&c2));
+
+ /* return the minimum of the two constraints, rotated */
+ if( dim == COLM )
+ { SemiRotateConstraint(c, back(y, ROWM), fwd(y, ROWM), theta, &c1);
+ ReflectConstraint(c3, c2);
+ SemiRotateConstraint(&dc, fwd(y, ROWM), back(y, ROWM), psi, &c3);
+ MinConstraint(c, &dc);
+ }
+ else
+ { SemiRotateConstraint(c, back(y, COLM), fwd(y, COLM), psi, &c1);
+ SemiRotateConstraint(&dc, fwd(y, COLM), back(y, COLM), theta, &c2);
+ MinConstraint(c, &dc);
+ }
+
+ debug1(DSC, DD, "RotateConstraint returning %s", EchoConstraint(c));
+} /* end RotateConstraint */
+
+/*@::InsertScale()@***********************************************************/
+/* */
+/* BOOLEAN InsertScale(x, c) */
+/* */
+/* Insert a @Scale object above x so that x is scaled horizontally to fit */
+/* constraint c. If this is not possible, owing to the necessary scale */
+/* factor being too small, then don't do it; return FALSE instead. */
+/* */
+/*****************************************************************************/
+
+BOOLEAN InsertScale(OBJECT x, CONSTRAINT *c)
+{ int scale_factor; OBJECT prnt;
+ scale_factor = ScaleToConstraint(back(x, COLM), fwd(x, COLM), c);
+ if( scale_factor >= 0.2 * SF )
+ {
+ New(prnt, SCALE);
+ underline(prnt) = underline(x);
+ FposCopy(fpos(prnt), fpos(x));
+
+ /* set horizontal size and scale factor */
+ bc(constraint(prnt)) = scale_factor;
+ back(prnt, COLM) = ( back(x, COLM) * scale_factor ) / SF;
+
+ /* *** slightly too small?
+ fwd(prnt, COLM) = ( fwd(x, COLM) * scale_factor ) / SF;
+ *** */
+ fwd(prnt, COLM) = find_min(bfc(*c) - back(prnt, COLM), fc(*c));
+
+ /* set vertical size and scale factor */
+ fc(constraint(prnt)) = 1 * SF;
+ back(prnt, ROWM) = back(x, ROWM);
+ fwd(prnt, ROWM) = fwd(x, ROWM);
+
+ /* link prnt above x and return */
+ ReplaceNode(prnt, x);
+ Link(prnt, x);
+ return TRUE;
+ }
+ else return FALSE;
+} /* end InsertScale */
+
+
+/*@::CatConstrained()@********************************************************/
+/* */
+/* static CatConstrained(x, xc, ratm, y, dim, OBJECT *why) */
+/* */
+/* Calculate the size constraint of object x, as for Constrained below. */
+/* y is the enclosing VCAT etc. object; ratm is TRUE if a ^ lies after */
+/* x anywhere. dim is COLM or ROWM. */
+/* */
+/* The meaning of the key variables is as follows: */
+/* */
+/* be The amount by which back(x, dim) can increase from zero */
+/* without having any impact on size(y, dim). Thereafter, */
+/* any increase causes an equal increase in size(y, dim). */
+/* */
+/* fe The amount by which fwd(x, dim) can increase from zero */
+/* without having any impact on size(y, dim). Thereafter, */
+/* any increase causes an equal increase in size(y, dim). */
+/* */
+/* backy, The value that back(y, dim) and fwd(y, dim) would have if x */
+/* fwdy was definite with size 0,0. They will in general be larger */
+/* than the present values if x is indefinite, and smaller */
+/* if x is definite, although it depends on marks and gaps. */
+/* */
+/*****************************************************************************/
+
+static void CatConstrained(OBJECT x, CONSTRAINT *xc, BOOLEAN ratm,
+OBJECT y, int dim, OBJECT *why)
+{ int side; /* the size of y that x is on: BACK, ON, FWD */
+ CONSTRAINT yc; /* constraints on y */
+ FULL_LENGTH backy, fwdy; /* back(y), fwd(y) would be if x was (0, 0) */
+ FULL_LENGTH be, fe; /* amount back(x), fwd(x) can be for free */
+ FULL_LENGTH beffect, feffect; /* scratch variables for calculations */
+ FULL_LENGTH seffect; /* scratch variables for calculations */
+ OBJECT link, sg, pg; /* link to x, its successor and predecessor */
+ OBJECT prec_def, sd; /* definite object preceding (succeeding) x */
+ int tb, tbf, tf, tbc, tbfc, tfc, mxy, myz;
+
+ Constrained(y, &yc, dim, why);
+ if( constrained(yc) )
+ {
+ /* find the link of x, and its neighbours and their links */
+ link = UpDim(x, dim);
+ SetNeighbours(link, ratm, &pg, &prec_def, &sg, &sd, &side);
+
+ /* amount of space available at x without changing the size of y */
+ be = pg == nilobj ? 0 : ExtraGap(fwd(prec_def, dim), 0, &gap(pg), BACK);
+ fe = sg == nilobj ? 0 : ExtraGap(0, back(sd, dim), &gap(sg), FWD);
+
+ if( is_indefinite(type(x)) )
+ {
+ /* insert two lengths and delete one */
+ beffect = pg==nilobj ? 0 : MinGap(fwd(prec_def, dim), 0, 0, &gap(pg));
+ feffect = sg==nilobj ? 0 : MinGap(0, back(sd,dim), fwd(sd,dim), &gap(sg));
+ seffect = pg==nilobj ?
+ sg == nilobj ? 0 : back(sd, dim) :
+ sg == nilobj ? fwd(prec_def, dim) :
+ MinGap(fwd(prec_def, dim), back(sd, dim), fwd(sd, dim), &gap(sg));
+
+ switch( side )
+ {
+ case BACK: backy = back(y, dim) + beffect + feffect - seffect;
+ fwdy = fwd(y, dim);
+ break;
+
+ case ON: /* must be first, other cases prohibited */
+ backy = 0;
+ fwdy = fwd(y, dim) + feffect;
+ break;
+
+ case FWD: backy = back(y, dim);
+ fwdy = fwd(y, dim) + beffect + feffect - seffect;
+ break;
+ }
+ }
+
+ else /* x is definite */
+
+ { beffect = pg == nilobj ? back(x, dim) :
+ MinGap(fwd(prec_def, dim), back(x,dim), fwd(x,dim), &gap(pg)) -
+ MinGap(fwd(prec_def, dim), 0, 0, &gap(pg));
+
+ feffect = sg == nilobj ? fwd(x, dim) :
+ MinGap(fwd(x, dim), back(sd, dim), fwd(sd, dim), &gap(sg)) -
+ MinGap(0, back(sd, dim), fwd(sd, dim), &gap(sg));
+
+ switch( side )
+ {
+ case BACK: backy = back(y, dim) - beffect - feffect;
+ fwdy = fwd(y, dim);
+ break;
+
+ case ON: backy = back(y, dim) - beffect;
+ fwdy = fwd(y, dim) - feffect;
+ break;
+
+ case FWD: backy = back(y, dim);
+ fwdy = fwd(y, dim) - beffect - feffect;
+ break;
+ }
+ }
+
+ debug5(DSC, DD, " side: %s, backy: %s, fwdy: %s, be: %s, fe: %s",
+ Image(side), EchoLength(backy), EchoLength(fwdy),
+ EchoLength(be), EchoLength(fe) );
+
+ if( !FitsConstraint(backy, fwdy, yc) )
+ SetConstraint(*xc, -1, -1, -1);
+ else switch( side )
+ {
+
+ case BACK:
+
+ tbc = bc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bc(yc) - backy;
+ tbfc = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - backy - fwdy;
+ mxy = find_min(tbc, tbfc);
+ tb = find_min(MAX_FULL_LENGTH, be + mxy);
+ tbf = find_min(MAX_FULL_LENGTH, be + fe + mxy);
+ tf = find_min(MAX_FULL_LENGTH, fe + mxy);
+ SetConstraint(*xc, tb, tbf, tf);
+ break;
+
+
+ case ON:
+
+ tbc = bc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bc(yc) - backy;
+ tbfc = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - backy - fwdy;
+ tfc = fc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : fc(yc) - fwdy;
+ mxy = find_min(tbc, tbfc);
+ myz = find_min(tfc, tbfc);
+ tb = find_min(MAX_FULL_LENGTH, be + mxy);
+ tbf = find_min(MAX_FULL_LENGTH, be + fe + tbfc);
+ tf = find_min(MAX_FULL_LENGTH, fe + myz);
+ SetConstraint(*xc, tb, tbf, tf);
+ break;
+
+
+ case FWD:
+
+ tfc = fc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : fc(yc) - fwdy;
+ tbfc = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - backy - fwdy;
+ mxy = find_min(tfc, tbfc);
+ tb = find_min(MAX_FULL_LENGTH, be + mxy);
+ tbf = find_min(MAX_FULL_LENGTH, be + fe + mxy);
+ tf = find_min(MAX_FULL_LENGTH, fe + mxy);
+ SetConstraint(*xc, tb, tbf, tf);
+ break;
+
+ }
+ } /* end if( constrained ) */
+ else SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
+} /* end CatConstrained */
+
+
+/*@::Constrained()@***********************************************************/
+/* */
+/* Constrained(x, xc, dim, why) */
+/* */
+/* Calculate the size constraint of object x, and return it in *xc. */
+/* */
+/* If the resulting constraint is a hard one caused by coming up against */
+/* a HIGH (vertical) or WIDE (horizontal), set *why to this object; if */
+/* not, leave *why unchanged. */
+/* */
+/*****************************************************************************/
+
+void Constrained(OBJECT x, CONSTRAINT *xc, int dim, OBJECT *why)
+{ OBJECT y, link, lp, rp, z, tlink, g; CONSTRAINT yc, hc, vc;
+ BOOLEAN ratm; FULL_LENGTH xback, xfwd; int tb, tf, tbf, tbc, tfc;
+ SetLengthDim(dim);
+ debug2(DSC, DD, "[ Constrained(%s, xc, %s, why), x =",
+ Image(type(x)), dimen(dim));
+ ifdebug(DSC, DD, DebugObject(x));
+ assert( Up(x) != x, "Constrained: x has no parent!" );
+
+ /* a CLOSURE which is external_ver is unconstrained in the ROWM direction */
+ /* a CLOSURE which is external_hor is unconstrained in both directions */
+ if( type(x) == CLOSURE && ((dim==ROWM && external_ver(x)) || external_hor(x)) )
+ {
+ SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
+ debug1(DSC, DD, "] Constrained returning %s (external)",EchoConstraint(xc));
+ return;
+ }
+
+ /* find y, the parent of x */
+ link = UpDim(x, dim); ratm = FALSE;
+ for( tlink = NextDown(link); type(tlink) == LINK; tlink = NextDown(tlink) )
+ { Child(g, tlink);
+ if( type(g) == GAP_OBJ && mark(gap(g)) ) ratm = TRUE;
+ }
+ y = tlink;
+ debug1(DSC, DDD, "parent y = %s", Image(type(y)));
+ ifdebug(DSC, DDD, DebugObject(y));
+
+ switch( type(y) )
+ {
+ case PLAIN_GRAPHIC:
+ case GRAPHIC:
+ case KERN_SHRINK:
+ case ONE_COL:
+ case ONE_ROW:
+ case HCONTRACT:
+ case VCONTRACT:
+ case HEXPAND:
+ case VEXPAND:
+ case START_HVSPAN:
+ case START_HSPAN:
+ case START_VSPAN:
+ case SPLIT:
+ case BACKGROUND:
+
+ Constrained(y, xc, dim, why);
+ break;
+
+
+ case HSCALE:
+ case VSCALE:
+
+ if( (dim == COLM) != (type(y) == HSCALE) ) Constrained(y, xc, dim, why);
+ else SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
+ break;
+
+
+ case HCOVER:
+ case VCOVER:
+
+ /* dubious, but not likely to arise anyway */
+ if( (dim == COLM) != (type(y) == HCOVER) ) Constrained(y, xc, dim, why);
+ else SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
+ break;
+
+
+ case SCALE:
+
+ Constrained(y, &yc, dim, why);
+ if( dim == COLM && bc(constraint(y)) == 0 )
+ {
+ /* Lout-supplied factor required later, could be tiny */
+ SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
+ }
+ else
+ { InvScaleConstraint(xc,
+ dim == COLM ? bc(constraint(y)) : fc(constraint(y)), &yc);
+ }
+ break;
+
+
+ case ROTATE:
+
+ Constrained(y, &hc, COLM, why); Constrained(y, &vc, ROWM, why);
+ RotateConstraint(xc, x, sparec(constraint(y)), &hc, &vc, dim);
+ break;
+
+
+ case WIDE:
+ case HIGH:
+
+ Constrained(y, xc, dim, why);
+ if( (type(y)==WIDE) == (dim==COLM) )
+ { MinConstraint(xc, &constraint(y));
+ *why = y;
+ }
+ break;
+
+
+ case HLIMITED:
+ case VLIMITED:
+
+ if( (type(y) == HLIMITED) == (dim == COLM) )
+ {
+ BOOLEAN still_searching = TRUE;
+ z = y;
+ SetConstraint(*xc, back(z, dim), size(z, dim), fwd(z, dim));
+ debug2(DSC, D, " [ %s (%s)", Image(type(z)), EchoConstraint(xc));
+ while( still_searching && Up(z) != z )
+ {
+ Parent(z, UpDim(z, dim));
+ switch( type(z) )
+ {
+ case VLIMITED:
+ case HLIMITED:
+ case COL_THR:
+ case ROW_THR:
+ case ONE_COL:
+ case ONE_ROW:
+ case HCONTRACT:
+ case VCONTRACT:
+ case SPLIT:
+ case START_VSPAN:
+ case START_HSPAN:
+
+ SetConstraint(*xc, back(z, dim), size(z, dim), fwd(z, dim));
+ debug2(DSC, DD, " let s = %s (%s)", Image(type(z)),
+ EchoConstraint(xc));
+ break;
+
+
+ case HSPANNER:
+ case VSPANNER:
+
+ /* SpannerAvailableSpace(z, dim, &b, &f); */
+ CopyConstraint(*xc, constraint(z));
+ debug2(DSC, D, " ] let s = %s (%s) and stop",
+ Image(type(z)), EchoConstraint(&constraint(z)));
+ still_searching = FALSE;
+ break;
+
+
+ default:
+
+ debug1(DSC, D, " ] stopping at %s", Image(type(z)));
+ still_searching = FALSE;
+ break;
+ }
+ }
+ *why = y;
+ }
+ else
+ {
+ Constrained(y, xc, dim, why);
+ }
+ break;
+
+
+ case VSPANNER:
+ case HSPANNER:
+
+ /* we're saying that a spanner has a fixed constraint that is */
+ /* determined just once in its life */
+ CopyConstraint(*xc, constraint(y));
+ debug2(DSC, DD, " Constrained(%s) = %s", Image(type(z)), EchoConstraint(xc));
+ /* SetConstraint(*xc, back(y, dim), size(y, dim), fwd(y, dim)); */
+ break;
+
+
+ case HSHIFT:
+ case VSHIFT:
+
+ if( (type(y) == HSHIFT) == (dim == COLM) )
+ { Constrained(y, &yc, dim, why);
+ tf = FindShift(y, x, dim);
+ SetConstraint(*xc,
+ find_min(bc(yc), bfc(yc)) - tf, bfc(yc), find_min(fc(yc), bfc(yc)) + tf);
+ }
+ else Constrained(y, xc, dim, why);
+ break;
+
+
+ case HEAD:
+
+ if( dim == ROWM )
+ SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
+ else
+ { CopyConstraint(yc, constraint(y));
+ debug1(DSC, DD, " head: %s; val is:", EchoConstraint(&yc));
+ ifdebug(DSC, DD, DebugObject(y));
+ goto REST_OF_HEAD; /* a few lines down */
+ }
+ break;
+
+
+ case COL_THR:
+ case ROW_THR:
+
+ assert( (type(y)==COL_THR) == (dim==COLM), "Constrained: COL_THR!" );
+ Constrained(y, &yc, dim, why);
+ tb = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - fwd(y, dim);
+ tb = find_min(bc(yc), tb);
+ tf = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - back(y, dim);
+ tf = find_min(fc(yc), tf);
+ SetConstraint(*xc, tb, bfc(yc), tf);
+ break;
+
+
+ case VCAT:
+ case HCAT:
+ case ACAT:
+
+ if( (type(y)==VCAT) == (dim==ROWM) )
+ { CatConstrained(x, xc, ratm, y, dim, why);
+ break;
+ }
+ Constrained(y, &yc, dim, why);
+ if( !constrained(yc) )
+ SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
+ else
+ {
+ REST_OF_HEAD:
+ /* let lp and rp be the links of the gaps delimiting */
+ /* the components joined to x (or parent if no such) */
+ for( lp = PrevDown(link); lp != y; lp = PrevDown(lp) )
+ { Child(z, lp);
+ if( type(z) == GAP_OBJ && !join(gap(z)) ) break;
+ }
+ for( rp = NextDown(link); rp != y; rp = NextDown(rp) )
+ { Child(z, rp);
+ if( type(z) == GAP_OBJ && !join(gap(z)) ) break;
+ }
+ if( lp == y && rp == y && !(type(y) == HEAD && seen_nojoin(y)) )
+ {
+ /* if whole object is joined, do this */
+ tb = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - fwd(y, dim);
+ tb = find_min(bc(yc), tb);
+ tf = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - back(y, dim);
+ tf = find_min(fc(yc), tf);
+ SetConstraint(*xc, tb, bfc(yc), tf);
+ }
+ else
+ {
+ /* if // or || is present, do this */
+ xback = xfwd = 0;
+ for(link = NextDown(lp); link != rp; link = NextDown(link) )
+ { Child(z, link);
+ if( type(z) == GAP_OBJ || is_index(type(z)) ) continue;
+ xback = find_max(xback, back(z, dim));
+ xfwd = find_max(xfwd, fwd(z, dim));
+ }
+ debug2(DSC, DD, " lp != rp; xback,xfwd = %s,%s",
+ EchoLength(xback), EchoLength(xfwd));
+ tbf = find_min(bfc(yc), fc(yc));
+ tbc = tbf == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : tbf - xfwd;
+ tfc = tbf == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : tbf - xback;
+ SetConstraint(*xc, tbc, tbf, tfc);
+ }
+ }
+ break;
+
+
+ default:
+
+ assert1(FALSE, "Constrained:", Image(type(y)));
+ break;
+
+ }
+ debug2(DSC, DD, "] Constrained %s returning %s", Image(type(x)),
+ EchoConstraint(xc));
+} /* end Constrained */
+
+
+/*@::EchoConstraint(), DebugConstrained()@************************************/
+/* */
+/* FULL_CHAR *EchoConstraint(c) */
+/* */
+/* Returns a string showing constraint *c, in centimetres. */
+/* */
+/*****************************************************************************/
+#if DEBUG_ON
+
+FULL_CHAR *EchoConstraint(CONSTRAINT *c)
+{ static char str[2][40];
+ static int i = 0;
+ i = (i+1) % 2;
+ sprintf(str[i], "<%s, %s, %s>", EchoLength(bc(*c)), EchoLength(bfc(*c)),
+ EchoLength(fc(*c)));
+ return AsciiToFull(str[i]);
+} /* end EchoConstraint */
+
+
+/*****************************************************************************/
+/* */
+/* DebugConstrained(x) */
+/* */
+/* Calculate and print the constraints of all closures lying within */
+/* sized object x. */
+/* */
+/*****************************************************************************/
+
+void DebugConstrained(OBJECT x)
+{ OBJECT y, link, why;
+ CONSTRAINT c;
+ debug1(DSC, DDD, "DebugConstrained( %s )", EchoObject(x) );
+ switch( type(x) )
+ {
+
+ case CROSS:
+ case FORCE_CROSS:
+ case ROTATE:
+ case BACKGROUND:
+ case INCGRAPHIC:
+ case SINCGRAPHIC:
+ case PLAIN_GRAPHIC:
+ case GRAPHIC:
+ case KERN_SHRINK:
+ case WORD:
+ case QWORD:
+ case START_HVSPAN:
+ case START_HSPAN:
+ case START_VSPAN:
+ case HSPAN:
+ case VSPAN:
+
+ break;
+
+
+ case CLOSURE:
+
+ Constrained(x, &c, COLM, &why);
+ debug2(DSC, DD, "Constrained( %s, &c, COLM ) = %s",
+ EchoObject(x), EchoConstraint(&c));
+ Constrained(x, &c, ROWM, &why);
+ debug2(DSC, DD, "Constrained( %s, &c, ROWM ) = %s",
+ EchoObject(x), EchoConstraint(&c));
+ break;
+
+
+ case SPLIT:
+
+ link = DownDim(x, COLM); Child(y, link);
+ DebugConstrained(y);
+ break;
+
+
+ case HEAD:
+ case ONE_COL:
+ case ONE_ROW:
+ case HCONTRACT:
+ case VCONTRACT:
+ case HLIMITED:
+ case VLIMITED:
+ case HEXPAND:
+ case VEXPAND:
+ case HSCALE:
+ case VSCALE:
+ case HCOVER:
+ case VCOVER:
+ case SCALE:
+ case WIDE:
+ case HIGH:
+
+ link = Down(x); Child(y, link);
+ DebugConstrained(y);
+ break;
+
+
+ case COL_THR:
+ case VCAT:
+ case HCAT:
+ case ACAT:
+
+ for( link = Down(x); link != x; link =NextDown(link) )
+ { Child(y, link);
+ if( type(y) != GAP_OBJ && !is_index(type(y)) ) DebugConstrained(y);
+ }
+ break;
+
+
+ default:
+
+ assert1(FALSE, "DebugConstrained:", Image(type(x)));
+ break;
+
+ }
+ debug0(DSC, DDD, "DebugConstrained returning.");
+} /* end DebugConstrained */
+#endif