Processing math: 56%

Paths in the Space of Arcs

Glenn Davis

2025-06-10



Introduction

library(polarzonoid)

In the User Guide vignette it is shown that there is are homeomorphisms An    Zn    S2n where An is the space of n or fewer pairwise disjoint arcs in the circle, and Zn is the polar zonoid in R2n+1.

In this vignette, we take some easily-defined paths in the sphere S2n, compute the corresponding paths in the space of arcs An, and display those paths as animated GIF plots. We make these plots with help of the package gifski and this function:

GIFfromarclist <- function( arclist, arcmat, index=1L, fps=5, vpsize=c(480,512) )
    {
    require( 'gifski' )

    # make temp folder
    pathtemp = tempdir()   # "./figs"     ;   if( ! file.exists(pathtemp) ) dir.create(pathtemp)
    count   = length( arclist )
    namevec = names( arclist )

    for( k in 1:count )
        {
        filename    = sprintf( "%s/plot%03d.png", pathtemp, k )
        png( filename=filename, width=vpsize[1], height=vpsize[2], units = "px" )
        u   = spherefromarcs( arclist[[k]] )
        plotarcs( arclist[[k]], labels=FALSE, margintext=namevec[k] )
        plotarcs( arcmat, labels=FALSE, rad=0.95, col='blue', lwd=1, add=TRUE )
        dev.off()
        }

    pathvec = dir( pathtemp, pattern="png$", full=T )
    gif_file = sprintf( "%s/animation%g.gif", pathtemp, index )
    out = 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)
    }



The Tubular Neighborhood of AnAn+1

An is a stratum in An+1 and there is a tubular neighborhood of An in An+1. Since the codimension is 2, the fiber of a point aAn in the neighborhood is an open 2-disk int(D2). The boundary of the 2-disk is a circle, which we think of this circle as a closed path of points at a small and equal distance from a.

We can compute this circle by mapping a to S2n+2 using the homeomorphism An+1 , computing the circle in \mathbb{S}^{2n+2}, and then mapping back to A_{n+1} using the inverse homeomorphism. The function we will use is:

circleofarcs <- function( arcmat, rad=0.1, count=180 )
    {
    res = spherefromarcs_plus( arcmat, n=nrow(arcmat)+1L )
    
    out     = vector( count, mode='list' )
    namevec = character( count )
    
    for( i in 1:count )
        {
        theta   = 2*pi * (i-1)/count     # theta is in radians, starting at 0
        u   = res$u  +  rad * ( cos(theta)*res$normal[ ,1]  +  sin(theta)*res$normal[ ,2] )
        out[[i]]      = arcsfromsphere( u )     # u is automatically unitized
        namevec[i]    = sprintf( "i = %d", i )
        }

    names(out)  = namevec

    return( out )
    }

The case of n{=}0 is easy to visualize. The space A_0 is 2 points, which map to the “poles” of the sphere \mathbb{S}^2. The empty arc maps to the “south” and the full circle maps to the “north” Around each pole is a small circle. For the “south pole” it is a circle of tiny arcs, almost empty. For the “north pole” it is a circle of very large arcs, almost the full circle. In both cases, the length of the arcs is constant, while the center loops around \mathbb{S}^1.



A Circle of Arcs in A_2 around a Single Arc in A_1

The goal of this section is to take a single arc a \in A_1 and plot the 2 arcs in A_2 that circle around a.

# arcmat1 is a single semicircle centered at (1,0)
arcmat1 = matrix( c(0,pi), nrow=1, ncol=2 )
circle  = circleofarcs( arcmat1, count=90 )
gif_file = GIFfromarclist( circle, arcmat1, index=1, vpsize=c(480,480) )
circle of arcs around a single arc
circle of arcs around a single arc

The original arc is drawn in blue, and shrunken a little so it does not overlap with the nearby pair of arcs.



A Circle of Arcs in A_3 around Two Arcs in A_2

This section is the same as the previous one, except we bump up the complexity. Now a \in A_2 is a pair of arcs, and we plot the 3 arcs in A_3 that circle around a.

# arcmat2 is: an arc filling quadrant #1, plus an arc filling quadrant #3
arcmat2 = matrix( c((1/4)*pi,pi/2, (5/4)*pi,pi/2), nrow=2, ncol=2, byrow=TRUE )
circle  = circleofarcs( arcmat2, count=90 )
gif_file = GIFfromarclist( circle, arcmat2, index=2, vpsize=c(480,480) )
circle of 3 arcs around a pair of arcs
circle of 3 arcs around a pair of arcs

The original pair of arcs are drawn in blue, and shrunken a little so they does not overlap with the nearby triple of arcs.



Empty Arc to Full Circle, and Back Again

In this one, the path in the sphere starts at the “south pole”, goes up through an arbitrary point along a great semicircle to the antipodal “north pole”, and then down the other side. The full path is a great circle, and is pieced together using the function slerp() (spherical linear interpolation) from [1].

poletopole <- function( arcmat, thetamax=pi/36, n=NULL )
    {
    u  = spherefromarcs( arcmat, n=n )

    #  make south and north poles
    m       = length(u)    
    south   = c( rep(0,m-1), -1 ) ;      north   = -south
    
    path1   = slerp( south, u, thetamax=thetamax )   #   from "south pole" to u
    path2   = slerp( u, north, thetamax=thetamax )   #   from u to "north pole"

    path    = rbind( path1, path2 ) # concatenate the 2 paths
    path    = rbind( path, -path )  # back down the other side to south pole again
    
    count   = nrow(path)
    out     = vector( count, mode='list' )
    for( i in 1:count )
        out[[i]] = arcsfromsphere( path[i, ] )

    names(out)  = sprintf( "y_%d = %.3f", m, path[ ,m] )

    return( out )    
    }
# arcmat3 is 3 arcs of different lengths
arcmat3 = matrix( c(0.375,0.75,  2.3,1.1,  4.6,2.8), ncol=2, byrow=TRUE )
arclist  = poletopole( arcmat3 )
gif_file = GIFfromarclist( arclist, arcmat3, index=3, fps=2, vpsize=c(480,480) )
empty to full circle, and back to empty on the other side
empty to full circle, and back to empty on the other side

The defining arcs are drawn in blue, and shrunken a little so they do not overlap with the arcs along the path. Note that at each step, there are 3 arcs, except at the poles, i.e. the empty arc and the full circle.

But it is easy to make an example where the number of arcs is not a constant.

# arcmat1 is a single arc, but it splits into 3 arcs on either side of the path from pole to pole
arcmat1 = matrix( c(1.5,2.9), ncol=2, byrow=TRUE )
arclist  = poletopole( arcmat1, n=3 )
gif_file = GIFfromarclist( arclist, arcmat1, index=4, fps=2, vpsize=c(480,480) )
empty to full circle, and back to empty on the other side
empty to full circle, and back to empty on the other side

The defining arc is drawn in blue, and shrunken a little so it does not overlap with the arcs along the path.



References

[1]
SHOEMAKE, Ken. Animating rotation with quaternion curves. International Conference on Computer Graphics and Interactive Techniques [online]. 1985, 19(3), 245. Available at: https://doi.org/10.1145/325165.325242



Session Information

This document was prepared Tue Jun 10, 2025 with the following configuration:
R version 4.5.0 (2025-04-11 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26100)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=C                          
[2] LC_CTYPE=English_United States.utf8   
[3] LC_MONETARY=English_United States.utf8
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.utf8    

time zone: America/Los_Angeles
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] gifski_1.32.0-2   flextable_0.9.7   polarzonoid_0.1-2

loaded via a namespace (and not attached):
 [1] katex_1.5.0             jsonlite_2.0.0          compiler_4.5.0         
 [4] equatags_0.2.1          Rcpp_1.0.14             zip_2.3.3              
 [7] xml2_1.3.8              jquerylib_0.1.4         fontquiver_0.2.1       
[10] systemfonts_1.2.3       textshaping_1.0.1       uuid_1.2-1             
[13] yaml_2.3.10             fastmap_1.2.0           R6_2.6.1               
[16] gdtools_0.4.2           curl_6.2.2              knitr_1.50             
[19] logger_0.4.0            openssl_2.3.2           bslib_0.9.0            
[22] rlang_1.1.6             V8_6.0.3                cachem_1.1.0           
[25] xfun_0.52               sass_0.4.10             cli_3.6.5              
[28] digest_0.6.37           grid_4.5.0              askpass_1.2.1          
[31] lifecycle_1.0.4         evaluate_1.0.3          glue_1.8.0             
[34] data.table_1.17.2       fontLiberation_0.1.0    officer_0.6.8          
[37] ragg_1.4.0              xslt_1.5.1              fontBitstreamVera_0.1.1
[40] rmarkdown_2.29          tools_4.5.0             htmltools_0.5.8.1