1 pv.SvgScene.area = function(scenes) {
  2   var e = scenes.$g.firstChild;
  3   if (!scenes.length) return e;
  4   var s = scenes[0];
  5 
  6   /* segmented */
  7   if (s.segmented) return this.areaSegment(scenes);
  8 
  9   /* visible */
 10   if (!s.visible) return e;
 11   var fill = s.fillStyle, stroke = s.strokeStyle;
 12   if (!fill.opacity && !stroke.opacity) return e;
 13 
 14   /** @private Computes the straight path for the range [i, j]. */
 15   function path(i, j) {
 16     var p1 = [], p2 = [];
 17     for (var k = j; i <= k; i++, j--) {
 18       var si = scenes[i],
 19           sj = scenes[j],
 20           pi = si.left + "," + si.top,
 21           pj = (sj.left + sj.width) + "," + (sj.top + sj.height);
 22 
 23       /* interpolate */
 24       if (i < k) {
 25         var sk = scenes[i + 1], sl = scenes[j - 1];
 26         switch (s.interpolate) {
 27           case "step-before": {
 28             pi += "V" + sk.top;
 29             pj += "H" + (sl.left + sl.width);
 30             break;
 31           }
 32           case "step-after": {
 33             pi += "H" + sk.left;
 34             pj += "V" + (sl.top + sl.height);
 35             break;
 36           }
 37         }
 38       }
 39 
 40       p1.push(pi);
 41       p2.push(pj);
 42     }
 43     return p1.concat(p2).join("L");
 44   }
 45 
 46   /** @private Computes the curved path for the range [i, j]. */
 47   function pathCurve(i, j) {
 48     var pointsT = [], pointsB = [], pathT, pathB;
 49 
 50     for (var k = j; i <= k; i++, j--) {
 51       var sj = scenes[j];
 52       pointsT.push(scenes[i]);
 53       pointsB.push({left: sj.left + sj.width, top: sj.top + sj.height});
 54     }
 55 
 56     if (s.interpolate == "basis") {
 57       pathT = pv.SvgScene.curveBasis(pointsT);
 58       pathB = pv.SvgScene.curveBasis(pointsB);
 59     } else if (s.interpolate == "cardinal") {
 60       pathT = pv.SvgScene.curveCardinal(pointsT, s.tension);
 61       pathB = pv.SvgScene.curveCardinal(pointsB, s.tension);
 62     } else { // monotone
 63       pathT = pv.SvgScene.curveMonotone(pointsT);
 64       pathB = pv.SvgScene.curveMonotone(pointsB);
 65     }
 66 
 67     return pointsT[0].left + "," + pointsT[0].top + pathT
 68          + "L" + pointsB[0].left + "," + pointsB[0].top + pathB;
 69   }
 70 
 71   /* points */
 72   var d = [], si, sj;
 73   for (var i = 0; i < scenes.length; i++) {
 74     si = scenes[i]; if (!si.width && !si.height) continue;
 75     for (var j = i + 1; j < scenes.length; j++) {
 76       sj = scenes[j]; if (!sj.width && !sj.height) break;
 77     }
 78     if (i && (s.interpolate != "step-after")) i--;
 79     if ((j < scenes.length) && (s.interpolate != "step-before")) j++;
 80     d.push(((j - i > 2
 81         && (s.interpolate == "basis"
 82         || s.interpolate == "cardinal"
 83         || s.interpolate == "monotone"))
 84         ? pathCurve : path)(i, j - 1));
 85     i = j - 1;
 86   }
 87   if (!d.length) return e;
 88 
 89   e = this.expect(e, "path", {
 90       "shape-rendering": s.antialias ? null : "crispEdges",
 91       "pointer-events": s.events,
 92       "cursor": s.cursor,
 93       "d": "M" + d.join("ZM") + "Z",
 94       "fill": fill.color,
 95       "fill-opacity": fill.opacity || null,
 96       "stroke": stroke.color,
 97       "stroke-opacity": stroke.opacity || null,
 98       "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null
 99     });
100   return this.append(e, scenes, 0);
101 };
102 
103 pv.SvgScene.areaSegment = function(scenes) {
104   var e = scenes.$g.firstChild, s = scenes[0], pathsT, pathsB;
105   if (s.interpolate == "basis"
106       || s.interpolate == "cardinal"
107       || s.interpolate == "monotone") {
108     var pointsT = [], pointsB = [];
109 
110     for (var i = 0, n = scenes.length; i < n; i++) {
111       var sj = scenes[n - i - 1];
112       pointsT.push(scenes[i]);
113       pointsB.push({left: sj.left + sj.width, top: sj.top + sj.height});
114     }
115 
116     if (s.interpolate == "basis") {
117       pathsT = this.curveBasisSegments(pointsT);
118       pathsB = this.curveBasisSegments(pointsB);
119     } else if (s.interpolate == "cardinal") {
120       pathsT = this.curveCardinalSegments(pointsT, s.tension);
121       pathsB = this.curveCardinalSegments(pointsB, s.tension);
122     } else { // monotone
123       pathsT = this.curveMonotoneSegments(pointsT);
124       pathsB = this.curveMonotoneSegments(pointsB);
125     }
126   }
127 
128   for (var i = 0, n = scenes.length - 1; i < n; i++) {
129     var s1 = scenes[i], s2 = scenes[i + 1];
130 
131     /* visible */
132     if (!s1.visible || !s2.visible) continue;
133     var fill = s1.fillStyle, stroke = s1.strokeStyle;
134     if (!fill.opacity && !stroke.opacity) continue;
135 
136     var d;
137     if (pathsT) {
138       var pathT = pathsT[i],
139           pathB = "L" + pathsB[n - i - 1].substr(1);
140 
141       d = pathT + pathB + "Z";
142     } else {
143       /* interpolate */
144       var si = s1, sj = s2;
145       switch (s1.interpolate) {
146         case "step-before": si = s2; break;
147         case "step-after": sj = s1; break;
148       }
149 
150       /* path */
151       d = "M" + s1.left + "," + si.top
152         + "L" + s2.left + "," + sj.top
153         + "L" + (s2.left + s2.width) + "," + (sj.top + sj.height)
154         + "L" + (s1.left + s1.width) + "," + (si.top + si.height)
155         + "Z";
156     }
157 
158     e = this.expect(e, "path", {
159         "shape-rendering": s1.antialias ? null : "crispEdges",
160         "pointer-events": s1.events,
161         "cursor": s1.cursor,
162         "d": d,
163         "fill": fill.color,
164         "fill-opacity": fill.opacity || null,
165         "stroke": stroke.color,
166         "stroke-opacity": stroke.opacity || null,
167         "stroke-width": stroke.opacity ? s1.lineWidth / this.scale : null
168       });
169     e = this.append(e, scenes, i);
170   }
171   return e;
172 };
173