Categories

The art of guessing

Yesterday I was looking online for a ‘pen tablet’ and I ran into an amazing one produced by Wacon.  The device is a Pen-on-screen. It is pretty much a screen on top of which you can draw. Great tool if you are willing to spend between $1.000 and $2.000. While looking at the product I notice a cool little Flash application that helps visualizing the tablet from multiple views. The application looks neat and smooth and I immediately started wondering how long it might take to build a similar one in Javascript. My guess, for a simple prototype was less that one hour, so I decide to go ahead and do it.

I am not going to mention how far off I was from my estimate, but in my little attempt to prototype this multi-view application, I stumble into common issue in the media and web development industry: estimates are often times wrong and deliverable are typically late.

When delivering late is not an options, what we end up doing to deal with wrong estimates, is to de-scope the project, meaning doing less and focus only on key features. It’s also very common to throw more bodies at the situation: adding more hours, hiring more people, etc.

 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
		<title>Multiview Image - Dot aproach</title>
		<script src="lib/jquery/jquery-1.3.2.min.js"></script>
	</head>
	<body>
		<style>
			/* CSS Code goes here */
		</style>
		<button onclick="view.setView('TL')">Top-Left</button>
		<button onclick="view.setView('TR')">Top-Right</button>
		<button onclick="view.setView('BL')">Bottom-Left</button>
		<button onclick="view.setView('BR')">Bottom-Right</button>
		<button onclick="view.setView('deafult')">Center</button>
		<div id="view" class="multiview-container">
			<div class="multiview-pane multiview-pane-tl"><div class="content">
				<a href='javascript:view.setView("TL")'>
					<img src="http://www.wacom.com/cintiq/cintiq12wx_360view/images/front_m.jpg" width="150"/>
				</a>
			</div></div>
			<div class="multiview-pane multiview-pane-tr"><div class="content">
				<a href='javascript:view.setView("TR")'>
					<img src="http://www.wacom.com/cintiq/cintiq12wx_360view/images/back_m.jpg" width="150"/>
				</a>
			</div></div>
			<div class="multiview-pane multiview-pane-bl"><div class="content">
				<a href='javascript:view.setView("BL")'>
					<img src="http://www.wacom.com/cintiq/cintiq12wx_360view/images/side_m.jpg" width="150"/>
				</a>
			</div></div>
			<div class="multiview-pane multiview-pane-br"><div class="content">
				<a href='javascript:view.setView("BR")'>
					<img src="http://www.wacom.com/cintiq/cintiq12wx_360view/images/controls_m.jpg" width="150"/>
				</a>
			</div></div>
			<div class="multiview-dot">
			</div>
		</div>
		<script>
			// Javascript code goes here
		</script>
	</body>
</html>

In this exercise I obviously had to re-size visual elements using some formula in Javascript. Literally not a big deal. Now, displaying things in HTML and using CSS to define alignments rules is something that I don't do on a daily basis, so I guessed it will be as simple as float some elements inside a table and that should be it. I guessed wrong. I first manipulated the DOM to place all my images inside an HTML table and tried to re-size rows and columns, but that didn't go well, images were distorted and dealing with tables is a pain in the neck (That was my first hour).

I took a much simple approach, I built a container and positioned a little red dot inside the container using jQuery. That was less that fifteen minutes and then the rest of my second hour was playing with CSS to align the images and re-size them according with the position of the dot. That worked and if I decide to build this little app I should probably make another estimate (hopefully not as wrong as the first one).

It is very easy to estimate those things that we do very often. When we face new circumstances we probably overlook important details, and trust our instinct and experience. I personally believe I am SuperRaul, although I use SanRaul instead (spanish for Saint-Raul). We tend to believe that if we know 80% of what has to be done we are fine, the other 20% should be easy. In the past I have seen project been held just because something was not working in IE, or laying thing out in the way the designer expected became a challenge. I personally have had to redo big portions of code in games were the performance of the computer was not the optimum.

Unfortunately, even when we carefully analyze the new challenges, build prototype and make conservative estimate, there is always a chance that something was not considered. In the end to be able to start, the only thing we can do is guess. The more we do it the better we will become in the art of guessing. It is then clear that the more things we have to guess the higher is the risk for our estimates to be wrong. I would say that depending on the situation we are allow to guess more, or guess less. The relationship between 'what we know' and 'what we guess' defines the situation in which we are in:

  • 100/0 - Boring. This is an impossible reference.
  • 80/20 - Manageable. You can always work late hours.
  • 60/40 - Risky but challenging. You will learn a lot (at least how not to do it again)
  • 40/60 - Bluffing. Praying every night is not a bad idea.
  • 20/80 - Start-up Mode. Nothing that goes wrong can surprise you (cool, right?)
  • 0/100 - Dreamer. You are probably changing career (I have done it)

Here is the rest of the code:

?View Code JAVASCRIPT
function log(msgs){
	for(var i=0; i< arguments.length; i++){
		console.log(arguments[i]);
	}
}
 
function MultiView(target, options){
	this.target = $(target) || (function(){throw 'ERROR: MultiView() requires a \'target\' parameter.'})();
	this.dot = this.target.find('.multiview-dot');
	this.paneTL = this.target.find('.multiview-pane-tl');
	this.paneTR = this.target.find('.multiview-pane-tr');
	this.paneBL = this.target.find('.multiview-pane-bl');
	this.paneBR = this.target.find('.multiview-pane-br');
	this.settings = null;
	this.options = options || {};
	this.maxWidth = null;
	this.maxHeight = null;
	this.ratio = null;
	this.currentSize = {};
 
 
	this.initialize = function(){
		this.settings = this.getDefaultSettings();
		$.extend(this.settings, this.options);
		this.setView('default');
	};
 
	this.setDotPosition = function(x, y){
		var that = this;
		this.currentSize = this.getCurrentSize();
		this.ratio = this.currentSize.width / this.currentSize.height;
		this.dot.animate({top:y, left:x}, {step:function(){
			var position = $(this).position();
			that.update(position.left, position.top);
		}, duration: that.settings.duration
		});
	};
 
 
	this.update = function(x, y){
		this.paneTL.css('left', 0);
		this.paneTL.css('top', 0);
		this.paneTL.width(x);
		this.paneTL.height(y);
		this.resizeImage(this.paneTL, x, y);
		//
		this.paneTR.css('left', x);
		this.paneTR.css('top', 0);
		this.paneTR.width(this.currentSize.width - x);
		this.paneTR.height(y);
		this.resizeImage(this.paneTR, this.currentSize.width - x, y);
		//
		this.paneBL.css('left', 0);
		this.paneBL.css('top', y);
		this.paneBL.width(x);
		this.paneBL.height(this.currentSize.height - y);
		this.resizeImage(this.paneBL, x, this.currentSize.height - y);
		//
		this.paneBR.css('left', x);
		this.paneBR.css('top', y);
		this.paneBR.width(this.currentSize.width - x);
		this.paneBR.height(this.currentSize.height - y);
		this.resizeImage(this.paneBR, this.currentSize.width - x, this.currentSize.height - y);
		//
	};
 
	this.resizeImage = function(pane, w, h){
		var isTooWide = (w/h) > this.ratio? true: false;
		var isTooTall = (w/h) < this.ratio? true: false;
 
		if(isTooWide){
			w = h * this.ratio;
		}
 
		if(isTooTall){
			h = w / this.ratio;
		}
 
		var img = pane.find('img');
		img.css('width', w);
		img.css('height', h);
	}
 
 
	this.getCurrentDotPosition = function(){
		var position = this.dot.position();
		var point = {};
			point.x = position.left;
			point.y = position.top;
		delete position;
		return point;
	};
 
 
	this.getCurrentSize = function(){
		var sizeObj = {};
			sizeObj.width  = $(this.target).width();
			sizeObj.height = $(this.target).height();
		return sizeObj;
	};
 
 
	this.getDefaultSettings = function(){
		var defalts = {};
		defalts.duration = 500;
		defalts.minWidth = 60;
		defalts.minHeight = 45;
		return defalts;
	};
 
 
 
	this.setView = function(position){
		var currentSize = this.getCurrentSize();
		switch (position.toLowerCase()){
			 case 'br':
			 	var x = this.settings.minWidth;
				var y = this.settings.minHeight;
			 break;
			 case 'bl':
			 	var x = currentSize.width - this.settings.minWidth;
				var y = this.settings.minHeight;
			 break;
			 case 'tr':
			 	var x = this.settings.minWidth;
				var y = currentSize.height - this.settings.minHeight;
			 break;
			 case 'tl':
			 	var x = currentSize.width - this.settings.minWidth;
				var y = currentSize.height - this.settings.minHeight;
			 break;
			 default:
			 	var x = currentSize.width/2;
				var y = currentSize.height/2;
			 break;
		}
 
		this.setDotPosition(x, y);
	};
 
 
	this.initialize();
}
 
var view = new MultiView($('#view'), {minWidth: 50, minHeight: 31});

CSS code:

.button-set{
	display: inline-block;
	float: left;
}
 
.multiview-container{
	position: relative;
	display: block;
	width: 630px;
	height: 397px;
	background: #ccc;
}
 
.multiview-dot{
	position: absolute;
	display: block;
	width: 2px;
	height: 2px;
	background: #f00;
}
 
.multiview-pane{
	position: absolute;
	display: block;
}
 
.multiview-pane-tl{
	/* nothing here */
}
 
.multiview-pane-tr{
	border-left: dotted 1px #333;
}
 
.multiview-pane-bl{
	border-top: dotted 1px #333;
}
 
.multiview-pane-br{
	border-top: dotted 1px #333;
	border-left: dotted 1px #333;
}
 
.multiview-pane div.content{
	position: absolute;
	display: block;
	width: 100%;
	height: 100%;
	padding: 0px;
	background: #fff;
}
 
.multiview-pane div.content img{
	position: absolute;
}
 
 
.multiview-pane-tl .content, .multiview-pane-tl .content img{
	bottom: 0px;
	right: 0px;
}
 
.multiview-pane-tr .content, .multiview-pane-tr .content img{
	bottom: 0px;
	left: 0px;
}
 
.multiview-pane-bl .content, .multiview-pane-bl .content img{
	top: 0px;
	right: 0px;
}
 
.multiview-pane-br .content, .multiview-pane-br .content img{
	top: 0px;
	left: 0px;
}
 
.multiview-pane a{
	color: transparent;
}

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

IMPORTANT! To be able to proceed, you need to solve the following simple math (so I know that you are a human) :-)

What is 10 + 3 ?
Please leave these two fields as-is: