Refactor Format literal to Formatter class that keeps precision stored.

This commit is contained in:
Jürg Lehni 2013-04-09 17:32:19 -07:00
parent 8da8f116d1
commit fbe8a558bd
13 changed files with 116 additions and 107 deletions

View file

@ -120,11 +120,11 @@ var Matrix = this.Matrix = Base.extend(/** @lends Matrix# */{
* @return {String} A string representation of this transform. * @return {String} A string representation of this transform.
*/ */
toString: function() { toString: function() {
var format = Format.number; var f = Formatter.instance;
return '[[' + [format(this._a), format(this._b), return '[[' + [f.number(this._a), f.number(this._b),
format(this._tx)].join(', ') + '], [' f.number(this._tx)].join(', ') + '], ['
+ [format(this._c), format(this._d), + [f.number(this._c), f.number(this._d),
format(this._ty)].join(', ') + ']]'; f.number(this._ty)].join(', ') + ']]';
}, },
/** /**

View file

@ -224,18 +224,16 @@ var Point = this.Point = Base.extend(/** @lends Point# */{
* @return {String} A string representation of the point. * @return {String} A string representation of the point.
*/ */
toString: function() { toString: function() {
var format = Format.number; var f = Formatter.instance;
return '{ x: ' + format(this.x) + ', y: ' + format(this.y) + ' }'; return '{ x: ' + f.number(this.x) + ', y: ' + f.number(this.y) + ' }';
}, },
_serialize: function(options) { _serialize: function(options) {
var format = Format.number, var f = options.formatter;
precision = options.precision; // For speed reasons, we directly call formatter.number() here, instead
// For speed reasons, we directly call Format.number() here with // of converting array through Base.serialize() which makes a copy.
// precision, instead of converting array through Base.serialize() which return [f.number(this.x),
// makes a copy. f.number(this.y)];
return [format(this.x, precision),
format(this.y, precision)];
}, },
/** /**

View file

@ -210,22 +210,21 @@ var Rectangle = this.Rectangle = Base.extend(/** @lends Rectangle# */{
* @return {String} A string representation of this rectangle. * @return {String} A string representation of this rectangle.
*/ */
toString: function() { toString: function() {
var format = Format.number; var f = Formatter.instance;
return '{ x: ' + format(this.x) return '{ x: ' + f.number(this.x)
+ ', y: ' + format(this.y) + ', y: ' + f.number(this.y)
+ ', width: ' + format(this.width) + ', width: ' + f.number(this.width)
+ ', height: ' + format(this.height) + ', height: ' + f.number(this.height)
+ ' }'; + ' }';
}, },
_serialize: function(options) { _serialize: function(options) {
var format = Format.number, var f = options.formatter;
precision = options.precision;
// See Point#_serialize() // See Point#_serialize()
return [format(this.x, precision), return [f.number(this.x),
format(this.y, precision), f.number(this.y),
format(this.width, precision), f.number(this.width),
format(this.height, precision)]; f.number(this.height)];
}, },
/** /**
@ -846,6 +845,8 @@ var LinkedRectangle = Rectangle.extend({
}; };
}, /** @lends Rectangle# */{ }, /** @lends Rectangle# */{
/** /**
* {@grouptitle Item Bounds}
*
* Specifies whether an item's bounds are selected and will also * Specifies whether an item's bounds are selected and will also
* mark the item as selected. * mark the item as selected.
* *

View file

@ -171,17 +171,16 @@ var Size = this.Size = Base.extend(/** @lends Size# */{
* @return {String} A string representation of the size. * @return {String} A string representation of the size.
*/ */
toString: function() { toString: function() {
var format = Format.number; var f = Formatter.instance;
return '{ width: ' + format(this.width) return '{ width: ' + f.number(this.width)
+ ', height: ' + format(this.height) + ' }'; + ', height: ' + f.number(this.height) + ' }';
}, },
_serialize: function(options) { _serialize: function(options) {
var format = Format.number, var f = options.formatter;
precision = options.precision;
// See Point#_serialize() // See Point#_serialize()
return [format(this.width, precision), return [f.number(this.width),
format(this.height, precision)]; f.number(this.height)];
}, },
/** /**

View file

@ -44,7 +44,7 @@ this.Base = Base.inject(/** @lends Base# */{
if (!/^_/.test(key)) { if (!/^_/.test(key)) {
var type = typeof value; var type = typeof value;
this.push(key + ': ' + (type === 'number' this.push(key + ': ' + (type === 'number'
? Format.number(value) ? Formatter.instance.number(value)
: type === 'string' ? "'" + value + "'" : value)); : type === 'string' ? "'" + value + "'" : value));
} }
}, []).join(', ') + ' }'; }, []).join(', ') + ' }';
@ -270,9 +270,11 @@ this.Base = Base.inject(/** @lends Base# */{
*/ */
serialize: function(obj, options, compact, dictionary) { serialize: function(obj, options, compact, dictionary) {
options = options || {}; options = options || {};
var root = !dictionary, var root = !dictionary,
res; res;
if (root) { if (root) {
options.formatter = new Formatter(options.precision);
// Create a simple dictionary object that handles all the // Create a simple dictionary object that handles all the
// storing and retrieving of dictionary definitions and // storing and retrieving of dictionary definitions and
// references, e.g. for symbols and gradients. Items that want // references, e.g. for symbols and gradients. Items that want
@ -325,7 +327,7 @@ this.Base = Base.inject(/** @lends Base# */{
res[i] = Base.serialize(obj[i], options, compact, res[i] = Base.serialize(obj[i], options, compact,
dictionary); dictionary);
} else if (typeof obj === 'number') { } else if (typeof obj === 'number') {
res = Format.number(obj, options.precision); res = options.formatter.number(obj, options.precision);
} else { } else {
res = obj; res = obj;
} }

View file

@ -107,7 +107,7 @@ var paper = new function() {
/*#*/ include('tool/Tool.js'); /*#*/ include('tool/Tool.js');
/*#*/ } // options.browser /*#*/ } // options.browser
/*#*/ include('util/Format.js'); /*#*/ include('util/Formatter.js');
/*#*/ include('util/CanvasProvider.js'); /*#*/ include('util/CanvasProvider.js');
/*#*/ include('util/Numerical.js'); /*#*/ include('util/Numerical.js');
/*#*/ include('util/BlendMode.js'); /*#*/ include('util/BlendMode.js');

View file

@ -223,7 +223,8 @@ var CurveLocation = this.CurveLocation = Base.extend(/** @lends CurveLocation# *
*/ */
toString: function() { toString: function() {
var parts = [], var parts = [],
point = this.getPoint(); point = this.getPoint(),
f = Formatter.instance;
if (point) if (point)
parts.push('point: ' + point); parts.push('point: ' + point);
var index = this.getIndex(); var index = this.getIndex();
@ -231,9 +232,9 @@ var CurveLocation = this.CurveLocation = Base.extend(/** @lends CurveLocation# *
parts.push('index: ' + index); parts.push('index: ' + index);
var parameter = this.getParameter(); var parameter = this.getParameter();
if (parameter != null) if (parameter != null)
parts.push('parameter: ' + Format.number(parameter)); parts.push('parameter: ' + f.number(parameter));
if (this._distance != null) if (this._distance != null)
parts.push('distance: ' + Format.number(this._distance)); parts.push('distance: ' + f.number(this._distance));
return '{ ' + parts.join(', ') + ' }'; return '{ ' + parts.join(', ') + ' }';
} }
}); });

View file

@ -206,8 +206,8 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
getPathData: function(/* precision */) { getPathData: function(/* precision */) {
var segments = this._segments, var segments = this._segments,
style = this._style, style = this._style,
format = Format.point,
precision = arguments[0], precision = arguments[0],
f = Formatter.instance,
parts = []; parts = [];
// TODO: Add support for H/V and/or relative commands, where appropriate // TODO: Add support for H/V and/or relative commands, where appropriate
@ -220,21 +220,21 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{
if (handle1.isZero() && handle2.isZero()) { if (handle1.isZero() && handle2.isZero()) {
if (!skipLine) { if (!skipLine) {
// L = absolute lineto: moving to a point with drawing // L = absolute lineto: moving to a point with drawing
parts.push('L' + format(point2, precision)); parts.push('L' + f.point(point2, precision));
} }
} else { } else {
// c = relative curveto: handle1, handle2 + end - start, // c = relative curveto: handle1, handle2 + end - start,
// end - start // end - start
var end = point2.subtract(point1); var end = point2.subtract(point1);
parts.push('c' + format(handle1, precision) parts.push('c' + f.point(handle1, precision)
+ ' ' + format(end.add(handle2), precision) + ' ' + f.point(end.add(handle2), precision)
+ ' ' + format(end, precision)); + ' ' + f.point(end, precision));
} }
} }
if (segments.length === 0) if (segments.length === 0)
return ''; return '';
parts.push('M' + format(segments[0]._point)); parts.push('M' + f.point(segments[0]._point));
for (i = 0, l = segments.length - 1; i < l; i++) for (i = 0, l = segments.length - 1; i < l; i++)
addCurve(segments[i], segments[i + 1], false); addCurve(segments[i], segments[i + 1], false);
// We only need to draw the connecting curve if it is not a line, and if // We only need to draw the connecting curve if it is not a line, and if

View file

@ -688,15 +688,15 @@ var Color = this.Color = Base.extend(new function() {
var properties = types[this._type], var properties = types[this._type],
parts = [], parts = [],
isGradient = this._type === 'gradient', isGradient = this._type === 'gradient',
format = Format.number; f = Formatter.instance;
for (var i = 0, l = properties.length; i < l; i++) { for (var i = 0, l = properties.length; i < l; i++) {
var value = this._components[i]; var value = this._components[i];
if (value != null) if (value != null)
parts.push(properties[i] + ': ' parts.push(properties[i] + ': '
+ (isGradient ? value : format(value))); + (isGradient ? value : f.number(value)));
} }
if (this._alpha != null) if (this._alpha != null)
parts.push('alpha: ' + format(this._alpha)); parts.push('alpha: ' + f.number(this._alpha));
return '{ ' + parts.join(', ') + ' }'; return '{ ' + parts.join(', ') + ' }';
}, },

View file

@ -15,8 +15,7 @@
* Paper.js DOM to a SVG DOM. * Paper.js DOM to a SVG DOM.
*/ */
new function() { new function() {
// Shortcut to Format.number var formatter = Formatter.instance,
var format = Format.number,
namespaces = { namespaces = {
href: 'http://www.w3.org/1999/xlink' href: 'http://www.w3.org/1999/xlink'
}; };
@ -26,7 +25,7 @@ new function() {
var val = attrs[key], var val = attrs[key],
namespace = namespaces[key]; namespace = namespaces[key];
if (typeof val === 'number') if (typeof val === 'number')
val = format(val); val = formatter.number(val);
if (namespace) { if (namespace) {
node.setAttributeNS(namespace, key, val); node.setAttributeNS(namespace, key, val);
} else { } else {
@ -72,11 +71,11 @@ new function() {
angle = decomposed.rotation, angle = decomposed.rotation,
scale = decomposed.scaling; scale = decomposed.scaling;
if (trans && !trans.isZero()) if (trans && !trans.isZero())
parts.push('translate(' + Format.point(trans) + ')'); parts.push('translate(' + formatter.point(trans) + ')');
if (!Numerical.isZero(scale.x - 1) || !Numerical.isZero(scale.y - 1)) if (!Numerical.isZero(scale.x - 1) || !Numerical.isZero(scale.y - 1))
parts.push('scale(' + Format.point(scale) +')'); parts.push('scale(' + formatter.point(scale) +')');
if (angle) if (angle)
parts.push('rotate(' + format(angle) + ')'); parts.push('rotate(' + formatter.number(angle) + ')');
attrs.transform = parts.join(' '); attrs.transform = parts.join(' ');
} else { } else {
attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')'; attrs.transform = 'matrix(' + matrix.getValues().join(',') + ')';
@ -223,7 +222,7 @@ new function() {
case 'polygon': case 'polygon':
var parts = []; var parts = [];
for(i = 0, l = segments.length; i < l; i++) for(i = 0, l = segments.length; i < l; i++)
parts.push(Format.point(segments[i]._point)); parts.push(formatter.point(segments[i]._point));
attrs = { attrs = {
points: parts.join(' ') points: parts.join(' ')
}; };
@ -296,8 +295,8 @@ new function() {
break; break;
} }
if (angle) { if (angle) {
attrs.transform = 'rotate(' + format(angle) + ',' attrs.transform = 'rotate(' + formatter.number(angle) + ','
+ Format.point(center) + ')'; + formatter.point(center) + ')';
// Tell applyStyle() that to transform the gradient the other way // Tell applyStyle() that to transform the gradient the other way
item._gradientMatrix = new Matrix().rotate(-angle, center); item._gradientMatrix = new Matrix().rotate(-angle, center);
} }
@ -320,7 +319,7 @@ new function() {
bounds = definition.getBounds(); bounds = definition.getBounds();
if (!symbolNode) { if (!symbolNode) {
symbolNode = createElement('symbol', { symbolNode = createElement('symbol', {
viewBox: Format.rectangle(bounds) viewBox: formatter.rectangle(bounds)
}); });
symbolNode.appendChild(exportSvg(definition)); symbolNode.appendChild(exportSvg(definition));
setDefinition(symbol, symbolNode); setDefinition(symbol, symbolNode);
@ -328,8 +327,8 @@ new function() {
attrs.href = '#' + symbolNode.id; attrs.href = '#' + symbolNode.id;
attrs.x += bounds.x; attrs.x += bounds.x;
attrs.y += bounds.y; attrs.y += bounds.y;
attrs.width = format(bounds.width); attrs.width = formatter.number(bounds.width);
attrs.height = format(bounds.height); attrs.height = formatter.number(bounds.height);
return createElement('use', attrs); return createElement('use', attrs);
} }
@ -431,7 +430,7 @@ new function() {
: entry.type === 'array' : entry.type === 'array'
? value.join(',') ? value.join(',')
: entry.type === 'number' : entry.type === 'number'
? format(value) ? formatter.number(value)
: value; : value;
} }
}); });

View file

@ -1,44 +0,0 @@
/**
* @name Format
* @namespace
* @private
*/
var Format = new function() {
// Cache for precision values, linking prec -> Math.pow(10, prec)
var precisions = {};
function number(val, prec) {
prec = prec
? precisions[prec] || (precisions[prec] = Math.pow(10, prec))
: 100000; // Default is 5
return Math.round(val * prec) / prec;
}
function point(val, prec, separator) {
return number(val.x, prec) + (separator || ',') + number(val.y, prec);
}
function size(val, prec, separator) {
return number(val.width, prec) + (separator || ',')
+ number(val.height, prec);
}
function rectangle(val, prec, separator) {
return point(val, prec, separator) + (separator || ',')
+ size(val, prec, separator);
}
return {
/**
* Utility function for rendering numbers as strings at a precision of
* up to the amount of fractional digits.
*
* @param {Number} num the number to be converted to a string
* @param {Number} [precision=5] the amount of fractional digits.
*/
number: number,
point: point,
size: size,
rectangle: rectangle
};
};

51
src/util/Formatter.js Normal file
View file

@ -0,0 +1,51 @@
/*
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
* http://paperjs.org/
*
* Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
* http://lehni.org/ & http://jonathanpuckey.com/
*
* Distributed under the MIT license. See LICENSE file for details.
*
* All rights reserved.
*/
/**
* @name Formatter
* @private
*/
var Formatter = Base.extend({
/**
* @param {Number} [precision=5] the amount of fractional digits.
*/
initialize: function(precision) {
this.precision = precision || 5;
this.multiplier = Math.pow(10, this.precision);
},
/**
* Utility function for rendering numbers as strings at a precision of
* up to the amount of fractional digits.
*
* @param {Number} num the number to be converted to a string
*/
number: function(val) {
return Math.round(val * this.multiplier) / this.multiplier;
},
point: function(val, separator) {
return this.number(val.x) + (separator || ',') + this.number(val.y);
},
size: function(val, separator) {
return this.number(val.width) + (separator || ',')
+ this.number(val.height);
},
rectangle: function(val, separator) {
return this.point(val, separator) + (separator || ',')
+ this.size(val, separator);
}
});
Formatter.instance = new Formatter(5);

View file

@ -60,14 +60,16 @@ function asyncTest(testName, expected) {
} }
function compareNumbers(number1, number2, message, precision) { function compareNumbers(number1, number2, message, precision) {
equals(Format.number(number1, precision), var formatter = new Formatter(precision);
Format.number(number2, precision), message); equals(formatter.number(number1, precision),
formatter.number(number2, precision), message);
} }
function compareArrays(array1, array2, message, precision) { function compareArrays(array1, array2, message, precision) {
var formatter = new Formatter(precision);
function format(array) { function format(array) {
return Base.each(array, function(value, index) { return Base.each(array, function(value, index) {
this[index] = Format.number(value, precision); this[index] = formatter.number(value, precision);
}, []).toString(); }, []).toString();
} }
equals(format(array1), format(array2), message); equals(format(array1), format(array2), message);