svgR

This example is the second early attempt in using svgR to write stories or plays. To make it easier to read, I’ve broke the code into 3 blocks. To see the result skip to the bottom of this page. Skip to output

The first block defines the actors in the play (story). That is, it defines an svgR tree and populates it with shapes.

WH<-c(600,300)
  begTxt<-c(20,30)
  begGrph<-c(20,45)
  center<-begGrph+c(60,70)
  alpha<-0.1*pi
  beta<-0.3*pi
  delta=2

  # point data
  angleColor<-c("pink","lightblue")
  sideID<-c('cos', 'hyp', 'sin')
  colors<-c(hyp="red",sin="blue", cos="green", "purple")
     
  #points to be used for triangles and arcs
  #origin is upper left
  alphaPts1<- matrix(c(c(0,0),c(cos(alpha),0), c(0,sin(alpha)), c(0,0)),2,4)
  alphaPts0<- matrix(c(cos(alpha),sin(alpha)),2,4)-alphaPts1
  betaPts1<-  matrix(c(c(0,0),c(cos(beta),0), c(0,-sin(beta)), c(0,0)),2,4)
  betaPts0<-  matrix(c(cos(beta),sin(alpha)),2,4)-betaPts1
  betaPts1<-  matrix(c(0,sin(alpha)+sin(beta)),2,4)+betaPts1
  
  # points for second backgroud
  pathOuter2<-list( 
    M=c(0,sin(alpha)),  
    L=c(cos(alpha),0),
    L=c(cos(alpha)+cos(beta), sin(beta)),
    L=c(cos(beta),sin(beta)+sin(alpha)),
    Z=0
  ) 
  
  ptsSAB1<- c(cos(alpha),0) 
  ptsSAB2<- c(0,sin(alpha)) + cos(alpha+beta)*c(cos(beta),sin(beta))
  ptsS3<-matrix(c(ptsSAB2,  c(0,sin(alpha)), c(cos(alpha),0), ptsSAB2),2,4)
  ptsS4<-matrix(
           c(ptsSAB2,  
           c(cos(beta),sin(alpha)+sin(beta)), 
           c(cos(alpha)+cos(beta), sin(beta)), 
           c(cos(alpha),0), 
           ptsSAB2), 
           2,5)

  # scales the points, should replace this
  scalePts<-function(x, f=120){
    if(inherits(x, "list")){
      x<-lapply(x, function(v)v*f)
    } else {
      x<-x*f
    }      
  }

  # builds arc for angle given pts of triangle
  pts2Arc<-function(pts, sweep, sf=30){
    norm<-function(v) as.vector(sqrt(v%*%v))
    normalize<-function(v){ v/norm(v) }
    vn<-function(v1,v2,sf){ v1+sf*normalize(v2-v1) }
    vn1<-vn(pts[,2],pts[,3],sf)
    vn2<-vn(pts[,2],pts[,1],sf)
    r<-norm(vn2) #norm(vn2)/1000
    list( M=pts[,2], L=vn1, A=c(r,r,0,0,1-sweep,vn2), Z=0)
  }
  
  # builds triangle from given points
  addTriangle %<c-% function(tri.id, tri.pts, sweep,  visibility='hidden'){
    tri.pts<-scalePts(tri.pts)
    g(  id=tri.id, visibility=visibility, fill="white",
        lapply(1:3, # the sides
               function(i){   #1=hyp, 2=sin, 3=cos            
                 line(id=paste0(tri.id,"-",sideID[i]), 
                      stroke.width=5, 
                      stroke=colors[[ sideID[i] ]],
                      xy1=tri.pts[,i], 
                      xy2=tri.pts[,i+1])           
               }                 
        ),
        polygon(id=paste0(tri.id,"-interior"),
              points=tri.pts[,1:4],  stroke="none"),
        path( id=paste0(tri.id,"-angle"),
              d=pts2Arc(tri.pts, sweep),
              fill=angleColor[1+sweep], 
              stroke="black" 
        )        
    )      
  }
  
  # Here we declare all objects to be animated
  
  doc<-svgR(  playBar(wh=WH),
    g(id = "textArea1",  transform=list(translate=begTxt)),
    g(id = "textArea2",  transform=list(translate=begTxt+c(0,20))),
    g(id = "textArea3",  transform=list(translate=begTxt+c(0,40))),
    g(id = "graphArea",  transform=list(translate=c(200,90)),
      g(id='graphAreaR',
        rect(id="Outer", 
             xy=scalePts(c(0,0)), 
             wh=scalePts(c(cos(alpha)+cos(beta), sin(alpha)+sin(beta))) ,
             stroke="black", fill="khaki", visibility='hidden'),
        rect(id="sinacosb", 
             wh=scalePts(c(cos(beta),sin(alpha))), 
             xy=scalePts(c(cos(alpha),0)), stroke="black", 
             fill="pink", visibility='hidden'),
        rect(id="cosasinb", wh=scalePts(c(cos(alpha),sin(beta))), 
             xy=scalePts(c(0,sin(alpha))), stroke="black", 
             fill="lightblue",  visibility='hidden'),
        path(id='Outer2', d=scalePts(pathOuter2), visibility='hidden',
             stroke='red', stroke.width=5,
             fill='khaki'
        ),
        g(id='angles', visibility='hidden',
          path( id=paste0('alpha','-angle'),
                d=pts2Arc(scalePts(alphaPts0), 0),
                fill=angleColor[1], 
                opacity=0.2,
                stroke="black" 
          ),        
          path( id=paste0('beta','-angle'),
                d=pts2Arc(scalePts(betaPts0), 1),
                fill=angleColor[2], 
                opacity=0.2,
                stroke="black" 
          ) 
        ),
        g(id="A", 
          addTriangle("triangle-I",  alphaPts0, sweep=0, visibility='visible'),
          addTriangle("triangle-II", alphaPts1, sweep=0, visibility ='hidden')
        ),
        g(id="B", opacity=1,
          addTriangle("triangle-III", betaPts0, sweep=1,  visibility='hidden'),
          addTriangle("triangle-IV",  betaPts1, sweep=1, visibility='hidden')
        ),
        g(id='Outer3-2', visibility='hidden',
          polygon(id='Outer3-2-Poly',  
                  points=scalePts(ptsS4)[,1:4],
                  stroke='red', stroke.width=2,
                  fill='khaki'
          ),
          line(id='Outer3-2-upperLeg',
               xy1=scalePts(ptsS4[,3]),
               xy2=scalePts(ptsS4[,4]),
               stroke.width=2,
               stroke='red'
          )
        ),
        addTriangle("triangle-V",  matrix(ptsS3,2,4), sweep=0, visibility='hidden'),
        line(id='sinAplusB', visibility='hidden',
             xy1=scalePts(ptsSAB1),
             xy2=scalePts(ptsSAB2),
             stoke.width=3,
             stroke='purple')
      )     
    )
  )->tmp

This second block simply defines the actions than can be performed by the actors. This is just a simply list functions to operate on the svgR object.

  # here we define our animation routines
  story.new<-function(adoc, dt=2){
    doc=adoc
    delta=dt  
    indx<-0
    textAreaIds<-list(textArea1=NULL,textArea2=NULL,textArea3=NULL)
    function(code){
      fnsList<-list(
        step=function(inc=1){
          indx<<-indx+inc
        },
        
        btime=function(){
          paste0(svgR:::playButton.click(),"+",indx*delta)
        },
        
        displayText=function(txt, color="black"){ 
          if(!is.null(textAreaIds[[textArea]])){
            tid<-textAreaIds[[textArea]]
            doc[[tid]](
              set(attributeName="visibility", to='hidden', 
                  begin=btime(), fill="freeze")
            )
          }
          txtId=paste0("txt-",indx )  
          doc[[textArea]]( 
            text(id=txtId, xy=c(0,0), visibility='hidden',           
                 stroke=color, font.size=20,
                 lapply(txt, function(tx){
                   if(grepl("^\\\\",tx)){
                     tspan(mathSymbol(tx))
                   } else {
                     tspan(tx)
                   }}),
                 set(attributeName="visibility", to="visible", begin=btime())
            ))
          textAreaIds[[textArea]]<<-txtId
          txtId
        },
        
        moveTo=function(id, from, to){
          doc[[id]](
            animateMotion( from=from, to=to , begin=btime(), dur=delta, fill="freeze")
          )
        },
        
        rotate=function(id, angles, about=c(0,0)){
          doc[[id]](
            animateTransform( 
              attributeName="transform",
              type="rotate",
              from=c(angles[1], about ),
              to=c(angles[2], about),             
              begin=btime(), 
              dur=delta, 
              fill="freeze"
            ))
        },
        
        setFill=function(id, value, dur=delta){
          doc[[id]](set(attributeName="fill", attributeType="CSS", to=value, begin=btime() , dur=dur)
          )  
        },
        
        setStrokeDecoration=function(id, dur=delta){
          doc[[id]](
            set(attributeName="stroke-width", attributeType="CSS", to=10, 
              begin=btime(), dur=dur),             
            set(attributeName="stroke-dasharray", attributeType="CSS", to=10, 
              begin=btime(), dur=dur)
          )
        },
        
        setStroke=function(id, value, dur=delta){
          doc[[id]](
            set(attributeName="stroke", to=value, begin=btime(), dur=dur )
          )  
        },
        
        setVisibilty=function(id, value, dur=delta){
          doc[[id]](
              set(attributeName="visibility", to=value, begin=btime(), dur=dur )
          )  
        },
        
        setOpacity=function(id, value, dur=delta){
          doc[[id]](
              set(attributeName="opacity", attributeType="CSS", to=value, begin=btime(), dur=dur)             
          )
        }
      )      
      eval(substitute(code), list2env(fnsList, parent.frame() ))
      tmp<-as.character(doc)
      tmp
    }
  }

This final block defines the story to be told. Each scene in the story ends with a call to the step function.

  story<-story.new(doc)
    
  # here we define our story
  story({
    textArea<-'textArea1'
    displayText('A right triangle')
    step()
    
    displayText(c('Angle ', '\\alpha' ), color='red')
    setFill('triangle-I-angle','red')
    step()
    
    displayText('Length=1', color=colors[1])
    setStrokeDecoration('triangle-I-hyp')
    step()
    
    displayText(c('sin( ','\\alpha',' )'),    color='blue')
    setStrokeDecoration('triangle-I-sin')
    step()
    
    displayText(c('cos( ', '\\alpha',' )'),color='green')
    setStrokeDecoration('triangle-I-cos')
    step()
    
    displayText('A second right triangle')
    setVisibilty('triangle-III','visible','indefinite')
    step()
    
    displayText(c('Angle ', '\\beta'), color='blue')
    setFill('triangle-III-angle','blue')
    step()
    
    displayText('Length=1', color=colors[1])
    setStrokeDecoration('triangle-III-hyp')
    step()
    
    displayText(c('sin( ', '\\beta' ,' )'),color='blue')
    setStrokeDecoration('triangle-III-sin')
    step()
    
    displayText(c('cos( ', '\\beta',' )' ),color='green')
    setStrokeDecoration('triangle-III-cos')
    step()
    
    displayText('Add a clone of the first triangle')
    setVisibilty('triangle-II','visible','indefinite')
    step()
    
    displayText('Add a clone of the second triangle')
    setVisibilty('triangle-IV','visible','indefinite')
    step()
    
    displayText('Slide the bottom to the right')
    moveTo('triangle-III', from=c(0,0), to=scalePts(c(cos(alpha) , 0)))
    moveTo('triangle-IV',  from=c(0,0), to=scalePts(c(cos(alpha) , 0)))
    step()
    
    displayText(c('sin( ', '\\alpha',' )'),    color='blue')
    setStrokeDecoration('triangle-I-sin')
    step()
    
    displayText(c('cos( ', '\\beta',' )'),    color='green')
    setStrokeDecoration('triangle-III-cos')
    step()
    
    displayText(  
      c('Area= sin( ','\\alpha',' )', '\\times', 'cos( ', '\\beta',' )'), 
      color='red'
    )
    setStrokeDecoration('triangle-I-sin')
    setStrokeDecoration('triangle-III-cos')
    setVisibilty('sinacosb', 'visible','indefinite')
    step()
    
    displayText(c('cos( ', '\\alpha' ,')'),    color='green')
    setStrokeDecoration('triangle-I-cos')
    step()
    
    displayText(c('sin( ', '\\beta',')'),    color='blue')
    setStrokeDecoration('triangle-IV-sin')
    step()
    
    displayText(  
      c('Area = cos( ', '\\alpha',' )','\\times',' sin( ', '\\beta'," )"
      ), color='blue'
    )
    setStrokeDecoration('triangle-I-cos')
    setStrokeDecoration('triangle-IV-sin')
    setVisibilty("cosasinb", 'visible','indefinite')
    step()
    
    displayText(  
      c('Area= sin(', '\\alpha', ') ', '\\times', ' cos(', '\\beta', ')',
        ' + ', 'cos(', '\\alpha', ') ', '\\times', ' sin(', '\\beta', ')'),
      color='brown'
    )
    setVisibilty("Outer", 'visible','indefinite')
    setVisibilty("sinacosb", 'hidden','indefinite')
    setVisibilty("cosasinb", 'hidden','indefinite')   
    step()
    
    moveTo("triangle-IV",  to=c(0,0), from=scalePts(c(cos(alpha) , 0)))
    step()
    
    setVisibilty('angles','visible','indefinite')
    moveTo("triangle-III",  from=scalePts(c(cos(alpha) , 0)), to=scalePts(c(cos(alpha) , -sin(alpha))))
    step()
    
    moveTo("triangle-I",  from=scalePts(c(0 , 0)), to=scalePts(c(cos(beta) , sin(beta))))
    step()
    
    textArea<-'textArea2'
    setVisibilty('Outer2','visible','indefinite')
    setVisibilty('Outer','hidden','indefinite')
    sapply(paste0("triangle-",c("I","II","III","IV")),
           function(id) setOpacity(id,0.3, dur='indefinite')
    )
    displayText(c('angle', '\\alpha'),color='red')
    setOpacity('alpha-angle',1)
    step()
    
    displayText(c('angle', '\\beta'),color='blue')
    setOpacity('beta-angle',1)
    step()
    
    displayText(c('angle', '\\alpha',' + ','\\beta'),color='black')
    setOpacity('alpha-angle',1, dur="indefinite")
    setOpacity('beta-angle',1, dur="indefinite")
    step()
    
    displayText('Rotate a little')
    rotate('graphAreaR', angles=c(0, -180*beta/pi), 
           about=scalePts(c((cos(alpha)+cos(beta))/2, (sin(alpha)+sin(beta))/2)))
    step()
    
    
    displayText('Drop a perpendicular')
    step(0.5)
    
    setVisibilty('sinAplusB','visible','indefinite')
    step()
    
    displayText('Length=1', color=colors[1])
    setStrokeDecoration('triangle-II-hyp')
    step()
    
    displayText(c('sin( ','\\alpha',' + ','\\beta' ,' )'), color='purple')
    setVisibilty('triangle-V-angle','hidden','indefinite')
    setVisibilty('triangle-V','visible','indefinite')
    setVisibilty('Outer3-2','visible','indefinite')
    setFill('triangle-V','lightyellow')
    setOpacity('triangle-V-interior',0.5,'indefinite')
    setStrokeDecoration('triangle-V-sin', 'indefinite')    
    setVisibilty("Outer2","hidden",'indefinite')
    step()

    setFill('triangle-V','khaki',"indefinite")
    step(0.5)

    
    displayText('But wait..')
    moveTo('triangle-V', from=scalePts(c(0,0)), 
       to=scalePts(c(cos(beta),sin(beta))))    
    step()
    
    setOpacity('triangle-V-interior',1,'indefinite')
    setStroke('triangle-V-hyp','khaki','indefinite')    
    setStroke('triangle-V-cos','red','indefinite')    
    step(.5) 
    
    displayText('Length=1', color=colors[1] )   
    setStrokeDecoration('Outer3-2-upperLeg', 'indefinite')    
    step()
    
    displayText(c('Area= 1 ','\\times','sin( ','\\alpha',' + ','\\beta' ,' )')
                ,'brown')
    setFill('Outer3-2-Poly','yellow')
    setFill('triangle-V-interior','yellow')
    step()
    
    textArea<-'textArea1'
    displayText( 'So' )
    textArea<-'textArea2'    
    displayText(    
      c('sin( ','\\alpha',' + ','\\beta' ,' )',
        ' = sin(', '\\alpha', ') ',  'cos(', '\\beta', ')',
        ' + ', 'cos(', '\\alpha', ') ',  'sin(', '\\beta', ')'),
      'brown'
    )      
  }
  )->tale
  doc
A right triangle Angle α Length=1 sin( α ) cos( α ) A second right triangle Angle β Length=1 sin( β ) cos( β ) Add a clone of the first triangle Add a clone of the second triangle Slide the bottom to the right sin( α ) cos( β ) Area= sin( α ) × cos( β ) cos( α ) sin( β ) Area = cos( α ) × sin( β ) Area= sin( α ) × cos( β ) + cos( α ) × sin( β ) So angle α angle β angle α + β Rotate a little Drop a perpendicular Length=1 sin( α + β ) But wait.. Length=1 Area= 1 × sin( α + β ) sin( α + β ) = sin( α ) cos( β ) + cos( α ) sin( β )

Press the button on the play bar to start the animation.