---
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:
- the classical zonohedra, with high symmetry
- zonohedra that arise naturally from colorimetry, which may contain hundreds of generators, but little symmetry
In the first case, 13 classical zonohedra have been taken from
@wikiZonohedron
and are built in to the package.
In the second case, an _object 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:
- **rgl** @rgl - for 3D plotting
- **microbenchmark** @microbenchmark - is suggested for its high-precision timer
- **logger** @logger - for event logging
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.
# 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) [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()`.
# 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 [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 )
```
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 )
}
```
# Colorimetry Zonohedra
In colorimetry, an object color solid is a zonohedron.
```{r, echo=TRUE, message=TRUE, warning=TRUE, fig.cap='object 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) )
```
![object 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.
# 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.
# References
\Appendix
## 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.
## 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( , 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.
# Session Information
This document was prepared
`r format(Sys.Date(), "%a %b %d, %Y")`
with the following configuration:
```{r, echo=FALSE, results='asis'}
options( old_opt )
sessionInfo()
```