## Tabulated() — documenting rendered images

the purpose of the 'tabulated' include file is to provide a macro for documenting key parameters, data, or other information inside rendered images, in formatted tables. achieving a consistent "look and feel" across a series of images becomes very easy, for instance when illustrating an object collection, as shown in one of the examples.

the `Tabulated()` macro uses a dictionary for its input, and there are a good number of optional keys to "tweak" the appearance and layout of tables. however, only two keys are actually needed to create a table – one providing the data to display, and one defining the types of values stored in the column(s).

each table is composed of two types of "cell", labels and data, which form the columns and rows, and an overlaying frame/grid. labels are optional, as is the table caption, though tables are rarely useful without. data may be of any of POV-Ray's basic types, that is, booleans and strings, floats and integers, and vectors, can be specified. tables are aligned with their top-left corner at origin (approximately), extending in to `+x` and `-y`, are less than half of one POV-Ray unit deep, and are created with a `no_shadow` modifier. table dimensions are calculated from the space required by the various items in relation to the respective font's largest glyph. labels, caption, and data each use a separate TrueType font, at slightly different, fixed, scales.

      // counts {boxes, cylinders, spheres}
      #declare D = dictionary {
        .DataColumns: array [1] {"I"},
        .DataTable  : array [3] {23,8,17},
        .RowLabels  : array [3] {"b","c","s"}
      };

      object {
        Tabulated(D)
        scale     some_factor
        translate some_place
      }

for example, say you're experimenting with a complex shape made up from many, still changing numbers of, boxes, spheres, and cylinders. the snippet shows how to make a (basic) table to document the numbers used in a given "try".

the Scene Description Language (SDL) code is written for the Persistence of Vision Raytracer version 3.8, or later[1].

shortcomings – a couple of known "issues"/bugs: a caption cannot be wider than the table, an error (with message) is thrown; if all labels are empty strings, the boxes for the column names are still shown. both external and internal border lines are drawn with the same (cylinder) radius, currently. while some of this stuff may get done, I am thinking about a different design altogether, a shift away from the current, column-driven approach, to "cell centric", for the next version. a design which will allow column groups, row and or column spanning cells, wrapping long text/string data into multiple lines, images in cells, etc.

### the interface

seen from the user's perspective, the include file provides three globally "visible" identifiers: one macro and two variables. there are however others and all visible identifiers, except `Tabulated()` and the variables, are prefixed 'tabu_'.

#### variables `Tabulated_{min,max}_extent`

these variables provide, for convenience, the overall dimensions of the table in 3-vectors, as returned by `min_extent()` and `max_extent()` calls. both variables are (re-)created every time `Tabulated()` runs.

#### macro `Tabulated(dict)`

the `Tabulated()` macro takes a single '`dictionary {}`' argument, and returns ("expands to") an '`union {}`' of objects forming a table.

of the list of keys below only the first two are required. all others will be given "sensible" default values, where applicable, when omitted. it is ok to add your own key/value pairs to the dictionary (except '.Note', which is reserved for future use).

.DataTable: array
  this array contains the data values to display in the table. it can be provided "inline", or be an identifier of an existing array. the array can simply be 2D, ie `[row][col]`, or just a single column (`[col]`). or you may have "sets" of differing values, in which case a 1D array (`[row]`s) of `array mixed` can be used. examples of all of these are shown in the demo scenes, see the "examples" section below.
  note – all data rows for a given column must contain values of the same type.
  there is no default for this mandatory key.
.DataColumns: array [n] string
  this array identifies the data type of the values in each respective column. that means there must be as many elements as there are columns in the '.DataTable' (and in the same order ;-)). each element must be a quoted "little language" string, which controls the conversion of array element value to display string, chosen from the following list:
  - "B" – for boolean type true/false values.
  - "Fp" – for float values, where p gives the desired precision, eg "F3" for three decimal places. the legal range for p is '0 <= p <= 16'.
  - "G" – a default for floats, fixed to six decimals; that is, identical to "F6".
  - "I" – for integer values.
  - "S" – for string values.
  - "Vnp" – for vector values, where n tells the number of "components" in vector, ie '2 <= n <= 5', and p is the precision, as for floats.
  there can be up to twenty data columns in a table. this key is mandatory.
.Padding: 2-vector
  the `.x` and `.y` components of the vector specify the cell-internal amount of padding for labels and data. the values represent percentages of the largest glyph in the respective font. the 'x' padding is added on both sides of an item, and 'y' above and below. the default is '`<.35,.2>`', thirty-five and twenty percent, respectively.
.Borders: bool
  this optional key is just an on/off "switch" controlling whether the table's frame displays. the default, when omitted, is show the frame.
.Lines: array [2] float
  the table's frame ("borders") is drawn using '`cylinder {}`' objects. use this key to provide a radius, and a colour index, cf '.Fg'. the built-in defaults are '`{.025,4}`', for thin, red "lines".
.ColLabels: array [n] string
  this key is used to add column name labels to the table. the array must have a single dimension and provide a string element for every data column in the table. there is no default.
.RowLabels: array [n] string
  exactly the same as '.ColLabels', except one name string/element for every data row.
.ColWidths: array [n] float
  usually, the column widths as calculated from the labels and data, plus padding, look ok. if you need, or want, the table's columns to be wider, use this key to provide the corresponding values, one for every data column. the legal range is from `1` to `40` units, or `-1` for "don't care". if a given width is insufficient for displaying the actual label or data, an error message will be shown.
.ColAlign: array [n] int
  all column data types have a default alignment, this keys provides overrides. there must be one element/value, from the list of three below, for every data column:
  1. left.
  2. centre.
  3. right.
  the defaults for the supported types are to display booleans centred, strings and vectors left-aligned, and all numbers right-aligned.
  note – name label alignments cannot be changed; they are fixed to centred for column names, and right-aligned for row names.
.Transpose: bool
  whether or not to transpose the table's columns and rows, default 'false'. note – setting this key is not compatible with '.ColWidths', an error results.
.Caption: array mixed
  typically this array will have three elements (in order): a caption string, an alignment (cf '.ColAlign'), and a boolean to place the caption above (when true) or below the table. optionally, the array can have a fourth element, a foreground colour index, see '.Fg'. this key has no default.
.Note: array [] string
  use this key to add "footnotes" to the table, there is no default. implementation/details remain to be decided.
.Hilite: array mixed
  this key enables highlighting a range of cells, the effect is akin to a very thin, tinted piece of "glass" placed above. the array always has two elements, specifying the 'from' and 'to' range as 2-vectors ('`<col,row>`'); the top-left data cell is '`<1,1>`'. the optional third element is a `rgbf` value to provide a custom tint. this key has no default other than the colour, '`rgbf <.65,.65,.9,.975>`'.
.Fonts: array [3] string
  the code uses three TrueType fonts for labels, data, and captions, and the font names in the array need to be provided in that order. if this key is omitted, the POV-Ray distributed fonts 'timrom.ttf' and 'cyrvetic.ttf' get used, the latter for data.
.Bg: array [3] pigment
  the background pigments for labels, data, and alternate data (cf '.Ledger'), in that order. the default is different, light-ish grays for all three, ie '`pigment {color rgb .nn}`' type values. use '`pigment {color rgbt 1}`' for transparent backgrounds.
.Fg: array [3] int
  the foreground/text colour indices, for labels, data, and alternate data (see '.Ledger'), in that order:
  0. black
  1. blue
  2. green
  3. cyan
  4. red
  5. magenta
  6. yellow
  7. white
  the default, when omitted, is red labels and all data in black, ie '`{4,0,0}`'.
.Emissive: bool
  selects which, of two, finishes to use for the table objects' appearance. set this key to 'true' in unlit, or low lighting, scenes, to apply a finish using the `emission` keyword. the default, when omitted, is 'false', ie use a non-emissive finish (for lit scenes). see '.Finishes' below.
.Finishes: array [2] finish
  use this key to provide alternatives for the built-in "regular" and emissive finishes (in that order); the code uses '`{ambient .1 diffuse .8 specular .1}`' and '`{ambient 0 emission .85}`', respectively; cf '.Emissive'.
.UseBool: array [2] string
  by default boolean type data displays as 'no' and 'yes'. use this key to provide alternative wording of your choice, first the false/'no' word, then its complement; for example: '`{"disabled","enabled"}`'.
.Ledger: bool
  tables with many rows and or columns can sometimes be difficult to take in. a ledger-style background, where alternate rows are coloured differently, can help improve "readability". set this key to enable the feature, however, four or more data rows are needed. the '.Fg' and '.Bg' keys provide the means to adjust/adapt the appearances.
.Logo: bool
  this key controls whether an "eye candy" POV-Ray logo will be displayed; the table must have both column and row labels, and the distribution 'logo.inc' file must have been included before calling the macro. default, when omitted, is show the logo, setting the key to 'false' suppresses.
.Verbose: bool
  when enabled, `Tabulated()` will print a summary "header" of the various settings to POV-Ray's debug stream. default 'false'.

for all strings, whether Row/Column labels or type `"S"` data columns, all provisos regarding POV-Ray's '`text {}`' object apply, that is, no newlines and such.

as mentioned above, `Tabulated()` calculates the required dimensions using a font's largest glyph. to clarify, for each font only the glyphs corresponding to `chr(1)` through to `chr(255)` are looked at; fonts may have physically larger glyphs in subsequent, higher "pages", than in the first.

### examples

this section introduces the demo code, example scenes which, taken together, illustrate the basic usage of the `Tabulated()` macro. each of the three examples named 'ex?.pov' demonstrates the use of one particular "shape" of array. two other scenes included simply show further usages of the macro, but they use specific fonts which you may need to change/adapt.

a table used as a billboard.

the archive also includes a little script which generates a "sampler" scene, a simple viewer for a given TrueType font. you will need a BASH (Bourne Again Shell) interpreter to run the script, as discussed below.

note – all scene code can be accessed by clicking on the corresponding image, which shows a render of the table after adding "bling". each example scene file, as shipped, will render the (mostly) all-defaults "before bling" version, the modifications needed are commented out, for quick editing. also, you may prefer to switch to an `orthographic` type camera instead, particularly when rendering wider tables.

the first image shown is included simply to make the point that a table does not necessarily have to look like "a table". without borders, `Tabulated()`'s output can make a passable billboard. it is also the only image where I "cheated", using a separate utility to crop the rendered image.

a fully labelled table.

the first example demonstrates use of an "ordinary" 2D data array, where the first dimension represents the rows, and the second the columns. the scene uses the POV-Ray '`superellipsoid {}`' object. the array has six sets (rows) of two parameters (columns), corresponding to the shape's "east-west" and "north-south" parameters; hence the array name `en_`. there are a couple of arrays storing colours and colour names, also with six elements each. these are used to colour-code the shapes, and to provide row labels for the table. the column labels are provided "inline". because the superellipsoids are lined-up to the right of the table, the rendered image is taller than wide. to compensate, a `camera` aspect ratio of `3/4` is used.

      ...

      /* object colours */
      #declare cv_ = array [6] {
        <0,0,1>, <0,1,0>, <0,1,1>,
        <1,0,0>, <1,0,1>, <1,1,0>
      };
      #declare cn_ = array [6] {
        "blue", "green", "cyan",
        "red", "magenta", "yellow"
      };

      /* east + north */
      #declare en_ = array [6][2] {
        {.50,.16}, {.50,.32}, {.50,.48},
        {.50,.64}, {.50,.80}, {.50,.96}
      };

      ...

      #declare D = dictionary {
        .DataColumns: array [2] {"F3","F3"},
        .DataTable  : en_
      };

      ...

when declaring the input `dictionary` for the macro, the (most) important thing is to provide the correct data type specification for each column, in order. since both parameters in this data table are of the same type, so are the column types; we use `"F3"` to get the `float` values displayed with three decimal digits precision. we then add the data array using its identifier. the second image shows the wildly unexciting result of rendering the table after adding labels and caption, as well as enabling the '.Ledger' style feature. it also shows that tables really need some labelling to provide context, meaning.

      jr@crow:tabu$ cd demo
      jr@crow:demo$ povray ex1.pov +l.. +w600 +h800 +a0.1

presuming that your current working directory is the archive's top (ie '/path/to/tabu'), the second snippet shows how to quick test render this example, and the others below. the `library_path` `(+l..)` option let's POV-Ray find the include file.

the second example, shown in the third image, is based on an 1D array where every element ("row") is an `array mixed`, think database table, storing information about the objects displayed, both calculated and entered "by hand" values. each of the four objects is a POV-Ray `mesh2` type, and part of a fictional science fiction objects collection. the meshes were created from GNU Triangulated Surfaces data files[2] found on the interweb. a plain background image was added to relieve the "drabness".

a customised table.

the objects, from left to right, correspond to the data rows, in order. their names are the same as the declared "identifiers", and the include file names; they are used to source the files, and as row labels in the table.

the thing to note is the `"V33"` type spec for the second column, a macro is used to round the object dimensions, an '<x,y,z>' vector, to the third decimal. the other columns, integers and booleans, do not have a precision.

while functional, the table's default colours sort of clash with the background, hence '.Fg', '.Bg', and '.Lines' keys were set to address this. also, given the image's width, the table can be made a little wider. '.ColWidths' is used to set the columns to uniform widths.

well, there are only so many examples one can contrive ;-), so the third, and final, is even more "lame" than the previous. it demonstrates the use of a single column table (array), with three rows storing the corner points of a '`triangle {}`'. the points are randomly generated, and the table displays those values (rounded up to make them look "nice"), as well as the `seed` value used; the seed is a number derived from the system time, and every render will draw a different triangle. the fourth image shows an instance.

another customised table.

as in the second example, the necessary (for the visuals) dictionary keys were added to make the result a little less plain; namely the foreground colours and background pigments, and '.Lines'.

you may feel .. hampered by the limited number of foreground (and line) colours. since the `Tabulated()` macro uses a simple array of 3-vector RGB values, it is easy, and safe, to override that by providing your own. simply, after including 'tabulated.inc', but before running the `Tabulated()` macro, declare an array like the one shown in the snippet, that is, the exact same name and number of elements, plus your (RGB) colour vectors. then those will be used for the remaining scene code.

      #declare tabu__data_colours_ = array [8] {
        <.01,.01,.01>, <0,0,1>, <0,1,0>, <0,1,1>,
        <1,0,0>, <1,0,1>, <1,1,0>, <1,1,1>
      };

the final, fifth, image was rendered from a scene created by the aforementioned (BASH) script utility, 'mkfntview.sh'. the scene uses the 'verdana.ttf' font, and the utility's output was "calibrated" using that font. some visual "bells and whistles" were added to make an appealing (I hope) example image. each generated scene displays one (of four) 16x16 glyph "pages", and a three-line "sampler", text set in the selected font. for convenience, a page can be selected from the command-line via POV-Ray's '`declare=page=N`' syntax, where `N` must be in range `0,..,3`.

a table displaying a font's glyphs.

note – the camera settings, and the table scales and positions, will, almost always, need tweaking, as fonts are so diverse. the snippet assumes that the utility has been copied to a `PATH` directory. naming a non-existing font, or a misspelling, will go unnoticed until POV-Ray tries to use that font; spaces in font names, or including a path/directory, also are a "no-no".

      $ mkfntview.sh verdana.ttf
      $ povray view_verdana_ttf.pov +w600 +h800 +a0.1

### references

although 'tabulated.inc' does not depend on any other includes, one of the three example/demo scenes does, for convenience. the necessary 'foreach.inc' was published in the POV-Ray news groups, but the download link below points to the current version. I also include a link to the latest 'ruled.inc', because, in my (biased :-)) opinion, it complements 'tabulated.inc' well.

`POV-Ray`
  the new to version 3.8 '`dictionary {}`' data/array type in particular.
`GTS`
  the GNU GTS file format, and the associated library, make conversion to POV-Ray '`mesh`' types very easy.
`Foreach()`
  a macro which calls a supplied-by-you "payload" macro, for every element, or selected, in a given array.
`Ruled()`
  an adaptation of Friedrich Lohmüller's Macros for a Squared Background.

"The End". enjoy.