使用 R 的规则(多边形)平铺
Regular (polygonal) tiling using R
我试图想出一个脚本来创建一个矩阵,该矩阵包含规则多边形平铺的相邻多边形的坐标,例如正方形和八边形之间插入了一点 space(space 图中未显示:在那里,多边形共享边)。
我的第一次尝试是从一个起点 "walk" 通过坐标系。但是,当我浏览平铺的第一列时,它变得很复杂,例如向上,现在必须向下走以创建第二列。要么我完全放弃这种方法,要么我想出一个更好的顺序方案。你能帮忙吗?
ID = 1 # not relevant here
p=c(0,0) # starting point
l_m=10 # length of an edge
NODES <- matrix(NA,nrow=0,ncol = 3)
calculateSquareUp <- function(p_start,l_m) { # draws the points of a square while walking upwards, counterclockwise
p1=p_start
p2=p1+c(l_m,0)
p3=p2+c(0,l_m)
p4=p3+c(-l_m,0)
NODES <- rbind(p1,p2,p3,p4)
NODES <- cbind(ID,NODES)
list("p_up"=p4,"p_down"=p2,"NODES"=NODES)
}
calculateOctagonUp <- function(p_start,l_m) { # draws the points of an octagon while walking upwards, counterclockwise
p1=p_start
p2=p1+c(l_m,0)
p3=p2+c(l_m,l_m)/sqrt(2)
p4=p3+c(0,l_m)
p5=p4+c(-l_m,l_m)/sqrt(2)
p6=p5+c(-l_m,0)
p7=p6-c(l_m,l_m)/sqrt(2)
p8=p7+c(0,-l_m)
NODES <- rbind(p1,p2,p3,p4,p5,p6,p7,p8)
NODES <- cbind(ID,NODES)
list("p_up"=p6,"p_down"=p4,"NODES"=NODES)
}
calculateSquareDown <- function(p_start,l_m) { # draws the points of a square while walking downwards, counterclockwise
p1=p_start
p2=p1+c(l_m,0)
p3=p2+c(0,-l_m)
p4=p3+c(-l_m,0)
NODES <- rbind(p1,p2,p3,p4)
NODES <- cbind(ID,NODES)
list("p_up"=p2, "p_down"=p2,"NODES"=NODES)
}
calculateOctagonDown <- function(p_start,l_m) { # draws the points of an octagon while walking downwards, counterclockwise
p1=p_start
p2=p1+c(l_m,0)
p3=p2+c(l_m,-l_m)/sqrt(2)
p4=p3+c(0,-l_m)
p5=p4+c(-l_m,-l_m)/sqrt(2)
p6=p5+c(-l_m,0)
p7=p6-c(-l_m,l_m)/sqrt(2)
p8=p7+c(0,l_m)
NODES <- rbind(p1,p2,p3,p4,p5,p6,p7,p8)
NODES <- cbind(ID,NODES)
list("p_up"=p3,"p_down"=p6, "NODES"=NODES)
}
space=c(10,10)
# Easy example: a 2x2 tiling
max_polygons=2 # per column
n_polygons=4 # total number of polygons
NODES <- matrix(NA,nrow=0,ncol = 3)
for (i in 1:n_polygons){
if(i < round(n_polygons/2,0)){ # if first column, walk upwards
for (j in 1:max_polygons) {
if (j%%2==0){
listOctagon = calculateOctagonUp(p_start=p,l_m=l_m)
p = listOctagon[[1]]+space
NODES <-rbind(NODES,listOctagon[[3]])
}
else {
listSquare = calculateSquareUp(p_start=p,l_m=l_m)
p = listSquare[[1]]+space
NODES <-rbind(NODES,listSquare[[3]])
}
}
} else if(i >round(n_polygons/2,0)){ # if second column, walk downwards
for (j in 1:max_polygons) {
if (j%%2==0){
listOctagon = calculateOctagonDown(p_start=p,l_m=l_m)
p = listOctagon[[2]]+space
NODES <-rbind(NODES,listOctagon[[3]])
}
else {
listSquare = calculateSquareDown(p_start=p,l_m=l_m)
p = listSquare[[2]]+space
NODES <-rbind(NODES,listSquare[[3]])
}
}
}
}
plot(NODES[,2],NODES[,3], type = "p")
这至少是解决 sf
的大部分方法。在这里,我们利用包的仿射变换功能,简单地复制一个由一个八边形和一个正方形组成的瓦片,想复制多少就复制多少。我借用并修改了你的八边形函数来制作一个快速的八边形。显然,这将创建一个如图所示的倾斜网格,但对 plot 函数的一些修改可以在您想要的任何方向上细分单元。关键是通过将 c(x, y)
添加到 sfc
对象来为下一个图块指定正确的翻译。
对于某些背景,sf
将点和线存储为 2 列矩阵,将多边形存储为矩阵列表。这就是为什么st_polygon
里面有对list
的调用。
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.0, proj.4 4.9.3
square <- st_polygon(
list(
matrix(c(0,0,0,1,1,1,1,0,0,0), ncol = 2, byrow = TRUE)
)
)
calculateOctagonUp <- function(p_start,l_m) { # draws the points of an octagon while walking upwards, counterclockwise
p1=p_start
p2=p1+c(l_m,0)
p3=p2+c(l_m,l_m)/sqrt(2)
p4=p3+c(0,l_m)
p5=p4+c(-l_m,l_m)/sqrt(2)
p6=p5+c(-l_m,0)
p7=p6-c(l_m,l_m)/sqrt(2)
p8=p7+c(0,-l_m)
rbind(p1,p2,p3,p4,p5,p6,p7,p8,p1)
}
octagon <- st_polygon(list(calculateOctagonUp(c(0,1), 1)))
unit <- st_sfc(square, octagon)
plot_grid <- function(tile, nrow, ncol){
col <- tile
for (i in 1:nrow){
col <- c(col, col + c(0, 2 + sqrt(2)))
}
grid <- col
for (i in 1:ncol){
grid <- c(grid, grid + c(1 + sqrt(2) / 2, -(1 + sqrt(2) / 2)))
}
plot(grid)
}
plot_grid(unit, 3, 3)
由 reprex package (v0.2.0) 创建于 2018-02-22。
我试图想出一个脚本来创建一个矩阵,该矩阵包含规则多边形平铺的相邻多边形的坐标,例如正方形和八边形之间插入了一点 space(space 图中未显示:在那里,多边形共享边)。
我的第一次尝试是从一个起点 "walk" 通过坐标系。但是,当我浏览平铺的第一列时,它变得很复杂,例如向上,现在必须向下走以创建第二列。要么我完全放弃这种方法,要么我想出一个更好的顺序方案。你能帮忙吗?
ID = 1 # not relevant here
p=c(0,0) # starting point
l_m=10 # length of an edge
NODES <- matrix(NA,nrow=0,ncol = 3)
calculateSquareUp <- function(p_start,l_m) { # draws the points of a square while walking upwards, counterclockwise
p1=p_start
p2=p1+c(l_m,0)
p3=p2+c(0,l_m)
p4=p3+c(-l_m,0)
NODES <- rbind(p1,p2,p3,p4)
NODES <- cbind(ID,NODES)
list("p_up"=p4,"p_down"=p2,"NODES"=NODES)
}
calculateOctagonUp <- function(p_start,l_m) { # draws the points of an octagon while walking upwards, counterclockwise
p1=p_start
p2=p1+c(l_m,0)
p3=p2+c(l_m,l_m)/sqrt(2)
p4=p3+c(0,l_m)
p5=p4+c(-l_m,l_m)/sqrt(2)
p6=p5+c(-l_m,0)
p7=p6-c(l_m,l_m)/sqrt(2)
p8=p7+c(0,-l_m)
NODES <- rbind(p1,p2,p3,p4,p5,p6,p7,p8)
NODES <- cbind(ID,NODES)
list("p_up"=p6,"p_down"=p4,"NODES"=NODES)
}
calculateSquareDown <- function(p_start,l_m) { # draws the points of a square while walking downwards, counterclockwise
p1=p_start
p2=p1+c(l_m,0)
p3=p2+c(0,-l_m)
p4=p3+c(-l_m,0)
NODES <- rbind(p1,p2,p3,p4)
NODES <- cbind(ID,NODES)
list("p_up"=p2, "p_down"=p2,"NODES"=NODES)
}
calculateOctagonDown <- function(p_start,l_m) { # draws the points of an octagon while walking downwards, counterclockwise
p1=p_start
p2=p1+c(l_m,0)
p3=p2+c(l_m,-l_m)/sqrt(2)
p4=p3+c(0,-l_m)
p5=p4+c(-l_m,-l_m)/sqrt(2)
p6=p5+c(-l_m,0)
p7=p6-c(-l_m,l_m)/sqrt(2)
p8=p7+c(0,l_m)
NODES <- rbind(p1,p2,p3,p4,p5,p6,p7,p8)
NODES <- cbind(ID,NODES)
list("p_up"=p3,"p_down"=p6, "NODES"=NODES)
}
space=c(10,10)
# Easy example: a 2x2 tiling
max_polygons=2 # per column
n_polygons=4 # total number of polygons
NODES <- matrix(NA,nrow=0,ncol = 3)
for (i in 1:n_polygons){
if(i < round(n_polygons/2,0)){ # if first column, walk upwards
for (j in 1:max_polygons) {
if (j%%2==0){
listOctagon = calculateOctagonUp(p_start=p,l_m=l_m)
p = listOctagon[[1]]+space
NODES <-rbind(NODES,listOctagon[[3]])
}
else {
listSquare = calculateSquareUp(p_start=p,l_m=l_m)
p = listSquare[[1]]+space
NODES <-rbind(NODES,listSquare[[3]])
}
}
} else if(i >round(n_polygons/2,0)){ # if second column, walk downwards
for (j in 1:max_polygons) {
if (j%%2==0){
listOctagon = calculateOctagonDown(p_start=p,l_m=l_m)
p = listOctagon[[2]]+space
NODES <-rbind(NODES,listOctagon[[3]])
}
else {
listSquare = calculateSquareDown(p_start=p,l_m=l_m)
p = listSquare[[2]]+space
NODES <-rbind(NODES,listSquare[[3]])
}
}
}
}
plot(NODES[,2],NODES[,3], type = "p")
这至少是解决 sf
的大部分方法。在这里,我们利用包的仿射变换功能,简单地复制一个由一个八边形和一个正方形组成的瓦片,想复制多少就复制多少。我借用并修改了你的八边形函数来制作一个快速的八边形。显然,这将创建一个如图所示的倾斜网格,但对 plot 函数的一些修改可以在您想要的任何方向上细分单元。关键是通过将 c(x, y)
添加到 sfc
对象来为下一个图块指定正确的翻译。
对于某些背景,sf
将点和线存储为 2 列矩阵,将多边形存储为矩阵列表。这就是为什么st_polygon
里面有对list
的调用。
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.0, proj.4 4.9.3
square <- st_polygon(
list(
matrix(c(0,0,0,1,1,1,1,0,0,0), ncol = 2, byrow = TRUE)
)
)
calculateOctagonUp <- function(p_start,l_m) { # draws the points of an octagon while walking upwards, counterclockwise
p1=p_start
p2=p1+c(l_m,0)
p3=p2+c(l_m,l_m)/sqrt(2)
p4=p3+c(0,l_m)
p5=p4+c(-l_m,l_m)/sqrt(2)
p6=p5+c(-l_m,0)
p7=p6-c(l_m,l_m)/sqrt(2)
p8=p7+c(0,-l_m)
rbind(p1,p2,p3,p4,p5,p6,p7,p8,p1)
}
octagon <- st_polygon(list(calculateOctagonUp(c(0,1), 1)))
unit <- st_sfc(square, octagon)
plot_grid <- function(tile, nrow, ncol){
col <- tile
for (i in 1:nrow){
col <- c(col, col + c(0, 2 + sqrt(2)))
}
grid <- col
for (i in 1:ncol){
grid <- c(grid, grid + c(1 + sqrt(2) / 2, -(1 + sqrt(2) / 2)))
}
plot(grid)
}
plot_grid(unit, 3, 3)
由 reprex package (v0.2.0) 创建于 2018-02-22。