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