This algorithm decomposes a general polygon into simple polygons and uses the “ear-clipping” algorithm to triangulate it. Polygons with holes are supported.

triangulate(x, y = NULL, z = NULL, random = TRUE, plot = FALSE, partial = NA)

## Arguments

x, y, z

Coordinates of a two-dimensional polygon in a format supported by xyz.coords. See Details for how z is handled.

random

Whether to use a random or deterministic triangulation.

plot

Whether to plot the triangulation; mainly for debugging purposes.

partial

If the triangulation fails, should partial results be returned?

## Details

Normally triangulate looks only at the x and y coordinates. However, if one of those is constant, it is replaced with the z coordinate if present.

The algorithm works as follows. First, it breaks the polygon into pieces separated by NA values in x or y. Each of these pieces should be a simple, non-self-intersecting polygon, separate from the other pieces. (Though some minor exceptions to this rule may work, none are guaranteed). The nesting of these pieces is determined.

The “outer” polygon(s) are then merged with the polygons that they immediately contain, and each of these pieces is triangulated using the ear-clipping algorithm.

Finally, all the triangulated pieces are put together into one result.

## Value

A three-by-n array giving the indices of the vertices of each triangle. (No vertices are added; only the original vertices are used in the triangulation.) The array has an integer vector attribute "nextvert"with one entry per vertex, giving the index of the next vertex to proceed counter-clockwise around outer polygon boundaries, clockwise around inner boundaries.

## References

See the Wikipedia article “polygon triangulation” for a description of the ear-clipping algorithm.

Duncan Murdoch

## Note

Not all inputs will succeed, even when a triangulation is possible. Generally using random = TRUE will find a successful triangulation if one exists, but it may occasionally take more than one try.

extrude3d for a solid extrusion of a polygon, polygon3d for a flat display; both use triangulate.

## Examples

theta <- seq(0, 2*pi, len = 25)[-25]
theta <- c(theta, NA, theta, NA, theta, NA, theta, NA, theta)
r <- c(rep(1.5, 24), NA, rep(0.5, 24), NA, rep(0.5, 24), NA, rep(0.3, 24), NA, rep(0.1, 24))
dx <- c(rep(0, 24), NA, rep(0.6, 24), NA, rep(-0.6, 24), NA, rep(-0.6, 24), NA, rep(-0.6, 24))
x <- r*cos(theta) + dx
y <- r*sin(theta)
plot(x, y, type = "n")
polygon(x, y)
triangulate(x, y, plot = TRUE)

#>      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
#> [1,]   73   72   71   71   39   38   70   37   36    36    70    35    34    33
#> [2,]   72   71   40   70   38   37   41   36    5    35    15    34    33    32
#> [3,]   39   39   39   40   73   73   40   73   73     5    41     5     5     5
#>      [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26]
#> [1,]    32    31    70    69    30    29    68    67    66    65    64    28
#> [2,]    31    30    69    68    29    28    67    66    65    64    63    24
#> [3,]     5     5    15    15     5     5    15    15    15    15    15     5
#>      [,27] [,28] [,29] [,30] [,31] [,32] [,33] [,34] [,35] [,36] [,37] [,38]
#> [1,]    28    63    27    26    49    48    62    62    61    47     5    60
#> [2,]    27    62    26    49    48    47    11    61    60    46    74    59
#> [3,]    24    15    24    24    24    24    15    11    11    24    73    11
#>      [,39] [,40] [,41] [,42] [,43] [,44] [,45] [,46] [,47] [,48] [,49] [,50]
#> [1,]    59     5    58    46     5    45    45    44     5    57    19     5
#> [2,]    58    51    57    45    52    19    44    15    53     6    21    54
#> [3,]    11    74    11    24    51    24    19    19    52    11    24    53
#>      [,51] [,52] [,53] [,54] [,55] [,56] [,57] [,58] [,59] [,60] [,61] [,62]
#> [1,]    15    21     5    57    56    11    15    24     1    15    19     2
#> [2,]    18    22    55    56    55    13    42     1     2    16    20     3
#> [3,]    19    24    54     6     6    15    41     5     5    18    21     5
#>      [,63] [,64] [,65] [,66] [,67] [,68] [,69] [,70] [,71] [,72] [,73] [,74]
#> [1,]     6     5     9    16     6    15    22     3    13    44     6    11
#> [2,]     9     6    10    17     8    43    23     4    14    43     7    12
#> [3,]    11    55    11    18     9    42    24     5    15    15     8    13
#>      [,75] [,76] [,77] [,78] [,79] [,80] [,81] [,82] [,83] [,84] [,85] [,86]
#> [1,]   122    95   121    95    95   120    77    77   119    77   119   118
#> [2,]   121   123   120   124    77   119   101   102    89   103   118   117
#> [3,]    95   122    95   123   124    95   124   101    95   102    89    89
#>      [,87] [,88] [,89] [,90] [,91] [,92] [,93] [,94] [,95] [,96] [,97] [,98]
#> [1,]   117    77   116    77    77   115   114    77   113    77    80   113
#> [2,]   116   104   115   105   106   114   113   107    83    80   108   112
#> [3,]    89   103    89   104   105    89    89   106    89   107   107    83
#>      [,99] [,100] [,101] [,102] [,103] [,104] [,105] [,106] [,107] [,108]
#> [1,]    80    112     83     95     83     77    111     95     78     89
#> [2,]   109    111     88     76     84     78    110     97     79     90
#> [3,]   108     83     89     77     88     80     83     76     80     95
#>      [,109] [,110] [,111] [,112] [,113] [,114] [,115] [,116] [,117] [,118]
#> [1,]    110     90     92     84     84    110     80     95     84     80
#> [2,]     82     92     94     87     86    109     82     96     85     81
#> [3,]     83     95     95     88     87     82    109     97     86     82
#>      [,119] [,120] [,121] [,122]
#> [1,]     90     92     97     98
#> [2,]     91     93     98     99
#> [3,]     92     94     76     76
#> attr(,"nextvert")
#>   [1]   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19
#>  [19]  20  21  22  23  24   1  NA  49  26  27  28  29  30  31  32  33  34  35
#>  [37]  36  37  38  39  40  41  42  43  44  45  46  47  48  NA  74  51  52  53
#>  [55]  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71
#>  [73]  72  73  NA  77  78  79  80  81  82  83  84  85  86  87  88  89  90  91
#>  [91]  92  93  94  95  96  97  98  99  76  NA 124 101 102 103 104 105 106 107
#> [109] 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
open3d()
polygon3d(x, y, x - y, col = "red")

{"x":{"material":{"color":"#000000","alpha":1,"lit":true,"ambient":"#000000","specular":"#FFFFFF","emission":"#000000","shininess":50,"smooth":true,"front":"filled","back":"filled","size":3,"lwd":1,"fog":true,"point_antialias":false,"line_antialias":false,"texture":null,"textype":"rgb","texmipmap":false,"texminfilter":"linear","texmagfilter":"linear","texenvmap":false,"depth_mask":true,"depth_test":"less","isTransparent":false,"polygon_offset":[0,0],"margin":"","floating":false,"tag":""},"rootSubscene":1876,"objects":{"1882":{"id":1882,"type":"triangles","material":{},"vertices":"0","colors":"2","centers":"3","normals":"1","ignoreExtent":false,"flags":32771},"1880":{"id":1880,"type":"light","vertices":[[0,0,1]],"colors":[[1,1,1,1],[1,1,1,1],[1,1,1,1]],"viewpoint":true,"finite":false},"1879":{"id":1879,"type":"background","material":{},"colors":"4","centers":"5","sphere":false,"fogtype":"none","fogscale":1,"flags":32768},"1881":{"id":1881,"type":"background","material":{"lit":false,"back":"lines"},"colors":"6","centers":"7","sphere":false,"fogtype":"none","fogscale":1,"flags":32768},"1876":{"id":1876,"type":"subscene","par3d":{"antialias":8,"FOV":30,"ignoreExtent":false,"listeners":1876,"mouseMode":{"none":"none","left":"trackball","right":"zoom","middle":"fov","wheel":"pull"},"observer":[0,0,11.5911102294922],"modelMatrix":[[1,0,0,0],[0,0.342020153999329,0.939692616462708,0],[0,-0.939692616462708,0.342020153999329,-11.5911102294922],[0,0,0,1]],"projMatrix":[[3.73205089569092,0,0,0],[0,3.73205089569092,0,0],[0,0,-3.86370348930359,-41.784610748291],[0,0,-1,0]],"skipRedraw":false,"userMatrix":[[1,0,0,0],[0,0.342020143325668,0.939692620785909,0],[0,-0.939692620785909,0.342020143325668,0],[0,0,0,1]],"userProjection":[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]],"scale":[1,1,1],"viewport":{"x":0,"y":0,"width":1,"height":1},"zoom":1,"bbox":[-1.5,1.5,-1.5,1.5,-2.12132024765015,2.12132024765015],"windowRect":[0,0,256,256],"family":"sans","font":1,"cex":1,"useFreeType":true,"fontname":"NULL","maxClipPlanes":2147483647,"glVersion":2.1,"activeSubscene":0},"embeddings":{"viewport":"replace","projection":"replace","model":"replace","mouse":"replace"},"objects":[1881,1882,1880],"subscenes":[],"flags":33027}},"crosstalk":{"key":[],"group":[],"id":[],"options":[]},"width":480,"height":480,"buffer":{"accessors":[{"bufferView":0,"componentType":5126,"count":366,"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":366,"type":"VEC3"},{"bufferView":2,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":3,"componentType":5126,"count":122,"type":"VEC3"},{"bufferView":4,"componentType":5126,"count":1,"type":"VEC4"},{"bufferView":5,"componentType":5121,"count":1,"type":"VEC3"},{"bufferView":6,"componentType":5121,"count":1,"type":"VEC4"},{"bufferView":7,"componentType":5121,"count":1,"type":"VEC3"}],"bufferViews":[{"buffer":0,"byteLength":4392,"byteOffset":0},{"buffer":0,"byteLength":4392,"byteOffset":4392},{"buffer":0,"byteLength":4,"byteOffset":8784},{"buffer":0,"byteLength":1464,"byteOffset":8788},{"buffer":0,"byteLength":16,"byteOffset":10252},{"buffer":0,"byteLength":3,"byteOffset":10268},{"buffer":0,"byteLength":4,"byteOffset":10271},{"buffer":0,"byteLength":3,"byteOffset":10275}],"buffers":[{"byteLength":10278,"bytes":"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\nA7kFv8BdlL6LKG6+ZmbmvoEFhb7KwUK+oZbGviQ5Wb4f9DO+si2uvpqZGb7KwUK+c9WevrcE\nnz2hlsa+A7kFv8BdlD7j50+/zDkgv1XSxb2Bfwe/AABAv4EFhb5/+vq+mpkZv5qZmb6amZm+\nzDkgv1XSxb2Bfwe/Whxcv5qZGb70tTW/AABAv4EFhb5/+vq+zDkgv1XSxb2Bfwe/ZmYmv6xc\nsb3ROhC/Whxcv5qZGb70tTW/ZmYmv6xcsb3ROhC/srMrv8PQkL2amRm/Whxcv5qZGb70tTW/\nechjv7cEnz0QqXe/5FMyv0oG1Lyysyu/MzMzv0/oYSMzMzO/echjv7cEnz0QqXe/ZmZmvzxu\nKSRmZma/5FMyv0oG1Lyysyu/srMrv8PQkL2amRm/echjv7cEn73j50+/Whxcv5qZGb70tTW/\nc9WevrcEnz2hlsa+ZmbmvoEFhT70tTW/A7kFv8BdlD7j50+/srMrv8PQkL2amRm/L8Uvv83M\nTL1i+CK/echjv7cEn73j50+/ZmZmvzxuKSRmZma/L8Uvv83MTL1i+CK/5FMyv0oG1Lyysyu/\nAABAv4EFhT5gQYG/4+dPvyQ5WT4WG4O/Whxcv5qZGT5gQYG/c9WevrcEnz2hlsa+si2uvpqZ\nGT5/+vq+ZmbmvoEFhT70tTW/si2uvpqZGb7KwUK+mpmZvgAAAACamZm+c9WevrcEnz2hlsa+\nWhxcv5qZGb70tTW/4+dPvyQ5Wb6amRm/AABAv4EFhb5/+vq+si2uvpqZGT5/+vq+oZbGviQ5\nWT6amRm/ZmbmvoEFhT70tTW/AABAv4EFhb5/+vq+MXotv8BdlL6hlsa+mpkZv5qZmb6amZm+\nZmZmvzxuKSRmZma/echjv7cEn73j50+/L8Uvv83MTL1i+CK/si2uvpqZGb7KwUK+c9WevrcE\nn72LKG6+mpmZvgAAAACamZm+Os0TvzvNEz86zRM/Os0TvzvNEz86zRM/Os0TvzvNEz86zRM/\nOs0TvzrNEz89zRM/Os0TvzrNEz89zRM/Os0TvzrNEz89zRM/O80TvzvNEz87zRM/O80TvzvN\nEz87zRM/O80TvzvNEz87zRM/O80TvzvNEz86zRM/O80TvzvNEz86zRM/O80TvzvNEz86zRM/\nOs0TvzvNEz86zRM/Os0TvzvNEz86zRM/Os0TvzvNEz86zRM/Oc0TvzrNEz86zRM/Oc0TvzrN\nEz86zRM/Oc0TvzrNEz86zRM/OM0Tvz3NEz84zRM/OM0Tvz3NEz84zRM/OM0Tvz3NEz84zRM/\nOs0TvzvNEz87zRM/Os0TvzvNEz87zRM/Os0TvzvNEz87zRM/O80TvzvNEz87zRM/O80TvzvN\nEz87zRM/O80TvzvNEz87zRM/O80TvzvNEz86zRM/O80TvzvNEz86zRM/O80TvzvNEz86zRM/\nOs0TvzvNEz86zRM/Os0TvzvNEz86zRM/Os0TvzvNEz86zRM/Os0TvzzNEz85zRM/Os0TvzzN\nEz85zRM/Os0TvzzNEz85zRM/PM0TvzvNEz83zRM/PM0TvzvNEz83zRM/PM0TvzvNEz83zRM/\nPM0Tvz3NEz84zRM/PM0Tvz3NEz84zRM/PM0Tvz3NEz84zRM/Pc0TvzbNEz89zRM/Pc0TvzbN\nEz89zRM/Pc0TvzbNEz89zRM/Os0TvzrNEz87zRM/Os0TvzrNEz87zRM/Os0TvzrNEz87zRM/\nOc0TvznNEz87zRM/Oc0TvznNEz87zRM/Oc0TvznNEz87zRM/Pc0TvzPNEz9AzRM/Pc0TvzPN\nEz9AzRM/Pc0TvzPNEz9AzRM/O80TvzvNEz84zRM/O80TvzvNEz84zRM/O80TvzvNEz84zRM/\nO80TvzjNEz88zRM/O80TvzjNEz88zRM/O80TvzjNEz88zRM/O80TvzrNEz85zRM/O80TvzrN\nEz85zRM/O80TvzrNEz85zRM/O80TvzrNEz83zRM/O80TvzrNEz83zRM/O80TvzrNEz83zRM/\nOc0TvznNEz89zRM/Oc0TvznNEz89zRM/Oc0TvznNEz89zRM/Os0TvznNEz87zRM/Os0TvznN\nEz87zRM/Os0TvznNEz87zRM/Oc0TvzvNEz87zRM/Oc0TvzvNEz87zRM/Oc0TvzvNEz87zRM/\nO80TvzrNEz87zRM/O80TvzrNEz87zRM/O80TvzrNEz87zRM/P80TvznNEz82zRM/P80TvznN\nEz82zRM/P80TvznNEz82zRM/OM0TvzrNEz87zRM/OM0TvzrNEz87zRM/OM0TvzrNEz87zRM/\nOs0TvzrNEz87zRM/Os0TvzrNEz87zRM/Os0TvzrNEz87zRM/Os0TvzvNEz85zRM/Os0TvzvN\nEz85zRM/Os0TvzvNEz85zRM/Pc0TvzrNEz85zRM/Pc0TvzrNEz85zRM/Pc0TvzrNEz85zRM/\nP80TvzjNEz84zRM/P80TvzjNEz84zRM/P80TvzjNEz84zRM/Os0TvznNEz88zRM/Os0TvznN\nEz88zRM/Os0TvznNEz88zRM/Nc0TvzzNEz89zRM/Nc0TvzzNEz89zRM/Nc0TvzzNEz89zRM/\nOs0Tvz3NEz86zRM/Os0Tvz3NEz86zRM/Os0Tvz3NEz86zRM/Rc0TvzbNEz81zRM/Rc0TvzbN\nEz81zRM/Rc0TvzbNEz81zRM/K80Tv0HNEz9BzRM/K80Tv0HNEz9BzRM/K80Tv0HNEz9BzRM/\nOc0TvzvNEz87zRM/Oc0TvzvNEz87zRM/Oc0TvzvNEz87zRM/Oc0TvzzNEz85zRM/Oc0TvzzN\nEz85zRM/Oc0TvzzNEz85zRM/O80TvzrNEz86zRM/O80TvzrNEz86zRM/O80TvzrNEz86zRM/\nOc0TvzzNEz87zRM/Oc0TvzzNEz87zRM/Oc0TvzzNEz87zRM/Os0TvzvNEz86zRM/Os0TvzvN\nEz86zRM/Os0TvzvNEz86zRM/Ss0Tvy3NEz83zRM/Ss0Tvy3NEz83zRM/Ss0Tvy3NEz83zRM/\nOc0TvznNEz87zRM/Oc0TvznNEz87zRM/Oc0TvznNEz87zRM/O80TvzrNEz87zRM/O80TvzrN\nEz87zRM/O80TvzrNEz87zRM/Os0TvzrNEz84zRM/Os0TvzrNEz84zRM/Os0TvzrNEz84zRM/\nN80TvzzNEz88zRM/N80TvzzNEz88zRM/N80TvzzNEz88zRM/O80TvzvNEz87zRM/O80TvzvN\nEz87zRM/O80TvzvNEz87zRM/Os0TvzrNEz86zRM/Os0TvzrNEz86zRM/Os0TvzrNEz86zRM/\nPM0TvznNEz85zRM/PM0TvznNEz85zRM/PM0TvznNEz85zRM/Mc0Tv0bNEz85zRM/Mc0Tv0bN\nEz85zRM/Mc0Tv0bNEz85zRM/Oc0TvzzNEz85zRM/Oc0TvzzNEz85zRM/Oc0TvzzNEz85zRM/\nPM0Tv0TNEz8uzRM/PM0Tv0TNEz8uzRM/PM0Tv0TNEz8uzRM/LM0Tv0HNEz9CzRM/LM0Tv0HN\nEz9CzRM/LM0Tv0HNEz9CzRM/N80Tvz3NEz87zRM/N80Tvz3NEz87zRM/N80Tvz3NEz87zRM/\nPM0TvznNEz85zRM/PM0TvznNEz85zRM/PM0TvznNEz85zRM/Nc0TvzXNEz9FzRM/Nc0TvzXN\nEz9FzRM/Nc0TvzXNEz9FzRM/SM0TvzfNEz8vzRM/SM0TvzfNEz8vzRM/SM0TvzfNEz8vzRM/\nO80TvzvNEz86zRM/O80TvzvNEz86zRM/O80TvzvNEz86zRM/Ps0TvzLNEz89zRM/Ps0TvzLN\nEz89zRM/Ps0TvzLNEz89zRM/Pc0TvznNEz85zRM/Pc0TvznNEz85zRM/Pc0TvznNEz85zRM/\nMc0Tvz7NEz9AzRM/Mc0Tvz7NEz9AzRM/Mc0Tvz7NEz9AzRM/P80TvznNEz82zRM/P80TvznN\nEz82zRM/P80TvznNEz82zRM/O80TvzvNEz87zRM/O80TvzvNEz87zRM/O80TvzvNEz87zRM/\nPM0TvzzNEz84zRM/PM0TvzzNEz84zRM/PM0TvzzNEz84zRM/I80Tvw7NEz99zRM/I80Tvw7N\nEz99zRM/I80Tvw7NEz99zRM/Ns0Tv0PNEz82zRM/Ns0Tv0PNEz82zRM/Ns0Tv0PNEz82zRM/\nO80TvzvNEz85zRM/O80TvzvNEz85zRM/O80TvzvNEz85zRM/Pc0TvznNEz83zRM/Pc0TvznN\nEz83zRM/Pc0TvznNEz83zRM/O80TvzvNEz84zRM/O80TvzvNEz84zRM/O80TvzvNEz84zRM/\nNc0Tvz3NEz88zRM/Nc0Tvz3NEz88zRM/Nc0Tvz3NEz88zRM/Pc0TvzXNEz88zRM/Pc0TvzXN\nEz88zRM/Pc0TvzXNEz88zRM/Q80TvzbNEz82zRM/Q80TvzbNEz82zRM/Q80TvzbNEz82zRM/\nNs0Tv0PNEz82zRM/Ns0Tv0PNEz82zRM/Ns0Tv0PNEz82zRM/H80Tv0zNEz9FzRM/H80Tv0zN\nEz9FzRM/H80Tv0zNEz9FzRM/QM0TvzjNEz85zRM/QM0TvzjNEz85zRM/QM0TvzjNEz85zRM/\nw8wTv6vNEz89zRM/w8wTv6vNEz89zRM/w8wTv6vNEz89zRM/Is0Tv1PNEz89zRM/Is0Tv1PN\nEz89zRM/Is0Tv1PNEz89zRM/Qs0TvzLNEz86zRM/Qs0TvzLNEz86zRM/Qs0TvzLNEz86zRM/\nTc0Tvy3NEz81zRM/Tc0Tvy3NEz81zRM/Tc0Tvy3NEz81zRM/K80Tv0DNEz9BzRM/K80Tv0DN\nEz9BzRM/K80Tv0DNEz9BzRM/N80Tvz3NEz86zRM/N80Tvz3NEz86zRM/N80Tvz3NEz86zRM/\nQs0TvzLNEz87zRM/Qs0TvzLNEz87zRM/Qs0TvzLNEz87zRM/Sc0TvzLNEz8yzRM/Sc0TvzLN\nEz8yzRM/Sc0TvzLNEz8yzRM/Pc0TvzfNEz87zRM/Pc0TvzfNEz87zRM/Pc0TvzfNEz87zRM/\nKM0Tvz3NEz9KzRM/KM0Tvz3NEz9KzRM/KM0Tvz3NEz9KzRM/Tc0TvzbNEz8szRM/Tc0TvzbN\nEz8szRM/Tc0TvzbNEz8szRM/O80TvznNEz86zRM/O80TvznNEz86zRM/O80TvznNEz86zRM/\nOs0TvzvNEz86zRM/Os0TvzvNEz86zRM/Os0TvzvNEz86zRM/Os0TvzjNEz87zRM/Os0TvzjN\nEz87zRM/Os0TvzjNEz87zRM/O80Tvz3NEz85zRM/O80Tvz3NEz85zRM/O80Tvz3NEz85zRM/\nM80TvzzNEz9AzRM/M80TvzzNEz9AzRM/M80TvzzNEz9AzRM/S80TvzDNEz8xzRM/S80TvzDN\nEz8xzRM/S80TvzDNEz8xzRM/PM0TvzrNEz85zRM/PM0TvzrNEz85zRM/PM0TvzrNEz85zRM/\nO80TvznNEz88zRM/O80TvznNEz88zRM/O80TvznNEz88zRM/M80Tvz7NEz8/zRM/M80Tvz7N\nEz8/zRM/M80Tvz7NEz8/zRM/Ms0Tv0fNEz84zRM/Ms0Tv0fNEz84zRM/Ms0Tv0fNEz84zRM/\nPM0TvznNEz83zRM/PM0TvznNEz83zRM/PM0TvznNEz83zRM/O80TvznNEz86zRM/O80TvznN\nEz86zRM/O80TvznNEz86zRM/M80Tvz7NEz87zRM/M80Tvz7NEz87zRM/M80Tvz7NEz87zRM/\nN80TvzfNEz9AzRM/N80TvzfNEz9AzRM/N80TvzfNEz9AzRM/N80TvzbNEz8+zRM/N80TvzbN\nEz8+zRM/N80TvzbNEz8+zRM/OM0TvzvNEz87zRM/OM0TvzvNEz87zRM/OM0TvzvNEz87zRM/\nOc0TvzrNEz85zRM/Oc0TvzrNEz85zRM/Oc0TvzrNEz85zRM/O80TvznNEz87zRM/O80TvznN\nEz87zRM/O80TvznNEz87zRM/O80TvzrNEz85zRM/O80TvzrNEz85zRM/O80TvzrNEz85zRM/\nNc0Tv0rNEz8tzRM/Nc0Tv0rNEz8tzRM/Nc0Tv0rNEz8tzRM/Os0TvzjNEz86zRM/Os0TvzjN\nEz86zRM/Os0TvzjNEz86zRM/P80TvzTNEz89zRM/P80TvzTNEz89zRM/P80TvzTNEz89zRM/\nOc0TvzrNEz86zRM/Oc0TvzrNEz86zRM/Oc0TvzrNEz86zRM/Os0TvzvNEz85zRM/Os0TvzvN\nEz85zRM/Os0TvzvNEz85zRM/Ms0Tv0HNEz86zRM/Ms0Tv0HNEz86zRM/Ms0Tv0HNEz86zRM/\nQM0Tvy/NEz9AzRM/QM0Tvy/NEz9AzRM/QM0Tvy/NEz9AzRM/M80Tv0TNEz82zRM/M80Tv0TN\nEz82zRM/M80Tv0TNEz82zRM/Qc0Tv0HNEz8szRM/Qc0Tv0HNEz8szRM/Qc0Tv0HNEz8szRM/\nP80TvzXNEz85zRM/P80TvzXNEz85zRM/P80TvzXNEz85zRM/O80TvznNEz84zRM/O80TvznN\nEz84zRM/O80TvznNEz84zRM/MM0Tv0TNEz85zRM/MM0Tv0TNEz85zRM/MM0Tv0TNEz85zRM/\nMc0Tv0TNEz87zRM/Mc0Tv0TNEz87zRM/Mc0Tv0TNEz87zRM/LM0Tv03NEz81zRM/LM0Tv03N\nEz81zRM/LM0Tv03NEz81zRM/Ps0TvzXNEz89zRM/Ps0TvzXNEz89zRM/Ps0TvzXNEz89zRM/\nNc0TvzrNEz89zRM/Nc0TvzrNEz89zRM/Nc0TvzrNEz89zRM/AQAAARpXyr1IL3q+ugMVPnKj\nI76WU5y+ugMVPkv+Y71KrS2+blvpPYmICL3or7C9SNdYPW3LHz3orzC9qz2oPUsotLxGp4q+\ngMl+PrgYX75A/sa+yuMuPm7LH73orzA9qz2ovYiICD3or7A9SNdYvUv+Yz1KrS0+blvpvRpX\nyj1IL3o+ugMVvlYTnLyfbrm+a62vPvAayz72uAM/71vxvQ1oDT9GoR0/ysmBvWv6Ab/6YQe/\n6/GsPIyIID9ErCg/hjsCveLdNT+gYy4/HEjvPFM1Sr8n4RO/s1BZvlBLYL8n4RO/U9SYvqag\ndb/LKQ6/tu3Ovt/zSz+gYy4/74HsPZNghL/MHgO/WqIFv48vjL+nAee+TN4kv2DRb77NHgO/\n6FSOPjRJYT9ErCg/vnNiPmRPI77LKQ6/46vKPg+1kb+owMC+yglDv7yQlL/+FZa+exZev7yL\nhD6bxfU+vnNivn7rXD5NAw4/25CtvrNpdD9GoR0/25CtPiKdk7/LE9Q9Xt6gv5hzHj5GoR0/\nwAjsvtYDgj9NAw4/wAjsPhv0m70n4RO/pGIAP1aJhz+bxfU+3i8UPwRlij/wGss+jzwvP+YB\nmj/qRvc+WGA4P4L1mj/or7A9g+qPPzfjoz1ErCg/3i8Uv5MGjD/eh7G+i2i4P6vuWLugYy4/\njzwvv+1/oj92ozs/ZlwJP153t72gYy4/jFJFv2ePub4gTma/bIYJP7A+lT/wuA2/KBvcP+5/\nor9A0NO8rDCfv8f4eL5vO14/0DyOv+6DBL40VJg/suSovzRJYb9ErCg/vPrEv+f6T7+wPpW/\n8wS1PvzC8j7uf6I/XJ5Lv3ajO7/tf6I/1CgAwARlir/wGss+wCu9v7NpdL9GoR0/fAXJv9YD\ngr9NAw4/fgXJvzOuhD8zroS/M64EQDKQdD9AOg+/OOXBP7A+tT8k+Pa9M67EPwAAgD5rELM/\naxCTv1aJh7+bxfU+vPrEv7A+tT/3QcK+L8/lP+5/or/8wvI+rjDfv9OgoD6chE+/g+qPP35L\nyz5AzUm/f7mXP3yM8T5Cwj6/QMSbPwAAAAB2o7s/dqO7vzOuhL8zroS/AAAAADOFaT+/WiK/\n+O/FP2t74D5wVIS/S3O8P2sQs78AAIA+axDTvwAAgD5rELO/axDTP3aju78AAAAAdqO7vwAA\nAAB2o7u/dqO7P6NMD7+kGBw+y1I2v6PK+r7+q5Y90ToQv+gC7L5w9R+8PwPnvh6Y575PZMq8\n2PHavsZO5b5sdim99x/Qvu8fE7887iQ+fls8vzRkF7/sgCk+bsRBv8ZO5b4vI3C9X0rHvh6Y\n5766M5q9LwvBvgDPG7/sgCk+Oy9Gv0QTIL887iQ+005Jv5HmI7+kGBw+uuxKvykGJ79Emg8+\nuuxKv+gC7L4gzri9Ys+9vqPK+r5mvDG+cOyhvqqdJ7+jYFo+0DVev6Ob+b7Ggny+g7R2vqNM\nD7+kGBy+84zQvsLbL7+qn/k9tw9Pv/AfE7887iS+wMjTvjAyQb+NPwY+E8JivzRkF7/sgCm+\n8AfavoYaQ7+HWQ09H/BLv+PARb9KGFY+9kZ7vwDPG7/sgCm+iN3ivqSb+b5eHBY+6lQiv0dM\nVb9TYiY+2+R+v3Nn1b7AhF6+J0pMvgQl6L7x/4G+J0pMvg4nyL6CbZQ9b0Ltvs2bKL8gDWC+\nCDHhvmPHPr/u2Sy+55ATv9o+Nr8SduO9GNAZvyYSOr/iytG9y9gfv4YaQ7+GWY08UoVHv5cr\nVL+GWY08Y5ZYvy6ITr+Qrcu9exI1v6Dk2r5LGFY+Y/giv3PAP79LaYe9S9MuvyjVQr9MNc+8\nfls8vxasTr/rSVQ+SN+Bv9jNu75TYiY+gn8Hv0A0or5Sk8W8C9uVvhSsTr/rSVS+mpkZvz4O\nyb7rSVQ+mpkZv5gGLb/0/pC+Pg7JvltRU79qRy6942xIv0A0or76Z5u9g7R2vpmYmD6ZmJg+\nmZiYPgAAgD8AAAABAQEBAAAA"}]},"context":{"shiny":false,"rmarkdown":null},"vertexShader":"#line 2 1\n// File 1 is the vertex shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\n\nattribute vec3 aPos;\nattribute vec4 aCol;\nuniform mat4 mvMatrix;\nuniform mat4 prMatrix;\nvarying vec4 vCol;\nvarying vec4 vPosition;\n\n#ifdef NEEDS_VNORMAL\nattribute vec3 aNorm;\nuniform mat4 normMatrix;\nvarying vec4 vNormal;\n#endif\n\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nattribute vec2 aTexcoord;\nvarying vec2 vTexcoord;\n#endif\n\n#ifdef FIXED_SIZE\nuniform vec3 textScale;\n#endif\n\n#ifdef FIXED_QUADS\nattribute vec3 aOfs;\n#endif\n\n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\nvarying float normz;\nuniform mat4 invPrMatrix;\n#else\nattribute vec3 aPos1;\nattribute vec3 aPos2;\nvarying float normz;\n#endif\n#endif // IS_TWOSIDED\n\n#ifdef FAT_LINES\nattribute vec3 aNext;\nattribute vec2 aPoint;\nvarying vec2 vPoint;\nvarying float vLength;\nuniform float uAspect;\nuniform float uLwd;\n#endif\n\n\nvoid main(void) {\n  \n#ifndef IS_BRUSH\n#if defined(NCLIPPLANES) || !defined(FIXED_QUADS) || defined(HAS_FOG)\n  vPosition = mvMatrix * vec4(aPos, 1.);\n#endif\n  \n#ifndef FIXED_QUADS\n  gl_Position = prMatrix * vPosition;\n#endif\n#endif // !IS_BRUSH\n  \n#ifdef IS_POINTS\n  gl_PointSize = POINTSIZE;\n#endif\n  \n  vCol = aCol;\n  \n#ifdef NEEDS_VNORMAL\n  vNormal = normMatrix * vec4(-aNorm, dot(aNorm, aPos));\n#endif\n  \n#ifdef IS_TWOSIDED\n#ifdef HAS_NORMALS\n  /* normz should be calculated *after* projection */\n  normz = (invPrMatrix*vNormal).z;\n#else\n  vec4 pos1 = prMatrix*(mvMatrix*vec4(aPos1, 1.));\n  pos1 = pos1/pos1.w - gl_Position/gl_Position.w;\n  vec4 pos2 = prMatrix*(mvMatrix*vec4(aPos2, 1.));\n  pos2 = pos2/pos2.w - gl_Position/gl_Position.w;\n  normz = pos1.x*pos2.y - pos1.y*pos2.x;\n#endif\n#endif // IS_TWOSIDED\n  \n#ifdef NEEDS_VNORMAL\n  vNormal = vec4(normalize(vNormal.xyz/vNormal.w), 1);\n#endif\n  \n#if defined(HAS_TEXTURE) || defined(IS_TEXT)\n  vTexcoord = aTexcoord;\n#endif\n  \n#if defined(FIXED_SIZE) && !defined(ROTATING)\n  vec4 pos = prMatrix * mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w;\n  gl_Position = pos + vec4(aOfs*textScale, 0.);\n#endif\n  \n#if defined(IS_SPRITES) && !defined(FIXED_SIZE)\n  vec4 pos = mvMatrix * vec4(aPos, 1.);\n  pos = pos/pos.w + vec4(aOfs,  0.);\n  gl_Position = prMatrix*pos;\n#endif\n  \n#ifdef FAT_LINES\n  /* This code was inspired by Matt Deslauriers' code in \n   https://mattdesl.svbtle.com/drawing-lines-is-hard */\n  vec2 aspectVec = vec2(uAspect, 1.0);\n  mat4 projViewModel = prMatrix * mvMatrix;\n  vec4 currentProjected = projViewModel * vec4(aPos, 1.0);\n  currentProjected = currentProjected/currentProjected.w;\n  vec4 nextProjected = projViewModel * vec4(aNext, 1.0);\n  vec2 currentScreen = currentProjected.xy * aspectVec;\n  vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\n  float len = uLwd;\n  vec2 dir = vec2(1.0, 0.0);\n  vPoint = aPoint;\n  vLength = length(nextScreen - currentScreen)/2.0;\n  vLength = vLength/(vLength + len);\n  if (vLength > 0.0) {\n    dir = normalize(nextScreen - currentScreen);\n  }\n  vec2 normal = vec2(-dir.y, dir.x);\n  dir.x /= uAspect;\n  normal.x /= uAspect;\n  vec4 offset = vec4(len*(normal*aPoint.x*aPoint.y - dir), 0.0, 0.0);\n  gl_Position = currentProjected + offset;\n#endif\n  \n#ifdef IS_BRUSH\n  gl_Position = vec4(aPos, 1.);\n#endif\n}","fragmentShader":"#line 2 2\n// File 2 is the fragment shader\n#ifdef GL_ES\n#ifdef GL_FRAGMENT_PRECISION_HIGH\nprecision highp float;\n#else\nprecision mediump float;\n#endif\n#endif\nvarying vec4 vCol; // carries alpha\nvarying vec4 vPosition;\n#if defined(HAS_TEXTURE) || defined (IS_TEXT)\nvarying vec2 vTexcoord;\nuniform sampler2D uSampler;\n#endif\n\n#ifdef HAS_FOG\nuniform int uFogMode;\nuniform vec3 uFogColor;\nuniform vec4 uFogParms;\n#endif\n\n#if defined(IS_LIT) && !defined(FIXED_QUADS)\nvarying vec4 vNormal;\n#endif\n\n#if NCLIPPLANES > 0\nuniform vec4 vClipplane[NCLIPPLANES];\n#endif\n\n#if NLIGHTS > 0\nuniform mat4 mvMatrix;\n#endif\n\n#ifdef IS_LIT\nuniform vec3 emission;\nuniform float shininess;\n#if NLIGHTS > 0\nuniform vec3 ambient[NLIGHTS];\nuniform vec3 specular[NLIGHTS]; // light*material\nuniform vec3 diffuse[NLIGHTS];\nuniform vec3 lightDir[NLIGHTS];\nuniform bool viewpoint[NLIGHTS];\nuniform bool finite[NLIGHTS];\n#endif\n#endif // IS_LIT\n\n#ifdef IS_TWOSIDED\nuniform bool front;\nvarying float normz;\n#endif\n\n#ifdef FAT_LINES\nvarying vec2 vPoint;\nvarying float vLength;\n#endif\n\nvoid main(void) {\n  vec4 fragColor;\n#ifdef FAT_LINES\n  vec2 point = vPoint;\n  bool neg = point.y < 0.0;\n  point.y = neg ? (point.y + vLength)/(1.0 - vLength) :\n                 -(point.y - vLength)/(1.0 - vLength);\n#if defined(IS_TRANSPARENT) && defined(IS_LINESTRIP)\n  if (neg && length(point) <= 1.0) discard;\n#endif\n  point.y = min(point.y, 0.0);\n  if (length(point) > 1.0) discard;\n#endif // FAT_LINES\n  \n#ifdef ROUND_POINTS\n  vec2 coord = gl_PointCoord - vec2(0.5);\n  if (length(coord) > 0.5) discard;\n#endif\n  \n#if NCLIPPLANES > 0\n  for (int i = 0; i < NCLIPPLANES; i++)\n    if (dot(vPosition, vClipplane[i]) < 0.0) discard;\n#endif\n    \n#ifdef FIXED_QUADS\n    vec3 n = vec3(0., 0., 1.);\n#elif defined(IS_LIT)\n    vec3 n = normalize(vNormal.xyz);\n#endif\n    \n#ifdef IS_TWOSIDED\n    if ((normz <= 0.) != front) discard;\n#endif\n    \n#ifdef IS_LIT\n    vec3 eye = normalize(-vPosition.xyz/vPosition.w);\n    vec3 lightdir;\n    vec4 colDiff;\n    vec3 halfVec;\n    vec4 lighteffect = vec4(emission, 0.);\n    vec3 col;\n    float nDotL;\n#ifdef FIXED_QUADS\n    n = -faceforward(n, n, eye);\n#endif\n    \n#if NLIGHTS > 0\n    for (int i=0;i<NLIGHTS;i++) {\n      colDiff = vec4(vCol.rgb * diffuse[i], vCol.a);\n      lightdir = lightDir[i];\n      if (!viewpoint[i])\n        lightdir = (mvMatrix * vec4(lightdir, 1.)).xyz;\n      if (!finite[i]) {\n        halfVec = normalize(lightdir + eye);\n      } else {\n        lightdir = normalize(lightdir - vPosition.xyz/vPosition.w);\n        halfVec = normalize(lightdir + eye);\n      }\n      col = ambient[i];\n      nDotL = dot(n, lightdir);\n      col = col + max(nDotL, 0.) * colDiff.rgb;\n      col = col + pow(max(dot(halfVec, n), 0.), shininess) * specular[i];\n      lighteffect = lighteffect + vec4(col, colDiff.a);\n    }\n#endif\n    \n#else // not IS_LIT\n    vec4 colDiff = vCol;\n    vec4 lighteffect = colDiff;\n#endif\n    \n#ifdef IS_TEXT\n    vec4 textureColor = lighteffect*texture2D(uSampler, vTexcoord);\n#endif\n    \n#ifdef HAS_TEXTURE\n#ifdef TEXTURE_rgb\n    vec4 textureColor = lighteffect*vec4(texture2D(uSampler, vTexcoord).rgb, 1.);\n#endif\n    \n#ifdef TEXTURE_rgba\n    vec4 textureColor = lighteffect*texture2D(uSampler, vTexcoord);\n#endif\n    \n#ifdef TEXTURE_alpha\n    vec4 textureColor = texture2D(uSampler, vTexcoord);\n    float luminance = dot(vec3(1.,1.,1.), textureColor.rgb)/3.;\n    textureColor =  vec4(lighteffect.rgb, lighteffect.a*luminance);\n#endif\n    \n#ifdef TEXTURE_luminance\n    vec4 textureColor = vec4(lighteffect.rgb*dot(texture2D(uSampler, vTexcoord).rgb, vec3(1.,1.,1.))/3., lighteffect.a);\n#endif\n    \n#ifdef TEXTURE_luminance_alpha\n    vec4 textureColor = texture2D(uSampler, vTexcoord);\n    float luminance = dot(vec3(1.,1.,1.),textureColor.rgb)/3.;\n    textureColor = vec4(lighteffect.rgb*luminance, lighteffect.a*textureColor.a);\n#endif\n    \n    fragColor = textureColor;\n\n#elif defined(IS_TEXT)\n    if (textureColor.a < 0.1)\n      discard;\n    else\n      fragColor = textureColor;\n#else\n    fragColor = lighteffect;\n#endif // HAS_TEXTURE\n    \n#ifdef HAS_FOG\n    // uFogParms elements: x = near, y = far, z = fogscale, w = (1-sin(FOV/2))/(1+sin(FOV/2))\n    // In Exp and Exp2: use density = density/far\n    // fogF will be the proportion of fog\n    // Initialize it to the linear value\n    float fogF;\n    if (uFogMode > 0) {\n      fogF = (uFogParms.y - vPosition.z/vPosition.w)/(uFogParms.y - uFogParms.x);\n      if (uFogMode > 1)\n        fogF = mix(uFogParms.w, 1.0, fogF);\n      fogF = fogF*uFogParms.z;\n      if (uFogMode == 2)\n        fogF = 1.0 - exp(-fogF);\n      // Docs are wrong: use (density*c)^2, not density*c^2\n      // https://gitlab.freedesktop.org/mesa/mesa/-/blob/master/src/mesa/swrast/s_fog.c#L58\n      else if (uFogMode == 3)\n        fogF = 1.0 - exp(-fogF*fogF);\n      fogF = clamp(fogF, 0.0, 1.0);\n      gl_FragColor = vec4(mix(fragColor.rgb, uFogColor, fogF), fragColor.a);\n    } else gl_FragColor = fragColor;\n#else\n    gl_FragColor = fragColor;\n#endif // HAS_FOG\n    \n}","players":[],"webGLoptions":{"preserveDrawingBuffer":true}},"evals":[],"jsHooks":[]}