Skip to content

Latest commit

 

History

History
441 lines (306 loc) · 29.4 KB

PackingStyles.md

File metadata and controls

441 lines (306 loc) · 29.4 KB

Arrangement of glasses on the page

Link to the main program: placemat.ps

Links to documentation: ▶︎ Introduction, and a first placemat  ▶︎ Fonts and glass decoration  ▶︎ Compound Strings and non‑ASCII characters  ▶︎ Page‑level controls  ▷︎ Arrangement of glasses on the page  ▶︎ Non‑Glasses Pages  ▶︎ Document‑level controls  ▶︎ Type sizes  ▶︎ Translations  ▶︎ Code injection  ▶︎ Bitmap images  ▶︎ Debugging


Introduction

The core purpose of the software is to have a place to put glasses. Already known are the PaperType (e.g., /A3), the margins, and how many glasses are to go on the page. Further, the user has much direct control over the layout of the glasses.

The user has much control, but the default is also good. Unless there is a compelling need to change things, users are encouraged to take the easy course of letting the defaults do their work.

The user specifies an array, PackingStyles, which is a list of generic packing styles. In turn each style from the list is taken, and each allowed variation is tested. Broadly, if a later-tested style allows a larger radius than the current best, then the old best is replaced by this better style.

If the element of PackingStyles is just the style, it doesn’t need to be in an array. But the elements of PackingStyles can have constraints and variations. If an element of PackingStyles is to have such extra detail, then that element must be an array, starting with the style name. E.g. (which is unrealistically verbose to show some of the possibilities):

[ /Diamonds  /OnlyIfSheetNumMin 1  /OnlyIfSheetNumMax 1  /GlassesNumMin 3  /GlassesNumMax 3  /OnlyIfOrientation /Landscape  /RowsNumMin 3  /RowsNumMax 4  /Mirror ]

The general mathematical problem, largest possible radius for n non-overlapping circles in a particular rectangle, is difficult (see PackoMania for solutions to many special cases). This code has enough generality that it often finds the best, and when it can’t, its radius is not much smaller than the mathematical optimum. E.g., for 14 glasses on /A3 see discussion in issue 151.

For many possible tastings, PackingStyles can be left at its default value. For example, if there are 24 different Madeiras over four /A4 pages, the defaults work (though, of course, very important, don’t forget to invite me). In such a case the default value of GlassesOnSheetsMaxPerSheet would cause GlassesOnSheets to be four page-defining arrays each of length six glasses, which would cause the chosen element of PackingStyles to be /RectangularDislocation.

But if table space were tight, it could be better to have fifteen on an /A3, six on an /A4, and three on the right side of an /A4, so using table space of only 210mm×3½ = 735mm per person.

There follow descriptions and pictures of the various allowable styles. (The two PDFs (non-/Array, /Array), from which the bitmaps were made, show the element of PackingStyles in the header.)

Efficient packings

There are several classes of packing which might use space quite efficiently.

Diamonds

/Diamonds: circles in each row and column lie midway between those in neighbouring rows and columns.

The code tests every number of rows from 1 to the number of glasses. If some of these are not wanted, for whatever reason, control is possible by the likes of [ /Diamonds /RowsNumMin 3 /RowsNumMax 4 ].

PackingStyles element
PaperType = width×height
Example
/Diamonds
Four glasses
/A4 = 210mm×297mm
A4_04_Diamonds
/Diamonds
Five glasses
/A4 = 210mm×297mm
A4_05_Diamonds
/Diamonds
Thirteen glasses
/A3 = 420mm×297mm
A3_13_Diamonds
/Diamonds
Nine glasses
/USLegal = 14″×8½″
USLegal_09_Diamonds
[ /Diamonds /Mirror ]
Nine glasses
/USLegal = 14″×8½″
USLegal_09_Diamonds_Mirror

The /Mirror flag chooses the alternate chirality, if that has a sensible meaning.

RectangularDislocation

/RectangularDislocation: The next four examples are rectangular, optionally with a horizontal ‘dislocation’ in the middle, that having, /Diamonds-style, circles lying between the circles in the adjacent row.

PackingStyles element
PaperType = width×height
Example
/RectangularDislocation
Five glasses
/A4 = 210mm×297mm
A4_05_RectangularDislocation
/RectangularDislocation
Six glasses
/A4 = 210mm×297mm
A4_06_RectangularDislocation
/RectangularDislocation
Eleven glasses
/A3 = 420mm×297mm
A3_11_RectangularDislocation
[ /RectangularDislocation /Mirror ]
Eleven glasses
/A3 = 420mm×297mm
A3_11_RectangularDislocation_Mirror

SquareGrid

/SquareGrid can be similar to /RectangularDislocation. In the latter the glasses fill the page: observe the six-glass example above, in which the circles touch their vertical neighbours, but have a slight gap from the horizontal neighbour. In /SquareGrid there is no dislocation, and by default the vertical and horizontal distances are the same and equal to the diameter. /SquareGrid has two optional one-parameter flags: /HorizontalAlignment (which must have a value of one of /Left, /Right, /Centre, or /Justify); and /VerticalAlignment (one of /Top, /Bottom, /Middle, or /Justify). Obviously either /Justify can allow distances to differ.

PackingStyles element
PaperType = width×height
Example
/SquareGrid
Six glasses
/A4 = 210mm×297mm
USL_06_SquareGrid

DiamondsAndRectangular

/DiamondsAndRectangular: for some numbers of glasses and aspect ratios, the best fit can come from a pattern that is a combination of /Diamonds and a rectangular pattern. The diamonds can be offset from the diamonds either vertically or horizontally.

PackingStyles element
PaperType = width×height
Example
/DiamondsAndRectangular
Fourteen glasses
/A3 = 420mm×297mm
A3_14_DiamondsAndRectangular
[ /DiamondsAndRectangular /RectColsToLeftOrRowsBelow 0 ]
Fourteen glasses
/A3 = 420mm×297mm
A3_14_DiamondsAndRectangular_RectColsToLeftOrRowsBelow_0
[ /DiamondsAndRectangular /RectColsToLeftOrRowsBelow 2 ]
Fourteen glasses
/A3 = 420mm×297mm
A3_14_DiamondsAndRectangular_RectColsToLeftOrRowsBelow_2
/DiamondsAndRectangular
Fourteen glasses
/Tabloid = 17″×11″
USL2_14_DiamondsAndRectangular
/DiamondsAndRectangular
Nine glasses
/USLegal = 14″×8½″
USLegal_09_DiamondsAndRectangular

DiamondsPlus

/DiamondsPlus is mostly three-row diamonds (or, if /Portrait, three-column), with two extras set between the three rows (columns). It is the best packing of seven or ten glasses on several paper sizes.

There is a generalisation of /DiamondsPlus over any number of rows (columns), not yet coded.

PackingStyles element
PaperType = width×height
Example
/DiamondsPlus
Seven glasses
/A4 = 210mm×297mm
A4_07_DiamondsPlus

RectangularAlternateNudge

/RectangularAlternateNudge: in this rectangular variant, alternate rows (or columns) are slightly nudged, better to fill the space.

Thus /RectangularAlternateNudge is a small deviation away from greater symmetry, about which the the author is unenthusiastic. Because of this non-enthusiasm, by default, /RectangularAlternateNudge comes with a sub-parameter: [ /RectangularAlternateNudge /ImprovementPointsMin 2 ]. This imposes an additional requirement: this packing style is chosen only if it is an improvement on the previous best radius of ≥ 2pt ≈ 0.7mm ≈ 0.028″.

The next table of examples shows six glasses on /USL = 8½″×11″ and on /A4 = 210mm×297mm, in all cases with margins of 24pt = ⅓″ ≈ 8.5mm, and space of 6pt ≈ 2.1mm for the header.

It shows four packing styles: /Diamonds; /SquareGrid; /RectangularDislocation; and /RectangularAlternateNudge.

On /USL, /RectangularAlternateNudge improves the radius by ≈ 2.6pt ≈0.9mm, and hence the diameter by ≈ 1.8mm. But on /A4 the improvement in the radius is much less: only ≈ 0.28pt ≈ 0.098mm, which seems insufficient to justify the asymmetry’s aesthetic damage.

/USL:
pt; inch; mm
/USL =
8½″×11″
/A4:
pt; inch; mm
/A4 =
210mm×297mm
/Diamonds
on /USL
Rad.Diam.
117.46234.93pt
1.6313.263″
41.4482.88mm
USL_06_Diamonds /Diamonds
on /A4
Rad.Diam.
119.57239.15pt
1.6613.322″
42.1884.37mm
A4_06_Diamonds
/SquareGrid
on /USL
Rad.Diam.
123246pt
1.7083.417″
43.3986.78mm
USL_06_SquareGrid /SquareGrid
on /A4
Rad.Diam.
131.31262.63pt
1.8243.648″
46.3392.65mm
A4_06_SquareGrid
/RectangularDislocation
on /USL
Rad.Diam.
123246pt
1.7083.417″
43.3986.78mm
USL_06_RectangularDislocation /RectangularDislocation
on /A4
Rad.Diam.
131.31262.63pt
1.8243.648″
46.3392.65mm
A4_06_RectangularDislocation
/RectangularAlternateNudge
on /USL
Rad.Diam.
125.57251.14pt
1.7443.488″
44.3088.60mm
USL_06_RectangularAlternateNudge /RectangularAlternateNudge
on /A4
Rad.Diam.
131.59263.18pt
1.8283.655″
46.4292.85mm
A4_06_RectangularAlternateNudge

RectangularAlternateSplitNudge

RectangularAlternateSplitNudge, by making the nudged rows go in both directions, lessens the aesthetic damage of the non-Split nudge, but makes an even smaller gain in the radius.

PackingStyles element
PaperType = width×height
Example
/RectangularAlternateSplitNudge
Ten glasses
/Tabloid = 17″×11″
USL2_10_RectangularAlternateSplitNudge
/RectangularAlternateSplitNudge
Twelve glasses
/Tabloid = 17″×11″
USL2_12_RectangularAlternateSplitNudge
[ /RectangularAlternateSplitNudge /Mirror ]
Twelve glasses
/Tabloid = 17″×11″
USL2_12_RectangularAlternateSplitNudge_Mirror

Both /RectangularAlternateSplitNudge and /RectangularAlternateNudge admit use of flags /ProhibitVerticalNudging or /ProhibitHorizontalNudging. Obviously using both would be strange.

Bespoke5 and Bespoke7

/Bespoke5 and /Bespoke7 are ignored for more than five and seven glasses, respectively.

PackingStyles element
PaperType = width×height
Example
/Bespoke5
Five glasses
/A4 = 297mm×210mm
A4_05_Bespoke5
[ /Bespoke5 /Mirror ]
Five glasses
/A4 = 297mm×210mm
A4_05_Bespoke5_Mirror
/Bespoke7
Seven glasses
/USLegal = 14″×8½″
USLegal_07_Bespoke7
[ /Bespoke7 /Mirror ]
Seven glasses
/USLegal = 14″×8½″
USLegal_07_Bespoke7_Mirror

Temple

/Temple is an intricate pattern, that has the largest radius for 10 glasses on /USL, and for 13 glasses on either /USLegal or /Tabloid. The sub-parameter /TempleExtraColsToLeftOrRowsBelow is followed by one integer, and is to /Temple as /RectColsToLeftOrRowsBelow is to /DiamondsAndRectangular.

PackingStyles element
PaperType = width×height
Example
/Temple
Ten glasses
/USL = 8½″×11″
USL_10_Temple
/Temple
Thirteen glasses
/USLegal = 14″×8½″
USLegal_13_Temple
/Temple
Thirteen glasses
/Tabloid = 17″×11″
USL2_13_Temple_TempleExtraColsToLeftOrRowsBelow_0

Inefficient packings

There are classes of packing which, though they can have decorative or other advantages, are not space efficient.

PostsAndLintel, and Arch

/PostsAndLintel arranges the circles around the left, top and right edges of the page, optionally with some circles at the bottom centre. This design might be particularly appropriate if the central circles held the candidate blends of a vintage, with some of the components around the edge. /Arch is similar, with the circles on a half ellipse.

PackingStyles element
PaperType = width×height
Example
[ /Arch /CentralGlasses 3 ]
Seven + three = ten glasses
/A3 = 420mm×297mm
A3_10_Arch_CentralGlasses_3
[ /PostsAndLintel /CentralGlasses 1 ]
Seven + one = eight glasses
/A3 = 420mm×297mm
A3_08_PostsAndLintel_CentralGlasses_1
[ /PostsAndLintel /CentralGlasses 2 /Mirror ]
Eight + two = ten glasses
/A3 = 420mm×297mm
A3_10_PostsAndLintel_CentralGlasses_2_Mirror

Sides, LeftSide, RightSide, TopRow, MiddleRow, BottomRow

/Sides, /LeftSide, /RightSide, /TopRow, /MiddleRow, and /BottomRow are mostly self-explanatory.

/RightSide (and, mutatis mutandis, /LeftSide) have a particular use. Consider a tasting with 17 glasses. This could be set as 6 + 6 + 5 on 3×/A4. But if table space were tight, 14 on 1×/A3 and 3 on 1×/RightSide /A4 would use only 2½ /A4 widths, if the unused half of the /A4 were tucked under the /A3. Suppressing the ornaments tucked under the /A3, such as HeadersCenter, would look neater. This can be done by adding to the array an extra flag or flags, /SuppressOrnamentsLeft, /SuppressOrnamentsCentre, /SuppressOrnamentsRight, which for the pages using that packing style suppresses the unwanted headers, footers, icons, and water boxes. It also accepts the /VerticalAlignment parameter. Example item of PackingStyles:

[ /RightSide  /GlassesNumMax 3  /OnlyIfOrientation /Portrait  /VerticalAlignment /Justify  /SuppressNonRightOrnaments ]
PackingStyles element
PaperType = width×height
Example
Sides
Six glasses
/A3 = 420mm×297mm
A3_06_Sides
TopRow
Four glasses
/A3 = 420mm×297mm
A3_04_TopRow
BottomRow
Four glasses
/A3 = 420mm×297mm
A3_04_BottomRow
MiddlemRow
Four glasses
/A3 = 420mm×297mm
A3_04_MiddleRow
/LeftSide
Three glasses
/A4 = 297mm×210mm
A4_03_LeftSide
/RightSide
Three glasses
/A4 = 297mm×210mm
A4_03_RightSide

Array: custom arrangements

It is possible to specify an arrangement with an array, thus:

[ /Array /Positions [0 2] [2 2] [4 2] [6 2]  [0 1] [3 1] [6 1]  [0 0] [3 0] [6 0] ]

The style is /Array. That can have any of the selection controls, such as /OnlyIfOrientation /Landscape. After which is /Positions. After the /Positions marker is a list of points, typically of the form [x y], with y increasing up the page. The code then chooses the radius and separately scales the x and y directions such that things fit as snugly as possible, obviously subject to the other upper bounds on the radius. The glass ordering is that given in the array: there is no subsequent sorting or ordering.

There is a more complicated variant, in which some of the sub-arrays are of the form [x y x' y']. The first two elements are used, as before, and fix the radius and the canvas. The circle at (x,y) is then moved in a straight line towards (x',y'). A circle stops moving if it collides with another circle (a tangential touch not being a collision); if it collides with the edge of the page; or it arrives at (x',y').

The example image shows, on /A3, an alternation of the previous code extract to:

[ /Array /Positions [0 2] [2 2 3 2] [4 2 3 2] [6 2]  [0 1] [3 1] [6 1]  [0 0] [3 0] [6 0] ]

A3_10_Array

The difference is the movement of the circles at (2,2) and (4,2), both towards (3,2). For the author’s taste, the closer relationship of the two B0 circles (same wine in single and magnum) means they should be touching, rather than equally spaced between A0 and C0.

Other flags, constraints, and sub-parameters

There are other flags and constraints and sub-parameters, as follows.

  • /Mirror: the alternate chirality. If there isn’t a sensible meaning to this, ignored.

  • /ShoveLeft and /ShoveRight: if there is spare space between the circles, probably because the radius has been shrunk to that on another sheet, the circles are shoved leftwards (or rightwards) against that margin. But not heeded in every base style.

  • /PackingDirectionTopToBottom bool and /PackingDirectionLeftToRight bool and /PackingNestingColumnMajor bool: by default most layouts start at the top-left, work across to the top-right, then start the second row slightly further down on the left. This can be changed: glasses can run right-to-left, bottom-to-top, and the nesting order of columns and rows can be exchanged. For large pre-poured tastings on /A3 or /Tabloid, generally one pre-pours the youngest first. If these are in the front row, it is more fiddly to lower subsequent older vintages into place. Adding /PackingNestingColumnMajor true fixes this. (The global value of PackingNestingColumnMajor is the default value within PackingStyles, and also affects the ordering in Cork-Display pages.)

  • /ProhibitVerticalNudging or /ProhibitHorizontalNudging: have the obvious effect in base styles /RectangularAlternateNudge and /RectangularAlternateSplitNudge.

  • /RectColsToLeftOrRowsBelow int chooses the position of the diamonds-style block in /DiamondsAndRectangular.

  • /TempleExtraColsToLeftOrRowsBelow int chooses the position of the ‘hole’ in /Temple.

  • /CentralGlasses int: both /PostsAndLintel and /Arch packings can have 0, 1, 2, or 3 glasses in the centre, the remainder running round the edge or semi-ellipse.

  • /RowsNumMin int and /RowsNumMax int: minimum and maximum number of rows permitted. Relevant in the /Diamonds, /RectangularDislocation, /SquareGrid, /RectangularAlternateNudge, and /PostsAndLintel packing styles.

  • /OnlyIfSheetNumMin int and /OnlyIfSheetNumMax int: specifying permitted values for the internal variable SheetNum, which is 0 on the first sheet, 1 on the second, etc.

  • /GlassesNumMin int and /GlassesNumMax int: permitted number of glasses. If outside range, this packing specification not used.

  • /OnlyIfOrientation name, the sub-parameter being one of /Landscape, /Portrait, or /Either, the last being the default if this sub-parameter absent. If Orientation not matching, this packing specification not used.

  • /ImprovementPointsMin num and /ImprovementProportionMin num: this specification used only if beating previous best radius by the required amount, either absolute (e.g., 2 points) or as a proportion (e.g., 0.01 = 1%).

If PackingStyles is empty, or contains only invalid/impossible layouts, then instead several of the regular layouts are tried.

Radius constraints

There is aesthetic merit in imposing that radii are consistent across pages within a single session. This is controlled by ShrinkRadii, which can be:

  • /NotAtAll, which leaves each page at its own best possible;
  • /ToSmallestSamePageOrdering, which ensures consistency within each session;
  • /ToSmallest, which ensures consistency over the whole document;
  • [ 0 0 0 … 1 1 1 … 2 2 ], which is an arbitrary array of the same length as GlassesOnSheets, pages’ radii being forced to their lesser if elements satisfy PostScript’s eq condition.

There is also the simple numerical parameter MaxRadius, defaulting to 150 ⇒ diameter ≤ 300pt = 4⅙″ = 105⅚mm.

Page size and glass diameters

the following shows glass diameters for various numbers of glasses on various page sizes, using the default value of PackingStyles, and assuming that MaxRadius is sufficently large.

Num
Glasses
/A4
210mm×297mm
/A3
420mm×297mm
/USLegal
14″×8½″
/USL
8½″×11″
/Tabloid
17″×11″
3 113.9mm, 4.49″ 164.6mm, 6.48″ 126.9mm, 5.00″ 113.1mm, 4.45″ 161.1mm, 6.34″
4 105.1mm, 4.14″ 151.6mm, 5.97″ 112.8mm, 4.44″ 105.6mm, 4.16″ 145.6mm, 5.73″
5 96.5mm, 3.80″ 140.0mm, 5.51″ 106.9mm, 4.21″ 96.6mm, 3.80″ 138.3mm, 5.45″
6 92.7mm, 3.65″ 134.4mm, 5.29″ 101.4mm, 3.99″ 88.6mm, 3.49″ 130.2mm, 5.13″
7 81.1mm, 3.19″ 117.2mm, 4.61″ 92.7mm, 3.65″ 79.3mm, 3.12″ 116.4mm, 4.58″
8 77.2mm, 3.04″ 111.2mm, 4.38″ 87.0mm, 3.42″ 76.5mm, 3.01″ 109.7mm, 4.32″
9 72.8mm, 2.87″ 105.0mm, 4.13″ 78.7mm, 3.10″ 73.2mm, 2.88″ 101.3mm, 3.99″
10 70.7mm, 2.78″ 100.8mm, 3.97″ 76.7mm, 3.02″ 67.2mm, 2.64″ 97.2mm, 3.83″
11 69.5mm, 2.74″ 100.8mm, 3.97″ 75.1mm, 2.96″ 66.3mm, 2.61″ 97.2mm, 3.83″
12 64.4mm, 2.53″ 94.7mm, 3.73″ 72.8mm, 2.86″ 65.1mm, 2.56″ 92.2mm, 3.63″
13 62.9mm, 2.47″ 91.0mm, 3.58″ 68.8mm, 2.71″ 60.7mm, 2.39″ 87.7mm, 3.45″
14 59.7mm, 2.35″ 86.4mm, 3.40″ 67.7mm, 2.67″ 57.9mm, 2.28″ 86.8mm, 3.42″
15 58.1mm, 2.29″ 83.9mm, 3.30″ 65.6mm, 2.58″ 56.8mm, 2.24″ 83.0mm, 3.27″
16 55.7mm, 2.19″ 80.4mm, 3.16″ 60.4mm, 2.38″ 55.9mm, 2.20″ 77.6mm, 3.06″
17 54.1mm, 2.13″ 78.0mm, 3.07″ 58.2mm, 2.29″ 52.1mm, 2.05″ 75.0mm, 2.95″
18 54.1mm, 2.13″ 78.0mm, 3.07″ 58.2mm, 2.29″ 52.1mm, 2.05″ 75.0mm, 2.95″
19 50.5mm, 1.99″ 75.5mm, 2.97″ 56.4mm, 2.22″ 49.7mm, 1.96″ 73.1mm, 2.88″
20 50.5mm, 1.99″ 73.3mm, 2.89″ 56.4mm, 2.22″ 49.7mm, 1.96″ 73.1mm, 2.88″
21 48.3mm, 1.90″ 69.5mm, 2.74″ 55.1mm, 2.17″ 49.1mm, 1.93″ 69.2mm, 2.72″

Measure the diameter of your glasses. Add something for two fingers. Add something for the inebriation of the owner of the fingers. Suggestion: ≤6 on /A4; ≤14 on /A3; ≤9 on /USLegal; ≤6 on /USL; ≤14 on /Tabloid. But sometimes table space is a binding constraint, in which case there must be less paper area, hence smaller circles.

The images show the packings for the diameters ≤ 4½″ ≈ 114mm, down to slightly below 3″ ≈ 76mm.

PaperType(s) Examples
/A4  /A3
Height: 297mm
Widths: 210mm  420mm
A4 A3
/USLegal
Height: 8½″
Width: 14″
USLegal
/USL  /Tabloid
Height: 11″
Widths: 8½″  17″
USL USL2

Related parameters

The Names are shown on each Glasses page. Whether a name is shown at the bottom of the page is controlled by NamesShowBottom, being an array of Booleans the same length as GlassesOnSheets, with default elements of true. And likewise at the top of the page, NamesShowTop, with default elements of false. Each is shown in the font NamesFont, at size not exceeding and usually equallingNamesFontSize, but wiggled smaller if neccessary to avoid the circles.

Code extracts

Some extracts from the code used to make some of examples on this page.

/ThePortForumIconPlacement /None def

/VoteRecorders false def
/CorkDisplayNumCopies 0 def
/NeckTagsNumCopies 0 def
/TastingNotePagesNumCopies 0 def
/DecantingNotesNumCopies 0 def

/TitlesFont     /LucidaGrande-Bold def
/CircletextFont /LucidaGrande-Bold def
/HeaderFont     /LucidaSans-TypewriterBold def

/FontSizesRatioTitlesMin 999 def

/CircletextsMinNumSpacesBetween 1.5 def
/CircletextFontSize 12 def

/InlineTitles false def

/WaterBoxes /None def

/ShrinkRadii /NotAtAll def
/MaxRadius 9999 def

/OutputLogToPage false def