// Copyright 2007 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview A thick wrapper around paths. * @author robbyw@google.com (Robby Walker) */ goog.provide('goog.graphics.ext.Path'); goog.require('goog.graphics.AffineTransform'); goog.require('goog.graphics.Path'); goog.require('goog.math.Rect'); /** * Creates a path object * @constructor * @extends {goog.graphics.Path} * @final */ goog.graphics.ext.Path = function() { goog.graphics.Path.call(this); }; goog.inherits(goog.graphics.ext.Path, goog.graphics.Path); /** * Optional cached or user specified bounding box. A user may wish to * precompute a bounding box to save time and include more accurate * computations. * @type {goog.math.Rect?} * @private */ goog.graphics.ext.Path.prototype.bounds_ = null; /** * Clones the path. * @return {!goog.graphics.ext.Path} A clone of this path. * @override */ goog.graphics.ext.Path.prototype.clone = function() { var output = /** @type {goog.graphics.ext.Path} */ (goog.graphics.ext.Path.superClass_.clone.call(this)); output.bounds_ = this.bounds_ && this.bounds_.clone(); return output; }; /** * Transforms the path. Only simple paths are transformable. Attempting * to transform a non-simple path will throw an error. * @param {!goog.graphics.AffineTransform} tx The transformation to perform. * @return {!goog.graphics.ext.Path} The path itself. * @override */ goog.graphics.ext.Path.prototype.transform = function(tx) { goog.graphics.ext.Path.superClass_.transform.call(this, tx); // Make sure the precomputed bounds are cleared when the path is transformed. this.bounds_ = null; return this; }; /** * Modify the bounding box of the path. This may cause the path to be * simplified (i.e. arcs converted to curves) as a side-effect. * @param {number} deltaX How far to translate the x coordinates. * @param {number} deltaY How far to translate the y coordinates. * @param {number} xFactor After translation, all x coordinates are multiplied * by this number. * @param {number} yFactor After translation, all y coordinates are multiplied * by this number. * @return {!goog.graphics.ext.Path} The path itself. */ goog.graphics.ext.Path.prototype.modifyBounds = function( deltaX, deltaY, xFactor, yFactor) { if (!this.isSimple()) { var simple = goog.graphics.Path.createSimplifiedPath(this); this.clear(); this.appendPath(simple); } return this.transform( goog.graphics.AffineTransform.getScaleInstance(xFactor, yFactor) .translate(deltaX, deltaY)); }; /** * Set the precomputed bounds. * @param {goog.math.Rect?} bounds The bounds to use, or set to null to clear * and recompute on the next call to getBoundingBox. */ goog.graphics.ext.Path.prototype.useBoundingBox = function(bounds) { this.bounds_ = bounds && bounds.clone(); }; /** * @return {goog.math.Rect?} The bounding box of the path, or null if the * path is empty. */ goog.graphics.ext.Path.prototype.getBoundingBox = function() { if (!this.bounds_ && !this.isEmpty()) { var minY; var minX = minY = Number.POSITIVE_INFINITY; var maxY; var maxX = maxY = Number.NEGATIVE_INFINITY; var simplePath = this.isSimple() ? this : goog.graphics.Path.createSimplifiedPath(this); simplePath.forEachSegment(function(type, points) { for (var i = 0, len = points.length; i < len; i += 2) { minX = Math.min(minX, points[i]); maxX = Math.max(maxX, points[i]); minY = Math.min(minY, points[i + 1]); maxY = Math.max(maxY, points[i + 1]); } }); this.bounds_ = new goog.math.Rect(minX, minY, maxX - minX, maxY - minY); } return this.bounds_; };