1 /*@z15.c:Size Constraints:MinConstraint(), EnlargeToConstraint()@*************/
2 /*                                                                           */
3 /*  THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.39)                       */
4 /*  COPYRIGHT (C) 1991, 2008 Jeffrey H. Kingston                             */
5 /*                                                                           */
6 /*  Jeffrey H. Kingston (jeff@it.usyd.edu.au)                                */
7 /*  School of Information Technologies                                       */
8 /*  The University of Sydney 2006                                            */
9 /*  AUSTRALIA                                                                */
10 /*                                                                           */
11 /*  This program is free software; you can redistribute it and/or modify     */
12 /*  it under the terms of the GNU General Public License as published by     */
13 /*  the Free Software Foundation; either Version 3, or (at your option)      */
14 /*  any later version.                                                       */
15 /*                                                                           */
16 /*  This program is distributed in the hope that it will be useful,          */
17 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
18 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
19 /*  GNU General Public License for more details.                             */
20 /*                                                                           */
21 /*  You should have received a copy of the GNU General Public License        */
22 /*  along with this program; if not, write to the Free Software              */
23 /*  Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA   */
24 /*                                                                           */
25 /*  FILE:         z15.c                                                      */
26 /*  MODULE:       Size Constraints                                           */
27 /*  EXTERNS:      MinConstraint(), EnlargeToConstraint(),                    */
28 /*                ReflectConstraint(), SemiRotateConstraint(),               */
29 /*                RotateConstraint(), InvScaleConstraint(), Constrained(),   */
30 /*                EchoConstraint(), DebugConstrained()                       */
31 /*                                                                           */
32 /*****************************************************************************/
33 #include <math.h>
34 #ifndef M_PI
35 #define M_PI 3.14159265358979323846
36 #endif
37 #include "externs.h"
38 
39 
40 /*****************************************************************************/
41 /*                                                                           */
42 /*  MinConstraint(xc, yc)                                                    */
43 /*                                                                           */
44 /*  Replace *xc by the minimum of the two constraints *xc and *yc.           */
45 /*                                                                           */
46 /*****************************************************************************/
47 
MinConstraint(CONSTRAINT * xc,CONSTRAINT * yc)48 void MinConstraint(CONSTRAINT *xc, CONSTRAINT *yc)
49 { bc(*xc)  = find_min(bc(*xc),  bc(*yc));
50   bfc(*xc) = find_min(bfc(*xc), bfc(*yc));
51   fc(*xc)  = find_min(fc(*xc),  fc(*yc));
52 } /* end MinConstraint */
53 
54 
55 /*****************************************************************************/
56 /*                                                                           */
57 /*  SetSizeToMaxForwardConstraint(b, f, c)                                   */
58 /*                                                                           */
59 /*  Set *b, *f to their largest possible value within constraint *c, such    */
60 /*  that *f is as large as possible.                                         */
61 /*                                                                           */
62 /*****************************************************************************/
63 
SetSizeToMaxForwardConstraint(FULL_LENGTH * b,FULL_LENGTH * f,CONSTRAINT * c)64 void SetSizeToMaxForwardConstraint(FULL_LENGTH *b, FULL_LENGTH *f, CONSTRAINT *c)
65 {
66   *f = find_min(bfc(*c), fc(*c));
67   *b = find_min(bc(*c), bfc(*c) - *f);
68 } /* end EnlargeToConstraint */
69 
70 
71 /*****************************************************************************/
72 /*                                                                           */
73 /*  EnlargeToConstraint(b, f, c)                                             */
74 /*                                                                           */
75 /*  Enlarge *b,*f to its largest possible value within constraint *c.        */
76 /*                                                                           */
77 /*****************************************************************************/
78 
EnlargeToConstraint(FULL_LENGTH * b,FULL_LENGTH * f,CONSTRAINT * c)79 void EnlargeToConstraint(FULL_LENGTH *b, FULL_LENGTH *f, CONSTRAINT *c)
80 {
81   *f = find_min(bfc(*c) - *b, fc(*c));
82 } /* end EnlargeToConstraint */
83 
84 
85 /*****************************************************************************/
86 /*                                                                           */
87 /*  ReflectConstraint(xc, yc)                                                */
88 /*                                                                           */
89 /*  Set xc to the constraint which is yc with its back and forward reversed. */
90 /*                                                                           */
91 /*****************************************************************************/
92 
93 #define ReflectConstraint(xc, yc)  SetConstraint(xc, fc(yc), bfc(yc), bc(yc))
94 
95 
96 /*@::ScaleToConstraint(), InvScaleConstraint(), etc@**************************/
97 /*                                                                           */
98 /*  int ScaleToConstraint(b, f, c)                                           */
99 /*                                                                           */
100 /*  Return the scale factor needed to scale object of size b, f down so it   */
101 /*  has a size which fits tightly into constraint c.                         */
102 /*                                                                           */
103 /*****************************************************************************/
104 
ScaleToConstraint(FULL_LENGTH b,FULL_LENGTH f,CONSTRAINT * c)105 int ScaleToConstraint(FULL_LENGTH b, FULL_LENGTH f, CONSTRAINT *c)
106 { float scale_factor;  int res;
107   debug3(DSC, DD, "ScaleToConstraint(%s, %s, %s)", EchoLength(b),
108     EchoLength(f), EchoConstraint(c));
109   scale_factor = 1.0;
110   if( b     > 0 )  scale_factor = find_min(scale_factor, (float) bc(*c)/b       );
111   if( b + f > 0 )  scale_factor = find_min(scale_factor, (float) bfc(*c)/(b + f));
112   if(     f > 0 )  scale_factor = find_min(scale_factor, (float) fc(*c)/f       );
113   res = scale_factor * SF;
114   debug2(DSC, DD, "ScaleToConstraint returning %.2f (%d)", scale_factor, res);
115   return res;
116 } /* end ScaleToConstraint */
117 
118 
119 /*****************************************************************************/
120 /*                                                                           */
121 /*  InvScaleConstraint(yc, sf, xc)                                           */
122 /*                                                                           */
123 /*  Scale constraint xc to the inverse of the scale factor sf.               */
124 /*                                                                           */
125 /*****************************************************************************/
126 
InvScaleConstraint(CONSTRAINT * yc,FULL_LENGTH sf,CONSTRAINT * xc)127 void InvScaleConstraint(CONSTRAINT *yc, FULL_LENGTH sf, CONSTRAINT *xc)
128 {
129 #if DEBUG_ON
130   char buff[10];
131 #endif
132   ifdebug(DSC, DD, sprintf(buff, "%.3f", (float) sf / SF));
133   debug2(DSC, DD, "InvScaleConstraint(yc, %s, %s)", buff, EchoConstraint(xc));
134   assert( sf > 0, "InvScaleConstraint: sf <= 0!" );
135   bc(*yc)  = bc(*xc)  == MAX_FULL_LENGTH ? MAX_FULL_LENGTH :
136     find_min(MAX_FULL_LENGTH, bc(*xc) * SF / sf);
137   bfc(*yc) = bfc(*xc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH :
138     find_min(MAX_FULL_LENGTH, bfc(*xc)* SF / sf);
139   fc(*yc)  = fc(*xc)  == MAX_FULL_LENGTH ? MAX_FULL_LENGTH :
140     find_min(MAX_FULL_LENGTH, fc(*xc) * SF / sf);
141   debug1(DSC, DD, "InvScaleConstraint returning %s", EchoConstraint(yc));
142 } /* end InvScaleConstraint */
143 
144 
145 /*****************************************************************************/
146 /*                                                                           */
147 /*  static SemiRotateConstraint(xc, u, v, angle, yc)                         */
148 /*                                                                           */
149 /*  Used by RotateConstraint to calculate one rotated constraint.            */
150 /*                                                                           */
151 /*****************************************************************************/
152 
SemiRotateConstraint(CONSTRAINT * xc,FULL_LENGTH u,FULL_LENGTH v,float angle,CONSTRAINT * yc)153 static void SemiRotateConstraint(CONSTRAINT *xc, FULL_LENGTH u, FULL_LENGTH v,
154 float angle, CONSTRAINT *yc)
155 { float cs, sn;
156 #if DEBUG_ON
157   char buff[20];
158 #endif
159   ifdebug(DSC, DD, sprintf(buff, "%.1f", angle * 360.0 / (2 * M_PI)));
160   debug4(DSC, DD, "SemiRotateConstraint(xc, %s, %s, %sd, %s",
161     EchoLength(u), EchoLength(v), buff, EchoConstraint(yc));
162   cs = cos(angle);  sn = sin(angle);
163   if( fabs(cs) < 1e-6 )
164     SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
165   else
166     SetConstraint(*xc,
167       find_min(MAX_FULL_LENGTH, (bc(*yc) - u * sn) / cs),
168       find_min(MAX_FULL_LENGTH, (bfc(*yc) - u * sn - v * sn) / cs),
169       find_min(MAX_FULL_LENGTH, (fc(*yc) - v * sn) / cs ));
170   debug1(DSC, DD, "SemiRotateConstraint returning %s", EchoConstraint(xc));
171 } /* end SemiRotateConstraint */
172 
173 
174 /*@::RotateConstraint()@******************************************************/
175 /*                                                                           */
176 /*  RotateConstraint(c, y, angle, hc, vc, dim)                               */
177 /*                                                                           */
178 /*  Take the object angle @Rotate y, which is supposed to be constrained     */
179 /*  horizontally by hc and vertically by vc, and determine a constraint      */
180 /*  (either horizontal or vertical, depending on dim) for y.                 */
181 /*                                                                           */
182 /*  The constraint returned is a trigonometric function of all these         */
183 /*  parameters, including the present size of y in dimension 1-dim.          */
184 /*                                                                           */
185 /*****************************************************************************/
186 
RotateConstraint(CONSTRAINT * c,OBJECT y,FULL_LENGTH angle,CONSTRAINT * hc,CONSTRAINT * vc,int dim)187 void RotateConstraint(CONSTRAINT *c, OBJECT y, FULL_LENGTH angle,
188 CONSTRAINT *hc, CONSTRAINT *vc, int dim)
189 { CONSTRAINT c1, c2, c3, dc;  float theta, psi;
190 #if DEBUG_ON
191   char buff[20];
192 #endif
193   ifdebug(DSC, DD, sprintf(buff, "%.1f", (float) angle / DG ));
194   debug4(DSC, DD, "RotateConstraint(c, y, %sd, %s, %s, %s)",
195 	buff, EchoConstraint(hc), EchoConstraint(vc), dimen(dim));
196 
197   /* work out angle in radians between 0 and 2*M_PI */
198   theta = (float) angle * 2 * M_PI / (float) (DG * 360);
199   while( theta < 0 ) theta += 2 * M_PI;
200   while( theta >= 2 * M_PI ) theta -= 2 * M_PI;
201   assert( 0 <= theta && theta <= 2 * M_PI, "RotateConstraint: theta!" );
202 
203   /* determine theta, c1, and c2 depending on which quadrant we are in */
204   if( theta <= M_PI / 2.0 )   /* first quadrant */
205   { theta = theta;
206     CopyConstraint(c1, *hc);
207     CopyConstraint(c2, *vc);
208   }
209   else if ( theta <= M_PI )   /* second quadrant */
210   { theta -= M_PI / 2.0;
211     ReflectConstraint(c1, *vc);
212     CopyConstraint(c2, *hc);
213   }
214   else if ( theta <= 3.0 * M_PI / 2.0 )   /* third quadrant */
215   { theta -= M_PI;
216     ReflectConstraint(c1, *hc);
217     ReflectConstraint(c2, *vc);
218   }
219   else /* fourth quadrant */
220   { theta -= 3.0 * M_PI / 2.0;
221     CopyConstraint(c1, *vc);
222     ReflectConstraint(c2, *hc);
223   }
224   psi = M_PI / 2.0 - theta;
225   debug2(DSC, DD, "  c1: %s;  c2: %s", EchoConstraint(&c1), EchoConstraint(&c2));
226 
227   /* return the minimum of the two constraints, rotated */
228   if( dim == COLM )
229   { SemiRotateConstraint(c, back(y, ROWM), fwd(y, ROWM), theta, &c1);
230     ReflectConstraint(c3, c2);
231     SemiRotateConstraint(&dc, fwd(y, ROWM), back(y, ROWM), psi, &c3);
232     MinConstraint(c, &dc);
233   }
234   else
235   { SemiRotateConstraint(c, back(y, COLM), fwd(y, COLM), psi, &c1);
236     SemiRotateConstraint(&dc, fwd(y, COLM), back(y, COLM), theta, &c2);
237     MinConstraint(c, &dc);
238   }
239 
240   debug1(DSC, DD, "RotateConstraint returning %s", EchoConstraint(c));
241 } /* end RotateConstraint */
242 
243 /*@::InsertScale()@***********************************************************/
244 /*                                                                           */
245 /*  BOOLEAN InsertScale(x, c)                                                */
246 /*                                                                           */
247 /*  Insert a @Scale object above x so that x is scaled horizontally to fit   */
248 /*  constraint c.  If this is not possible, owing to the necessary scale     */
249 /*  factor being too small, then don't do it; return FALSE instead.          */
250 /*                                                                           */
251 /*****************************************************************************/
252 
InsertScale(OBJECT x,CONSTRAINT * c)253 BOOLEAN InsertScale(OBJECT x, CONSTRAINT *c)
254 { int scale_factor; OBJECT prnt;
255   scale_factor = ScaleToConstraint(back(x, COLM), fwd(x, COLM), c);
256   if( scale_factor >= 0.2 * SF )
257   {
258     New(prnt, SCALE);
259     underline(prnt) = underline(x);
260     FposCopy(fpos(prnt), fpos(x));
261 
262     /* set horizontal size and scale factor */
263     bc(constraint(prnt)) = scale_factor;
264     back(prnt, COLM) = ( back(x, COLM) * scale_factor ) / SF;
265 
266     /* *** slightly too small?
267     fwd(prnt,  COLM) = ( fwd(x,  COLM) * scale_factor ) / SF;
268     *** */
269     fwd(prnt,  COLM) = find_min(bfc(*c) - back(prnt, COLM), fc(*c));
270 
271     /* set vertical size and scale factor */
272     fc(constraint(prnt)) = 1 * SF;
273     back(prnt, ROWM) = back(x, ROWM);
274     fwd(prnt, ROWM) = fwd(x, ROWM);
275 
276     /* link prnt above x and return */
277     ReplaceNode(prnt, x);
278     Link(prnt, x);
279     return TRUE;
280   }
281   else return FALSE;
282 } /* end InsertScale */
283 
284 
285 /*@::CatConstrained()@********************************************************/
286 /*                                                                           */
287 /*  static CatConstrained(x, xc, ratm, y, dim, OBJECT *why)                  */
288 /*                                                                           */
289 /*  Calculate the size constraint of object x, as for Constrained below.     */
290 /*  y is the enclosing VCAT etc. object;  ratm is TRUE if a ^ lies after     */
291 /*  x anywhere.  dim is COLM or ROWM.                                        */
292 /*                                                                           */
293 /*  The meaning of the key variables is as follows:                          */
294 /*                                                                           */
295 /*  be       The amount by which back(x, dim) can increase from zero         */
296 /*           without having any impact on size(y, dim).  Thereafter,         */
297 /*           any increase causes an equal increase in size(y, dim).          */
298 /*                                                                           */
299 /*  fe       The amount by which fwd(x, dim) can increase from zero          */
300 /*           without having any impact on size(y, dim).  Thereafter,         */
301 /*           any increase causes an equal increase in size(y, dim).          */
302 /*                                                                           */
303 /*  backy,   The value that back(y, dim) and fwd(y, dim) would have if x     */
304 /*  fwdy     was definite with size 0,0.  They will in general be larger     */
305 /*           than the present values if x is indefinite, and smaller         */
306 /*           if x is definite, although it depends on marks and gaps.        */
307 /*                                                                           */
308 /*****************************************************************************/
309 
CatConstrained(OBJECT x,CONSTRAINT * xc,BOOLEAN ratm,OBJECT y,int dim,OBJECT * why)310 static void CatConstrained(OBJECT x, CONSTRAINT *xc, BOOLEAN ratm,
311 OBJECT y, int dim, OBJECT *why)
312 { int side;			/* the size of y that x is on: BACK, ON, FWD */
313   CONSTRAINT yc;		/* constraints on y                          */
314   FULL_LENGTH backy=0, fwdy=0;	/* back(y), fwd(y) would be if x was (0, 0)  */
315   FULL_LENGTH be, fe;		/* amount back(x), fwd(x) can be for free    */
316   FULL_LENGTH beffect, feffect;	/* scratch variables for calculations        */
317   FULL_LENGTH seffect;		/* scratch variables for calculations        */
318   OBJECT link, sg, pg;	/* link to x, its successor and predecessor  */
319   OBJECT prec_def, sd;	/* definite object preceding (succeeding) x  */
320   int tb, tbf, tf, tbc, tbfc, tfc, mxy, myz;
321 
322   Constrained(y, &yc, dim, why);
323   if( constrained(yc) )
324   {
325     /* find the link of x, and its neighbours and their links */
326     link = UpDim(x, dim);
327     SetNeighbours(link, ratm, &pg, &prec_def, &sg, &sd, &side);
328 
329     /* amount of space available at x without changing the size of y */
330     be = pg == nilobj ? 0 : ExtraGap(fwd(prec_def, dim), 0, &gap(pg), BACK);
331     fe = sg == nilobj ? 0 : ExtraGap(0, back(sd, dim),      &gap(sg), FWD);
332 
333     if( is_indefinite(type(x)) )
334     {
335       /* insert two lengths and delete one */
336       beffect = pg==nilobj ? 0 : MinGap(fwd(prec_def, dim), 0, 0, &gap(pg));
337       feffect = sg==nilobj ? 0 : MinGap(0, back(sd,dim), fwd(sd,dim), &gap(sg));
338       seffect = pg==nilobj ?
339 	  sg == nilobj ? 0 : back(sd, dim) :
340 	  sg == nilobj ? fwd(prec_def, dim) :
341 	    MinGap(fwd(prec_def, dim), back(sd, dim), fwd(sd, dim), &gap(sg));
342 
343       switch( side )
344       {
345 	case BACK:	backy = back(y, dim) + beffect + feffect - seffect;
346 			fwdy  = fwd(y, dim);
347 			break;
348 
349 	case ON:	/* must be first, other cases prohibited */
350 			backy = 0;
351 			fwdy = fwd(y, dim) + feffect;
352 			break;
353 
354 	case FWD:	backy = back(y, dim);
355 			fwdy  = fwd(y, dim) + beffect + feffect - seffect;
356 			break;
357       }
358     }
359 
360     else /* x is definite */
361 
362     { beffect = pg == nilobj ? back(x, dim) :
363 	MinGap(fwd(prec_def, dim), back(x,dim), fwd(x,dim), &gap(pg)) -
364 	MinGap(fwd(prec_def, dim), 0,           0,          &gap(pg));
365 
366       feffect = sg == nilobj ? fwd(x, dim) :
367 	MinGap(fwd(x, dim), back(sd, dim), fwd(sd, dim), &gap(sg)) -
368 	MinGap(0,           back(sd, dim), fwd(sd, dim), &gap(sg));
369 
370       switch( side )
371       {
372 	case BACK:	backy = back(y, dim) - beffect - feffect;
373 			fwdy  = fwd(y, dim);
374 			break;
375 
376 	case ON:	backy = back(y, dim) - beffect;
377 			fwdy  = fwd(y, dim)  - feffect;
378 			break;
379 
380 	case FWD:	backy = back(y, dim);
381 			fwdy  = fwd(y, dim) - beffect - feffect;
382 			break;
383       }
384     }
385 
386     debug5(DSC, DD, "  side: %s, backy: %s, fwdy: %s, be: %s, fe: %s",
387 		Image(side), EchoLength(backy), EchoLength(fwdy),
388 		EchoLength(be), EchoLength(fe) );
389 
390     if( !FitsConstraint(backy, fwdy, yc) )
391       SetConstraint(*xc, -1, -1, -1);
392     else switch( side )
393     {
394 
395       case BACK:
396 
397 	tbc = bc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bc(yc) - backy;
398 	tbfc = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - backy - fwdy;
399 	mxy = find_min(tbc, tbfc);
400 	tb  = find_min(MAX_FULL_LENGTH, be + mxy);
401 	tbf = find_min(MAX_FULL_LENGTH, be + fe + mxy);
402 	tf  = find_min(MAX_FULL_LENGTH, fe + mxy);
403 	SetConstraint(*xc, tb, tbf, tf);
404 	break;
405 
406 
407       case ON:
408 
409 	tbc = bc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bc(yc) - backy;
410 	tbfc = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - backy - fwdy;
411 	tfc = fc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : fc(yc) - fwdy;
412 	mxy = find_min(tbc, tbfc);
413 	myz = find_min(tfc, tbfc);
414 	tb  = find_min(MAX_FULL_LENGTH, be + mxy);
415 	tbf = find_min(MAX_FULL_LENGTH, be + fe + tbfc);
416 	tf  = find_min(MAX_FULL_LENGTH, fe + myz);
417 	SetConstraint(*xc, tb, tbf, tf);
418 	break;
419 
420 
421       case FWD:
422 
423 	tfc = fc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : fc(yc) - fwdy;
424 	tbfc = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - backy - fwdy;
425 	mxy = find_min(tfc, tbfc);
426 	tb  = find_min(MAX_FULL_LENGTH, be + mxy);
427 	tbf = find_min(MAX_FULL_LENGTH, be + fe + mxy);
428 	tf  = find_min(MAX_FULL_LENGTH, fe + mxy);
429 	SetConstraint(*xc, tb, tbf, tf);
430 	break;
431 
432     }
433   } /* end if( constrained ) */
434   else SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
435 } /* end CatConstrained */
436 
437 
438 /*@::Constrained()@***********************************************************/
439 /*                                                                           */
440 /*  Constrained(x, xc, dim, why)                                             */
441 /*                                                                           */
442 /*  Calculate the size constraint of object x, and return it in *xc.         */
443 /*                                                                           */
444 /*  If the resulting constraint is a hard one caused by coming up against    */
445 /*  a HIGH (vertical) or WIDE (horizontal), set *why to this object; if      */
446 /*  not, leave *why unchanged.                                               */
447 /*                                                                           */
448 /*****************************************************************************/
449 
Constrained(OBJECT x,CONSTRAINT * xc,int dim,OBJECT * why)450 void Constrained(OBJECT x, CONSTRAINT *xc, int dim, OBJECT *why)
451 { OBJECT y, link, lp, rp, z, tlink, g;  CONSTRAINT yc, hc, vc;
452   BOOLEAN ratm;  FULL_LENGTH xback, xfwd;  int tb, tf, tbf, tbc, tfc;
453   SetLengthDim(dim);
454   debug2(DSC, DD, "[ Constrained(%s, xc, %s, why), x =",
455     Image(type(x)), dimen(dim));
456   ifdebug(DSC, DD, DebugObject(x));
457   assert( Up(x) != x, "Constrained: x has no parent!" );
458 
459   /* a CLOSURE which is external_ver is unconstrained in the ROWM direction */
460   /* a CLOSURE which is external_hor is unconstrained in both directions   */
461   if( type(x) == CLOSURE && ((dim==ROWM && external_ver(x)) || external_hor(x)) )
462   {
463     SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
464     debug1(DSC, DD, "] Constrained returning %s (external)",EchoConstraint(xc));
465     return;
466   }
467 
468   /* find y, the parent of x */
469   link = UpDim(x, dim);  ratm = FALSE;
470   for( tlink = NextDown(link);  type(tlink) == LINK;  tlink = NextDown(tlink) )
471   { Child(g, tlink);
472     if( type(g) == GAP_OBJ && mark(gap(g)) )  ratm = TRUE;
473   }
474   y = tlink;
475   debug1(DSC, DDD, "parent y = %s", Image(type(y)));
476   ifdebug(DSC, DDD, DebugObject(y));
477 
478   switch( type(y) )
479   {
480     case PLAIN_GRAPHIC:
481     case GRAPHIC:
482     case LINK_SOURCE:
483     case LINK_DEST:
484     case LINK_DEST_NULL:
485     case LINK_URL:
486     case KERN_SHRINK:
487     case BEGIN_HEADER:
488     case SET_HEADER:
489     case ONE_COL:
490     case ONE_ROW:
491     case HCONTRACT:
492     case VCONTRACT:
493     case HEXPAND:
494     case VEXPAND:
495     case START_HVSPAN:
496     case START_HSPAN:
497     case START_VSPAN:
498     case SPLIT:
499     case BACKGROUND:
500 
501       Constrained(y, xc, dim, why);
502       break;
503 
504 
505     case HMIRROR:
506     case VMIRROR:
507 
508       if( (dim == COLM) == (type(y) == HMIRROR) )
509       {
510         Constrained(y, &yc, dim, why);
511 	FlipConstraint(*xc, yc);
512       }
513       else
514         Constrained(y, xc, dim, why);
515       break;
516 
517 
518     case HSCALE:
519     case VSCALE:
520 
521       if( (dim == COLM) != (type(y) == HSCALE) )  Constrained(y, xc, dim, why);
522       else SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
523       break;
524 
525 
526     case HCOVER:
527     case VCOVER:
528 
529       /* dubious, but not likely to arise anyway */
530       if( (dim == COLM) != (type(y) == HCOVER) )  Constrained(y, xc, dim, why);
531       else SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
532       break;
533 
534 
535     case SCALE:
536 
537       Constrained(y, &yc, dim, why);
538       if( dim == COLM && bc(constraint(y)) == 0 )
539       {
540 	/* Lout-supplied factor required later, could be tiny */
541 	SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
542       }
543       else
544       { InvScaleConstraint(xc,
545 	  dim == COLM ? bc(constraint(y)) : fc(constraint(y)), &yc);
546       }
547       break;
548 
549 
550     case ROTATE:
551 
552       Constrained(y, &hc, COLM, why);  Constrained(y, &vc, ROWM, why);
553       RotateConstraint(xc, x, sparec(constraint(y)), &hc, &vc, dim);
554       break;
555 
556 
557     case WIDE:
558     case HIGH:
559 
560       Constrained(y, xc, dim, why);
561       if( (type(y)==WIDE) == (dim==COLM) )
562       { MinConstraint(xc, &constraint(y));
563 	*why = y;
564       }
565       break;
566 
567 
568     case HLIMITED:
569     case VLIMITED:
570 
571       if( (type(y) == HLIMITED) == (dim == COLM) )
572       {
573 	BOOLEAN still_searching = TRUE;
574 	z = y;
575 	SetConstraint(*xc, back(z, dim), size(z, dim), fwd(z, dim));
576 	debug2(DSC, D, "  [ %s (%s)", Image(type(z)), EchoConstraint(xc));
577 	while( still_searching && Up(z) != z )
578 	{
579           Parent(z, UpDim(z, dim));
580 	  switch( type(z) )
581 	  {
582 	    case VLIMITED:
583 	    case HLIMITED:
584 	    case COL_THR:
585 	    case ROW_THR:
586 	    case ONE_COL:
587 	    case ONE_ROW:
588 	    case HCONTRACT:
589 	    case VCONTRACT:
590 	    case SPLIT:
591 	    case START_VSPAN:
592 	    case START_HSPAN:
593 
594 	      SetConstraint(*xc, back(z, dim), size(z, dim), fwd(z, dim));
595 	      debug2(DSC, DD, "    let s = %s (%s)", Image(type(z)),
596 	        EchoConstraint(xc));
597 	      break;
598 
599 
600 	    case HSPANNER:
601 	    case VSPANNER:
602 
603 	      /* SpannerAvailableSpace(z, dim, &b, &f); */
604 	      CopyConstraint(*xc, constraint(z));
605 	      debug2(DSC, D, "  ] let s = %s (%s) and stop",
606 		Image(type(z)), EchoConstraint(&constraint(z)));
607 	      still_searching = FALSE;
608 	      break;
609 
610 
611 	    default:
612 
613 	      debug1(DSC, D, "  ] stopping at %s", Image(type(z)));
614 	      still_searching = FALSE;
615 	      break;
616 	  }
617 	}
618 	*why = y;
619       }
620       else
621       {
622         Constrained(y, xc, dim, why);
623       }
624       break;
625 
626 
627     case VSPANNER:
628     case HSPANNER:
629 
630       /* we're saying that a spanner has a fixed constraint that is */
631       /* determined just once in its life                           */
632       CopyConstraint(*xc, constraint(y));
633       debug2(DSC, DD, "  Constrained(%s) = %s", Image(type(y)), EchoConstraint(xc));
634       /* SetConstraint(*xc, back(y, dim), size(y, dim), fwd(y, dim)); */
635       break;
636 
637 
638     case HSHIFT:
639     case VSHIFT:
640 
641       if( (type(y) == HSHIFT) == (dim == COLM) )
642       { Constrained(y, &yc, dim, why);
643 	tf = FindShift(y, x, dim);
644 	SetConstraint(*xc,
645 	  find_min(bc(yc), bfc(yc)) - tf, bfc(yc), find_min(fc(yc), bfc(yc)) + tf);
646       }
647       else Constrained(y, xc, dim, why);
648       break;
649 
650 
651     case HEAD:
652 
653       if( dim == ROWM )
654 	SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
655       else
656       {	CopyConstraint(yc, constraint(y));
657 	debug1(DSC, DD, "  head: %s; val is:", EchoConstraint(&yc));
658 	ifdebug(DSC, DD, DebugObject(y));
659 	goto REST_OF_HEAD;   /* a few lines down */
660       }
661       break;
662 
663 
664     case COL_THR:
665     case ROW_THR:
666 
667       assert( (type(y)==COL_THR) == (dim==COLM), "Constrained: COL_THR!" );
668       Constrained(y, &yc, dim, why);
669       tb = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - fwd(y, dim);
670       tb = find_min(bc(yc), tb);
671       tf = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - back(y, dim);
672       tf = find_min(fc(yc), tf);
673       SetConstraint(*xc, tb, bfc(yc), tf);
674       break;
675 
676 
677     case VCAT:
678     case HCAT:
679     case ACAT:
680 
681       if( (type(y)==VCAT) == (dim==ROWM) )
682       {	CatConstrained(x, xc, ratm, y, dim, why);
683 	break;
684       }
685       Constrained(y, &yc, dim, why);
686       if( !constrained(yc) )
687 	SetConstraint(*xc, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
688       else
689       {
690 	REST_OF_HEAD:
691 	/* let lp and rp be the links of the gaps delimiting */
692 	/* the components joined to x (or parent if no such) */
693 	for( lp = PrevDown(link);  lp != y;  lp = PrevDown(lp) )
694 	{ Child(z, lp);
695 	  if( type(z) == GAP_OBJ && !join(gap(z)) )  break;
696 	}
697 	for( rp = NextDown(link);  rp != y;  rp = NextDown(rp) )
698 	{ Child(z, rp);
699 	  if( type(z) == GAP_OBJ && !join(gap(z)) )  break;
700 	}
701 	if( lp == y && rp == y && !(type(y) == HEAD && seen_nojoin(y)) )
702 	{
703 	  /* if whole object is joined, do this */
704           tb = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - fwd(y, dim);
705           tb = find_min(bc(yc), tb);
706           tf = bfc(yc) == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : bfc(yc) - back(y, dim);
707           tf = find_min(fc(yc), tf);
708           SetConstraint(*xc, tb, bfc(yc), tf);
709 	}
710 	else
711 	{
712 	  /* if // or || is present, do this */
713 	  xback = xfwd = 0;
714 	  for(link = NextDown(lp); link != rp;  link = NextDown(link) )
715 	  { Child(z, link);
716 	    if( type(z) == GAP_OBJ || is_index(type(z)) )  continue;
717 	    xback = find_max(xback, back(z, dim));
718 	    xfwd = find_max(xfwd, fwd(z, dim));
719 	  }
720 	  debug2(DSC, DD, "  lp != rp; xback,xfwd = %s,%s",
721 			EchoLength(xback), EchoLength(xfwd));
722 	  tbf = find_min(bfc(yc), fc(yc));
723 	  tbc = tbf == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : tbf - xfwd;
724 	  tfc = tbf == MAX_FULL_LENGTH ? MAX_FULL_LENGTH : tbf - xback;
725 	  SetConstraint(*xc, tbc, tbf, tfc);
726 	}
727       }
728       break;
729 
730 
731     default:
732 
733       assert1(FALSE, "Constrained:", Image(type(y)));
734       break;
735 
736   }
737   debug2(DSC, DD, "] Constrained %s returning %s", Image(type(x)),
738     EchoConstraint(xc));
739 } /* end Constrained */
740 
741 
742 /*@::EchoConstraint(), DebugConstrained()@************************************/
743 /*                                                                           */
744 /*  FULL_CHAR *EchoConstraint(c)                                             */
745 /*                                                                           */
746 /*  Returns a string showing constraint *c, in centimetres.                  */
747 /*                                                                           */
748 /*****************************************************************************/
749 #if DEBUG_ON
750 
EchoConstraint(CONSTRAINT * c)751 FULL_CHAR *EchoConstraint(CONSTRAINT *c)
752 { static char str[2][40];
753   static int i = 0;
754   i = (i+1) % 2;
755   sprintf(str[i], "<%s, %s, %s>", EchoLength(bc(*c)), EchoLength(bfc(*c)),
756     EchoLength(fc(*c)));
757   return AsciiToFull(str[i]);
758 } /* end EchoConstraint */
759 
760 
761 /*****************************************************************************/
762 /*                                                                           */
763 /*  DebugConstrained(x)                                                      */
764 /*                                                                           */
765 /*  Calculate and print the constraints of all closures lying within         */
766 /*  sized object x.                                                          */
767 /*                                                                           */
768 /*****************************************************************************/
769 
DebugConstrained(OBJECT x)770 void DebugConstrained(OBJECT x)
771 { OBJECT y, link, why;
772   CONSTRAINT c;
773   debug1(DSC, DDD, "DebugConstrained( %s )", EchoObject(x) );
774   switch( type(x) )
775   {
776 
777     case CROSS:
778     case FORCE_CROSS:
779     case ROTATE:
780     case BACKGROUND:
781     case INCGRAPHIC:
782     case SINCGRAPHIC:
783     case PLAIN_GRAPHIC:
784     case GRAPHIC:
785     case LINK_SOURCE:
786     case LINK_DEST:
787     case LINK_DEST_NULL:
788     case LINK_URL:
789     case KERN_SHRINK:
790     case WORD:
791     case QWORD:
792     case START_HVSPAN:
793     case START_HSPAN:
794     case START_VSPAN:
795     case HSPAN:
796     case VSPAN:
797 
798       break;
799 
800 
801     case CLOSURE:
802 
803       Constrained(x, &c, COLM, &why);
804       debug2(DSC, DD, "Constrained( %s, &c, COLM ) = %s",
805 	EchoObject(x), EchoConstraint(&c));
806       Constrained(x, &c, ROWM, &why);
807       debug2(DSC, DD, "Constrained( %s, &c, ROWM ) = %s",
808 	EchoObject(x), EchoConstraint(&c));
809       break;
810 
811 
812     case SPLIT:
813 
814       link = DownDim(x, COLM);  Child(y, link);
815       DebugConstrained(y);
816       break;
817 
818 
819     case HEAD:
820     case ONE_COL:
821     case ONE_ROW:
822     case HCONTRACT:
823     case VCONTRACT:
824     case HLIMITED:
825     case VLIMITED:
826     case HEXPAND:
827     case VEXPAND:
828     case HMIRROR:
829     case VMIRROR:
830     case HSCALE:
831     case VSCALE:
832     case HCOVER:
833     case VCOVER:
834     case SCALE:
835     case WIDE:
836     case HIGH:
837 
838       link = Down(x);  Child(y, link);
839       DebugConstrained(y);
840       break;
841 
842 
843     case COL_THR:
844     case VCAT:
845     case HCAT:
846     case ACAT:
847 
848       for( link = Down(x);  link != x;  link =NextDown(link) )
849       {	Child(y, link);
850 	if( type(y) != GAP_OBJ && !is_index(type(y)) )  DebugConstrained(y);
851       }
852       break;
853 
854 
855     default:
856 
857       assert1(FALSE, "DebugConstrained:", Image(type(x)));
858       break;
859 
860   }
861   debug0(DSC, DDD, "DebugConstrained returning.");
862 } /* end DebugConstrained */
863 #endif
864