R-ohjelmointi.org

Tilastotieteellistä ohjelmointia R-kielellä

Kiertoja ja peilauksia R:llä

Talon muotoisilla pelinappuloilla leikkiminen tuotti lopputuloksena tähden muotoisen kuvion. Heräsi kysymys, miten paljon tähteä on mahdollista laajentaa. Pelinappulat ja rakentelijan kärsivällisyys loppuivat pian kesken, mutta onneksi R taipui siihen, mihin sorminäppäryys ei riittänyt. Samalla kuvion symmetrisyys tarjosi hyvän tilaisuuden harjoitella kiertoja ja peilauksia.

nappulatähti

Yllä oleva pelinappuloista rakennettu tähti ei itse asiassa laskennallisesti ole edes mahdollinen, ja kuvasta näkyykin, että nappulat eivät ole kaikin paikoin ihan tiiviisti. Osoittautui kuitenkin, että jos talon seinän korkeudeksi valitaan noin 1.355 * katon sivu, tähteä voidaan laajentaa loputtomasti.

Tähden rakennuspalikat

Tähdessä on monta symmetriatasoa, jotka helpottavat sen laskennallista kokoamista. Lähdin ensiksi kokoamaan pystysakaraa, ja sen jälkeen muodostin muut sakarat tästä siirtojen ja kiertojen avulla. Pääperiaate on selitetty seuraavassa kuvassa ja tekstissä.

vaiheet

Tähti on helpoin rakentaa kahden talon kappaleista, joissa on ylempänä pystyssä oleva talo ja alempana nurin päin oleva talo. Lisäksi tarvitaan joissain kohdissa yhden talon kappaleita (1). Sakarasta on tarpeen rakentaa aluksi vain puolikas (2), sillä toinen sakara saadaan tästä peilaamalla pystyakselin suhteen (3). Kun pystysakara on muodostettu, saadaan siirron (pystyakselin suunnassa katon sivun, a, verran ylöspäin) ja 45 asteen kierron avulla muodostettua tästä myös diagonaalin suuntainen sakara (5). Kokonainen tähti muodostuu, kun tätä kahden sakaran osaa kierretään kolmesti 90 astetta.

Määritellään ensin kiertofunktiot rotaatiomatriisin pohjalta x- ja y-koordinaateille erikseen. Funktioille annetaan syötteeksi kierrettävän kappaleen koordinaatit sekä kiertokulma a, joka on oletuksena 0 astetta. Tähdessä tarvitaan vain 45 asteen ja 90 asteen sekä näiden monikertojen kiertoja, mutta kiertofunktiot mahdollistavat minkä tahansa astemäärän suuruisen kierron.

rotx <- function(x, y, a=0) {
  zx <- x * cos(a) - y * sin(a)
  return(zx)
}
 
roty <- function(x, y, a=0) {
  zy <- x * sin(a) + y * cos(a)
  return(zy)
}

Seuraavaksi määritellään funktiot, jotka piirtävät yksittäisen kappaleen sekä sen sakaran pystyakselin suhteen peilatun peilikuvan. Nämä ottavat syötteenä kappaleen ylimmän pisteen koordinaatit, kiertokulman ja kappaleen värin. Koordinaatit annetaan aina pystysakaran mukaan, ja kierrot ilmoitetaan suhteessa tähden pystyakseliin. Ensin pitää kuitenkin määritellä talomonikulmion mittasuhteet.

# Mittasuhteet
a <- 1                                   # katon sivu
b <- 1.355 * a                           # seinä
h <- b + (1/sqrt(2)) * a                 # korkeus 
k <- h - b                               # katon korkeus
c <- sqrt(2) * a                         # pohja
 
# Kahden talon kappale
pala1 <- function(x, y, angle, color) {
 
  # Lähtökoordinaatit
  x2 <- x - 0.5 * c
  x4 <- x + 0.5 * c
  x9 <- x - c
  y2 <- y - k
  y3 <- y - h
  y6 <- y - 2* h
  y7 <- y - h - b
  px1 <- c(x, x2, x2, x4, x4)
  py1 <- c(y, y2, y3, y3, y2)
  px2 <- c(x2, x, x, x9, x9)
  py2 <- c(y6, y7, y3, y3, y7)
 
  # Kierretyt koordinaatit
  rx1 <- rotx(px1, py1, a=angle)
  ry1 <- roty(px1, py1, a=angle)
  rx2 <- rotx(px2, py2, a=angle)
  ry2 <- roty(px2, py2, a=angle)
  rx3 <- rotx(-px1, py1, a=angle)
  ry3 <- roty(-px1, py1, a=angle)
  rx4 <- rotx(-px2, py2, a=angle)
  ry4 <- roty(-px2, py2, a=angle)
 
  # Kaksi palaa ja peilikuvat
  polygon(rx1, ry1, col=color)
  polygon(rx2, ry2, col=color)
  polygon(rx3, ry3, col=color)
  polygon(rx4, ry4, col=color)
 
}
 
 
# Yhden talon kappale
pala2 <- function(x, y, angle, color) {
 
  # Lähtökoordinaatit
  x2 <- x - 0.5 * c
  x4 <- x + 0.5 * c
  y2 <- y - k
  y3 <- y - h
  px1 <- c(x, x2, x2, x4, x4)
  py1 <- c(y, y2, y3, y3, y2)
 
  # Kierretyt koordinaatit
  rx1 <- rotx(px1, py1, a=angle)
  ry1 <- roty(px1, py1, a=angle)
  rx2 <- rotx(-px1, py1, a=angle)
  ry2 <- roty(-px1, py1, a=angle)
 
  # Talo ja peilikuva
  polygon(rx1, ry1, col=color)
  polygon(rx2, ry2, col=color)
 
}

Tähden kasaaminen

Nyt kaikki tähden rakentamiseen tarvittavat peruselementit on määritelty. Enää tarvitaan ohjeet siitä, miten näistä kasataan tähti. Kappaleiden pinoamisperiaate on esitetty ylempänä olevassa kaavakuvassa (kohta 2): puolikkaan sakaran viistosuuntainen kerros kerrallaan. Myös kerrosnumerointi näkyy kuvassa. Sakaran toinen puolikas rakentuu samanaikaisesti peilikuvana (kohta 3).

Tähti piirretään funktiolla, jonka runkona on kolme sisäkkäistä silmukkaa. Ensimmäinen käy läpi tähden kerros kerrokselta, toinen laskee kussakin kerroksessa tarvittavan kappaleiden määrän ja kolmas määrittelee piirrettävän sakaran kiertokulman sekä piirtää kappaleet oikeille paikoilleen sakara kerrallaan. Diagonaalisia sakaroita piirrettäessä tehdään lisäksi koordinaattien siirto etäämmäs origosta, ja parittomissa kerroksissa lisätään kerroksen alareunaan yksittäisestä talosta koostuva kappale.

tahti <- function(r, col) {
 
  # Riittävän pitkä värivektori
  col1 <- rep(col, (r %/% length(col)) * length(col) + 1)
 
  # Tyhjä kuva
  par(mar=c(1,1,1,1))
  plot(0, 0, pch=4, xlim=c(-(r+1)*h-r*b,(r+1)*h+r*b), ylim=c((-(r+1)*h-r*b),(r+1)*h+r*b),
       bty="n", axes=F, xlab=NA, ylab=NA, main=NA, sub=NA, asp=1)
 
  # Kulmat (45 asteen välein)
  ang <- c(0:3 * pi/2, seq(1, 7, by=2) * pi/4) 
 
  # Kerrosten 0 ja 1 x-koordinaattien alustus
  x0 <- c(0,-0.5*c,-0.5*c,0.5*c,0.5*c)
  x1 <- 0
 
  # Kerros 0
  for(j in 1:8) {
    col2 <- ifelse(j %in% 1:4, col1[1], col1[2])
    y0 <- if(j %in% 1:4) c(0,k,h,h,k) else c(0,k,h,h,k) + a
    polygon(x=rotx(x0, y0, ang[j]), y=roty(x0, y0, ang[j]), col=col2)
  }
 
  # Kerros 1
  for(j in 1:8) {
    col2 <- ifelse(j %in% 1:4, col1[2], col1[3])
    y1 <- ifelse(j %in% 1:4, 2 * h, 2 * h + a)
    pala2(x=x1, y=y1, angle=ang[j], color=col2)
  }
 
  # Kerrokset 2 - r
  if(r > 1) { 
 
    # Kerrosten läpikäynti
    for(i in 2:r) {
 
      # Kerroksen ylin kärkikoordinaatti
      x <- 0
      y <- (i + 1) * h + (i - 1) * b
 
      # Parittomien kerrosten alin palikka
      if(i %% 2 != 0) {
 
        ux <- x - (i - 1) * 0.5 * c
 
        for(j in 1:8) {
          col3 <- ifelse(j %in% 1:4, col1[i+1], col1[i+2])
          uy <- if(j %in% 1:4) y - 0.5 * (i - 1) * (h + b) else y - 0.5 * (i - 1) * (h + b) + 1
          pala2(ux, uy, angle=ang[j], color=col3)
 
        }
      }
 
      # Loput palikat
      for(j in 1:(i %/% 2)) { 
 
        # Palikan ylimmän kärkipisteen x-koordinaatti
        px <- x - (j - 1) * c
 
        # Piirretään sakara kerrallaan
        for(l in 1:8) { 
          col4 <- ifelse(l %in% 1:4, col1[i+1], col1[i+2])
          py <- if(l %in% 1:4) y - (j - 1) * (h + b) else y - (j - 1) * (h + b) + a
          pala1(px, py, angle=ang[l], color=col4)
 
        }
      }
    }    
  }
}

Funktiota kutsutaan antamalla sille sakaran kerrosten määrä sekä vektori, joka sisältää käytettävät värit. Funktio kierrättää värit automaattisesti, jos kerroksia on enemmän kuin syötteenä annettuja värejä.

tahti(8, c(”blue”, ”white”))

tahti1

Kokeilin ensin rakentaa tähden yksi sakaran vaakarivi kerrallaan, mutta tämä malli oli hankala toteuttaa eikä sallinut yli kahden värin värimallia. Sen sijaan yllä määritellyssä funktiossa tähti voidaan värittää mielivaltaisella värimäärällä, kuten esimerkissä alla. Kannattaa huomioida, että funktion suorituskyky alkaa hyytyä noin 30. kerroksen kohdalla. Tämä on kuitenkin selvästi enemmän kuin alkuperäisillä fyysisillä pelinappuloilla olisi ollut mahdollista saavuttaa.

rainbow <- c("white","pink","red","orange","yellow","green","cyan","blue","violet")
tahti(16, rainbow)

tahti2

Animoitu tähti

Pistetään lopuksi vähän animaatiota kehiin paketin animation avulla. Tämäkin onnistuu R:llä helpommin kuin pelinappuloilla!

library(animation)
setwd("C:/Users/MyDir/gif/")
rainbow2 <- rev(rep(rainbow, 2))
ani.options(ani.width=500, ani.height=500)
saveGIF({for(i in 1:9) if(i %% 2 == 0) tahti(16, rainbow2[(length(rainbow2)+1-(i+8)):(length(rainbow2)+1-i)])})

animation


Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *

Category