An R package for the automatic generation of Raven-like matrices according to rules

Last updated 2022-11-18

Files

  • Class and Methods.R e Class and Methods extension.R e Class and Methods extension.R contengono le definizioni delle classi e dei metodi presentati in seguito. (VANNO MESSE IN ORDINE E BISOGNA RIDURRE A UN UNICO FILE)

  • Shapes_list.R contiene le forme sviluppate di default.

  • Rules_27102022.R contiene le regole per la creazone delle matrici.

Classe campo

La classe field contiene le informazioni neccessarie per plottare il contenuto di una cella della matrice.

Vedi codice
field <- list(
  shape = NULL,
  size.x = list(),
  size.y = list(),
  rotation = list(),
  pos.x = list(),
  pos.y = list(),
  lty =list(),
  lwd = list(),
  num = list(),
  nv = list(),
  shade =list(),  
  visible = NULL, 
  tag = list()
)
  • shape è un vettore contente stringhe di caratteri, ogni elemento della stringa indica la forma che si vuole sviluppare, per esempio c('circle','square')

  • size.x e size.y un vettore o un scalare che danno informazione per il semi-asse maggiore e minore del elisse entro il quale è inscritto il poligono (corrispondono a radius.x e radius.y del pacchetto DescTools)

library(DescTools)
Canvas(c(-5,5),c(-5,5)) # non è necessario il secondo c(-5,5) basta Canvas(c(-5,5))
DrawRegPolygon(x = 0, y = 0, radius.x=9,radius.y = 5, nv = 100)
DrawRegPolygon(x = 0, y = 0, radius.x=9,radius.y = 5, nv = 3)

  • rotation angolo di rotazione in radianti (corrisponde a rot del pacchetto DescTools)

  • x e y un vettore delle coordinate xy del centro del polygono (corrispondono a x e y del pacchetto DescTools)

  • lty e lwd rispettivamente il tipo di linea per il perimetro e lo spessore della stessa. Le figure di default hanno lty=1 e lwd=2 (corrispondono a lty e lwd del pacchetto DescTools)

  • num il numero di volte che un oggetto è ripetuto sulla cella (più un desiderata che altro al momento)

  • nv il numero di vertici del poligono (per cerchi/elipsi nv = 100)

  • shade è il riempimento monocromatico di una forma

  • visible un vettore binario che contiene un 1 se la forma nella posizione relativa deve venire disegnata, uno 0 se non deve venire disegnata. Per esempio list(shapes=c('circle','square'), visible = c(0,1)) plottera solo il quadrato e non il cerchio.

Classe matrice

La classe Raven_matrix contiene tutte le informazioni neccessarie per classificare la creazione della matrice.

Vedi codice
Raven<-list(
    Sq1 = list(),
    Sq2 = list(),
    Sq3 = list(),
    
    Sq4 = list(),
    Sq5 = list(),
    Sq6 = list(),
    
    Sq7 = list(),
    Sq8 = list(),
    Sq9 = list(),
    hrule = list(),
    vrule = list()
  )

I campi da Sq1 a Sq9 corrispondono alle celle della matrice.

##      [,1]  [,2]  [,3] 
## [1,] "Sq1" "Sq2" "Sq3"
## [2,] "Sq4" "Sq5" "Sq6"
## [3,] "Sq7" "Sq8" "Sq9"

Ogni cella della matrice andrà a contenere un oggetto di classe field contenente tutte le possibili forme al suo interno.

I campi hrule e vrule sono due vettori contententi le regole.

  • hrule contiene le regole che verranno applicate secondo la logica orizzontale. L’ordine delle regole nel vettore corrisponde al ordine di applicazione.
  • vrule contiene le regole che verranno applicate secondo la logica verticale. L’ordine delle regole nel vettore corrisponde al ordine di applicazione.

Logiche più complesse vengono ottenute tramite composizioni di orizzontale e verticale.

Nella seuente tabella è presente una lista delle forme al momento implementate nel pacchetto. Nella colonna name è presente il nome della forma, nella colonna num_shapes il numero di forme di cui è composta la figura. Il resto delle colonne (i.e., small, fill , rotate) i tag che specificano diverse caratteristiche della figura, nello specifico se la figura può essere rimpicciolita, se può essere usata come riempimento e se può essere ruotata.

Vedi tabella delle forme
name num_shapes small fill rotate
bow.tie 2 FALSE TRUE TRUE
circle 1 TRUE FALSE FALSE
cross 1 FALSE TRUE FALSE
cross.dice 1 FALSE FALSE FALSE
diagline 1 FALSE TRUE FALSE
diagline.inv 1 FALSE TRUE FALSE
dice 1 FALSE FALSE FALSE
dot 0 FALSE TRUE FALSE
e.hexagon 1 TRUE FALSE TRUE
ellipse 1 TRUE FALSE FALSE
h.arc.left.down 1 TRUE FALSE FALSE
h.arc.left.up 1 TRUE FALSE FALSE
h.arc.right.down 1 TRUE FALSE FALSE
h.arc.right.up 1 TRUE FALSE FALSE
hexagon 1 TRUE TRUE FALSE
hline 1 FALSE TRUE FALSE
horizontal.eight 2 FALSE FALSE FALSE
horizontal_eight 1 FALSE TRUE FALSE
lily 4 FALSE FALSE FALSE
pentagon 1 TRUE FALSE FALSE
pie.2 2 FALSE TRUE FALSE
pie.2.inv 2 FALSE TRUE FALSE
pie.4 4 FALSE TRUE FALSE
rot.hexagon 1 TRUE FALSE FALSE
s.horizontal 2 FALSE TRUE FALSE
s.horizontal.inv 2 FALSE TRUE FALSE
s.lily 1 FALSE TRUE FALSE
s.vertical 2 FALSE TRUE FALSE
s.vertical.inv 2 FALSE TRUE FALSE
s_horizontal 1 FALSE TRUE FALSE
s_horizontal.inv 1 FALSE TRUE FALSE
s_vertical 1 FALSE TRUE FALSE
s_vertical.inv 1 FALSE TRUE FALSE
semi.circle 1 FALSE TRUE TRUE
semi.circle.inv 1 FALSE TRUE TRUE
semi.circle.inv1 1 FALSE TRUE TRUE
semi.circle1 1 FALSE TRUE TRUE
slice 1 FALSE TRUE TRUE
square 1 TRUE FALSE FALSE
square4 4 FALSE FALSE FALSE
star 2 TRUE TRUE FALSE
triangle 1 TRUE FALSE FALSE
u.bow.tie 1 FALSE TRUE TRUE
u.pie.2 1 FALSE TRUE FALSE
u.pie.2.inv 1 FALSE TRUE FALSE
u.pie.4 1 FALSE TRUE FALSE
u.star 1 TRUE TRUE FALSE
v.arc.left.down 1 TRUE FALSE FALSE
v.arc.left.up 1 TRUE FALSE FALSE
v.arc.right.down 1 TRUE FALSE FALSE
v.arc.right.up 1 TRUE FALSE FALSE
vertical.eight 2 FALSE FALSE FALSE
vertical_eight 1 FALSE TRUE FALSE
vline 1 FALSE TRUE FALSE
X 1 FALSE TRUE FALSE

Forme di Default

Forme Geometriche

Segmenti

Archi

Forme puntinate

Concatenazione di Forme

La funzione cof concatena diversi campi esattamente secondo la stessa logica di c()

Mostra codice
source("Class and Methods.R")
cof(square(),circle(),pentagon())
## $shape
## [1] "square"   "circle"   "pentagon"
## 
## $size.x
## $size.x[[1]]
## [1] 15
## 
## $size.x[[2]]
## [1] 10
## 
## $size.x[[3]]
## [1] 15
## 
## 
## $size.y
## $size.y[[1]]
## [1] 15
## 
## $size.y[[2]]
## [1] 10
## 
## $size.y[[3]]
## [1] 15
## 
## 
## $theta.1
## $theta.1[[1]]
## [1] 0
## 
## $theta.1[[2]]
## [1] 0
## 
## $theta.1[[3]]
## [1] 0
## 
## 
## $theta.2
## $theta.2[[1]]
## [1] 0
## 
## $theta.2[[2]]
## [1] 0
## 
## $theta.2[[3]]
## [1] 0
## 
## 
## $rotation
## $rotation[[1]]
## [1] 0.7853982
## 
## $rotation[[2]]
## [1] 0
## 
## $rotation[[3]]
## [1] 1.570796
## 
## 
## $pos.x
## $pos.x[[1]]
## [1] 0
## 
## $pos.x[[2]]
## [1] 0
## 
## $pos.x[[3]]
## [1] 0
## 
## 
## $pos.y
## $pos.y[[1]]
## [1] 0
## 
## $pos.y[[2]]
## [1] 0
## 
## $pos.y[[3]]
## [1] 0
## 
## 
## $lty
## $lty[[1]]
## [1] 1
## 
## $lty[[2]]
## [1] 1
## 
## $lty[[3]]
## [1] 1
## 
## 
## $lwd
## $lwd[[1]]
## [1] 3
## 
## $lwd[[2]]
## [1] 2
## 
## $lwd[[3]]
## [1] 3
## 
## 
## $num
## $num[[1]]
## [1] 1
## 
## $num[[2]]
## [1] 1
## 
## $num[[3]]
## [1] 1
## 
## 
## $nv
## $nv[[1]]
## [1] 4
## 
## $nv[[2]]
## [1] 100
## 
## $nv[[3]]
## [1] 5
## 
## 
## $shade
## $shade[[1]]
## [1] NA
## 
## $shade[[2]]
## [1] NA
## 
## $shade[[3]]
## [1] NA
## 
## 
## $visible
## [1] 1 1 1
## 
## $tag
## $tag[[1]]
## [1] "simple" "small" 
## 
## $tag[[2]]
## [1] "simple" "small" 
## 
## $tag[[3]]
## [1] "simple" "small" 
## 
## 
## attr(,"class")
## [1] "field"

Creazione di una matrice

La funzione Raven crea una matrice di Raven in cui tutte le celle della matrice vengono inizializzate identiche alla cella Sq1, e vengono inizializzate le regole.

  • St1 classe field che contiene i campi di partenza per tutte le celle
  • hrule vettore delle regole applicabili con logica orizzontale
  • vrule vettore delle regole applicabili con logica verticale
M<-Raven(st1=square(),hrule=c("size"),vrule=c("identity"))
draw(M)

Nella matrice M le regole non sono ancora state applicate, infatti nel plot d’esempio i quadrati hanno tutti le stesse dimensioni.

L’estensione del metodo apply applica le regole presenti nella matrice a tutte le celle. Al momento applica le regole prima in riga e poi in colonna.

M<-apply(M)
draw(M)

Regole (Field)

Rotazione

Il metodo rotazione cambia il valore field$rotation di rotazioni fisse con un angolo di \(\frac{\pi}{4}\)

  • obj il contenuto di una cella della matrice
  • n rappresenta l’ennesimo elemento a cui viene applicata la regola. Per esempio se la regola viene applicata orizzontalmente n =1 per Sq1,Sq4 e Sq7, n =2 per Sq2,Sq5 e Sq8 e per finire n =3 per Sq3,Sq6 e Sq9.
Vedi codice
rotation.field<-function(obj,n,...) {
  obj$rotation[[1]]<-obj$rotation[[1]]+(n-1)*pi/4
  return(obj)
}

La rotazione può essere applicata con le regole orizzontali, verticali e diagonalmente da basso a sinistra a alto-destra (TL-LR) applicando prima in orizzontale poi verticale.

#Horizontal
M<-apply(Raven(st1=pentagon(),hrule=c("rotation"),vrule=c("identity")))
draw(M)

#Vertical
M<-apply(Raven(st1=pentagon(),hrule=c("identity"),vrule=c("rotation")))
draw(M)

# Top Left Low Right
M<-apply(Raven(st1=pentagon(),hrule=c("rotation"),vrule=c("rotation")))
draw(M)

Dimensione

Il metodo di cambio di dimensioni, riduce i valori di field$size.x e field$size.y di un valore costante pari a numero di riga (o colonna) della matrice per \(k=.9\). Se per esempio la regola è applicata orizzontalmente, gli elementi nelle celle Sq1,Sq4,sq7della prima colonna hanno dimensioni \((\frac{x}{k},\frac{y}{k})\), gli elementi nelle celle Sq2,Sq5,sq8 hanno dimensioni \((\frac{x}{2k},\frac{y}{2k})\) e quelli nella terza colonna \((\frac{x}{3k},\frac{y}{3k})\).

Vedi codice
size.field<-function(obj,n,...) {
  obj$size.x[[1]]<-obj$size.x[[1]]/(n*.9)
  obj$size.y[[1]]<-obj$size.y[[1]]/(n*.9)
  return(obj)
}

La cambio di dimensione può essere applicata con le regole orizzontali, verticali e diagonalmente da basso a sinistra a alto-destra (TL-LR) applicando prima in orizzontale poi verticale.

#Horizontal
M<-apply(Raven(st1=circle(),hrule=c("size"),vrule=c("identity")))
draw(M)

#Vertical
M<-apply(Raven(st1=circle(),hrule=c("identity"),vrule=c("size")))
draw(M)

# Top Left Low Right
M<-apply(Raven(st1=circle(),hrule=c("size"),vrule=c("size")))
draw(M)

Cambio delle forme

Il metodo di cambio di forma manipola il campo field$visible in modo di alternare tre diverse forme. L’ordine di applicazione va dalla terza alla prima forma. Per ogni cella individua quali forme sono visibili - Se più di una forma è visibile le rende tutte invisibili e seleziona solo quella in posizione \(n\). Dove \(n\) è l’indice di riga (o di colonna) - Se una sola forma è visibile la regola la mette invisibile e seleziona quella a distante \(n\) nel vettore index

Vedi codice
diff_shapes.field<-function(obj,n,...) {
  if(length(obj$visible)!=3)
  {
    stop("You must have at least three forms to change shapes!")
  }
  #index<-c(3:1,3:1,3:1) TL-LR
  index<-c(1:3,1:3,1:3) #TR-LL
  pos<-which(obj$visible==1)
  if(length(pos)>1){
    obj$visible[pos]<-0
    obj$visible[index[n]]<-1
  }else {
    obj$visible[pos]<-0
    obj$visible[index[pos+n]]<-1
  }
  return(obj)
}

Il cambio di forma può essere applicato con le regole orizzontali, verticali e diagonalmente da basso a sinistra a alto-destra (TL-LR) applicando prima in orizzontale poi verticale.

#Horizontal
M<-apply(Raven(st1=cof(circle(),square(),pentagon()),hrule=c("diff_shapes"),vrule=c("identity")))
draw(M)

#Vertical
M<-apply(Raven(st1=cof(circle(),square(),pentagon()),hrule=c("identity"),vrule=c("diff_shapes")))
draw(M)

# Top Left Low Right
M<-apply(Raven(st1=cof(circle(),square(),pentagon()),hrule=c("diff_shapes"),vrule=c("diff_shapes")))
draw(M)

Contorno

La regola per il cambiamento del contorno può cambiare sia lo spessore che il tipo di contorno utilizzando i parametri standard di R lwd e lty. Tre diversi spessori vengono utilizzati dipendenti dal indice di riga (o di colonna) \(n\). Le forme di default hanno come spessore di partenza \(2\). Il tipo di contorno cambia allo stesso modo in funzione del indice di riga (o di colonna) \(n\).

margin.field<-function(obj,n,rules,...){
  index<-c(3:1,3:1,3:1)
  if(grepl("lwd",rules)){
    obj$lwd[[1]]<- index[obj$lwd[[1]]+n]+1
  }else if(grepl("lty",rules)){
    obj$lty[[1]]<-index[obj$lty[[1]]+n]
    }
  return(obj)
}

Spessore del contorno

#Horizontal
M<-apply(Raven(st1=pentagon(),hrule=c("lwd"),vrule=c("identity")))
draw(M)

#Vertical
M<-apply(Raven(st1=pentagon(),hrule=c("identity"),vrule=c("lwd")))
draw(M)

# Top Left Low Right
M<-apply(Raven(st1=pentagon(),hrule=c("lwd"),vrule=c("lwd")))
draw(M)

Tipo del contorno

#Horizontal
M<-apply(Raven(st1=pentagon(),hrule=c("lty"),vrule=c("identity")))
draw(M)

#Vertical
M<-apply(Raven(st1=pentagon(),hrule=c("identity"),vrule=c("lty")))
draw(M)

# Top Left Low Right
M<-apply(Raven(st1=pentagon(),hrule=c("lty"),vrule=c("lty")))
draw(M)

Riempimento

Il riempimento monocromatico viene applicato campo per campo.

Riempimento monocromatico

La regola per il rimepimento monocromatico cambia il campo field$shade tra i valori white, black e grey. I tre diversi riempimenti vengono assegnati tramite l’indice di riga (o di colonna) \(n\). Il solo perimentro si ottiene con field$shade="none"

fill.field<-function(obj,n,...){
  index <- rep(c("white","grey","black"),3)
   pos <- index==obj$shade[[1]]
  if(is.na(sum(pos)))
  {
    obj$shade[[1]]<-index[n] 
  }else{
    pos <- which(pos)
    obj$shade[[1]]<-index[pos+n]
  }
  return(obj)
}
#Horizontal
M<-apply(Raven(st1=pentagon(),hrule=c("fill"),vrule=c("identity")))
draw(M)

#Vertical
M<-apply(Raven(st1=pentagon(),hrule=c("identity"),vrule=c("fill")))
draw(M)

# Top Left Low Right
M<-apply(Raven(st1=pentagon(),hrule=c("fill"),vrule=c("fill")))
draw(M)

Multifill

Riempimento con le righe

M1<-apply(Raven(bow.tie(),
                hrule= "multifill", vrule = "multifill"))
draw(M1)

Riempimento con altre forme ?????

m1 = apply(Raven(st1 = cof(dot(), 
                            s.lily(), 
                            square(s.x = 5, s.y = 5, 
                                   shd = "black", rot = pi/2)), 
                 hrule = "diff_shapes"))
m2 = apply(Raven(st1=pentagon(),
                 hrule=c("identity"),
                 vrule=c("identity")))
draw(com(m1, m2))

Logica

Le regole logiche vengono applicate per riga e per colonna con 3 o più forme. Presento gli elementi basi per applicare la logica in termini insiemistici.

logic.field<-function(obj,n,rule,seed,...) {
    if(length(obj$shape)<3)
    {
      stop("You must have three forms to apply a logical AND !")
    }
  ##gestione di più immagini
  domain<-1:length(obj$shape)
  obj$visible[domain]<-1
  set.seed(seed)
  fixed<-sample(domain,round(length(obj$shape)/5))
  domain<-setdiff(domain,fixed)
  half<-length(domain)%/%2
  index<-list()
  index[[1]]<-sample(domain,half)
  index[[2]]<-sample(setdiff(domain,index[[1]]),half)
  
  if(rule=="AND"){
    index[[3]]<-union(index[[1]],index[[2]])
    obj$visible[index[[n]]]<-0
  }else if(rule=="OR"){
    if(n<3){
      obj$visible[index[[n]]]<-0
    }
  }else if(rule=="XOR"){
    index[[3]]<-union(setdiff(domain,union(index[[1]],index[[2]])),fixed)
    obj$visible[index[[n]]]<-0
  }
  return(obj)
}
  • domainè l’insieme delle forme \(D\)
  • fixed è l’intersezione tra la prima e la seconda cella \(C_1 \cap C_2\)
  • index[[1]] è l’insieme delle forme nella prima cella meno l’intersezione \(C_1 / \{ C_1 \cap C_2\}\)
  • index[[2]] è l’insieme delle forme nella seconda cella meno l’intersezione \(C_2 / \{ C_1 \cap C_2\}\)

Questi insiemi vengono mantenuti costanti per riga (o per colonna) tramite dei differenti semi.

AND

#Horizontal
M<-apply(Raven(st1=cof(pentagon(),vertical.eight(),horizontal.eight()),hrule=c("AND"),vrule=c("identity")))
draw(M)

#Vertical
M<-apply(Raven(st1=cof(pentagon(),vertical.eight(),horizontal.eight()),vrule=c("AND"),hrule=c("identity")))
draw(M)

OR

#Horizontal
M<-apply(Raven(st1=cof(pentagon(),vertical.eight(),horizontal.eight()),hrule=c("OR"),vrule=c("identity")))
draw(M)

#Vertical
M<-apply(Raven(st1=cof(pentagon(),vertical.eight(),horizontal.eight()),vrule=c("OR"),hrule=c("identity")))
draw(M)

Xor

#Horizontal
M<-apply(Raven(st1=cof(pentagon(),vertical.eight(),horizontal.eight()),hrule=c("XOR"),vrule=c("identity")))
draw(M)

#Vertical
M<-apply(Raven(st1=cof(pentagon(),vertical.eight(),horizontal.eight()),vrule=c("XOR"),hrule=c("identity")))
draw(M)