1 /**
  2  * Constructs a new dot mark with default properties. Dots 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 a dot; a dot is simply a sized glyph centered at a given
  7  * point that can also be stroked and filled. The <tt>size</tt> property is
  8  * proportional to the area of the rendered glyph to encourage meaningful visual
  9  * encodings. Dots can visually encode up to eight dimensions of data, though
 10  * this may be unwise due to integrality. See {@link pv.Mark} for details on the
 11  * prioritization of redundant positioning properties.
 12  *
 13  * <p>See also the <a href="../../api/Dot.html">Dot guide</a>.
 14  *
 15  * @extends pv.Mark
 16  */
 17 pv.Dot = function() {
 18   pv.Mark.call(this);
 19 };
 20 
 21 pv.Dot.prototype = pv.extend(pv.Mark)
 22     .property("size", Number)
 23     .property("radius", Number)
 24     .property("shape", String)
 25     .property("angle", Number)
 26     .property("lineWidth", Number)
 27     .property("strokeStyle", pv.color)
 28     .property("fillStyle", pv.color);
 29 
 30 pv.Dot.prototype.type = "dot";
 31 
 32 /**
 33  * The size of the dot, in square pixels. Square pixels are used such that the
 34  * area of the dot is linearly proportional to the value of the size property,
 35  * facilitating representative encodings.
 36  *
 37  * @see #radius
 38  * @type number
 39  * @name pv.Dot.prototype.size
 40  */
 41 
 42 /**
 43  * The radius of the dot, in pixels. This is an alternative to using
 44  * {@link #size}.
 45  *
 46  * @see #size
 47  * @type number
 48  * @name pv.Dot.prototype.radius
 49  */
 50 
 51 /**
 52  * The shape name. Several shapes are supported:<ul>
 53  *
 54  * <li>cross
 55  * <li>triangle
 56  * <li>diamond
 57  * <li>square
 58  * <li>circle
 59  * <li>tick
 60  * <li>bar
 61  *
 62  * </ul>These shapes can be further changed using the {@link #angle} property;
 63  * for instance, a cross can be turned into a plus by rotating. Similarly, the
 64  * tick, which is vertical by default, can be rotated horizontally. Note that
 65  * some shapes (cross and tick) do not have interior areas, and thus do not
 66  * support fill style meaningfully.
 67  *
 68  * <p>Note: it may be more natural to use the {@link pv.Rule} mark for
 69  * horizontal and vertical ticks. The tick shape is only necessary if angled
 70  * ticks are needed.
 71  *
 72  * @type string
 73  * @name pv.Dot.prototype.shape
 74  */
 75 
 76 /**
 77  * The rotation angle, in radians. Used to rotate shapes, such as to turn a
 78  * cross into a plus.
 79  *
 80  * @type number
 81  * @name pv.Dot.prototype.angle
 82  */
 83 
 84 /**
 85  * The width of stroked lines, in pixels; used in conjunction with
 86  * <tt>strokeStyle</tt> to stroke the dot's shape.
 87  *
 88  * @type number
 89  * @name pv.Dot.prototype.lineWidth
 90  */
 91 
 92 /**
 93  * The style of stroked lines; used in conjunction with <tt>lineWidth</tt> to
 94  * stroke the dot's shape. The default value of this property is a categorical
 95  * color.
 96  *
 97  * @type string
 98  * @name pv.Dot.prototype.strokeStyle
 99  * @see pv.color
100  */
101 
102 /**
103  * The fill style; if non-null, the interior of the dot is filled with the
104  * specified color. The default value of this property is null, meaning dots are
105  * not filled by default.
106  *
107  * @type string
108  * @name pv.Dot.prototype.fillStyle
109  * @see pv.color
110  */
111 
112 /**
113  * Default properties for dots. By default, there is no fill and the stroke
114  * style is a categorical color. The default shape is "circle" with size 20.
115  *
116  * @type pv.Dot
117  */
118 pv.Dot.prototype.defaults = new pv.Dot()
119     .extend(pv.Mark.prototype.defaults)
120     .size(20)
121     .shape("circle")
122     .lineWidth(1.5)
123     .strokeStyle(pv.Colors.category10().by(pv.parent));
124 
125 /**
126  * Constructs a new dot anchor with default properties. Dots support five
127  * different anchors:<ul>
128  *
129  * <li>top
130  * <li>left
131  * <li>center
132  * <li>bottom
133  * <li>right
134  *
135  * </ul>In addition to positioning properties (left, right, top bottom), the
136  * anchors support text rendering properties (text-align, text-baseline). Text is
137  * rendered to appear outside the dot. Note that this behavior is different from
138  * other mark anchors, which default to rendering text <i>inside</i> the mark.
139  *
140  * <p>For consistency with the other mark types, the anchor positions are
141  * defined in terms of their opposite edge. For example, the top anchor defines
142  * the bottom property, such that a bar added to the top anchor grows upward.
143  *
144  * @param {string} name the anchor name; either a string or a property function.
145  * @returns {pv.Anchor}
146  */
147 pv.Dot.prototype.anchor = function(name) {
148   var scene;
149   return pv.Mark.prototype.anchor.call(this, name)
150     .def("$wedge.anchor", function() {
151         scene = this.scene.target;
152       })
153     .left(function() {
154         var s = scene[this.index];
155         switch (this.name()) {
156           case "bottom":
157           case "top":
158           case "center": return s.left;
159           case "left": return null;
160         }
161         return s.left + s.radius;
162       })
163     .right(function() {
164         var s = scene[this.index];
165         return this.name() == "left" ? s.right + s.radius : null;
166       })
167     .top(function() {
168         var s = scene[this.index];
169         switch (this.name()) {
170           case "left":
171           case "right":
172           case "center": return s.top;
173           case "top": return null;
174         }
175         return s.top + s.radius;
176       })
177     .bottom(function() {
178         var s = scene[this.index];
179         return this.name() == "top" ? s.bottom + s.radius : null;
180       })
181     .textAlign(function() {
182         switch (this.name()) {
183           case "left": return "right";
184           case "bottom":
185           case "top":
186           case "center": return "center";
187         }
188         return "left";
189       })
190     .textBaseline(function() {
191         switch (this.name()) {
192           case "right":
193           case "left":
194           case "center": return "middle";
195           case "bottom": return "top";
196         }
197         return "bottom";
198       });
199 };
200 
201 /** @private Sets radius based on size or vice versa. */
202 pv.Dot.prototype.buildImplied = function(s) {
203   if (s.radius == null) s.radius = Math.sqrt(s.size);
204   else if (s.size == null) s.size = s.radius * s.radius;
205   pv.Mark.prototype.buildImplied.call(this, s);
206 };
207