|
7 | 7 | * camera provides a methods for converting between a map's coordinate system |
8 | 8 | * to display pixel coordinates. |
9 | 9 | * |
10 | | - * For the moment, all camera trasforms are assumed to be expressible as |
| 10 | + * For the moment, all camera transforms are assumed to be expressible as |
11 | 11 | * 4x4 matrices. More general cameras may follow that break this assumption. |
12 | 12 | * |
13 | 13 | * The interface for the camera is relatively stable for "map-like" views, |
14 | | - * i.e. when the camera is pointing in the direction [0, 0, -1], and placed |
| 14 | + * e.g. when the camera is pointing in the direction [0, 0, -1], and placed |
15 | 15 | * above the z=0 plane. More general view changes and events have not yet |
16 | 16 | * been defined. |
17 | 17 | * |
|
27 | 27 | * * {@link geo.event.camera.viewport} when the viewport changes |
28 | 28 | * |
29 | 29 | * By convention, protected methods do not update the internal matrix state, |
30 | | - * public methods do. For now, there are two primary methods that are |
31 | | - * inteded to be used by external classes to mutate the internal state: |
| 30 | + * public methods do. There are a few primary methods that are intended to |
| 31 | + * be used by external classes to mutate the internal state: |
32 | 32 | * |
33 | 33 | * * bounds: Set the visible bounds (for initialization and zooming) |
34 | 34 | * * pan: Translate the camera in x/y by an offset (for panning) |
| 35 | + * * viewFromCenterSizeRotation: set the camera view based on a center |
| 36 | + * point, boundary size, and rotation angle. |
35 | 37 | * |
36 | 38 | * @class |
37 | 39 | * @extends geo.object |
|
521 | 523 | * @returns {object} bounds object |
522 | 524 | */ |
523 | 525 | this._getBounds = function () { |
524 | | - var pt, bds = {}; |
525 | | - |
526 | | - |
527 | | - // get lower bounds |
528 | | - pt = this.displayToWorld({ |
529 | | - x: 0, y: this._viewport.height |
| 526 | + var ul, ur, ll, lr, bds = {}; |
| 527 | + |
| 528 | + // get corners |
| 529 | + ul = this.displayToWorld({x: 0, y: 0}); |
| 530 | + ur = this.displayToWorld({x: this._viewport.width, y: 0}); |
| 531 | + ll = this.displayToWorld({x: 0, y: this._viewport.height}); |
| 532 | + lr = this.displayToWorld({ |
| 533 | + x: this._viewport.width, |
| 534 | + y: this._viewport.height |
530 | 535 | }); |
531 | | - bds.left = pt.x; |
532 | | - bds.bottom = pt.y; |
533 | 536 |
|
534 | | - // get upper bounds |
535 | | - pt = this.displayToWorld({ |
536 | | - x: this._viewport.width, y: 0 |
537 | | - }); |
538 | | - bds.right = pt.x; |
539 | | - bds.top = pt.y; |
| 537 | + bds.left = Math.min(ul.x, ur.x, ll.x, lr.x); |
| 538 | + bds.bottom = Math.min(ul.y, ur.y, ll.y, lr.y); |
| 539 | + bds.right = Math.max(ul.x, ur.x, ll.x, lr.x); |
| 540 | + bds.top = Math.max(ul.y, ur.y, ll.y, lr.y); |
540 | 541 |
|
541 | 542 | return bds; |
542 | 543 | }; |
|
558 | 559 | * @return {this} Chainable |
559 | 560 | */ |
560 | 561 | this._setBounds = function (bounds) { |
| 562 | + var size = { |
| 563 | + width: bounds.right - bounds.left, |
| 564 | + height: bounds.top - bounds.bottom |
| 565 | + }; |
| 566 | + var center = { |
| 567 | + x: (bounds.left + bounds.right) / 2, |
| 568 | + y: (bounds.bottom + bounds.top) / 2 |
| 569 | + }; |
| 570 | + |
| 571 | + this._viewFromCenterSizeRotation(center, size, 0); |
| 572 | + return this; |
| 573 | + }; |
561 | 574 |
|
| 575 | + /** |
| 576 | + * Sets the view matrix so that the given world center is centered, at |
| 577 | + * least a certain width and height are visible, and a rotation is applied. |
| 578 | + * The resulting bounds may be larger in width or height than the values if |
| 579 | + * the viewport is a different aspect ratio. |
| 580 | + * |
| 581 | + * @protected |
| 582 | + * @param {object} center |
| 583 | + * @param {number} center.x |
| 584 | + * @param {number} center.y |
| 585 | + * @param {object} size |
| 586 | + * @param {number} size.width |
| 587 | + * @param {number} size.height |
| 588 | + * @param {number} rotation in clockwise radians. Optional |
| 589 | + * @return {this} Chainable |
| 590 | + */ |
| 591 | + this._viewFromCenterSizeRotation = function (center, size, rotation) { |
562 | 592 | var translate = geo.util.vec3AsArray(), |
563 | 593 | scale = geo.util.vec3AsArray(), |
564 | 594 | c_ar, v_ar, w, h; |
565 | 595 |
|
566 | | - bounds.near = bounds.near || 0; |
567 | | - bounds.far = bounds.far || 1; |
568 | | - |
569 | 596 | // reset view to the identity |
570 | 597 | this._resetView(); |
571 | 598 |
|
572 | | - w = Math.abs(bounds.right - bounds.left); |
573 | | - h = Math.abs(bounds.top - bounds.bottom); |
| 599 | + w = Math.abs(size.width); |
| 600 | + h = Math.abs(size.height); |
574 | 601 | c_ar = w / h; |
575 | 602 | v_ar = this._viewport.width / this._viewport.height; |
576 | 603 |
|
|
589 | 616 | scale[2] = 1; |
590 | 617 | this._scale(scale); |
591 | 618 |
|
| 619 | + if (rotation) { |
| 620 | + this._rotate(rotation); |
| 621 | + } |
| 622 | + |
592 | 623 | // translate to the new center. |
593 | | - translate[0] = -(bounds.left + bounds.right) / 2; |
594 | | - translate[1] = -(bounds.bottom + bounds.top) / 2; |
| 624 | + translate[0] = -center.x; |
| 625 | + translate[1] = -center.y; |
595 | 626 | translate[2] = 0; |
596 | 627 |
|
597 | 628 | this._translate(translate); |
| 629 | + |
| 630 | + return this; |
| 631 | + }; |
| 632 | + |
| 633 | + /** |
| 634 | + * Public exposure of the viewFromCenterSizeRotation function. |
| 635 | + */ |
| 636 | + this.viewFromCenterSizeRotation = function (center, size, rotation) { |
| 637 | + this._viewFromCenterSizeRotation(center, size, rotation); |
| 638 | + this._update(); |
598 | 639 | return this; |
599 | 640 | }; |
600 | 641 |
|
|
624 | 665 | * @param {number} zoom The zoom scale to apply |
625 | 666 | */ |
626 | 667 | this.zoom = function (zoom) { |
| 668 | + if (zoom === 1) { |
| 669 | + return; |
| 670 | + } |
627 | 671 | mat4.scale(this._view, this._view, [ |
628 | 672 | zoom, |
629 | 673 | zoom, |
|
632 | 676 | this._update(); |
633 | 677 | }; |
634 | 678 |
|
| 679 | + /** |
| 680 | + * Rotate the view matrix by the given amount. |
| 681 | + * |
| 682 | + * @param {number} rotation Counter-clockwise rotation angle in radians. |
| 683 | + * @param {object} center Center of rotation in world space coordinates. |
| 684 | + * @param {vec3} axis acis of rotation. Defaults to [0, 0, -1] |
| 685 | + */ |
| 686 | + this._rotate = function (rotation, center, axis) { |
| 687 | + if (!rotation) { |
| 688 | + return; |
| 689 | + } |
| 690 | + axis = axis || [0, 0, -1]; |
| 691 | + if (!center) { |
| 692 | + center = [0, 0, 0]; |
| 693 | + } else if (center.x !== undefined) { |
| 694 | + center = [center.x || 0, center.y || 0, center.z || 0]; |
| 695 | + } |
| 696 | + var invcenter = [-center[0], -center[1], -center[2]]; |
| 697 | + mat4.translate(this._view, this._view, center); |
| 698 | + mat4.rotate(this._view, this._view, rotation, axis); |
| 699 | + mat4.translate(this._view, this._view, invcenter); |
| 700 | + }; |
| 701 | + |
635 | 702 | /** |
636 | 703 | * Returns a CSS transform that converts (by default) from world coordinates |
637 | 704 | * into display coordinates. This allows users of this module to |
|
0 commit comments