BrushController.js 32.8 KB
Newer Older
P
pah100 已提交
1 2 3 4 5 6 7 8 9 10
/**
 * Box selection tool.
 *
 * @module echarts/component/helper/BrushController
 */

define(function (require) {

    var Eventful = require('zrender/mixin/Eventful');
    var zrUtil = require('zrender/core/util');
11
    var BoundingRect = require('zrender/core/BoundingRect');
P
pah100 已提交
12 13 14 15
    var graphic = require('../../util/graphic');
    var interactionMutex = require('./interactionMutex');
    var DataDiffer = require('../../data/DataDiffer');

P
pah100 已提交
16
    var curry = zrUtil.curry;
P
pah100 已提交
17 18 19 20 21 22 23
    var each = zrUtil.each;
    var map = zrUtil.map;
    var mathMin = Math.min;
    var mathMax = Math.max;
    var mathPow = Math.pow;

    var COVER_Z = 10000;
P
pah100 已提交
24
    var UNSELECT_THRESHOLD = 6;
P
pah100 已提交
25
    var MIN_RESIZE_LINE_WIDTH = 6;
26
    var MUTEX_RESOURCE_KEY = 'globalPan';
P
pah100 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

    var DIRECTION_MAP = {
        w: [0, 0],
        e: [0, 1],
        n: [1, 0],
        s: [1, 1]
    };
    var CURSOR_MAP = {
        w: 'ew',
        e: 'ew',
        n: 'ns',
        s: 'ns',
        ne: 'nesw',
        sw: 'nesw',
        nw: 'nwse',
        se: 'nwse'
    };
    var DEFAULT_BRUSH_OPT = {
        brushStyle: {
            lineWidth: 2,
            stroke: 'rgba(0,0,0,0.3)',
P
pah100 已提交
48
            fill: 'rgba(0,0,0,0.1)'
P
pah100 已提交
49 50
        },
        transformable: true,
P
pah100 已提交
51 52
        brushMode: 'single',
        removeOnClick: false
P
pah100 已提交
53 54 55 56 57 58 59 60
    };

    var baseUID = 0;

    /**
     * @alias module:echarts/component/helper/BrushController
     * @constructor
     * @mixin {module:zrender/mixin/Eventful}
61
     * @event module:echarts/component/helper/BrushController#brush
P
pah100 已提交
62
     *        params:
P
pah100 已提交
63
     *            areas: Array.<Array>, coord relates to container group,
P
pah100 已提交
64 65 66 67 68
     *                                    If no container specified, to global.
     *            opt {
     *                isEnd: boolean,
     *                removeOnClick: boolean
     *            }
P
pah100 已提交
69 70 71 72 73
     *
     * @param {module:zrender/zrender~ZRender} zr
     */
    function BrushController(zr) {

P
pah100 已提交
74 75 76 77
        if (__DEV__) {
            zrUtil.assert(zr);
        }

P
pah100 已提交
78 79 80
        Eventful.call(this);

        /**
P
pah100 已提交
81
         * @type {module:zrender/zrender~ZRender}
P
pah100 已提交
82
         * @private
P
pah100 已提交
83
         */
P
pah100 已提交
84
        this._zr = zr;
P
pah100 已提交
85 86

        /**
P
pah100 已提交
87
         * @type {module:zrender/container/Group}
P
pah100 已提交
88 89
         * @readOnly
         */
P
pah100 已提交
90
        this.group = new graphic.Group();
P
pah100 已提交
91 92

        /**
P
pah100 已提交
93
         * Only for drawing (after enabledBrush).
94 95 96
         *     'line', 'rect', 'polygon' or false
         *     If passing false/null/undefined, disable brush.
         *     If passing 'auto', determined by panel.defaultBrushType
P
pah100 已提交
97 98
         * @private
         * @type {string}
P
pah100 已提交
99
         */
P
pah100 已提交
100
        this._brushType;
P
pah100 已提交
101 102

        /**
P
pah100 已提交
103
         * Only for drawing (after enabledBrush).
104
         *
P
pah100 已提交
105 106
         * @private
         * @type {Object}
P
pah100 已提交
107
         */
P
pah100 已提交
108
        this._brushOption;
P
pah100 已提交
109 110

        /**
P
pah100 已提交
111
         * @private
P
pah100 已提交
112
         * @type {Object}
P
pah100 已提交
113
         */
P
pah100 已提交
114 115
        this._panels;

P
pah100 已提交
116 117
        /**
         * @private
P
pah100 已提交
118
         * @type {Array.<nubmer>}
P
pah100 已提交
119 120 121 122 123
         */
        this._track = [];

        /**
         * @private
P
pah100 已提交
124
         * @type {boolean}
P
pah100 已提交
125 126 127 128 129
         */
        this._dragging;

        /**
         * @private
P
pah100 已提交
130
         * @type {Array}
P
pah100 已提交
131 132 133 134 135
         */
        this._covers = [];

        /**
         * @private
P
pah100 已提交
136
         * @type {moudule:zrender/container/Group}
P
pah100 已提交
137 138 139
         */
        this._creatingCover;

P
pah100 已提交
140 141 142 143 144 145 146
        /**
         * true means global panel
         * @private
         * @type {module:zrender/container/Group|boolean}
         */
        this._creatingPanel;

147 148 149 150 151 152
        /**
         * @private
         * @type {boolean}
         */
        this._enableGlobalPan;

P
pah100 已提交
153 154
        /**
         * @private
P
pah100 已提交
155
         * @type {boolean}
P
pah100 已提交
156
         */
P
pah100 已提交
157 158 159
        if (__DEV__) {
            this._mounted;
        }
P
pah100 已提交
160 161

        /**
P
pah100 已提交
162
         * @private
P
pah100 已提交
163 164 165 166 167 168
         * @type {string}
         */
        this._uid = 'brushController_' + baseUID++;

        /**
         * @private
P
pah100 已提交
169
         * @type {Object}
P
pah100 已提交
170 171 172
         */
        this._handlers = {};
        each(mouseHandlers, function (handler, eventName) {
P
pah100 已提交
173
            this._handlers[eventName] = zrUtil.bind(handler, this);
P
pah100 已提交
174 175 176 177 178 179 180 181 182 183 184
        }, this);
    }

    BrushController.prototype = {

        constructor: BrushController,

        /**
         * If set to null/undefined/false, select disabled.
         * @param {Object} brushOption
         * @param {string|boolean} brushOption.brushType 'line', 'rect', 'polygon' or false
185 186 187
         *                          If passing false/null/undefined, disable brush.
         *                          If passing 'auto', determined by panel.defaultBrushType.
         *                              ('auto' can not be used in global panel)
P
pah100 已提交
188 189
         * @param {number} [brushOption.brushMode='single'] 'single' or 'multiple'
         * @param {boolean} [brushOption.transformable=true]
P
pah100 已提交
190
         * @param {boolean} [brushOption.removeOnClick=false]
P
pah100 已提交
191 192 193 194 195
         * @param {Object} [brushOption.brushStyle]
         * @param {number} [brushOption.brushStyle.width]
         * @param {number} [brushOption.brushStyle.lineWidth]
         * @param {string} [brushOption.brushStyle.stroke]
         * @param {string} [brushOption.brushStyle.fill]
P
pah100 已提交
196
         * @param {number} [brushOption.z]
P
pah100 已提交
197 198
         */
        enableBrush: function (brushOption) {
P
pah100 已提交
199 200
            if (__DEV__) {
                zrUtil.assert(this._mounted);
P
pah100 已提交
201 202
            }

P
pah100 已提交
203 204 205 206 207 208 209 210
            this._brushType && doDisableBrush(this);
            brushOption.brushType && doEnableBrush(this, brushOption);

            return this;
        },

        /**
         * @param {Array.<Object>} panelOpts If not pass, it is global brush.
211 212 213 214 215
         *        Each items: {
         *            panelId, // mandatory.
         *            rect, // mandatory.
         *            defaultBrushType // optional, only used when brushType is 'auto'.
         *        }
P
pah100 已提交
216 217 218 219 220 221 222 223 224 225
         */
        setPanels: function (panelOpts) {
            var oldPanels = this._panels || {};
            var newPanels = this._panels = panelOpts && panelOpts.length && {};
            var thisGroup = this.group;

            newPanels && each(panelOpts, function (panelOpt) {
                var panelId = panelOpt.panelId;
                var panel = oldPanels[panelId];
                if (!panel) {
P
pah100 已提交
226
                    panel = new graphic.Rect({
P
pah100 已提交
227
                        silent: true,
P
pah100 已提交
228
                        invisible: true
P
pah100 已提交
229 230 231
                    });
                    thisGroup.add(panel);
                }
232 233 234 235 236 237 238 239

                var rect = panelOpt.rect;
                // Using BoundingRect to normalize negative width/height.
                if (!(rect instanceof BoundingRect)) {
                    rect = BoundingRect.create(rect);
                }

                panel.attr('shape', rect.plain());
P
pah100 已提交
240
                panel.__brushPanelId = panelId;
241
                panel.__defaultBrushType = panelOpt.defaultBrushType;
P
pah100 已提交
242 243 244 245 246 247 248
                newPanels[panelId] = panel;
                oldPanels[panelId] = null;
            });

            each(oldPanels, function (panel) {
                panel && thisGroup.remove(panel);
            });
P
pah100 已提交
249 250 251 252 253

            return this;
        },

        /**
254
         * @param {Object} [opt]
255
         * @return {boolean} [opt.enableGlobalPan=false]
256 257 258
         * @return {boolean} [opt.position=[0, 0]]
         * @return {boolean} [opt.rotation=0]
         * @return {boolean} [opt.scale=[1, 1]]
P
pah100 已提交
259
         */
P
pah100 已提交
260 261
        mount: function (opt) {
            opt = opt || {};
P
pah100 已提交
262

P
pah100 已提交
263 264 265
            if (__DEV__) {
                this._mounted = true; // should be at first.
            }
P
pah100 已提交
266

267 268
            this._enableGlobalPan = opt.enableGlobalPan;

269 270
            var thisGroup = this.group;
            this._zr.add(thisGroup);
P
pah100 已提交
271

272 273 274 275 276
            thisGroup.attr({
                position: opt.position || [0, 0],
                rotation: opt.rotation || 0,
                scale: opt.scale || [1, 1]
            });
P
pah100 已提交
277 278 279 280 281 282 283 284 285 286

            return this;
        },

        eachCover: function (cb, context) {
            each(this._covers, cb, context);
        },

        /**
         * Update covers.
P
pah100 已提交
287
         * @param {Array.<Object>} brushOptionList Like:
P
pah100 已提交
288
         *        [
P
pah100 已提交
289 290
         *            {id: 'xx', brushType: 'line', range: [23, 44], brushStyle, transformable},
         *            {id: 'yy', brushType: 'rect', range: [[23, 44], [23, 54]]},
P
pah100 已提交
291 292
         *            ...
         *        ]
293
         *        `brushType` is required in each cover info. (can not be 'auto')
P
pah100 已提交
294
         *        `id` is not mandatory.
P
pah100 已提交
295 296
         *        `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default.
         *        If brushOptionList is null/undefined, all covers removed.
P
pah100 已提交
297
         */
P
pah100 已提交
298
        updateCovers: function (brushOptionList) {
P
pah100 已提交
299 300
            if (__DEV__) {
                zrUtil.assert(this._mounted);
P
pah100 已提交
301 302
            }

P
pah100 已提交
303 304 305 306
            brushOptionList = zrUtil.map(brushOptionList, function (brushOption) {
                return zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true);
            });

P
pah100 已提交
307 308 309 310
            var tmpIdPrefix = '\0-brush-index-';
            var oldCovers = this._covers;
            var newCovers = this._covers = [];
            var controller = this;
311
            var creatingCover = this._creatingCover;
P
pah100 已提交
312

P
pah100 已提交
313 314 315
            (new DataDiffer(oldCovers, brushOptionList, oldGetKey, getKey))
                .add(addOrUpdate)
                .update(addOrUpdate)
P
pah100 已提交
316 317 318 319 320
                .remove(remove)
                .execute();

            return this;

P
pah100 已提交
321 322 323
            function getKey(brushOption, index) {
                return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index)
                    + '-' + brushOption.brushType;
P
pah100 已提交
324 325
            }

P
pah100 已提交
326 327
            function oldGetKey(cover, index) {
                return getKey(cover.__brushOption, index);
P
pah100 已提交
328 329
            }

P
pah100 已提交
330
            function addOrUpdate(newIndex, oldIndex) {
P
pah100 已提交
331
                var newBrushOption = brushOptionList[newIndex];
332 333 334 335 336 337 338 339 340 341 342 343 344 345
                // Consider setOption in event listener of brushSelect,
                // where updating cover when creating should be forbiden.
                if (oldIndex != null && oldCovers[oldIndex] === creatingCover) {
                    newCovers[newIndex] = oldCovers[oldIndex];
                }
                else {
                    var cover = newCovers[newIndex] = oldIndex != null
                        ? (
                            oldCovers[oldIndex].__brushOption = newBrushOption,
                            oldCovers[oldIndex]
                        )
                        : endCreating(controller, createCover(controller, newBrushOption));
                    updateCoverAfterCreation(controller, cover);
                }
P
pah100 已提交
346 347 348
            }

            function remove(oldIndex) {
349 350 351
                if (oldCovers[oldIndex] !== creatingCover) {
                    controller.group.remove(oldCovers[oldIndex]);
                }
P
pah100 已提交
352 353 354 355
            }
        },

        unmount: function () {
356 357 358 359 360 361
            if (__DEV__) {
                if (!this._mounted) {
                    return;
                }
            }

P
pah100 已提交
362 363
            this.enableBrush(false);

364 365 366
            // container may 'removeAll' outside.
            clearCovers(this);
            this._zr.remove(this.group);
P
pah100 已提交
367 368 369 370

            if (__DEV__) {
                this._mounted = false; // should be at last.
            }
P
pah100 已提交
371 372 373 374 375 376 377 378 379 380 381 382 383

            return this;
        },

        dispose: function () {
            this.unmount();
            this.off();
        }
    };

    zrUtil.mixin(BrushController, Eventful);


P
pah100 已提交
384 385
    function doEnableBrush(controller, brushOption) {
        var zr = controller._zr;
P
pah100 已提交
386

387 388 389 390
        // Consider roam, which takes globalPan too.
        if (!controller._enableGlobalPan) {
            interactionMutex.take(zr, MUTEX_RESOURCE_KEY, controller._uid);
        }
P
pah100 已提交
391

P
pah100 已提交
392
        each(controller._handlers, function (handler, eventName) {
P
pah100 已提交
393 394 395
            zr.on(eventName, handler);
        });

P
pah100 已提交
396 397
        controller._brushType = brushOption.brushType;
        controller._brushOption = zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true);
P
pah100 已提交
398 399
    }

P
pah100 已提交
400 401
    function doDisableBrush(controller) {
        var zr = controller._zr;
P
pah100 已提交
402

P
pah100 已提交
403
        interactionMutex.release(zr, MUTEX_RESOURCE_KEY, controller._uid);
P
pah100 已提交
404

P
pah100 已提交
405
        each(controller._handlers, function (handler, eventName) {
P
pah100 已提交
406 407 408
            zr.off(eventName, handler);
        });

P
pah100 已提交
409
        controller._brushType = controller._brushOption = null;
P
pah100 已提交
410 411
    }

P
pah100 已提交
412 413
    function createCover(controller, brushOption) {
        var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption);
P
pah100 已提交
414
        cover.__brushOption = brushOption;
P
pah100 已提交
415
        updateZ(cover, brushOption);
P
pah100 已提交
416
        controller.group.add(cover);
P
pah100 已提交
417 418 419
        return cover;
    }

P
pah100 已提交
420 421
    function endCreating(controller, creatingCover) {
        var coverRenderer = getCoverRenderer(creatingCover);
P
pah100 已提交
422
        if (coverRenderer.endCreating) {
P
pah100 已提交
423
            coverRenderer.endCreating(controller, creatingCover);
P
pah100 已提交
424
            updateZ(creatingCover, creatingCover.__brushOption);
P
pah100 已提交
425
        }
P
pah100 已提交
426 427 428 429 430
        return creatingCover;
    }

    function updateCoverShape(controller, cover) {
        var brushOption = cover.__brushOption;
P
pah100 已提交
431 432 433
        getCoverRenderer(cover).updateCoverShape(
            controller, cover, brushOption.range, brushOption
        );
P
pah100 已提交
434 435
    }

P
pah100 已提交
436 437 438 439 440 441
    function updateZ(cover, brushOption) {
        var z = brushOption.z;
        z == null && (z = COVER_Z);
        cover.traverse(function (el) {
            el.z = z;
            el.z2 = z; // Consider in given container.
P
pah100 已提交
442 443 444
        });
    }

P
pah100 已提交
445 446 447
    function updateCoverAfterCreation(controller, cover) {
        getCoverRenderer(cover).updateCommon(controller, cover);
        updateCoverShape(controller, cover);
P
pah100 已提交
448 449
    }

P
pah100 已提交
450 451
    function getCoverRenderer(cover) {
        return coverRenderers[cover.__brushOption.brushType];
P
pah100 已提交
452 453
    }

P
pah100 已提交
454
    function getPanelByPoint(controller, x, y) {
P
pah100 已提交
455 456 457
        var panels = controller._panels;
        if (!panels) {
            return true; // Global panel
P
pah100 已提交
458 459
        }
        var panel;
P
pah100 已提交
460
        each(panels, function (pn) {
P
pah100 已提交
461 462 463 464 465
            pn.contain(x, y) && (panel = pn);
        });
        return panel;
    }

P
pah100 已提交
466 467 468 469 470 471 472 473 474 475 476
    function getPanelByCover(controller, cover) {
        var panels = controller._panels;
        if (!panels) {
            return true; // Global panel
        }
        var panelId = cover.__brushOption.panelId;
        // User may give cover without coord sys info,
        // which is then treated as global panel.
        return panelId != null ? panels[panelId] : true;
    }

P
pah100 已提交
477
    function clearCovers(controller) {
478 479 480
        var covers = controller._covers;
        var originalLength = covers.length;
        each(covers, function (cover) {
P
pah100 已提交
481 482
            controller.group.remove(cover);
        }, controller);
483 484 485
        covers.length = 0;

        return !!originalLength;
P
pah100 已提交
486 487
    }

P
pah100 已提交
488
    function trigger(controller, opt) {
P
pah100 已提交
489
        var areas = map(controller._covers, function (cover) {
P
pah100 已提交
490
            var brushOption = cover.__brushOption;
P
pah100 已提交
491 492
            var range = zrUtil.clone(brushOption.range);

P
pah100 已提交
493 494
            return {
                brushType: brushOption.brushType,
P
pah100 已提交
495 496
                panelId: brushOption.panelId,
                range: range
P
pah100 已提交
497
            };
P
pah100 已提交
498
        });
P
pah100 已提交
499

P
pah100 已提交
500
        controller.trigger('brush', areas, {
501 502 503
            isEnd: !!opt.isEnd,
            removeOnClick: !!opt.removeOnClick
        });
P
pah100 已提交
504 505
    }

P
pah100 已提交
506 507
    function shouldShowCover(controller) {
        var track = controller._track;
P
pah100 已提交
508 509 510 511 512 513 514 515 516 517 518 519 520 521

        if (!track.length) {
            return false;
        }

        var p2 = track[track.length - 1];
        var p1 = track[0];
        var dx = p2[0] - p1[0];
        var dy = p2[1] - p1[1];
        var dist = mathPow(dx * dx + dy * dy, 0.5);

        return dist > UNSELECT_THRESHOLD;
    }

P
pah100 已提交
522 523
    function getTrackEnds(track) {
        var tail = track.length - 1;
P
pah100 已提交
524
        tail < 0 && (tail = 0);
P
pah100 已提交
525
        return [track[0], track[tail]];
P
pah100 已提交
526 527
    }

P
pah100 已提交
528 529 530
    function createBaseRectCover(doDrift, controller, brushOption, edgeNames) {
        var cover = new graphic.Group();

P
pah100 已提交
531
        cover.add(new graphic.Rect({
P
pah100 已提交
532
            name: 'main',
P
pah100 已提交
533
            style: makeStyle(brushOption),
P
pah100 已提交
534 535 536
            silent: true,
            draggable: true,
            cursor: 'move',
P
pah100 已提交
537
            drift: curry(doDrift, controller, cover, 'nswe'),
P
pah100 已提交
538
            ondragend: curry(trigger, controller, {isEnd: true})
P
pah100 已提交
539
        }));
P
pah100 已提交
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558

        each(
            edgeNames,
            function (name) {
                cover.add(new graphic.Rect({
                    name: name,
                    style: {opacity: 0},
                    draggable: true,
                    silent: true,
                    invisible: true,
                    drift: curry(doDrift, controller, cover, name),
                    ondragend: curry(trigger, controller, {isEnd: true})
                }));
            }
        );

        return cover;
    }

P
pah100 已提交
559
    function updateBaseRect(controller, cover, localRange, brushOption) {
P
pah100 已提交
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
        var lineWidth = brushOption.brushStyle.lineWidth || 0;
        var handleSize = mathMax(lineWidth, MIN_RESIZE_LINE_WIDTH);
        var x = localRange[0][0];
        var y = localRange[1][0];
        var xa = x - lineWidth / 2;
        var ya = y - lineWidth / 2;
        var x2 = localRange[0][1];
        var y2 = localRange[1][1];
        var x2a = x2 - handleSize + lineWidth / 2;
        var y2a = y2 - handleSize + lineWidth / 2;
        var width = x2 - x;
        var height = y2 - y;
        var widtha = width + lineWidth;
        var heighta = height + lineWidth;

P
pah100 已提交
575
        updateRectShape(controller, cover, 'main', x, y, width, height);
P
pah100 已提交
576 577

        if (brushOption.transformable) {
P
pah100 已提交
578 579 580 581 582 583 584 585 586
            updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta);
            updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta);
            updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize);
            updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize);

            updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize);
            updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize);
            updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize);
            updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize);
P
pah100 已提交
587
        }
P
pah100 已提交
588
    }
P
pah100 已提交
589

P
pah100 已提交
590
    function updateCommon(controller, cover) {
P
pah100 已提交
591 592
        var brushOption = cover.__brushOption;
        var transformable = brushOption.transformable;
P
pah100 已提交
593 594 595 596

        var mainEl = cover.childAt(0);
        mainEl.useStyle(makeStyle(brushOption));
        mainEl.attr({
P
pah100 已提交
597 598 599
            silent: !transformable,
            cursor: transformable ? 'move' : 'default'
        });
P
pah100 已提交
600 601 602 603 604 605 606 607 608 609 610 611 612 613

        each(
            ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'],
            function (name) {
                var el = cover.childOfName(name);
                var globalDir = getGlobalDirection(controller, name);

                el && el.attr({
                    silent: !transformable,
                    invisible: !transformable,
                    cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null
                });
            }
        );
P
pah100 已提交
614 615
    }

P
pah100 已提交
616
    function updateRectShape(controller, cover, name, x, y, w, h) {
P
pah100 已提交
617
        var el = cover.childOfName(name);
P
pah100 已提交
618 619 620
        el && el.setShape(pointsToRect(
            clipByPanel(controller, cover, [[x, y], [x + w, y + h]])
        ));
P
pah100 已提交
621 622
    }

P
pah100 已提交
623 624 625 626
    function makeStyle(brushOption) {
        return zrUtil.defaults({strokeNoScale: true}, brushOption.brushStyle);
    }

P
pah100 已提交
627 628 629 630 631 632 633 634 635 636
    function formatRectRange(x, y, x2, y2) {
        var min = [mathMin(x, x2), mathMin(y, y2)];
        var max = [mathMax(x, x2), mathMax(y, y2)];

        return [
            [min[0], max[0]], // x range
            [min[1], max[1]] // y range
        ];
    }

P
pah100 已提交
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
    function getTransform(controller) {
        return graphic.getTransform(controller.group);
    }

    function getGlobalDirection(controller, localDirection) {
        if (localDirection.length > 1) {
            localDirection = localDirection.split('');
            var globalDir = [
                getGlobalDirection(controller, localDirection[0]),
                getGlobalDirection(controller, localDirection[1])
            ];
            (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse();
            return globalDir.join('');
        }
        else {
            var map = {w: 'left', e: 'right', n: 'top', s: 'bottom'};
            var inverseMap = {left: 'w', right: 'e', top: 'n', bottom: 's'};
            var globalDir = graphic.transformDirection(
                map[localDirection], getTransform(controller)
            );
            return inverseMap[globalDir];
        }
    }

P
pah100 已提交
661
    function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) {
P
pah100 已提交
662
        var brushOption = cover.__brushOption;
P
pah100 已提交
663
        var rectRange = toRectRange(brushOption.range);
P
pah100 已提交
664
        var localDelta = toLocalDelta(controller, dx, dy);
P
pah100 已提交
665 666 667

        each(name.split(''), function (namePart) {
            var ind = DIRECTION_MAP[namePart];
P
pah100 已提交
668
            rectRange[ind[0]][ind[1]] += localDelta[ind[0]];
P
pah100 已提交
669 670
        });

P
pah100 已提交
671
        brushOption.range = fromRectRange(formatRectRange(
P
pah100 已提交
672
            rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1]
P
pah100 已提交
673
        ));
P
pah100 已提交
674

P
pah100 已提交
675 676
        updateCoverAfterCreation(controller, cover);
        trigger(controller, {isEnd: false});
P
pah100 已提交
677 678
    }

P
pah100 已提交
679
    function driftPolygon(controller, cover, dx, dy, e) {
P
pah100 已提交
680
        var range = cover.__brushOption.range;
P
pah100 已提交
681
        var localDelta = toLocalDelta(controller, dx, dy);
P
pah100 已提交
682 683

        each(range, function (point) {
P
pah100 已提交
684 685
            point[0] += localDelta[0];
            point[1] += localDelta[1];
P
pah100 已提交
686 687
        });

P
pah100 已提交
688 689
        updateCoverAfterCreation(controller, cover);
        trigger(controller, {isEnd: false});
P
pah100 已提交
690 691
    }

P
pah100 已提交
692 693 694 695
    function toLocalDelta(controller, dx, dy) {
        var thisGroup = controller.group;
        var localD = thisGroup.transformCoordToLocal(dx, dy);
        var localZero = thisGroup.transformCoordToLocal(0, 0);
P
pah100 已提交
696

P
pah100 已提交
697 698 699
        return [localD[0] - localZero[0], localD[1] - localZero[1]];
    }

P
pah100 已提交
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
    function clipByPanel(controller, cover, data) {
        var panel = getPanelByCover(controller, cover);
        if (panel === true) { // Global panel
            return zrUtil.clone(data);
        }

        var panelRect = panel.getBoundingRect();

        return zrUtil.map(data, function (point) {
            var x = point[0];
            x = mathMax(x, panelRect.x);
            x = mathMin(x, panelRect.x + panelRect.width);
            var y = point[1];
            y = mathMax(y, panelRect.y);
            y = mathMin(y, panelRect.y + panelRect.height);
            return [x, y];
        });
    }

    function pointsToRect(points) {
        var xmin = mathMin(points[0][0], points[1][0]);
        var ymin = mathMin(points[0][1], points[1][1]);
        var xmax = mathMax(points[0][0], points[1][0]);
        var ymax = mathMax(points[0][1], points[1][1]);

        return {
            x: xmin,
            y: ymin,
            width: xmax - xmin,
            height: ymax - ymin
        };
    }

P
pah100 已提交
733 734 735 736 737 738 739
    function resetCursor(controller, e) {
        var x = e.offsetX;
        var y = e.offsetY;
        var zr = controller._zr;

        if (controller._brushType) { // If active
            var panels = controller._panels;
P
pah100 已提交
740 741 742 743 744 745 746 747
            var covers = controller._covers;
            var inCover;

            for (var i = 0; i < covers.length; i++) {
                if (coverRenderers[covers[i].__brushOption.brushType].contain(covers[i], x, y)) {
                    inCover = true;
                    break;
                }
P
pah100 已提交
748
            }
P
pah100 已提交
749 750 751 752 753 754 755 756 757 758

            if (!inCover) {
                if (panels) { // Brush on panels
                    each(panels, function (panel) {
                        panel.contain(x, y) && zr.setCursorStyle('crosshair');
                    });
                }
                else { // Global brush
                    zr.setCursorStyle('crosshair');
                }
P
pah100 已提交
759 760 761 762
            }
        }
    }

P
pah100 已提交
763 764 765 766 767
    function preventDefault(e) {
        var rawE = e.event;
        rawE.preventDefault && rawE.preventDefault();
    }

P
pah100 已提交
768 769 770 771
    function mainShapeContain(cover, x, y) {
        return cover.childOfName('main').contain(x, y);
    }

P
pah100 已提交
772
    function updateCoverByMouse(controller, e, isEnd) {
P
pah100 已提交
773 774
        var x = e.offsetX;
        var y = e.offsetY;
P
pah100 已提交
775
        var creatingCover = controller._creatingCover;
P
pah100 已提交
776
        var panel = controller._creatingPanel;
P
pah100 已提交
777
        var thisBrushOption = controller._brushOption;
P
tweak  
pah100 已提交
778
        var eventParams;
P
pah100 已提交
779

P
pah100 已提交
780
        controller._track.push(controller.group.transformCoordToLocal(x, y));
P
pah100 已提交
781

P
pah100 已提交
782
        if (shouldShowCover(controller) || creatingCover) {
P
pah100 已提交
783

P
pah100 已提交
784 785 786
            if (panel && !creatingCover) {
                thisBrushOption.brushMode === 'single' && clearCovers(controller);
                var brushOption = zrUtil.clone(thisBrushOption);
787
                brushOption.brushType = determineBrushType(brushOption.brushType, panel);
P
pah100 已提交
788 789 790 791
                brushOption.panelId = panel === true ? null : panel.__brushPanelId;
                creatingCover = controller._creatingCover = createCover(controller, brushOption);
                controller._covers.push(creatingCover);
            }
P
pah100 已提交
792

P
pah100 已提交
793
            if (creatingCover) {
794
                var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)];
P
pah100 已提交
795
                var coverBrushOption = creatingCover.__brushOption;
P
pah100 已提交
796

P
pah100 已提交
797 798 799
                coverBrushOption.range = coverRenderer.getCreatingRange(
                    clipByPanel(controller, creatingCover, controller._track)
                );
P
pah100 已提交
800

P
pah100 已提交
801 802 803
                if (isEnd) {
                    endCreating(controller, creatingCover);
                    coverRenderer.updateCommon(controller, creatingCover);
P
pah100 已提交
804
                }
P
pah100 已提交
805

P
pah100 已提交
806 807
                updateCoverShape(controller, creatingCover);

P
tweak  
pah100 已提交
808
                eventParams = {isEnd: isEnd};
P
pah100 已提交
809
            }
P
pah100 已提交
810 811 812 813 814 815 816 817 818 819
        }
        else if (
            isEnd
            && thisBrushOption.brushMode === 'single'
            && thisBrushOption.removeOnClick
        ) {
            // Help user to remove covers easily, only by a tiny drag, in 'single' mode.
            // But a single click do not clear covers, because user may have casual
            // clicks (for example, click on other component and do not expect covers
            // disappear).
820
            // Only some cover removed, trigger action, but not every click trigger action.
P
tweak  
pah100 已提交
821 822 823
            if (getPanelByPoint(controller, x, y) && clearCovers(controller)) {
                eventParams = {isEnd: isEnd, removeOnClick: true};
            }
P
pah100 已提交
824
        }
P
tweak  
pah100 已提交
825 826

        return eventParams;
P
pah100 已提交
827 828
    }

829 830 831 832 833 834 835 836 837 838 839 840 841
    function determineBrushType(brushType, panel) {
        if (brushType === 'auto') {
            if (__DEV__) {
                zrUtil.assert(
                    panel && panel.__defaultBrushType,
                    'MUST have defaultBrushType when brushType is "atuo"'
                );
            }
            return panel.__defaultBrushType;
        }
        return brushType;
    }

P
pah100 已提交
842 843 844
    var mouseHandlers = {

        mousedown: function (e) {
P
pah100 已提交
845 846 847 848 849 850
            if (this._dragging) {
                // In case some browser do not support globalOut,
                // and release mose out side the browser.
                handleDragEnd.call(this, e);
            }
            else if (!e.target || !e.target.draggable) {
P
pah100 已提交
851 852 853 854 855 856

                preventDefault(e);

                var x = e.offsetX;
                var y = e.offsetY;

P
pah100 已提交
857
                this._creatingCover = null;
P
pah100 已提交
858
                var panel = this._creatingPanel = getPanelByPoint(this, x, y);
P
pah100 已提交
859

P
pah100 已提交
860
                if (panel) {
P
pah100 已提交
861
                    this._dragging = true;
P
pah100 已提交
862
                    this._track = [this.group.transformCoordToLocal(x, y)];
P
pah100 已提交
863 864 865 866 867
                }
            }
        },

        mousemove: function (e) {
P
pah100 已提交
868 869 870
            // set Cursor
            resetCursor(this, e);

P
pah100 已提交
871 872 873 874
            if (this._dragging) {

                preventDefault(e);

P
tweak  
pah100 已提交
875 876 877
                var eventParams = updateCoverByMouse(this, e, false);

                eventParams && trigger(this, eventParams);
P
pah100 已提交
878 879 880
            }
        },

P
pah100 已提交
881
        mouseup: handleDragEnd //,
P
pah100 已提交
882

P
pah100 已提交
883 884 885 886
        // FIXME
        // in tooltip, globalout should not be triggered.
        // globalout: handleDragEnd
    };
P
pah100 已提交
887

P
pah100 已提交
888 889
    function handleDragEnd(e) {
        if (this._dragging) {
P
pah100 已提交
890

P
pah100 已提交
891 892
            preventDefault(e);

P
tweak  
pah100 已提交
893
            var eventParams = updateCoverByMouse(this, e, true);
P
pah100 已提交
894 895 896

            this._dragging = false;
            this._track = [];
P
tweak  
pah100 已提交
897 898 899 900
            this._creatingCover = null;

            // trigger event shoule be at final, after procedure will be nested.
            eventParams && trigger(this, eventParams);
P
pah100 已提交
901
        }
P
pah100 已提交
902
    }
P
pah100 已提交
903 904 905 906 907 908 909

    /**
     * key: brushType
     * @type {Object}
     */
    var coverRenderers = {

P
pah100 已提交
910
        lineX: getLineRenderer(0),
P
pah100 已提交
911

P
pah100 已提交
912
        lineY: getLineRenderer(1),
P
pah100 已提交
913 914

        rect: {
P
pah100 已提交
915
            createCover: function (controller, brushOption) {
P
pah100 已提交
916 917 918 919 920 921 922 923 924 925 926 927 928
                return createBaseRectCover(
                    curry(
                        driftRect,
                        function (range) {
                            return range;
                        },
                        function (range) {
                            return range;
                        }
                    ),
                    controller,
                    brushOption,
                    ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw']
P
pah100 已提交
929 930
                );
            },
P
pah100 已提交
931 932
            getCreatingRange: function (localTrack) {
                var ends = getTrackEnds(localTrack);
P
pah100 已提交
933 934
                return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]);
            },
P
pah100 已提交
935
            updateCoverShape: function (controller, cover, localRange, brushOption) {
P
pah100 已提交
936
                updateBaseRect(controller, cover, localRange, brushOption);
P
pah100 已提交
937
            },
P
pah100 已提交
938 939
            updateCommon: updateCommon,
            contain: mainShapeContain
P
pah100 已提交
940 941 942
        },

        polygon: {
P
pah100 已提交
943
            createCover: function (controller, brushOption) {
P
pah100 已提交
944 945 946 947
                var cover = new graphic.Group();

                // Do not use graphic.Polygon because graphic.Polyline do not close the
                // border of the shape when drawing, which is a better experience for user.
P
pah100 已提交
948
                cover.add(new graphic.Polyline({
P
pah100 已提交
949
                    name: 'main',
P
pah100 已提交
950
                    style: makeStyle(brushOption),
P
pah100 已提交
951 952
                    silent: true
                }));
P
pah100 已提交
953 954 955

                return cover;
            },
P
pah100 已提交
956 957
            getCreatingRange: function (localTrack) {
                return localTrack;
P
pah100 已提交
958
            },
P
pah100 已提交
959
            endCreating: function (controller, cover) {
P
pah100 已提交
960
                cover.remove(cover.childAt(0));
P
pah100 已提交
961 962
                // Use graphic.Polygon close the shape.
                cover.add(new graphic.Polygon({
P
pah100 已提交
963
                    name: 'main',
P
pah100 已提交
964
                    draggable: true,
P
pah100 已提交
965 966
                    drift: curry(driftPolygon, controller, cover),
                    ondragend: curry(trigger, controller, {isEnd: true})
P
pah100 已提交
967
                }));
P
pah100 已提交
968
            },
P
pah100 已提交
969
            updateCoverShape: function (controller, cover, localRange, brushOption) {
P
pah100 已提交
970 971 972
                cover.childAt(0).setShape({
                    points: clipByPanel(controller, cover, localRange)
                });
P
pah100 已提交
973
            },
P
pah100 已提交
974 975
            updateCommon: updateCommon,
            contain: mainShapeContain
P
pah100 已提交
976 977 978
        }
    };

P
pah100 已提交
979
    function getLineRenderer(xyIndex) {
P
pah100 已提交
980 981
        return {
            createCover: function (controller, brushOption) {
P
pah100 已提交
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
                return createBaseRectCover(
                    curry(
                        driftRect,
                        function (range) {
                            var rectRange = [range, [0, 100]];
                            xyIndex && rectRange.reverse();
                            return rectRange;
                        },
                        function (rectRange) {
                            return rectRange[xyIndex];
                        }
                    ),
                    controller,
                    brushOption,
                    [['w', 'e'], ['n', 's']][xyIndex]
                );
P
pah100 已提交
998 999 1000
            },
            getCreatingRange: function (localTrack) {
                var ends = getTrackEnds(localTrack);
P
pah100 已提交
1001 1002
                var min = mathMin(ends[0][xyIndex], ends[1][xyIndex]);
                var max = mathMax(ends[0][xyIndex], ends[1][xyIndex]);
P
pah100 已提交
1003 1004 1005 1006

                return [min, max];
            },
            updateCoverShape: function (controller, cover, localRange, brushOption) {
P
pah100 已提交
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
                var brushWidth = brushOption.brushStyle.width;
                var otherExtent;
                // If brushWidth not specified, fit the panel.
                if (brushWidth == null) {
                    var panel = getPanelByCover(controller, cover);
                    var base = 0;
                    if (panel !== true) {
                        var rect = panel.getBoundingRect();
                        brushWidth = xyIndex ? rect.width : rect.height;
                        base = xyIndex ? rect.x : rect.y;
                    }
                    // FIXME
                    // do not support global panel yet.
                    otherExtent = [base, base + (brushWidth || 0)];
                }
                else {
                    otherExtent = [-brushWidth / 2, brushWidth / 2];
                }
                var rectRange = [localRange, otherExtent];
P
pah100 已提交
1026
                xyIndex && rectRange.reverse();
P
pah100 已提交
1027

P
pah100 已提交
1028
                updateBaseRect(controller, cover, rectRange, brushOption);
P
pah100 已提交
1029
            },
P
pah100 已提交
1030 1031
            updateCommon: updateCommon,
            contain: mainShapeContain
P
pah100 已提交
1032 1033 1034
        };
    }

P
pah100 已提交
1035 1036
    return BrushController;
});