---
title: "zonohedra User Guide"
author: "Glenn Davis"
date: "`r Sys.Date()`"
output: 
  rmarkdown::html_vignette:
    toc: true
    toc_depth: 2
    number_sections: false
bibliography: bibliography.bib

# csl: iso690-numeric-brackets-cs.csl
csl: personal.csl
# csl: institute-of-mathematical-statistics.csl
# csl: transactions-on-mathematical-software.csl
vignette: >
  %\VignetteIndexEntry{zonohedra User Guide}
  %\VignetteEngine{knitr::rmarkdown}
---


```{css, echo=FALSE}
body {
  max-width: 750px;     /* make a little wider, default is 700px */
}

/*
div.figure {
 border: 1px;
 border-style: groove;
}
*/
```


```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
old_opt = options( width=120 )

# if( !file.exists("figs") ) dir.create("figs")

require("rgl",quietly=TRUE)
rgl::setupKnitr(autoprint = TRUE)
```



# Introduction

A _zonohedron_, roughly speaking, is the projection of
a high-dimensional cube to $\mathbb{R}^3$.
For a precise definition see the [Zonotopes](zonotopes.html) vignette,
section 1.3.
A zonohedron is a special type of convex polyhedron.

The goal of this package is to construct any zonohedron,
but especially the ones in these 2 families:
<ul>
<li> the classical zonohedra, with high symmetry </li> 
<li> zonohedra that arise naturally from colorimetry, which may contain hundreds of generators, but little symmetry</li> 
</ul>

In the first case, 13 classical zonohedra have been taken from
@wikiZonohedron
and are built in to the package.
In the second case, an _optimal color solid_ is viewed as a zonohedron;
this connection was discovered by Paul Centore
and is explained very clearly in @Centore2013.

```{r, echo=TRUE,  message=FALSE}
library(zonohedra)
library(rgl)
```

The package dependencies are:

<ul>
<li>**rgl**  @rgl - for 3D plotting</li>
<li>**microbenchmark**  @microbenchmark  - is suggested for its high-precision timer</li>
<li>**logger**  @logger - for event logging</li>
</ul>

Some of the figures below are displayed with **WebGL** -
a JavaScript API for rendering interactive 2D and 3D graphics.
Try using the left mouse button to rotate and the scroll wheel to zoom.


<br><br>

# Polar Zonohedra

The generators for a polar zonohedra are particularly simple -
they are equally distributed on a circle that
is in a plane parallel to the xy-plane and
whose center is on the z-axis.
Construct polar zonohedra with 5 and 25 generators and plot them.

```{r, echo=TRUE,  message=TRUE,  warning=TRUE, fig.width=8, fig.height=4, fig.cap='polar zonohedra with 5 generators (left) and 25 generators (right) &emsp;&emsp; [both of these are interactive WebGL widgets]', fig.keep='none', fig.show='hide', out.width="100%", cache=FALSE }
rgl::mfrow3d( 1, 2 )
pz5 = polarzonohedron( 5 ) ;  plot( pz5, ewd=5 )
rgl::next3d()
plot( polarzonohedron( 25 ), ewd=3 )
rgl::rglwidget( webgl=TRUE )
```


In these 2 plots, the black dot is the origin,
the 5 vertices nearest to the origin are the 5 generators,
and the white dot is the point (0,0,$\pi$).
Each of the generators is assigned a unique color,
and every other edge with that color is parallel to the generator.
All parallelograms with an edge of that color form the
_zone_ or _belt_ for that generator.
Each belt is a topological annulus.
For more details on these polar zonohedra, see @Chilton1963.

Print the generators of the first zonohedron `pz5`;
they are the columns of this 3x5 matrix.
```{r, echo=TRUE, message=FALSE}
getmatrix( pz5 )
```

A function similar to `polarzonohedron()` is `regularprism()`.



<br><br>


<br><br>


# Classic Zonohedra

There are 13 classic zonohedron available in the package,
as a list of 3xN matrices,
where N is the number of generators.
The global data variable is 
`classics.genlist`, with S3 class `'genlist'`.
The 13 matrices in the list are taken from @Eppstein.

```{r, echo=TRUE, message=FALSE}
classics.genlist
```

Extract the matrix of generators for the `truncated cuboctahedron`,
which is abbreviated by `TC`.

```{r, echo=TRUE, message=TRUE}
mat = classics.genlist[['TC']] ; mat
```




Create the truncated cuboctahedron and plot it, with filled faces.


```{r, rgl=TRUE, echo=TRUE,  message=TRUE,  warning=TRUE, fig.width=8, fig.height=5, out.width="100%", fig.align="center", fig.cap='truncated cuboctahedron &emsp;&emsp;&emsp;&emsp; [This is an interactive WebGL widget]', fig.keep='last', fig.show='hide', cache=FALSE }
rgl::par3d( userMatrix = rotationMatrix( -20*pi/180, 0, 1, 1) )
zono = zonohedron( mat )
plot( zono, type='f' )
rgl::rglwidget( webgl=TRUE )
```

<br>
Before continuing, define function `spinit()` used for creating animated GIFs.

```{r, echo=TRUE, message=FALSE, warning=FALSE}
library(gifski)

#   zono        the zonohedron
#   id          unique ID for this animation, a positive integer
#   fps         frames per second
#   duration    of the animation, in seconds
#   revolutions number of revolutions
#   vpsize      viewport size = (width,height)
spinit <- function( zono, index, fps=5, duration=8, revolutions=1, vpsize=c(480,480) ) {
#  enlarge viewport
wr = par3d( "windowRect" ) 
par3d( windowRect = c( wr[1:2], wr[1:2] + vpsize ) )
pathtemp = "./figs" ;   if( ! file.exists(pathtemp) ) dir.create(pathtemp)  # make temp folder
#  make a lot of .PNG files in pathtemp
movie3d( spin3d( getcenter(zono), rpm=revolutions*60/duration ), duration=duration, fps=fps, startTime=1/fps,
           convert=F, movie='junk', dir=pathtemp, verbose=F, webshot=F )
#  combine all the .PNGs into a single .GIF
pathvec = dir( pathtemp, pattern="png$", full=T )
gif_file = sprintf( "./figs/animation%g.gif", index ) 
# if( file.exists(gif_file) )  file.remove( gif_file )
out = gifski::gifski( pathvec, gif_file=gif_file, delay=1/fps, progress=F, width=vpsize[1], height=vpsize[2] )
res = file.remove( pathvec )  # cleanup the .PNG files, leaving just the .GIF

return( out )
}
```


<br><br>

# Colorimetry Zonohedra

In colorimetry, an optimal color solid is a zonohedron.

```{r, echo=TRUE, message=TRUE, warning=TRUE, fig.cap='optimal color solid', fig.keep='last', fig.show='hide', cache=FALSE }
# colorimetry.genlist[[1]] is a 3x81 matrix with the CIE 1931 CMFs at 5nm interval
zono5 = zonohedron( colorimetry.genlist[[1]] )
plot( zono5, type='f' )
gif_file = spinit( zono5, 2, vpsize=c(256,256) )
```

![optimal color solid at 5nm interval](`r gif_file`){width=60%}

In this figure, the black dot is the _black point_ [0,0,0].
The white dot is the _white point_, i.e. the column sums of the
generating matrix.

<br><br>

# Future Work

Here are a few possible improvements and additions.

**export**   
There should be a way to export a zonohedron as
a quadrilateral mesh in some standard format(s).

**vignettes**  
There should be more vignettes.
One idea is to show ways
to examine individual hyperplanes and facets of a zonohedron.
Another idea is to display some interesting Minkowski sums of a few
classic zonohedra.


<br><br>

# References

<div id="refs"></div>


<br><br>

\Appendix


<br><br>


## Appendix A - Methods

The constructor `zonohedron()` uses the optimizations in
Paul Heckbert's memo @Heckbert1985.
The key step is sorting points that lie on a great circle on the sphere.
This efficient method is $O(N^2\log(N))$;
whereas the naive method is $O(N 2^N)$.

The central symmetry is used whenever possible,
and when used this can speed things up by a factor of 2.
To further speed things up, many of the methods use C/C++.

The function `grpDuplicated()` was written by Long Qu,
with a small modification of the return value by myself.
It is written in C/C++ and is implemented with `std::unordered_map`.
The code was taken from the discontinued package **uniqueAtomMat**,
see @uniqueAtomMat.

<br><br>


## Appendix B - Logging

Logging is performed using the package **logger**, see @logger.
This is a powerful package that allows a separate configuration
for logging from within **zonohedra**, and that is what I have done.
During package loading, the logging threshold is changed from `INFO` to `WARN`.
To change it back again, one can execute:  
`log_threshold( INFO, namespace="zonohedra" )`

The layout callback functions is customized;
it adds the name of the calling function to the message.
To install your own layout function, you can execute:  
`log_layout( <your function>, namespace="zonohedra" )`

The appender callback functions is also customized;
it comes to an immediate stop if the message level is `ERROR` or `FATAL`.
To return to the default behavior, you can execute:  
`log_appender( appender_console, namespace="zonohedra" )`

The formatter callback function is forced to be `formatter_sprintf()`;
this should not be changed.


<br><br>

# Session Information

This document was prepared
`r format(Sys.Date(), "%a %b %d, %Y")`
with the following configuration:
<pre>
```{r, echo=FALSE, results='asis'}
options( old_opt )
sessionInfo()
```
</pre>
