| 1 | package charts {
|
|---|
| 2 |
|
|---|
| 3 | import flash.events.Event;
|
|---|
| 4 | import flash.events.MouseEvent;
|
|---|
| 5 | import charts.series.Element;
|
|---|
| 6 | import flash.display.BlendMode;
|
|---|
| 7 | import flash.display.Sprite;
|
|---|
| 8 |
|
|---|
| 9 | import charts.series.dots.DefaultDotProperties;
|
|---|
| 10 | import charts.series.dots.dot_factory;
|
|---|
| 11 |
|
|---|
| 12 | import flash.utils.Timer;
|
|---|
| 13 | import flash.events.TimerEvent;
|
|---|
| 14 | import charts.series.dots.PointDotBase;
|
|---|
| 15 |
|
|---|
| 16 | public class Line extends Base
|
|---|
| 17 | {
|
|---|
| 18 | // JSON style:
|
|---|
| 19 | protected var props:Properties;
|
|---|
| 20 | private var dot_style:Properties;
|
|---|
| 21 | private var on_show:Properties;
|
|---|
| 22 | private var line_style:LineStyle;
|
|---|
| 23 |
|
|---|
| 24 | private var on_show_timer:Timer;
|
|---|
| 25 | private var on_show_start:Boolean;
|
|---|
| 26 |
|
|---|
| 27 | public function Line( json:Object ) {
|
|---|
| 28 |
|
|---|
| 29 | var root:Properties = new Properties({
|
|---|
| 30 | values: [],
|
|---|
| 31 | width: 2,
|
|---|
| 32 | colour: '#3030d0',
|
|---|
| 33 | text: '', // <-- default not display a key
|
|---|
| 34 | 'font-size': 12,
|
|---|
| 35 | tip: '#val#',
|
|---|
| 36 | loop: false,
|
|---|
| 37 | axis: 'left'
|
|---|
| 38 | });
|
|---|
| 39 | this.props = new Properties(json, root);
|
|---|
| 40 |
|
|---|
| 41 | this.line_style = new LineStyle(json['line-style']);
|
|---|
| 42 | this.dot_style = new DefaultDotProperties(json['dot-style'], this.props.get('colour'), this.props.get('axis'));
|
|---|
| 43 |
|
|---|
| 44 | //
|
|---|
| 45 | // see scatter base
|
|---|
| 46 | //
|
|---|
| 47 | var on_show_root:Properties = new Properties( {
|
|---|
| 48 | type: "none", // "pop-up",
|
|---|
| 49 | cascade: 0.5,
|
|---|
| 50 | delay: 0
|
|---|
| 51 | });
|
|---|
| 52 | this.on_show = new Properties(json['on-show'], on_show_root);
|
|---|
| 53 | this.on_show_start = true;// this.on_show.get('type');
|
|---|
| 54 | //
|
|---|
| 55 | //
|
|---|
| 56 |
|
|---|
| 57 | this.key = this.props.get('text');
|
|---|
| 58 | this.font_size = this.props.get('font-size');
|
|---|
| 59 |
|
|---|
| 60 | this.values = this.props.get('values');
|
|---|
| 61 | this.add_values();
|
|---|
| 62 |
|
|---|
| 63 | //
|
|---|
| 64 | // this allows the dots to erase part of the line
|
|---|
| 65 | //
|
|---|
| 66 | this.blendMode = BlendMode.LAYER;
|
|---|
| 67 | }
|
|---|
| 68 |
|
|---|
| 69 | //
|
|---|
| 70 | // called from the Base object
|
|---|
| 71 | //
|
|---|
| 72 | protected override function get_element( index:Number, value:Object ): Element {
|
|---|
| 73 |
|
|---|
| 74 | if ( value is Number )
|
|---|
| 75 | value = { value:value };
|
|---|
| 76 |
|
|---|
| 77 | var tmp:Properties = new Properties(value, this.dot_style);
|
|---|
| 78 |
|
|---|
| 79 | // Minor hack, replace all #key# with this key text,
|
|---|
| 80 | // we do this *after* the merge.
|
|---|
| 81 | tmp.set( 'tip', tmp.get('tip').replace('#key#', this.key) );
|
|---|
| 82 |
|
|---|
| 83 | // attach the animation bits:
|
|---|
| 84 | tmp.set('on-show', this.on_show);
|
|---|
| 85 |
|
|---|
| 86 | return dot_factory.make( index, tmp );
|
|---|
| 87 | }
|
|---|
| 88 |
|
|---|
| 89 |
|
|---|
| 90 | // Draw lines...
|
|---|
| 91 | public override function resize( sc:ScreenCoordsBase ): void {
|
|---|
| 92 | this.x = this.y = 0;
|
|---|
| 93 |
|
|---|
| 94 | this.move_dots(sc);
|
|---|
| 95 |
|
|---|
| 96 | if ( this.on_show_start )
|
|---|
| 97 | this.start_on_show_timer();
|
|---|
| 98 | else
|
|---|
| 99 | this.draw();
|
|---|
| 100 |
|
|---|
| 101 | }
|
|---|
| 102 |
|
|---|
| 103 | //
|
|---|
| 104 | // this is a bit dirty, as the dots animate we draw the line 60 times a second
|
|---|
| 105 | //
|
|---|
| 106 | private function start_on_show_timer(): void {
|
|---|
| 107 | this.on_show_start = false;
|
|---|
| 108 | this.on_show_timer = new Timer(1000 / 60); // <-- 60 frames a second = 1000ms / 60
|
|---|
| 109 | this.on_show_timer.addEventListener("timer", animationManager);
|
|---|
| 110 | // Start the timer
|
|---|
| 111 | this.on_show_timer.start();
|
|---|
| 112 | }
|
|---|
| 113 |
|
|---|
| 114 | protected function animationManager(eventArgs:TimerEvent): void {
|
|---|
| 115 |
|
|---|
| 116 | this.draw();
|
|---|
| 117 |
|
|---|
| 118 | if( !this.still_animating() ) {
|
|---|
| 119 | tr.ace( 'Line.as : on show animation stop' );
|
|---|
| 120 | this.on_show_timer.stop();
|
|---|
| 121 | }
|
|---|
| 122 | }
|
|---|
| 123 |
|
|---|
| 124 | private function still_animating(): Boolean {
|
|---|
| 125 | var i:Number;
|
|---|
| 126 | var tmp:Sprite;
|
|---|
| 127 |
|
|---|
| 128 | for ( i=0; i < this.numChildren; i++ ) {
|
|---|
| 129 |
|
|---|
| 130 | tmp = this.getChildAt(i) as Sprite;
|
|---|
| 131 |
|
|---|
| 132 | // filter out the line masks
|
|---|
| 133 | if( tmp is PointDotBase )
|
|---|
| 134 | {
|
|---|
| 135 | var e:PointDotBase = tmp as PointDotBase;
|
|---|
| 136 | if ( e.is_tweening() )
|
|---|
| 137 | return true;
|
|---|
| 138 | }
|
|---|
| 139 | }
|
|---|
| 140 | return false;
|
|---|
| 141 | }
|
|---|
| 142 |
|
|---|
| 143 | //
|
|---|
| 144 | // this is called from both resize and the animation manager
|
|---|
| 145 | //
|
|---|
| 146 | protected function draw(): void {
|
|---|
| 147 | this.graphics.clear();
|
|---|
| 148 | this.draw_line();
|
|---|
| 149 | }
|
|---|
| 150 |
|
|---|
| 151 | // this is also called from area
|
|---|
| 152 | protected function draw_line(): void {
|
|---|
| 153 |
|
|---|
| 154 |
|
|---|
| 155 | this.graphics.lineStyle( this.props.get_colour('width'), this.props.get_colour('colour') );
|
|---|
| 156 |
|
|---|
| 157 | if( this.line_style.style != 'solid' )
|
|---|
| 158 | this.dash_line();
|
|---|
| 159 | else
|
|---|
| 160 | this.solid_line();
|
|---|
| 161 |
|
|---|
| 162 | }
|
|---|
| 163 |
|
|---|
| 164 | public function move_dots( sc:ScreenCoordsBase ): void {
|
|---|
| 165 |
|
|---|
| 166 | var i:Number;
|
|---|
| 167 | var tmp:Sprite;
|
|---|
| 168 |
|
|---|
| 169 | for ( i=0; i < this.numChildren; i++ ) {
|
|---|
| 170 |
|
|---|
| 171 | tmp = this.getChildAt(i) as Sprite;
|
|---|
| 172 |
|
|---|
| 173 | // filter out the line masks
|
|---|
| 174 | if( tmp is Element )
|
|---|
| 175 | {
|
|---|
| 176 | var e:Element = tmp as Element;
|
|---|
| 177 |
|
|---|
| 178 | // tell the point where it is on the screen
|
|---|
| 179 | // we will use this info to place the tooltip
|
|---|
| 180 | e.resize( sc );
|
|---|
| 181 | }
|
|---|
| 182 | }
|
|---|
| 183 | }
|
|---|
| 184 |
|
|---|
| 185 | public function solid_line(): void {
|
|---|
| 186 |
|
|---|
| 187 | var first:Boolean = true;
|
|---|
| 188 | var i:Number;
|
|---|
| 189 | var tmp:Sprite;
|
|---|
| 190 | var x:Number;
|
|---|
| 191 | var y:Number;
|
|---|
| 192 |
|
|---|
| 193 | for ( i=0; i < this.numChildren; i++ ) {
|
|---|
| 194 |
|
|---|
| 195 | tmp = this.getChildAt(i) as Sprite;
|
|---|
| 196 |
|
|---|
| 197 | // filter out the line masks
|
|---|
| 198 | if( tmp is Element )
|
|---|
| 199 | {
|
|---|
| 200 | var e:Element = tmp as Element;
|
|---|
| 201 |
|
|---|
| 202 | if( first )
|
|---|
| 203 | {
|
|---|
| 204 | this.graphics.moveTo(e.x, e.y);
|
|---|
| 205 | x = e.x;
|
|---|
| 206 | y = e.y;
|
|---|
| 207 | first = false;
|
|---|
| 208 | }
|
|---|
| 209 | else
|
|---|
| 210 | this.graphics.lineTo(e.x, e.y);
|
|---|
| 211 | }
|
|---|
| 212 | }
|
|---|
| 213 |
|
|---|
| 214 | if ( this.props.get('loop') ) {
|
|---|
| 215 | // close the line loop (radar charts)
|
|---|
| 216 | this.graphics.lineTo(x, y);
|
|---|
| 217 | }
|
|---|
| 218 | }
|
|---|
| 219 |
|
|---|
| 220 | // Dashed lines by Arseni
|
|---|
| 221 | public function dash_line(): void {
|
|---|
| 222 |
|
|---|
| 223 | var first:Boolean = true;
|
|---|
| 224 |
|
|---|
| 225 | var prev_x:int = 0;
|
|---|
| 226 | var prev_y:int = 0;
|
|---|
| 227 | var on_len_left:Number = 0;
|
|---|
| 228 | var off_len_left:Number = 0;
|
|---|
| 229 | var on_len:Number = this.line_style.on; //Stroke Length
|
|---|
| 230 | var off_len:Number = this.line_style.off; //Space Length
|
|---|
| 231 | var now_on:Boolean = true;
|
|---|
| 232 |
|
|---|
| 233 | for ( var i:Number = 0; i < this.numChildren; i++ ) {
|
|---|
| 234 | var tmp:Sprite = this.getChildAt(i) as Sprite;
|
|---|
| 235 | //
|
|---|
| 236 | // filter out the line masks
|
|---|
| 237 | //
|
|---|
| 238 | if( tmp is Element )
|
|---|
| 239 | {
|
|---|
| 240 | var e:Element = tmp as Element;
|
|---|
| 241 |
|
|---|
| 242 | if( first )
|
|---|
| 243 | {
|
|---|
| 244 | this.graphics.moveTo(e.x, e.y);
|
|---|
| 245 | on_len_left = on_len;
|
|---|
| 246 | off_len_left = off_len;
|
|---|
| 247 | now_on = true;
|
|---|
| 248 | first = false;
|
|---|
| 249 | prev_x = e.x;
|
|---|
| 250 | prev_y = e.y;
|
|---|
| 251 | var x_tmp_1:Number = prev_x;
|
|---|
| 252 | var x_tmp_2:Number;
|
|---|
| 253 | var y_tmp_1:Number = prev_y;
|
|---|
| 254 | var y_tmp_2:Number;
|
|---|
| 255 | }
|
|---|
| 256 | else {
|
|---|
| 257 | var part_len:Number = Math.sqrt((e.x - prev_x) * (e.x - prev_x) + (e.y - prev_y) * (e.y - prev_y) );
|
|---|
| 258 | var sinus:Number = ((e.y - prev_y) / part_len);
|
|---|
| 259 | var cosinus:Number = ((e.x - prev_x) / part_len);
|
|---|
| 260 | var part_len_left:Number = part_len;
|
|---|
| 261 | var inside_part:Boolean = true;
|
|---|
| 262 |
|
|---|
| 263 | while (inside_part) {
|
|---|
| 264 | //Draw Lines And spaces one by one in loop
|
|---|
| 265 | if ( now_on ) {
|
|---|
| 266 | //Draw line
|
|---|
| 267 | //If whole stroke fits
|
|---|
| 268 | if ( on_len_left < part_len_left ) {
|
|---|
| 269 | //Fits - draw whole stroke
|
|---|
| 270 | x_tmp_2 = x_tmp_1 + on_len_left * cosinus;
|
|---|
| 271 | y_tmp_2 = y_tmp_1 + on_len_left * sinus;
|
|---|
| 272 | x_tmp_1 = x_tmp_2;
|
|---|
| 273 | y_tmp_1 = y_tmp_2;
|
|---|
| 274 | part_len_left = part_len_left - on_len_left;
|
|---|
| 275 | now_on = false;
|
|---|
| 276 | off_len_left = off_len;
|
|---|
| 277 | } else {
|
|---|
| 278 | //Does not fit - draw part of the stroke
|
|---|
| 279 | x_tmp_2 = e.x;
|
|---|
| 280 | y_tmp_2 = e.y;
|
|---|
| 281 | x_tmp_1 = x_tmp_2;
|
|---|
| 282 | y_tmp_1 = y_tmp_2;
|
|---|
| 283 | on_len_left = on_len_left - part_len_left;
|
|---|
| 284 | inside_part = false;
|
|---|
| 285 | }
|
|---|
| 286 | this.graphics.lineTo(x_tmp_2, y_tmp_2);
|
|---|
| 287 | } else {
|
|---|
| 288 | //Draw space
|
|---|
| 289 | //If whole space fits
|
|---|
| 290 | if ( off_len_left < part_len_left ) {
|
|---|
| 291 | //Fits - draw whole space
|
|---|
| 292 | x_tmp_2 = x_tmp_1 + off_len_left * cosinus;
|
|---|
| 293 | y_tmp_2 = y_tmp_1 + off_len_left * sinus;
|
|---|
| 294 | x_tmp_1 = x_tmp_2;
|
|---|
| 295 | y_tmp_1 = y_tmp_2;
|
|---|
| 296 | part_len_left = part_len_left - off_len_left;
|
|---|
| 297 | now_on = true;
|
|---|
| 298 | on_len_left = on_len;
|
|---|
| 299 | } else {
|
|---|
| 300 | //Does not fit - draw part of the space
|
|---|
| 301 | x_tmp_2 = e.x;
|
|---|
| 302 | y_tmp_2 = e.y;
|
|---|
| 303 | x_tmp_1 = x_tmp_2;
|
|---|
| 304 | y_tmp_1 = y_tmp_2;
|
|---|
| 305 | off_len_left = off_len_left - part_len_left;
|
|---|
| 306 | inside_part = false;
|
|---|
| 307 | }
|
|---|
| 308 | this.graphics.moveTo(x_tmp_2, y_tmp_2);
|
|---|
| 309 | }
|
|---|
| 310 | }
|
|---|
| 311 | }
|
|---|
| 312 | prev_x = e.x;
|
|---|
| 313 | prev_y = e.y;
|
|---|
| 314 | }
|
|---|
| 315 | }
|
|---|
| 316 | }
|
|---|
| 317 |
|
|---|
| 318 | public override function get_colour(): Number {
|
|---|
| 319 | return this.props.get_colour('colour');
|
|---|
| 320 | }
|
|---|
| 321 | }
|
|---|
| 322 | }
|
|---|