/**
* A 2D brush module for drawing contiguous lines in a stamp-like fashion.
* @module BB.LineBrush2D
* @extends BB.BaseBrush2D
*/
define(['./BB', './BB.BaseBrush2D', './BB.Color', "./BB.MathUtils"],
function( BB, BaseBrush2D, Color, MathUtils){
'use strict';
BB.BaseBrush2D = BaseBrush2D;
BB.Color = Color;
BB.MathUtils = MathUtils;
var justReset = false;
var controllerModuleHasIsDown = false;
/**
* A 2D brush module for drawing contiguous lines in a stamp-like fashion.
* What makes BB.LineBrush2D fundamentally different from BB.BaseBrush
* is that each new drawing instance is influenced by the previous position of
* the brush (usually to adjust for drawing angle or brush width).
* @class BB.LineBrush2D
* @constructor
* @extends BB.BaseBrush2D
* @param {Object} [config] A optional config hash to initialize any of
* BB.LineBrush2D's public properties.
* @example <code class="code prettyprint"> var lineBrush = new BB.LineBrush2D({ width: 100,
* height: 100, variant: "soft" }); </code>
*/
BB.LineBrush2D = function(config) {
BB.BaseBrush2D.call(this, config);
/**
* The brush's previous x position. This property is unique to
* BB.LineBrush.
* @property prevX
* @type Number
* @default null
*/
this.prevX = null;
/**
* The brush's previous y position. This property is unique to
* BB.LineBrush.
* @property prevY
* @type Number
* @default null
*/
this.prevY = null;
/**
* The type of brush. This property should be treated as read-only.
* @property type
* @type String
* @default "line"
*/
this.type = "line";
/**
* The current brush variant.
* @property variant
* @type String
* @default solid
*/
this.variant = "solid";
/**
* The brush's line weight.
* @property weight
* @type Number
* @default 1
*/
this.weight = 1;
this.delta = 0;
/**
* An array of all supported variants.
* @property variants
* @type Array
*/
this.variants = [
'solid',
'ink',
'ink-osc',
'soft',
'lines',
'calligraphy'
];
/**
* Keeps track of wether or not the controllerModule passed into update
* was made active (for instance if it was pressed) this frame.
* @property variants
* @protected
* @type Boolean
*/
this._lineStartedThisFrame = !this.hidden;
if (config) {
if (typeof config.variant === 'string') this.variant = config.variant;
if (typeof config.weight === 'number') this.weight = config.weight;
}
};
BB.LineBrush2D.prototype = Object.create(BB.BaseBrush2D.prototype);
BB.LineBrush2D.prototype.constructor = BB.LineBrush2D;
/**
* Update method. Usually called once per animation frame.
* @method update
* @param {Object} controllerModule An object with x and y properties and
* optionally an isDown boolean (used for beginning and ending
* strokeds/marks).
* @example <code class="code prettyprint">
* var mouseInput = new BB.MouseInput(document.getElementById('canvas'));<br>
* var pointer = new BB.Pointer(mouseInput);<br>
* var brush = new BB.LineBrush2D();<br>
* <br>
* // called once per animation frame (from somewhere else in your app)<br>
* function update() {<br>
* mouseInput.update();<br>
* pointer.update();<br>
* brush.update(pointer); // update the brush using the pointer<br>
* }
* </code>
*/
BB.LineBrush2D.prototype.update = function(controllerModule) {
BB.BaseBrush2D.prototype.update.call(this, controllerModule);
if (controllerModule.hasOwnProperty('isDown')) {
controllerModuleHasIsDown = true;
this.hidden = (controllerModule.isDown === false);
} else {
controllerModuleHasIsDown = false;
}
};
/**
* Draws the brush to the context. Usually called once per animation frame.
* @method draw
* @param {Object} context The HTML5 canvas context you would like to draw
* to.
*/
BB.LineBrush2D.prototype.draw = function(context) {
context = BB.BaseBrush2D.prototype.draw.call(this, context);
context.save();
context.lineJoin = "round";
context.lineCap = "round";
if (typeof this.variant !== 'string' ||
this.variants.indexOf(this.variant) === -1) {
throw new Error("BB.LineBrush2D.draw: " + this.variant + " is not a valid variant for BB.LineBrush2D");
}
// draw down here...
if (!this.hidden) {
if (controllerModuleHasIsDown) {
if (this._lineStartedThisFrame) {
context.beginPath();
context.moveTo(this.x, this.y);
this._lineStartedThisFrame = false;
} else { // we are in the middle of the line
var r, g, b, alphaFloat;
if (this.color && this.color instanceof BB.Color) {
r = this.color.r;
g = this.color.g;
b = this.color.b;
alphaFloat = BB.MathUtils.map(this.color.a, 0, 255, 0.0, 1.0);
} else {
r = 255;
g = 255;
b = 255;
alphaFloat = 1.0;
}
if(this.variant == 'solid'){
context.lineWidth = this.weight;
context.lineTo(this.x, this.y);
context.strokeStyle = "rgba(" + r + ", " + g + ", " + b + ", " + alphaFloat + ")";
context.stroke();
context.closePath();
context.beginPath();
context.moveTo(this.x, this.y);
} else if(this.variant == 'ink'){
// var dx2 = (this.prevX > this.x) ? this.prevX - this.x : this.x - this.prevX;
// var dy2 = (this.prevY > this.y) ? this.prevY - this.y : this.y - this.prevY;
// this.weight = Math.abs(dx2 - dy2);
// if( this.weight > 100){ this.weight = 100; }
// context.lineWidth = BB.MathUtils.map(this.weight, 0, 100, this.width / 2.5, this.width * 2.5);
// context.lineTo(this.x, this.y);
// context.strokeStyle = "rgba(" + r + ", " + g + ", " + b + ", " + alphaFloat + ")";
// context.stroke();
// context.closePath();
// context.beginPath();
// context.moveTo(this.x, this.y);
var dx = (this.prevX > this.x) ? this.prevX - this.x : this.x - this.prevX;
var dy = (this.prevY > this.y) ? this.prevY - this.y : this.y - this.prevY;
this.delta = Math.abs(dx - dy);
if(this.delta > this.weight){
this.weight+=4;
if(this.weight>=this.delta) this.weight = this.delta;
} else {
this.weight--;
if(this.weight<=this.delta) this.weight = this.delta;
}
if(this.weight > 100) this.weight=100;
else if(this.weight<2) this.weight=2;
context.lineWidth = BB.MathUtils.map(this.weight, 2, 100, this.width / 4, this.width * 4);
context.lineTo(this.x, this.y);
context.strokeStyle = "rgba(" + r + ", " + g + ", " + b + ", " + alphaFloat + ")";
context.stroke();
context.closePath();
context.beginPath();
context.moveTo(this.x, this.y);
} else if(this.variant == 'ink-osc'){
this.weight = 2 + Math.abs( Math.sin( Date.now() * 0.003 ) * 50 );
context.lineWidth = BB.MathUtils.map(this.weight, 2, 52, this.width / 2, this.width * 2);
context.lineTo(this.x, this.y);
context.strokeStyle = "rgba(" + r + ", " + g + ", " + b + ", " + alphaFloat + ")";
context.stroke();
context.closePath();
context.beginPath();
context.moveTo(this.x, this.y);
} else if(this.variant == 'soft'){
var dist = BB.MathUtils.dist(this.prevX, this.prevY, this.x, this.y);
var angle = BB.MathUtils.angleBtw(this.prevX, this.prevY, this.x, this.y);
for (var i = 0; i < dist; i++) {
var x = this.prevX + (Math.sin(angle) * i);
var y = this.prevY + (Math.cos(angle) * i);
var gradient = context.createRadialGradient(x, y, this.width/6, x, y, this.width/2);
gradient.addColorStop(0, "rgba(" + r + ", " + g + ", " + b + ', 0.1)');
gradient.addColorStop(1, "rgba(" + r + ", " + g + ", " + b + ', 0)');
context.fillStyle = gradient;
context.fillRect(x - this.width/2, y - this.width/2, this.width, this.width);
}
} else if(this.variant == 'lines' || this.variant == 'calligraphy'){
if(this.variant == 'lines'){ context.lineWidth = (this.width < 1) ? 1 : this.width * 0.05; }
if(this.variant == 'calligraphy'){ context.lineWidth = this.width * 0.25; }
context.strokeStyle = "rgb(" + r + ", " + g + ", " + b + ")";
context.moveTo(this.prevX, this.prevY);
context.lineTo(this.x, this.y);
context.stroke();
context.moveTo(this.prevX - this.width * 0.2, this.prevY - this.width * 0.2);
context.lineTo(this.x - this.width * 0.2, this.y - this.width * 0.2);
context.stroke();
context.moveTo(this.prevX - this.width * 0.1, this.prevY - this.width * 0.1);
context.lineTo(this.x - this.width * 0.1, this.y - this.width * 0.1);
context.stroke();
context.moveTo(this.prevX + this.width * 0.1, this.prevY + this.width * 0.1);
context.lineTo(this.x + this.width * 0.1, this.y + this.width * 0.1);
context.stroke();
context.moveTo(this.prevX + this.width * 0.2, this.prevY + this.width * 0.2);
context.lineTo(this.x + this.width * 0.2, this.y + this.width * 0.2);
context.stroke();
}
}
} else { // this controller has no "button", so assume it is always pressed
}
} else {
this._lineStartedThisFrame = true;
}
context.restore();
this.prevX = this.x;
this.prevY = this.y;
};
return BB.LineBrush2D;
});