1 /** 2 * Constructs a new area mark with default properties. Areas are not typically 3 * constructed directly, but by adding to a panel or an existing mark via 4 * {@link pv.Mark#add}. 5 * 6 * @class Represents an area mark: the solid area between two series of 7 * connected line segments. Unsurprisingly, areas are used most frequently for 8 * area charts. 9 * 10 * <p>Just as a line represents a polyline, the <tt>Area</tt> mark type 11 * represents a <i>polygon</i>. However, an area is not an arbitrary polygon; 12 * vertices are paired either horizontally or vertically into parallel 13 * <i>spans</i>, and each span corresponds to an associated datum. Either the 14 * width or the height must be specified, but not both; this determines whether 15 * the area is horizontally-oriented or vertically-oriented. Like lines, areas 16 * can be stroked and filled with arbitrary colors. 17 * 18 * <p>See also the <a href="../../api/Area.html">Area guide</a>. 19 * 20 * @extends pv.Mark 21 */ 22 pv.Area = function() { 23 pv.Mark.call(this); 24 }; 25 26 pv.Area.prototype = pv.extend(pv.Mark) 27 .property("width", Number) 28 .property("height", Number) 29 .property("lineWidth", Number) 30 .property("strokeStyle", pv.color) 31 .property("fillStyle", pv.color) 32 .property("segmented", Boolean) 33 .property("interpolate", String) 34 .property("tension", Number); 35 36 pv.Area.prototype.type = "area"; 37 38 /** 39 * The width of a given span, in pixels; used for horizontal spans. If the width 40 * is specified, the height property should be 0 (the default). Either the top 41 * or bottom property should be used to space the spans vertically, typically as 42 * a multiple of the index. 43 * 44 * @type number 45 * @name pv.Area.prototype.width 46 */ 47 48 /** 49 * The height of a given span, in pixels; used for vertical spans. If the height 50 * is specified, the width property should be 0 (the default). Either the left 51 * or right property should be used to space the spans horizontally, typically 52 * as a multiple of the index. 53 * 54 * @type number 55 * @name pv.Area.prototype.height 56 */ 57 58 /** 59 * The width of stroked lines, in pixels; used in conjunction with 60 * <tt>strokeStyle</tt> to stroke the perimeter of the area. Unlike the 61 * {@link Line} mark type, the entire perimeter is stroked, rather than just one 62 * edge. The default value of this property is 1.5, but since the default stroke 63 * style is null, area marks are not stroked by default. 64 * 65 * <p>This property is <i>fixed</i> for non-segmented areas. See 66 * {@link pv.Mark}. 67 * 68 * @type number 69 * @name pv.Area.prototype.lineWidth 70 */ 71 72 /** 73 * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to 74 * stroke the perimeter of the area. Unlike the {@link Line} mark type, the 75 * entire perimeter is stroked, rather than just one edge. The default value of 76 * this property is null, meaning areas are not stroked by default. 77 * 78 * <p>This property is <i>fixed</i> for non-segmented areas. See 79 * {@link pv.Mark}. 80 * 81 * @type string 82 * @name pv.Area.prototype.strokeStyle 83 * @see pv.color 84 */ 85 86 /** 87 * The area fill style; if non-null, the interior of the polygon forming the 88 * area is filled with the specified color. The default value of this property 89 * is a categorical color. 90 * 91 * <p>This property is <i>fixed</i> for non-segmented areas. See 92 * {@link pv.Mark}. 93 * 94 * @type string 95 * @name pv.Area.prototype.fillStyle 96 * @see pv.color 97 */ 98 99 /** 100 * Whether the area is segmented; whether variations in fill style, stroke 101 * style, and the other properties are treated as fixed. Rendering segmented 102 * areas is noticeably slower than non-segmented areas. 103 * 104 * <p>This property is <i>fixed</i>. See {@link pv.Mark}. 105 * 106 * @type boolean 107 * @name pv.Area.prototype.segmented 108 */ 109 110 /** 111 * How to interpolate between values. Linear interpolation ("linear") is the 112 * default, producing a straight line between points. For piecewise constant 113 * functions (i.e., step functions), either "step-before" or "step-after" can be 114 * specified. To draw open uniform b-splines, specify "basis". To draw cardinal 115 * splines, specify "cardinal"; see also {@link #tension}. 116 * 117 * <p>This property is <i>fixed</i>. See {@link pv.Mark}. 118 * 119 * @type string 120 * @name pv.Area.prototype.interpolate 121 */ 122 123 /** 124 * The tension of cardinal splines; used in conjunction with 125 * interpolate("cardinal"). A value between 0 and 1 draws cardinal splines with 126 * the given tension. In some sense, the tension can be interpreted as the 127 * "length" of the tangent; a tension of 1 will yield all zero tangents (i.e., 128 * linear interpolation), and a tension of 0 yields a Catmull-Rom spline. The 129 * default value is 0.7. 130 * 131 * <p>This property is <i>fixed</i>. See {@link pv.Mark}. 132 * 133 * @type number 134 * @name pv.Area.prototype.tension 135 */ 136 137 /** 138 * Default properties for areas. By default, there is no stroke and the fill 139 * style is a categorical color. 140 * 141 * @type pv.Area 142 */ 143 pv.Area.prototype.defaults = new pv.Area() 144 .extend(pv.Mark.prototype.defaults) 145 .lineWidth(1.5) 146 .fillStyle(pv.Colors.category20().by(pv.parent)) 147 .interpolate("linear") 148 .tension(.7); 149 150 /** @private Sets width and height to zero if null. */ 151 pv.Area.prototype.buildImplied = function(s) { 152 if (s.height == null) s.height = 0; 153 if (s.width == null) s.width = 0; 154 pv.Mark.prototype.buildImplied.call(this, s); 155 }; 156 157 /** @private Records which properties may be fixed. */ 158 pv.Area.fixed = { 159 lineWidth: 1, 160 lineJoin: 1, 161 strokeStyle: 1, 162 fillStyle: 1, 163 segmented: 1, 164 interpolate: 1, 165 tension: 1 166 }; 167 168 /** 169 * @private Make segmented required, such that this fixed property is always 170 * evaluated, even if the first segment is not visible. Also cache which 171 * properties are normally fixed. 172 */ 173 pv.Area.prototype.bind = function() { 174 pv.Mark.prototype.bind.call(this); 175 var binds = this.binds, 176 required = binds.required, 177 optional = binds.optional; 178 for (var i = 0, n = optional.length; i < n; i++) { 179 var p = optional[i]; 180 p.fixed = p.name in pv.Area.fixed; 181 if (p.name == "segmented") { 182 required.push(p); 183 optional.splice(i, 1); 184 i--; 185 n--; 186 } 187 } 188 189 /* Cache the original arrays so they can be restored on build. */ 190 this.binds.$required = required; 191 this.binds.$optional = optional; 192 }; 193 194 /** 195 * @private Override the default build behavior such that fixed properties are 196 * determined dynamically, based on the value of the (always) fixed segmented 197 * property. Any fixed properties are only evaluated on the first instance, 198 * although their values are propagated to subsequent instances, so that they 199 * are available for property chaining and the like. 200 */ 201 pv.Area.prototype.buildInstance = function(s) { 202 var binds = this.binds; 203 204 /* Handle fixed properties on secondary instances. */ 205 if (this.index) { 206 var fixed = binds.fixed; 207 208 /* Determine which properties are fixed. */ 209 if (!fixed) { 210 fixed = binds.fixed = []; 211 function f(p) { return !p.fixed || (fixed.push(p), false); } 212 binds.required = binds.required.filter(f); 213 if (!this.scene[0].segmented) binds.optional = binds.optional.filter(f); 214 } 215 216 /* Copy fixed property values from the first instance. */ 217 for (var i = 0, n = fixed.length; i < n; i++) { 218 var p = fixed[i].name; 219 s[p] = this.scene[0][p]; 220 } 221 } 222 223 /* Evaluate all properties on the first instance. */ 224 else { 225 binds.required = binds.$required; 226 binds.optional = binds.$optional; 227 binds.fixed = null; 228 } 229 230 pv.Mark.prototype.buildInstance.call(this, s); 231 }; 232 233 /** 234 * Constructs a new area anchor with default properties. Areas support five 235 * different anchors:<ul> 236 * 237 * <li>top 238 * <li>left 239 * <li>center 240 * <li>bottom 241 * <li>right 242 * 243 * </ul>In addition to positioning properties (left, right, top bottom), the 244 * anchors support text rendering properties (text-align, text-baseline). Text 245 * is rendered to appear inside the area. The area anchor also propagates the 246 * interpolate, eccentricity, and tension properties such that an anchored area 247 * or line will match positions between control points. 248 * 249 * <p>For consistency with the other mark types, the anchor positions are 250 * defined in terms of their opposite edge. For example, the top anchor defines 251 * the bottom property, such that an area added to the top anchor grows upward. 252 * 253 * @param {string} name the anchor name; either a string or a property function. 254 * @returns {pv.Anchor} 255 */ 256 pv.Area.prototype.anchor = function(name) { 257 var scene; 258 return pv.Mark.prototype.anchor.call(this, name) 259 .def("$area.anchor", function() { 260 scene = this.scene.target; 261 }) 262 .interpolate(function() { 263 return scene[this.index].interpolate; 264 }) 265 .eccentricity(function() { 266 return scene[this.index].eccentricity; 267 }) 268 .tension(function() { 269 return scene[this.index].tension; 270 }); 271 }; 272