From: dsc Date: Wed, 10 Nov 2010 04:49:08 +0000 (-0800) Subject: Improves shape rendering options. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=63047de8ec19ada99e218830c2e2bd097d2c72aa;p=tanks.git Improves shape rendering options. --- diff --git a/src/portal/layer.js b/src/portal/layer.js index b3a2f7f..6772ea6 100644 --- a/src/portal/layer.js +++ b/src/portal/layer.js @@ -31,7 +31,7 @@ Layer = new Y.Class('Layer', { // Transforms transform : null, // Object - + useCanvasScaling : false, // default to CSS3 scaling /// Setup /// @@ -46,10 +46,10 @@ Layer = new Y.Class('Layer', { this.boundingBox = new Loc.Rect(0,0, 0,0); this.transform = { - origin : new Loc('50%','50%'), + origin : new Loc('50%','50%'), // rotational origin rotate : 0, scale : new Loc(1.0,1.0), - translate : new Loc(0,0) + translate : new Loc(0,0) // translates canvas }; this.canvas = jQuery(''); @@ -157,16 +157,15 @@ Layer = new Y.Class('Layer', { if (w === undefined) return this.layerWidth; - this.layerWidth = w; - this.layer.width(w); - - var nb = this.negBleed.x , v = this.canvasWidth = w + nb + this.posBleed.x; - this.canvas.css({ - 'width' : v+'px', - 'margin-left' : (-nb)+'px' - }); + this.layerWidth = w; + this.layer.width(w).css('margin-left', (-nb)+'px') + this.canvas.width(v); + // this.canvas.css({ + // 'width' : v+'px', + // 'margin-left' : (-nb)+'px' + // }); this.canvas[0].width = v; return this; @@ -176,15 +175,15 @@ Layer = new Y.Class('Layer', { if (h === undefined) return this.layerHeight; - this.layerHeight = h; - this.layer.height(h); - var nb = this.negBleed.y , v = this.canvasHeight = h + nb + this.posBleed.y; - this.canvas.css({ - 'height' : v+'px', - 'margin-top' : (-nb)+'px' - }); + this.layerHeight = h; + this.layer.height(h).css('margin-top', (-nb)+'px') + this.canvas.height(v); + // this.canvas.css({ + // 'height' : v+'px', + // 'margin-top' : (-nb)+'px' + // }); this.canvas[0].height = v; return this; @@ -221,6 +220,27 @@ Layer = new Y.Class('Layer', { return this; }, + stroke : function stroke(style, width){ + if (style === undefined && width === undefined) + return this.strokeStyle; + + if (width !== undefined) + this.lineWidth = width; + + this.dirty = true; + this.strokeStyle = style; + return this; + }, + + fill : function fill(style){ + if (style === undefined) + return this.fillStyle; + + this.dirty = true; + this.fillStyle = style; + return this; + }, + hide : makeDelegate('hide'), show : makeDelegate('show'), @@ -265,34 +285,34 @@ Layer = new Y.Class('Layer', { * to all sublayers (preserving knowledge of their individual scaling). */ scale : function scale(sx,sy){ + var o = this.transform.scale; if (arguments.length === 0) - return { 'x': this.absScaleX, 'y': this.absScaleY }; + return o.absolute(this.layerWidth, this.layerHeight); // Record my relative scaling... + o.x = sx; + o.y = sy; this.dirty = true; - this.scaleX = sx; - this.scaleY = sy; - - // Propogate... - var p = this.parent - , ps = (p ? p.scale() : { x:1.0, y:1.0 }) ; - return this._scale(ps.x, ps.y); + return this._applyTransforms(); }, - _scale : function _scale(parentScaleX, parentScaleY){ - var absX = this.absScaleX = this.scaleX * parentScaleX - , absY = this.absScaleY = this.scaleY * parentScaleY - ; - - // Apply absolute scaling... - this.ctx.scale(absX, absY); - - // And propogate down the line - this.children.invoke('_scale', absX, absY); + /** + * Translates draw calls by (x,y) within this layer only. This allows you to + * functionally move the coordinate system of the layer. + */ + translate : function translate(x,y){ + var o = this.transform.translate; + if (arguments.length === 0) + return o.absolute(this.layerWidth, this.layerHeight); + // Record my relative scaling... + o.x = x; + o.y = y; + this.dirty = true; return this; }, + // origin : new Loc('50%','50%'), // rotate : 0, // scale : new Loc(1.0,1.0), @@ -303,7 +323,7 @@ Layer = new Y.Class('Layer', { if (t.rotate !== 0) tfns.push('rotate('+t.rotate+'rad)'); - if (t.scale.x !== 1 || t.scale.y !== 1) + if (!this.useCanvasScaling && (t.scale.x !== 1 || t.scale.y !== 1)) tfns.push('scale('+t.scale.x+','+t.scale.y+')'); var trans = (tfns.length ? tfns.join(' ') : 'none') @@ -389,18 +409,22 @@ Layer = new Y.Class('Layer', { _openPath : function _openPath(ctx){ var self = this - , w = this.canvasWidth - , h = this.canvasHeight + , alwaysClear = this.alwaysClear , neg = this.negBleed - ; + , t = this.transform + , w = this.canvasWidth, h = this.canvasHeight ; ctx.beginPath(); ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(-w,-h, 2*w,2*h); + + if (this.useCanvasScaling && (t.scale.x !== 1 || t.scale.y !== 1)) + ctx.scale(t.scale.x,t.scale.y); + ctx.translate(neg.x, neg.y); + ctx.translate(t.translate.x, t.translate.y); // Set context attributes - var alwaysClear = !!this.alwaysClear; CONTEXT_ATTRS.forEach(function(name){ if (self[name] !== undefined) ctx[name] = self[name]; @@ -408,10 +432,6 @@ Layer = new Y.Class('Layer', { delete ctx[name]; }); - // ctx.rotate(this.absRotation); - // ctx.translate(-this.originX, -this.originY); - // ctx.scale(this.absScaleX, this.absScaleY); - return this; }, diff --git a/src/portal/math/line.js b/src/portal/math/line.js index abe1250..280fb79 100644 --- a/src/portal/math/line.js +++ b/src/portal/math/line.js @@ -36,6 +36,24 @@ math.Line = new Y.Class('Line', math.Vec, { return new math.Line(this.x1,this.y1, this.x2,this.y2, this.tdist); }, + equals : function equals(line){ + return ( this.slope === line.slope + && this.x1 === line.x1 && this.y1 === line.y1 + && this.x2 === line.x2 && this.y2 === line.y2 ); + }, + + intersects : function intersects(x,y){ + var o = x; + if (o instanceof math.Line) + return this.slope !== o.slope || this.equals(o); + + if (o instanceof math.Vec) { + x = o.x; + y = o.y; + } + return this.calcY(x) === y; + }, + setTScale : function setTScale(tdist){ if (tdist) { this.tdist = tdist; @@ -77,20 +95,24 @@ math.Line = new Y.Class('Line', math.Vec, { }, tangent : function tangent(at){ - var _theta = Math.PI/2 + this.theta - , x = (at.x !== this.x1 ? this.x1 : this.x2) - at.x - , y = (at.y !== this.y1 ? this.y1 : this.y2) - at.y - , _x = at.x + y - , _y = at.y - x - return new math.Line(at.x,at.y, _x,_y, this.tdist); + var slope = this.slope; + + if ( slope === 0 ) + return new math.Line(at.x,at.y, at.x,at.y+1, this.tdist); + + if ( !isFinite(slope) ) + return new math.Line(at.x,at.y, at.x+1,at.y, this.tdist); + + var x1 = at.x, y1 = at.y + , x2 = at.x - at.y + (at.y !== this.y1 ? this.y1 : this.y2) + , y2 = at.y + at.x - (at.x !== this.x1 ? this.x1 : this.x2) ; + return new math.Line(x1,y1, x2,y2, this.tdist); }, toString : function toString(){ - return 'Line('+this.x1.toFixed(2)+','+this.y1.toFixed(2)+', '+ - this.x2.toFixed(2)+','+this.y2.toFixed(2)+', '+ - 'slope='+this.slope.toFixed(3)+')'; + return 'Line( '+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+')'; } }); -})(); +})(); \ No newline at end of file diff --git a/src/portal/math/vec.js b/src/portal/math/vec.js index 33cf4f2..7ac8e02 100644 --- a/src/portal/math/vec.js +++ b/src/portal/math/vec.js @@ -63,7 +63,7 @@ math.Vec = new Y.Class('Vec', [], { }, toString : function toString(){ - return '['+this.x.toFixed(3)+', '+this.y.toFixed(3)+']'; + return '('+this.x.toFixed(3)+', '+this.y.toFixed(3)+')'; } }); diff --git a/src/portal/shape.js b/src/portal/shape.js index a31817b..50d3895 100644 --- a/src/portal/shape.js +++ b/src/portal/shape.js @@ -21,79 +21,18 @@ Shape = new Y.Class('Shape', Layer, { }); -Rect = new Y.Class('Rect', Shape, { - _cssClasses : 'portal layer shape rect', - - init : function initRect(w, h){ - Layer.init.call(this); - - this.width(w) - .height(h); - // .origin(w/2, h/2); - }, - - drawShape : function drawShape(ctx){ - ctx.rect(0,0, this.canvasWidth,this.canvasHeight); - ctx.fill(); - } - -}); -Circle = new Y.Class('Circle', Shape, { - _cssClasses : 'portal layer shape circle', - - init : function initCircle(radius){ - Layer.init.call(this); - - var d = radius * 2; - this.radius = this.negBleed.x = this.negBleed.y = radius; - this.width(d).height(d); - // .origin(radius,radius); - }, - - drawShape : function drawShape(ctx){ - var r = this.radius; - ctx.arc(0,0, r, 0, Math.PI*2, false); - ctx.fill(); - ctx.stroke(); - } +Line = new Y.Class('Line', Shape, { + _cssClasses : 'portal layer shape line', -}); - -Polygon = new Y.Class('Polygon', Shape, { - _cssClasses : 'portal layer shape polygon', + useCanvasScaling : true, + fillStyle : 'transparent', + strokeStyle : "#000000", + lineWidth : 1, - /** - * Expects two arrays of coordinate-halfs, which could be zipped - * together to make the numbered coordinates. - * x0 and y0 will always be 0. - */ - init : function initPolygon(xs, ys){ - Layer.init.call(this); - - var xs = this._calcDimension('x', xs) - , ys = this._calcDimension('y', ys) - , w = Math.max.apply(Math, xs) - , h = Math.max.apply(Math, ys) - ; - - this.points = Y(xs).zip(ys).map(Loc.instantiate, Loc); - this.width(w) - .height(h); - // .origin(w/2, h/2); - }, + drawDefinitionPoints : false, + invertY : false, - drawShape : function drawShape(ctx){ - this.points.forEach(function(loc, i){ - ctx.lineTo(loc.x, loc.y); - }); - ctx.fill(); - } -}); - -// Er, this won't do. It's only a line-segment. -Line = new Y.Class('Line', Shape, { - _cssClasses : 'portal layer shape line', init : function initLine(x,y){ Layer.init.call(this); @@ -110,25 +49,35 @@ Line = new Y.Class('Line', Shape, { if (top === undefined && left === undefined) return this.line.p1; - if (top && Y.isPlainObject(top)) - var pos = top; - else - var pos = { 'top': top, 'left':left }; + var pos = Y.isPlainObject(top) ? top : {'top':top, 'left':left}; this.x1 = pos.left; this.x2 += this.x1; this.y1 = pos.top; this.y2 += this.y1; - this.line = new math.Line(this.x1,this.y1, this.x2,this.y2); + + this.line = new math.Line(this.x1,this.y1, this.x2,this.y2, (this.line||{}).tdist); return this; }, + origin : function origin(x,y){ + var o = this.transform.origin; + if (arguments.length === 0) + return o.absolute(this.layerWidth, this.layerHeight); + + o.x = x; + o.y = y; + this.ctx.translate(x,y); + this.dirty = true; + return this._applyTransforms(); + }, + appendTo : function appendTo(parent){ var r = Layer.prototype.appendTo.call(this, parent); - this.fixSize(); + this._fixSize(); return r; }, - fixSize : function fixSize(){ + _fixSize : function _fixSize(){ var p = this.parent , pw = p.canvasWidth, ph = p.canvasHeight , w = this.canvasWidth, h = this.canvasHeight @@ -146,30 +95,45 @@ Line = new Y.Class('Line', Shape, { }, drawShape : function drawShape(ctx){ - this.fixSize(); - var x1,y1, x2,y2 + this._fixSize(); + var x1,y1, x2,y2, t = this.transform.translate , line = this.line, p1 = line.p1, p2 = line.p2 - , w = this.canvasWidth, h = this.canvasHeight + , minW = -t.x, minH = -t.y + , maxW = this.canvasWidth+minW, maxH = this.canvasHeight+minH ; - x1 = 0; y1 = line.calcY(x1); - if (y1 < 0) { - y1 = 0; x1 = line.calcX(y1); + x1 = minW; y1 = line.calcY(x1); + if (isNaN(y1) || !isFinite(y1)) { + y1 = minH; x1 = line.calcX(y1); } - x2 = w; y2 = line.calcY(x2); - if (y2 > h) { - y2 = h; x2 = line.calcX(y2); + x2 = maxW; y2 = line.calcY(x2); + if (isNaN(y2) || !isFinite(y2)) { + y2 = maxH; x2 = line.calcX(y2); } - ctx.moveTo(x1,y1); - ctx.lineTo(x2,y2); - ctx.stroke(); - ctx.closePath(); + if (this.invertY){ + y1 = -y1; + y2 = -y2; + } + + try { + ctx.moveTo(x1,y1); + ctx.lineTo(x2,y2); + ctx.stroke(); + ctx.closePath(); + } catch(e) { + console.error(this+'.drawShape()'); + console.log(' points:', x1,y1, ' ', x2,y2); + console.log(' bounds:', minW,minH, ' ', maxW,maxH); + console.log(' ::', this, line); + } // Show definition points - this.point(p1.x,p1.y, 'rgba(69,150,255,0.4)'); - this.point(p2.x,p2.y, 'rgba(69,150,255,0.4)'); + if ( this.drawDefinitionPoints ) { + this.point(p1.x,p1.y, 'rgba(69,150,255,0.4)'); + this.point(p2.x,p2.y, 'rgba(69,150,255,0.4)'); + } }, @@ -184,6 +148,78 @@ Line.fromPoints = function fromPoints(x1,y1, x2,y2){ }; + +Rect = new Y.Class('Rect', Shape, { + _cssClasses : 'portal layer shape rect', + + init : function initRect(w, h){ + Layer.init.call(this); + + this.width(w) + .height(h); + // .origin(w/2, h/2); + }, + + drawShape : function drawShape(ctx){ + ctx.rect(0,0, this.canvasWidth,this.canvasHeight); + ctx.fill(); + } + +}); + +Circle = new Y.Class('Circle', Shape, { + _cssClasses : 'portal layer shape circle', + + init : function initCircle(radius){ + Layer.init.call(this); + + var d = radius * 2; + this.radius = this.negBleed.x = this.negBleed.y = radius; + this.width(d).height(d); + // .origin(radius,radius); + }, + + drawShape : function drawShape(ctx){ + var r = this.radius; + ctx.arc(0,0, r, 0, Math.PI*2, false); + ctx.fill(); + ctx.stroke(); + } + +}); + +Polygon = new Y.Class('Polygon', Shape, { + _cssClasses : 'portal layer shape polygon', + + /** + * Expects two arrays of coordinate-halfs, which could be zipped + * together to make the numbered coordinates. + * x0 and y0 will always be 0. + */ + init : function initPolygon(xs, ys){ + Layer.init.call(this); + + var xs = this._calcDimension('x', xs) + , ys = this._calcDimension('y', ys) + , w = Math.max.apply(Math, xs) + , h = Math.max.apply(Math, ys) + ; + + this.points = Y(xs).zip(ys).map(Loc.instantiate, Loc); + this.width(w) + .height(h); + // .origin(w/2, h/2); + }, + + drawShape : function drawShape(ctx){ + this.points.forEach(function(loc, i){ + ctx.lineTo(loc.x, loc.y); + }); + ctx.fill(); + } +}); + + Triangle = new Y.Class('Triangle', Polygon, { _cssClasses : 'portal layer shape polygon triangle', diff --git a/test/math/index.php b/test/math/index.php index da6937a..2c6a179 100644 --- a/test/math/index.php +++ b/test/math/index.php @@ -9,21 +9,57 @@ - -
+
+
+ +
+
+ + + ( , ) + ( , ) + +
+
+ +
+

Trigonometry and Reflection Test

+
    +
  • The main line is pink (and the light-orange line is its tangent).
  • +
  • Click anywhere to add a point. It will be purple, and its reflection in the main line will be orange.
  • +
  • You can drag the blue control points of the main line to change it.
  • +
+
+