Tuesday, July 3, 2012

Step 7a - The Comments Object

I haven't posted anything for awhile since I had a crazy/fun work project going on. I learned four new tools/technologies: RabbitMQ, HTML5 drag and drop file uploading, Phil Sturgeon's CodeIgniter REST libraries and PostGreSQL. It was fun, but limited my time for the Go Reader :-(  But since it's winding down, I've got more free time to devote to finishing this up. 

Go Comments

Every stone in Go means something. Sometimes the difference of a single line can have a completely contrary meaning. If black plays on the third line, she may want to create a safe group. On the fourth line she may be testing the waters and be prepared to run.

As a beginner, I find it difficult interpreting the meaning of each play. Fortunately, there are a lot of games out there professionals and strong amateurs have commented on. For this post I'll talk about displaying these comments.

Programmatically, the challenge in displaying the comments is:
  • The commented area shouldn't be covered, so the user can see the comments and the stones referred to at the same time
  • The commented stone can appear just about anywhere on the board, so the comment must position itself in different places, so as to not overlap the stone
  • It's easy to figure out a single stone mentioned in the comment, but hard to know what other stones are involved. So there is a danger of accidentally overlapping stones you want the user to see
Some ways of addressing these problems are to:
  • Have semi-transparent comment balloons, so stones are visible underneath the comment
  • Allow the user to close the comment balloon after reading it
Since there is only one comment at a time, my first attempt was to display it on a single canvas on top of the other ones. I thought this would work well because HTML5 canvases are transparent. After my first test, I realized this approach wasn't going to work, though. It came out looking like this:


( The left side is the comment object. ) I found out that when you draw text into the canvas object it doesn't wrap the words :-(

So there seemed to be three obvious solutions:
  • Add in line breaks into my stored comments and perform a carriage return in my code when I detect those
  • Use a JavaScript function to automatically calculate line breaks
  • Give up the idea of a canvas and use a div instead, which has the word-wrap built in
The last one seemed like the simplest solution. Since there's already a wheel out there, re-invent it? Although it would have been more interesting to use a canvas, it seemed the wrong tool for the job. Besides, it'd be good to work on my paltry CSS skills for a bit.

Drawing the Bubble

The comment bubble ended up being just a plain div, with the following CSS style:

#comment_div {
    color: black;
    background-color:#FCF6CF;
    opacity: 0.5;
    border-radius: 20px;
    border-color: black;
    border-width: 2px;
    border-style: solid;
    padding: 20px;
    z-index:1000;
    overflow:hidden;
}

The text color is black. The opacity is 50%. I gave it some curved corners with the border-radius attribute. The padding ensures the text doesn't go right up to the edge, and the z-index field ensures the div floats on top of everything else.

One difficult decision was the choice of overflow:hidden vs overflow:auto. If I had set the div's style to overflow:auto, it would have scrolled the content. with overflow:hidden it just chops the text off at the end of the div. I was worried that on a touch interface the overflow:auto would be difficult to manage. Maybe a control that expands the comment ballon to full screen if there is overflow will be good for the future. 

Not Obscuring Stones

In order to avoid obscuring any important stones on the board, I employed three methods:
  1. As I mentioned above the comment bubble is semi-transparent
  2. I position the bubble away from the main stone mentioned
  3. If the user clicks/touches the bubble it disappears
For #2 it took awhile for me to realize just a simple algorithm will work to position the bubble. For the display method, all I need to do is send it the position of a stone that is being referenced in the comment. Then I calculate the quadrant that stone is in, and put the comment bubble in the opposite quadrant.

For example, if the mentioned stone is in the top, right then the comment bubble is placed in the bottom, left. This seems to work fine.

Here is the code for the "algorithm" :-)


 display: (stoneX, stoneY ) ->

  #figure out the quadrant of the referenced stone
  widthLoc = if stoneX < 10 then 'left' else 'right'
  heightLoc = if stoneY < 10 then 'top' else 'bottom'

  #position the comment in the diagonally-opposed quadrant
  x = if widthLoc is 'left' then @quadrantWidth else 0
  y = if heightLoc is 'top' then @quadrantWidth else 0
  
  #move the div to the correct position over the board
  position = $('#goBoardImage').offset()
  position.left = position.left + x
  position.top = position.top + y
  $('#comment_div').css(position)
  $('#comment_div').show()

I know I could have reduced the "algorithm" from 4 lines down to 2. However, it's not really a computation-heavy area of the code. And I like it, if I can just walk up to it later and figure out what it's doing at a glance.

Here's What it Looks Like
I figured I could use the first comment bubble to provide initial instructions:

And here's the comment bubble automatically positioning itself in a the quadrant diagonally-opposite from the first stone played:





What's Next

Since I'm already in tl;dr - territory with this post I'll finish off the comment object in the next post. I'll put in how I modified the JavaScript SGF parser to pull out the comments and added in a pointer from the comment to the mentioned stone.

1 comment: