Сделать правую ручку
У меня есть проблема, когда у меня есть куча длин и вы хотите начать с начала (притворяйтесь, что я смотрю на положительный конец оси y), я делаю правый шаг и двигаюсь положительно вдоль оси x для расстояние length_i. В это время я делаю очередной поворот вправо, прохожу расстояние length_i и повторяю n раз. Я могу это сделать, но я думаю, что есть более эффективный способ сделать это, и мне не хватает математического фона:
## Fake Data
set.seed(11)
dat <- data.frame(id = LETTERS[1:6], lens=sample(2:9, 6),
x1=NA, y1=NA, x2=NA, y2=NA)
## id lens x1 y1 x2 y2
## 1 A 4 NA NA NA NA
## 2 B 2 NA NA NA NA
## 3 C 5 NA NA NA NA
## 4 D 8 NA NA NA NA
## 5 E 6 NA NA NA NA
## 6 F 9 NA NA NA NA
## Add a cycle of 4 column
dat[, "cycle"] <- rep(1:4, ceiling(nrow(dat)/4))[1:nrow(dat)]
##For loop to use the information from cycle column
for(i in 1:nrow(dat)) {
## set x1, y1
if (i == 1) {
dat[1, c("x1", "y1")] <- 0
} else {
dat[i, c("x1", "y1")] <- dat[(i - 1), c("x2", "y2")]
}
col1 <- ifelse(dat[i, "cycle"] %% 2 == 0, "x1", "y1")
col2 <- ifelse(dat[i, "cycle"] %% 2 == 0, "x2", "y2")
dat[i, col2] <- dat[i, col1]
col3 <- ifelse(dat[i, "cycle"] %% 2 != 0, "x2", "y2")
col4 <- ifelse(dat[i, "cycle"] %% 2 != 0, "x1", "y1")
mag <- ifelse(dat[i, "cycle"] %in% c(1, 4), 1, -1)
dat[i, col3] <- dat[i, col4] + (dat[i, "lens"] * mag)
}
Это дает желаемый результат:
> dat
id lens x1 y1 x2 y2 cycle
1 A 4 0 0 4 0 1
2 B 2 4 0 4 -2 2
3 C 5 4 -2 -1 -2 3
4 D 8 -1 -2 -1 6 4
5 E 6 -1 6 5 6 1
6 F 9 5 6 5 -3 2
Здесь это как сюжет:
library(ggplot2); library(grid)
ggplot(dat, aes(x = x1, y = y1, xend = x2, yend = y2)) +
geom_segment(aes(color=id), size=3, arrow = arrow(length = unit(0.5, "cm"))) +
ylim(c(-10, 10)) + xlim(c(-10, 10))
Это кажется медленным и неуклюжим. Я предполагаю, что лучший способ сделать это, чем элементы, которые я делаю в цикле for
. Какой более эффективный способ сохранить программные права?
![enter image description here]()
Ответы
Ответ 1
(Как предложено @DWin). Вот решение, использующее комплексные числа, которые гибки для любого типа turn
, а не только под углом 90 градусов (-pi/2 радиан). Все векторизация:
set.seed(11)
dat <- data.frame(id = LETTERS[1:6], lens = sample(2:9, 6),
turn = -pi/2)
dat <- within(dat, { facing <- pi/2 + cumsum(turn)
move <- lens * exp(1i * facing)
position <- cumsum(move)
x2 <- Re(position)
y2 <- Im(position)
x1 <- c(0, head(x2, -1))
y1 <- c(0, head(y2, -1))
})
dat[c("id", "lens", "x1", "y1", "x2", "y2")]
# id lens x1 y1 x2 y2
# 1 A 4 0 0 4 0
# 2 B 2 4 0 4 -2
# 3 C 5 4 -2 -1 -2
# 4 D 8 -1 -2 -1 6
# 5 E 6 -1 6 5 6
# 6 F 9 5 6 5 -3
Переменная turn
должна действительно считаться входом вместе с lens
. Сейчас все обороты -pi/2
радиан, но вы можете установить каждый из них на все, что захотите. Все остальные переменные являются выходами.
Теперь немного поиграем с ним:
trace.path <- function(lens, turn) {
facing <- pi/2 + cumsum(turn)
move <- lens * exp(1i * facing)
position <- cumsum(move)
x <- c(0, Re(position))
y <- c(0, Im(position))
plot.new()
plot.window(range(x), range(y))
lines(x, y)
}
trace.path(lens = seq(0, 1, length.out = 200),
turn = rep(pi/2 * (-1 + 1/200), 200))
![enter image description here]()
(Моя попытка репликации графика здесь: http://en.wikipedia.org/wiki/Turtle_graphics)
Я также попробую:
trace.path(lens = seq(1, 10, length.out = 1000),
turn = rep(2 * pi / 10, 1000))
trace.path(lens = seq(0, 1, length.out = 500),
turn = seq(0, pi, length.out = 500))
trace.path(lens = seq(0, 1, length.out = 600) * c(1, -1),
turn = seq(0, 8*pi, length.out = 600) * seq(-1, 1, length.out = 200))
Не стесняйтесь добавлять свои!
Ответ 2
Это еще один метод, использующий сложные числа. Вы можете повернуть вектор "вправо" в комплексной плоскости, умножив его на -1i
. Приведенный ниже код делает первый обход в положительном X (ось Re() - al), и каждый последующий обход будет повернут на "правый"
imVecs <- lengths*c(0-1i)^(0:3)
imVecs
# [1] 9+0i 0-5i -9+0i 0+9i 8+0i 0-5i -8+0i 0+7i 8+0i 0-1i -5+0i 0+3i 4+0i 0-7i -4+0i 0+2i
#[17] 3+0i 0-7i -5+0i 0+8i
cumsum(imVecs)
# [1] 9+0i 9-5i 0-5i 0+4i 8+4i 8-1i 0-1i 0+6i 8+6i 8+5i 3+5i 3+8i 7+8i 7+1i 3+1i 3+3i 6+3i 6-4i 1-4i
#[20] 1+4i
plot(cumsum(imVecs))
lines(cumsum(imVecs))
![enter image description here]()
Это подход к использованию комплексных поворотов плоскости для поворота на 45 градусов вправо:
> sqrt(-1i)
[1] 0.7071068-0.7071068i
> imVecs <- lengths*sqrt(0-1i)^(0:7)
Warning message:
In lengths * sqrt(0 - (0+1i))^(0:7) :
longer object length is not a multiple of shorter object length
> plot(cumsum(imVecs))
> lines(cumsum(imVecs))
И сюжет:
![enter image description here]()
Ответ 3
Это не очень красивый сюжет, но я включил его, чтобы показать, что этот "векторный" расчет координат дает правильные результаты, которые не должны быть слишком сложными для адаптации к вашим потребностям:
xx <- c(1,0,-1,0)
yy <- c(0,-1,0,1)
coords <- suppressWarnings(cbind(x = cumsum(c(0,xx*dat$lens)),
y = cumsum(c(0,yy*dat$lens))))
plot(coords, type="l", xlim=c(-10,10), ylim=c(-10,10))
![enter image description here]()
Ответ 4
Возможно, было бы полезно подумать об этом с точки зрения расстояния и опоры. Расстояние задается dat$lens
и подшипник угол перемещения относительно некоторой произвольной опорной линии (скажем, ось х). Затем на каждом шаге
x.new = x.old + distance * cos(bearing)
y.new = y.old + distance * sin(bearing)
bearing = bearing + increment
Здесь, поскольку мы начинаем в начале координат и перемещаемся в направлении + x, (x,y)=(0,0)
, а начало отсчета начинается с 0 градусов. Правый поворот - это просто шаг приращения -90 градусов (-pi/2 радиан). Итак, в R-коде, используя ваше определение dat
:
x <-0
y <- 0
bearing <- 0
for (i in 1:nrow(dat)){
dat[i,c(3,4)] <- c(x,y)
length <- dat[i,2]
x <- x + length * cos(bearing)
y <- y + length * sin(bearing)
dat[i,c(5,6)] <- c(x,y)
bearing <- bearing - pi/2
}
Это дает то, что у вас было, и имеет то преимущество, что вы можете обновить его очень просто, чтобы сделать повороты влево или на 45 градусов, или что-то еще. Вы даже можете добавить столбец bearing.increment
в dat
, чтобы создать случайное блуждание.
Ответ 5
Очень похоже на решение Джоша:
lengths <- sample(1:10, 20, repl=TRUE)
x=cumsum(lengths*c(1,0,-1,0))
y=cumsum(lengths*c(0,1,0,-1))
cbind(x,y)
x y
[1,] 9 0
[2,] 9 5
[3,] 0 5
[4,] 0 -4
[5,] 8 -4
[6,] 8 1
[7,] 0 1
[8,] 0 -6
[9,] 8 -6
[10,] 8 -5
[11,] 3 -5
[12,] 3 -8
[13,] 7 -8
[14,] 7 -1
[15,] 3 -1
[16,] 3 -3
[17,] 6 -3
[18,] 6 4
[19,] 1 4
[20,] 1 -4
Базовая графика:
plot(cbind(x,y))
arrows(cbind(x,y)[-20,1],cbind(x,y)[-20,2], cbind(x,y)[-1,1], cbind(x,y)[-1,2] )
![enter image description here]()
Это подчеркивает тот факт, что и Джош, и мои решения "вращаются неправильно", поэтому вам нужно изменить знаки на наших "матрицах перехода". И мы, вероятно, должны были начать с (0,0), но вам не придется беспокоиться о том, чтобы адаптировать это к вашим потребностям.