---
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 */
}
h1{
font-size: 25pt; /* make the level 1 headers smaller */
}
/*
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_ is the linear image of a high-dimensional cube
$[0,1]^N$ to $\mathbb{R}^3$.
A zonohedron is a special type of convex polyhedron.
The images of the standard basis of $\mathbb{R}^N$ are called the
_generators_ of the zonohedron.
The goal of this package is to construct _any_ zonohedron from the generators,
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 )
```
Some WebM video plots in the next section are made with help of the package **av** and this function:
```{r, echo=TRUE, message=FALSE, warning=FALSE}
# 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 = tempdir() #"./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 .webm
pathvec = dir( pathtemp, pattern="png$", full=T )
webm_file = sprintf( "%s/animation%g.webm", pathtemp, index )
out = av::av_encode_video( pathvec, output=webm_file, framerate=fps, codec='libvpx-vp9', verbose=F )
res = file.remove( pathvec ) # cleanup the .PNG files, leaving just the .webm
return( out )
}
```
Embedding videos in the HTML output is done with the help of the package **base64enc** and this function:
```{r, echo=TRUE, message=FALSE}
video2html <- function( path, attributes="controls loop autoplay muted" )
{
requireNamespace( "base64enc", quietly=TRUE )
i = regexpr( "[.][a-z]+$", path, ignore.case=T )
if( i < 0 ) return('')
ext = substring( path, i+1 ) # extract the extension, and skip over the '.'
part1 = sprintf( ''
return( paste0( part1, part2, part3, collapse='' ) )
}
```
# 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' )
webm_file = spinit( zono5, 2, vpsize=c(480,480) )
```
```{r, echo=FALSE, message=TRUE, warning=TRUE }
video_html = video2html(webm_file)
knitr::raw_html( video_html, meta=NULL, cacheable=FALSE )
unlink( dirname(webm_file) )
```
object color solid at 5nm interval; this zonohedron has 81 generators
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).
DONE. Version 0.5-0 adds `as.mesh3d().zonohedron()`, which is derived from `rgl::as.mesh3d()`.
**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 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:
`library( logger )`
`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: