HTML5 Canvas: Smooth, variable width lines with transparency.

When making a drawing app with HTML5 canvas, you might come up against the problem of making a smooth, variable width line. Eventually you might find that you can make several calls to ctx.quadraticCurveTo(x,y,xc,yc) and get some kind of effect. If you stroke every 2 or 3 points, then as long as the line width is very small, you won’t notice the jaggedness.

Unfortunately, if you try to do this with semi-transparent lines, the effect is truly horrible. To solve this problem, instead of drawing lines, I simply draw very thin shapes, and fill them with a color. This allows for 1) variable thickness and 2) nice transparency. Variable transparency is a problem I haven’t quite figured out yet. It may require composing the shape from individual pixels, which is simply too slow, especially in IE.

What I am writing is a drawing App that works with the Wacom Web Plugin, and therefore varies line thickness based on pressure. I would like to vary opacity too, but this seems problematic right now. A solution that may be possible is to fill the shape with a complex gradient, if it is possible to create semi-transparent stops.

Here is an image of what the program does so far:

test

 

Each line is actually a full shape, with varying degrees of opacity. Thickness and thinness is accomplished with pen pressure from the Wacom Intuous5 tablet.

As the line is drawn, a temporary line is shown to the user, when they lift the pen, that line disappears, and a full rendering of it is done as a shape instead of a line. This is slower, but necessary for simplicity. This is meant to be a quick and easy sketch program for increasing your visual library and practicing gesture drawing.

As the person draws, the points are collected up into an array of vector objects which store the x,y, and pressure of the pen at the time that point was hit. The rendering function is like this:

$this.renderVectorsAsPath = function (vectors) {
			if ( ! vectors[0] )
				return;
			var x,y,xc,yc,v,p,w,i;
			var started = false;
			var ctx = $this._context;
			var color = '';
			var pType = vectors[0].penType;

			var index = 0;

			if (pType == 'pen') {
		    	color = $this.options.lineColor;
		    } else {
		    	color = $this.options.backgroundColor;
		    	w = w + (w * p);
		    }
			ctx.strokeStyle = color;
			ctx.fillStyle = color;
			ctx.globalAlpha = $this.wGetOpacity() / 100;
			console.log( ctx.globalAlpha );
			ctx.lineWidth = 0.1;
			var lastx,lasty;
			// First we draw the line
			for ( i = 0; i < vectors.length - 1; i++) {
				index++;
				v = vectors[i];
				x = v.x;
				y = v.y;
				p = v.pressure;
				w = v.lineWidth;
				lastx = x;
				lasty = y;
				if ( ! started ) {
					ctx.beginPath();
					ctx.moveTo(x,y);
					started = true;
					continue;
				}

				xc = ( x + vectors[i + 1].x ) / 2;
				yc = ( y + vectors[i + 1].y ) / 2;
				ctx.quadraticCurveTo(x,y,xc,yc);	
			}
			var cv, nv,dx,dy,xr,yr,fx,fy,t;
			// Then we backup and draw a mirrored line
			for ( i = vectors.length - 1; i > 0; i-- ) {	
				cv = vectors[i];
				nv = vectors[ i - 1 ];
				if ( ! nv )
					continue;
				t = cv.pressure * cv.lineWidth;
				dx = nv.x - cv.x;
				dy = nv.y - cv.y
				fx = dx / ( Math.sqrt( (dx * dx ) + (dy * dy ) ) );
				fy = dy / ( Math.sqrt( (dx * dx ) + (dy * dy ) ) );
				xr = cv.x + t * fy;
				yr = cv.y - t * fx;
				xc = ( xr + nv.x ) / 2;
				yc = ( yr + nv.y ) / 2;
				ctx.lineTo(xr,yr);
			}
			ctx.closePath();	
			ctx.fill();
		}

The second part is thanks to my father, a mathematician and physicist who helped me figure out how to calculate the points directly. Math is something I am currently working on.

His “equation” as I call it, goes something like this:

jadczyk_line_eq1

I wouldn’t have been able to come up with this on my own.

Posted in Computer Science, HTML5, HTML5 Canvas, Javascript Tutorials, Tips and Tricks, Topics, Tutorials, Web Design | Tagged , , , , , , | 2 Comments

HTML5 Canvas, Pixel Compositing, Transparency, and why all the trivia!

One thing I really hate about new technology are all the early adopters who incessantly post the same trivial examples. They cross link them too. So you can go from one page to the next, and see essentially the same example, just with the bits renamed or moved around.

At a certain point, I got it into my head that I wanted to make a quickie HTML5 App that works with my Intuous5 Wacom Pad. Wacom has a web plugin that let’s you get data about pen pressure and so on from the tablet. So I started to work on it, and found the said trivial examples and started mashing something up. Pretty soon it became apparent that if I wanted my lines to look good, I might have to anti-alias them, or so I am trying, I don’t know if it will work, because I just spent the better part of 2 hours looking for a solution to a simple problem.

If you look for examples of pixel level manipulation for HTML5 Canvas, you will find the same trivial examples over and over again, which basically say:

var id = context.createImageData(1,1);

var d = id.data;

d[0] = r; // where r is 0...255
...
context.putImageData(id,x,y);

If you actually want a full trivial example, use google, they abound.

The problem with the above is: it replaces the entire pixel, all the way to the background, erasing any colors beneath it. The alpha channel of the pixel doesn’t make it transparent to what was under the pixel, it makes it transparent to the background (probably white).

What I wanted to do was composite the new pixel ontop of the old pixel, and have the alpha value indicate how much of the other color should show through.

After much searching, I came up with this:

$this.wPlot = function (x, y, color, alpha) {
			if ( !color )
				color = $this.options.lineColor;
			if ( !alpha )
				alpha = $this.wPressure();
			var rgb = hexToRgb(color);
			var existing_pixel = $this._context.getImageData(x,y,1,1);
			var epd = existing_pixel.data;
			var d = $this.pData.pixel.data;
			var nrgb = {
				r: Math.floor( ( rgb.r * alpha) + (epd[0] * ( 1.0 - alpha) ) ),
				g: Math.floor( ( rgb.g * alpha) + (epd[1] * ( 1.0 - alpha) ) ),
				b: Math.floor( ( rgb.b * alpha) + (epd[2] * ( 1.0 - alpha) ) ),
			}
			d[0] =  nrgb.r;
			d[1] =  nrgb.g;
			d[2] =  nrgb.b;
			d[3] = 255;
			//console.log(rgb.r, "/", rgb.g, "/",rgb.b,"  ",nrgb.r, ' - ', nrgb.g, ' - ', nrgb.b, ' | ', epd[0], '/', epd[1], '/', epd[2], ' || ',  alpha);
			$this._context.putImageData( $this.pData.pixel, x, y );
		};

This seems to work as I had hoped, I mucked about with the color and it seems to do right.

A point here: alpha is a value between 0..1, and notice d[3] is set to 255. That’s because the pixel alpha channel shows through the real background, not the background I have painted, so you need to keep that as 255 and precompute the alpha.

Posted in HTML5, HTML5 Canvas, Javascript Tutorials, Topics, Tutorials, Web Design | Tagged , , , , , , | Leave a comment

Backbone.js + Rails

You all know that my opinion of Ruby On Rails couldn’t be any lower if it was under the basement carpet of a Pompeii1 house, so this post won’t be very suprising.

So, awhile back I was having a discussion with a  friend about how, as a general rule, about 90% of a web app should really be taking place on the browser, and this was vindicated in my opinion with several other developers coming to much the same conclusion and releasing some very interesting application frameworks for javascript Single Page Applications. Of course this conversation took place when jQuery was just really starting to take off. The problem at that time was that no one was really talking about a client side application in javascript methodology, how such a thing could or would work.

In hindsight it all seems obvious, but actually, Backbone and other such frameworks represent some really great out of the box creativity. Anyway, after my last Rails project, I more or less swore I would never work with that ass backwards platform ever again. I decided to revisit this idea of Single Page Applications and low and behold I stumble onto Backbone.js, and I decide I am going to learn to use this tool. While looking around for some books on the topics of jQuery and Backbone (I’ve more or less decided to give up the ghost and use jQuery ui elements in all future applications cause rolling your own is just too much work.) low an behold I find a link to this: http://www.backbonerails.com. I thought to myself, you have to be shitting me. Mixing Backbone.js and Ruby on Rails is like taking a sexy girl back to your apartment and making her diddle herself across the room while you try to solve a Rubik’s Cube with your toes.

Rails is the big shining Mecca of Shit2 that passes for a software platform these days, so I understand that all the new kids want to make use of cool by association until graduation is over, and everyone realizes that the flashy douche bag will end up a used car salesman, slowly killing his soul, beating his wife and kids, and crying himself to sleep in a second hand recliner while he reminisces about the glory days over a bottle of cheap scotch.

Normally I would find such an ironic situation truly sad, but must admit the idea of a pot-bellied DHH sobbing like a little girl into a dixie cup filled with Tambowie is strangely satisfying.

The whole idea of Rails, the fundamental flaw of Rails can be summed up by drawing you a nice little picture.backbonevsrails

Using Rails on the Server side of a Backbone Application is like putting on astroglide3 before attempting to service4 a meat grinder. Deploying Rails Applications is actually MORE difficult than deploying a Java Application (go figure). Any and all time and money saved during development of a Rails application will quickly be consumed in the cost of server and the hours of headaches of deployment. It’s such a headache that tools have been created to automate the deployment of Rails Applications, and those tools themselves are a Headache to use, with confusing pointless options and defaults that would only work on trivially simple applications.

Backbone and Ruby aren’t just apples and oranges, they are Apples and a Pile of Steaming Dog Poo (PSDP).

This is because MVC was a stupid stupid idea for web applications on the server. MVC in the browser is right inline with the kinds of issues it was meant to solve.  The extent of the code on your server should be: 1) can you do this, and 2) here’s a collection object of what you asked for and 3) What you asked me to save has been saved.

The webserver should become nothing more than a database layer.

Posted in Information News, Ruby on Rails, Topics, Web Design | Tagged , , | 2 Comments