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") 28 .property("height") 29 .property("lineWidth") 30 .property("strokeStyle") 31 .property("fillStyle") 32 .property("segmented") 33 .property("interpolate"); 34 35 pv.Area.prototype.type = "area"; 36 37 /** 38 * The width of a given span, in pixels; used for horizontal spans. If the width 39 * is specified, the height property should be 0 (the default). Either the top 40 * or bottom property should be used to space the spans vertically, typically as 41 * a multiple of the index. 42 * 43 * @type number 44 * @name pv.Area.prototype.width 45 */ 46 47 /** 48 * The height of a given span, in pixels; used for vertical spans. If the height 49 * is specified, the width property should be 0 (the default). Either the left 50 * or right property should be used to space the spans horizontally, typically 51 * as a multiple of the index. 52 * 53 * @type number 54 * @name pv.Area.prototype.height 55 */ 56 57 /** 58 * The width of stroked lines, in pixels; used in conjunction with 59 * <tt>strokeStyle</tt> to stroke the perimeter of the area. Unlike the 60 * {@link Line} mark type, the entire perimeter is stroked, rather than just one 61 * edge. The default value of this property is 1.5, but since the default stroke 62 * style is null, area marks are not stroked by default. 63 * 64 * <p>This property is <i>fixed</i> for non-segmented areas. See 65 * {@link pv.Mark}. 66 * 67 * @type number 68 * @name pv.Area.prototype.lineWidth 69 */ 70 71 /** 72 * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to 73 * stroke the perimeter of the area. Unlike the {@link Line} mark type, the 74 * entire perimeter is stroked, rather than just one edge. The default value of 75 * this property is null, meaning areas are not stroked by default. 76 * 77 * <p>This property is <i>fixed</i> for non-segmented areas. See 78 * {@link pv.Mark}. 79 * 80 * @type string 81 * @name pv.Area.prototype.strokeStyle 82 * @see pv.color 83 */ 84 85 /** 86 * The area fill style; if non-null, the interior of the polygon forming the 87 * area is filled with the specified color. The default value of this property 88 * is a categorical color. 89 * 90 * <p>This property is <i>fixed</i> for non-segmented areas. See 91 * {@link pv.Mark}. 92 * 93 * @type string 94 * @name pv.Area.prototype.fillStyle 95 * @see pv.color 96 */ 97 98 /** 99 * Whether the area is segmented; whether variations in fill style, stroke 100 * style, and the other properties are treated as fixed. Rendering segmented 101 * areas is noticeably slower than non-segmented areas. 102 * 103 * <p>This property is <i>fixed</i>. See {@link pv.Mark}. 104 * 105 * @type boolean 106 * @name pv.Area.prototype.segmented 107 */ 108 109 /** 110 * How to interpolate between values. Linear interpolation ("linear") is the 111 * default, producing a straight line between points. For piecewise constant 112 * functions (i.e., step functions), either "step-before" or "step-after" can be 113 * specified. 114 * 115 * <p>Note: this property is currently supported only on non-segmented areas. 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 * Default properties for areas. By default, there is no stroke and the fill 125 * style is a categorical color. 126 * 127 * @type pv.Area 128 */ 129 pv.Area.prototype.defaults = new pv.Area() 130 .extend(pv.Mark.prototype.defaults) 131 .lineWidth(1.5) 132 .fillStyle(defaultFillStyle) 133 .interpolate("linear"); 134 135 /** 136 * Constructs a new area anchor with default properties. Areas support five 137 * different anchors:<ul> 138 * 139 * <li>top 140 * <li>left 141 * <li>center 142 * <li>bottom 143 * <li>right 144 * 145 * </ul>In addition to positioning properties (left, right, top bottom), the 146 * anchors support text rendering properties (text-align, text-baseline). Text is 147 * rendered to appear inside the area polygon. 148 * 149 * <p>To facilitate stacking of areas, the anchors are defined in terms of their 150 * opposite edge. For example, the top anchor defines the bottom property, such 151 * that the area grows upwards; the bottom anchor instead defines the top 152 * property, such that the area grows downwards. Of course, in general it is 153 * more robust to use panels and the cousin accessor to define stacked area 154 * marks; see {@link pv.Mark#scene} for an example. 155 * 156 * @param {string} name the anchor name; either a string or a property function. 157 * @returns {pv.Anchor} 158 */ 159 pv.Area.prototype.anchor = function(name) { 160 var area = this; 161 return pv.Mark.prototype.anchor.call(this, name) 162 .left(function() { 163 switch (this.name()) { 164 case "bottom": 165 case "top": 166 case "center": return area.left() + area.width() / 2; 167 case "right": return area.left() + area.width(); 168 } 169 return null; 170 }) 171 .right(function() { 172 switch (this.name()) { 173 case "bottom": 174 case "top": 175 case "center": return area.right() + area.width() / 2; 176 case "left": return area.right() + area.width(); 177 } 178 return null; 179 }) 180 .top(function() { 181 switch (this.name()) { 182 case "left": 183 case "right": 184 case "center": return area.top() + area.height() / 2; 185 case "bottom": return area.top() + area.height(); 186 } 187 return null; 188 }) 189 .bottom(function() { 190 switch (this.name()) { 191 case "left": 192 case "right": 193 case "center": return area.bottom() + area.height() / 2; 194 case "top": return area.bottom() + area.height(); 195 } 196 return null; 197 }) 198 .textAlign(function() { 199 switch (this.name()) { 200 case "bottom": 201 case "top": 202 case "center": return "center"; 203 case "right": return "right"; 204 } 205 return "left"; 206 }) 207 .textBaseline(function() { 208 switch (this.name()) { 209 case "right": 210 case "left": 211 case "center": return "middle"; 212 case "top": return "top"; 213 } 214 return "bottom"; 215 }); 216 }; 217 218 /** 219 * @private Overrides the default behavior of {@link pv.Mark.buildImplied} such 220 * that the width and height are set to zero if null. 221 * 222 * @param s a node in the scene graph; the instance of the mark to build. 223 */ 224 pv.Area.prototype.buildImplied = function(s) { 225 if (s.height == null) s.height = 0; 226 if (s.width == null) s.width = 0; 227 pv.Mark.prototype.buildImplied.call(this, s); 228 }; 229 230 /** @private */ 231 var pv_Area_specials = {left:1, top:1, right:1, bottom:1, width:1, height:1, name:1}; 232 233 /** @private */ 234 pv.Area.prototype.bind = function() { 235 pv.Mark.prototype.bind.call(this); 236 var binds = this.binds, 237 properties = binds.properties, 238 specials = binds.specials = []; 239 for (var i = 0, n = properties.length; i < n; i++) { 240 var p = properties[i]; 241 if (p.name in pv_Area_specials) specials.push(p); 242 } 243 }; 244 245 /** @private */ 246 pv.Area.prototype.buildInstance = function(s) { 247 if (this.index && !this.scene[0].segmented) { 248 this.buildProperties(s, this.binds.specials); 249 this.buildImplied(s); 250 } else { 251 pv.Mark.prototype.buildInstance.call(this, s); 252 } 253 }; 254