svgR

This is a simplistic example of a line graph drawn from scratch, which allows multiple lines.

Skip to output

We split this up into 4 parts, the first two are reusable, the second two are the data dependent.

First the resuable piece is a function called addLayout. It draws the background area, i.e. the grid with labels, a title, etc.

addLayout %<c-% function(layout){
    xy<-layout$xy
    wh<-layout$wh
    nxy<-sapply(layout$xyTics,length)
    dxy<-wh/(1+nxy)
    list(
      g( id="layout",
         rect(id="rect.my",  xy=xy, wh=wh, stroke="black", stroke.width=3, fill="none" ),
         lapply( 1:nxy[1], #verticle
                 function(i){ 
                   xy1=xy+i*dxy*c(1,0)
                   xy2=xy1+wh*c(0,1)               
                   line( xy1=xy1, xy2=xy2, stroke="grey" , stroke.dasharray="2,3" )
                 } 
         ),
         lapply( 1:nxy[2], #horizontal
                 function(i){ 
                   xy1=xy+i*dxy*c(0,1)
                   xy2=xy1+wh*c(1,0)
                   line( xy1=xy1, xy2=xy2, stroke="grey" , stroke.dasharray="2,3"  )
                 } 
         )      
      ),
      g(id="xTics", 
        lapply(1:nxy[1], function(i){
          cxy<-xy+i*dxy*c(1,0)+wh*c(0,1)+c(0,10)
          text(cxy=cxy, font.size=8, layout$xyTics[[1]][i] )
        }        
        )
      ),
      g(id="yTics", 
        lapply(1:nxy[2], function(i){
          cxy<-xy-i*dxy*c(0,1)+wh*c(0,1)-c(5,0)
          text(xy=cxy, layout$xyTics[[2]][i], font.size=8, text.anchor="end" )
        }        
        )
      ),
      g(id="decor",
        text(id="X.label", layout$labels[1], font.size=10,
             cxy=xy+wh*c(0.5,1)+c(0,30)
        ),
        
        text(id="Y.label", layout$labels[2], font.size=10,
             cxy=xy+wh*c(0,.5)-c(50,0),
             transform=list(rotate=c(-90, xy+wh*c(0,.5)-c(50,0)))
        ),
        text(id="Main", layout$main[1], font.size=15,
             cxy=xy+wh*c(0.5,0)-c(0,15)
        )
      )
    )
  }

The second adds a curve (polyline) to the chart

  addCurve %<c-% function(layout, curve, stroke="black", fill="blue"){
    #' @param pts matrix whose are point to plot
    #' @return svg coords of points
    svgCoordinates<-function(layout, pts){
      xy<-layout$xy
      wh<-layout$wh
      nxy<-sapply(layout$xyTics,length)
      dxy<-wh/(1+nxy)*c(1,-1) 
      B<-rbind(xy+ wh*c(0,1)+dxy,xy+ wh*c(1,0)-dxy, xy+dxy*c(1,-1))  
      B<-cbind(B,1)
      XY<-rbind(cbind(layout$xRng, layout$yRng), c(layout$xRng[1], layout$yRng[2]))
      XY<-cbind(XY,1)
      A<-solve(XY,B)
      pts1<-cbind(pts,1)
      coords<-split( (pts1%*%A)[,1:2] , 1:nrow(pts1)) 
      coords
    }
    svgPts<-svgCoordinates(layout, curve)
    x0<-c(svgPts[[1]][1], layout$xy[2]+ layout$wh[2])
    if(fill!="none"){
      x1<-c(svgPts[[length(svgPts)]][1], layout$xy[2]+ layout$wh[2])
      polyPts<-c(list(x0),svgPts,list(x1))
      polygon(fill=fill, points=polyPts, opacity=0.3, stroke=stroke)   
    } else {
      polyline(fill="none", points=svgPts, opacity=0.3, stroke=stroke)
    }   
  }

The is just the data specification with it’s layout

  xRng<-c(1980,2010)
  yRng<-c(130,230)
  
  layout<-list(
    xy=c(60,50),
    wh=c(400,200),
    xRng=xRng,
    yRng=yRng,
    xyTics=list(
      xTics=paste(seq(xRng[1],xRng[2],by=5)),
      yTics=paste(seq(yRng[1],yRng[2], by=10),"lbs")
    ),  
    labels=c("Year","Weight"),
    main="The Weight of Married Life" 
  )
  # some ficticous data
  x<-1976:2002
  y<-runif(length(x),-5,5) + seq(160,230, length.out=length(x))
  male.pts<-cbind(x,y)
  
  x<-1976:2012
  y<-runif(length(x),-10,10) + seq(130,190, length.out=length(x))
  female.pts<-cbind(x,y)

The final piece is putting it all together. Note, for more curves we need only and more addCurve lines.

  #graph it
  svgR(wh=c(500,300),
    addLayout(layout),
    addCurve(layout, male.pts, "blue", "lightblue"),
    addCurve( layout, female.pts, "red", "pink")
  )
1980 1985 1990 1995 2000 2005 2010 130 lbs 140 lbs 150 lbs 160 lbs 170 lbs 180 lbs 190 lbs 200 lbs 210 lbs 220 lbs 230 lbs Year Weight The Weight of Married Life