In his book The Elements of Typographic Style, Bringhurst presents a hexagonal pattern for geometrically constructing the elements of the page layout.

Question: Can the dimensions be simply extracted geometrically using TikZ, tkz-eu­clide, Metafont/post, or similar LaTeX packages? (Geometric elegance is key here, not just a brute force solution demonstrating that TeX is turing complete, and hence can solve the implicit problem.)

I originally thought that this would be a great example of the geometric methods available through, for example, tkz-eu­clide, but have been unable to come up with an explicit construction. Of course, one can follow the construction through to obtain an implicit set of equations with a unique solution which I have used to calculate the parameters in the following code, but can one solve the problem elegantly using the geometric methods found in these packages?


    \path (0,0) coordinate[label=below:{ll}] (ll)
    -- (1,0) coordinate[label=below:{lr}] (lr)
    -- ++(60:1) coordinate[label=right:{r}] (r)
    -- ++(120:1) coordinate[label=above:{ur}] (ur)
    -- ++(180:1) coordinate[label=above:{ul}] (ul)
    -- ++(240:1) coordinate[label=left:{l}] (l)
    -- ++(300:1) coordinate (ll);
    \coordinate[label=below right:{a}] 
                  (a) at (1.27106916798686, 0.469505571318668);
    \coordinate (tll) at (0.117300021398114, 0.294835256924744);
    \coordinate (tlr) at (0.798192881206518, 0.294835256924744);
    \coordinate (tur) at (0.798192881206518, 1.61553741320908);
    \coordinate (tul) at (0.117300021398114, 1.61553741320908);
    \coordinate[label=below:{x}] (x) at (0.435936138301, 0);
    \coordinate[label=above:{y}] (y)   at (0.689095323638, 1.73205080756888);
    \tkzInterLL(l,ur)(a,ul) \tkzGetPoint{A}
    \tkzInterLL(ll,y)(ul,a) \tkzGetPoint{B}
    \tkzInterLL(l,ur)(ll,y) \tkzGetPoint{C} 
    \tkzInterLL(ll,a)(l,x) \tkzGetPoint{D}
    \tkzDefCircle[in](A,B,C) \tkzGetPoint{U} \tkzGetLength{rU}
    \tkzDefCircle[in](tll,tlr,D) \tkzGetPoint{L} \tkzGetLength{rL}

    % Locate page number
    \tkzInterLL(L,Lr)(tur,tlr) \tkzGetPoint{pagenumber}

    % Locate running header
    \tkzDefShiftPoint[U](0,\rU pt){Uu}
    \tkzInterLL(Uu,Uur)(y,r) \tkzGetPoint{header}

    \draw (ll) -- (lr) -- (r) -- (ur) -- (ul) -- (l) -- cycle;
    \draw (ur) -- (l) -- (x) -- (r) -- (y) -- (ll) -- (a) -- (ul);
    \draw (l) -- (ur);
    \draw (tul) -- (tur) -- (tlr) -- (tll) -- cycle;
    \draw (ul) -- (ur) -- (lr) -- (ll) -- cycle;

    \tkzDrawCircle[R](U, \rU pt)
    \tkzDrawCircle[R](L, \rL pt)
    \tkzDrawSegments(Uu,header L,pagenumber)

Bringhurst's page layout with Hexagonal Pattern

Update: To clarify the problem, in addition to the intersection constraints implicit in the picture, one must also impose that the textblock be rectangular with strict vertical/horizontal orientation. This, along with the constraints that the points lie within the hexagon (not somewhere outside) are enough to ensure that the solution is unique up to an overall scaling that is fixed by the roughly 5.2"x9" physical paper size used for Bringhurst's book. (As mentioned by @percusse, the hexagon gives an exact ratio 5.2:5.2*2*cos(30º) = 5.2:9.006664202....)

The construction can be completely specified with simple geometric intersections etc. if two numbers are specified. In particular, one can specify the fraction y along ul--ur and x along ll--lr (z.y=y[z.ul,z.ur] and z.x=x[z.ll,z.lr] in metapost notation).

The exact solution can be found algebraically to be:

q = (81 - √3186)^(1/3)
y = q/3 - 2 + 5/q ≈ 0.689095323637659465372247907...
x = (y^2 + 4*y + 3)/(-2*y^2 - 4*y + 18) ≈ 0.4359361383008273751170828497...

Additional consistent solutions exist, but the points x and y lie outside of the corresponding sides of the hexagon: this is the unique solution with the textblock lying within the page. Since these are solutions of cubic equations, I have my doubts that a complete solution lies within the linear solving abilities of Metapost, but think there still might be a nice geometric construction.

Reference: This answer to the question: Bringhurst chapter style in memoir

  • What now? You might add information which dimensions you actually need. What is the mathematical, geometrical background of this? How are x, y and a defined? Anyway, if TikZ can draw it, it can tell you how long it is. Jan 8 '13 at 21:49
  • Is there actually a unique solution? The points x and y appear to be arbitrary and independent. Jan 8 '13 at 22:14
  • @Brent.Longborough: If you require the page and text-area to be rectangular, and the lines to go through the points as specified, then there is a unique solutionk but it seems you must first write the equations implicitly and then solve... I can't find a good geometric solution.
    – mforbes
    Jan 8 '13 at 23:30
  • @Qrrbrbirlbel: x, y, and a are implicitly defined by the geometric constraints in the picture and the constraints that the page and text-block are rectangular with vertical alignment.
    – mforbes
    Jan 8 '13 at 23:34
  • We know the dimensions of the page and the text area as well? Or just the page?
    – Scott H.
    Jan 9 '13 at 2:04

Just to get the ball rolling (and to get to play with MetaPost), here is what I have so far: (updated with a few ideas I had, I still need to test out Barbara’s and mforbes’ hints and ideas)

  defaultscale := .8; % make the text a little smaller
  u := 1.5in;
  phi := 1.618033988749895;

  for i:= 0 upto 5: z.hexc[i] = right scaled u rotated (i*360/6); endfor;
  path hexa; hexa := for i:=0 upto5: z.hexc[i]--endfor cycle;
  fill hexa withcolor .9[green,white]; draw hexa;
  dotlabel.rt("r", z.hexc0); dotlabel.urt("ur", z.hexc1);
  dotlabel.ulft("ul", z.hexc2); dotlabel.lft("l", z.hexc3);
  dotlabel.llft("ll", z.hexc4); dotlabel.lrt("lr", z.hexc5);

  path recto; recto := z.hexc1--z.hexc2--z.hexc4--z.hexc5--cycle;
  path verso; verso := recto shifted (-1u,0);
  for i:=verso,recto:%,recto shifted (1u,0):
    draw i withpen pencircle scaled 1.2; endfor;

  picture mforbes; mforbes :=
    z.a   = (1.27106916798686u, 0.469505571318668u);
    z.tll = (0.117300021398114u, 0.294835256924744u);
    z.tlr = (0.798192881206518u, 0.294835256924744u);
    z.tur = (0.798192881206518u, 1.61553741320908u);
    z.tul = (0.117300021398114u, 1.61553741320908u);
    z.x   = (0.435936138301u, 0);
    z.y   = (0.689095323638u, 1.73205080756888u);
  drawoptions( withcolor .5 red );
  draw mforbes shifted llcorner recto;

  % Bringhurst's lines:
  draw z.hexc1--z.hexc3--z.x shifted llcorner recto--z.hexc0--
    z.y shifted llcorner recto--z.hexc4--z.a shifted llcorner recto--z.hexc2
    %dashed evenly

  % Van de Graaf: http://en.wikipedia.org/wiki/Canons_of_page_construction
  picture van_de_Graaf; van_de_Graaf := image(
    path Van_de_Graaf[];

    Van_de_Graaf0 := ulcorner verso--lrcorner recto--ulcorner recto
      --llcorner verso--urcorner recto;
    draw Van_de_Graaf0 dashed evenly;

    % subpath (0,1) is the first line, (1,2) is the second, etc.
    z.vdG0 = (subpath(3,4) of Van_de_Graaf0) intersectiontimes
      (subpath(3,4) of hexa);
    z.vdG1 = point xpart z.vdG0 of subpath(3,4) of hexa; % show this point
    z.vdG2 = point ypart z.vdG0 of subpath(1,2) of hexa; % on this path
    % ypart(z.vdG1) == xpart(z.vdG1) ?
    dotlabel.lft("vdG1", z.vdG1);
    dotlabel.urt("vdG2", z.vdG2);
    % Close, but no cigar. Back to the drawing board.

    % van de Graaf continued to project lines from the intersections:
    z.vdG3 = (subpath(1,2) of Van_de_Graaf0) intersectionpoint
      (subpath(3,4) of Van_de_Graaf0);
    z.vdG4 = (xpart z.vdG3, ypart urcorner recto);
    z.vdG5 = (subpath(0,1) of Van_de_Graaf0) intersectionpoint
      (subpath(2,3) of Van_de_Graaf0);
    z.vdG6 = (xpart z.vdG5, ypart urcorner verso);
    dotlabels.ulft(vdG3, vdG4, vdG5, vdG6);
    Van_de_Graaf1 := (z.vdG5)--(z.vdG4);
    Van_de_Graaf2 := (z.vdG6)--(z.vdG3);
    drawoptions( dashed evenly );
    draw Van_de_Graaf1; draw Van_de_Graaf2;
    % Nope. Not even close.

    z.guess_a = whatever[z.vdG6,z.vdG3] = whatever[z.hexc0,z.hexc5];
    % Did I hit it? Pleasepleasepleasepleaseplease...
    dotlabel.lrt("guess a", z.guess_a);
    % Nope.

    % That intersection of cross-page vdG's looks promising
  draw van_de_Graaf withcolor .5 white;

  z.golden1 = point 1/phi of subpath(5,4) of hexa;
  z.golden2 = point 1/phi of subpath(2,1) of hexa;
  drawoptions( withcolor .5[green,red] );
  dotlabel.bot(btex $\phi$ etex, z.golden1);
  dotlabel.top(btex $\phi$ etex, z.golden2);


(I haven't used MetaPost much, so if you have suggestions on improving, please share)

enter image description here

  • This is the type of construction that I tried, but as you see, unless you know a couple of exact values beforehand, such constructions will not give a properly oriented rectangular text-block. The constraint of a rectangular (and vertical) text-block defines the (unique?) solution. My question is: can one elegantly construct and solve for this solution geometrically without first having to know one or more of the special parameters? (I was hoping that MetaPost would allow me to form the constraints, and would then solve the system for me, but I still don't see how.)
    – mforbes
    Jan 12 '13 at 17:07
  • @mforbes, Yeah I was merely trying to get people to see this question. I'll put some juicy bounty up to further tingle people's interest. MetaPost has a system for solving linear equations for example with its whatever-notation (so for example whatever[z3,z3]), but I don't know how to tell it the rectangular constraints. Though, after seeing some jaw-dropping examples done with MetaPost I have a feeling it is doable.
    – morbusg
    Jan 12 '13 at 19:08
  • I am still hoping that it is doable, but unfortunately, I think that – unless some geometric trick/feature is found – the resulting equations are going to be non-linear. (P.S. You can remove a as a mystery point by constraining right text-block border to be vertical. P.P.S. your labelling is a little different than mine: ll etc. in my diagram are the page, not text boundaries.)
    – mforbes
    Jan 12 '13 at 21:57
  • 1
    You could possibly use dotprod to access the constraint that the sides of the type area form a rectangle (i.e. dotprod = 0 for adjacent sides). It seems that you can use whatevers in dot product expressions. I played around with it for a bit but because I have no idea what I'm doing, I couldn't make it work.
    – Scott H.
    Jan 16 '13 at 2:25
  • 1
    @barbarabeeton: Your square is very close to a square, but is not exactly a square (the slope of the diagonal is 1.0147...). @ScottH.: That is the idea I had, but the problem is that the whatever whatevers I use, I end up with non-linear equations. Given the exact relationships outlined in my update, I suspect that one may be able to construct x from y using whatevers but think that getting y might be out of the question.
    – mforbes
    Jan 19 '13 at 0:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.