November 2008

Layout test — experimenting with Raphael

I’ve been looking for a light-weight, cross-platform Javascript library for doing 2D vector graphics. I specifically wanted vector graphics with a full DOM for handling events and such, vs raster graphics (e.g. canvas). I think I found what I’m looking for in Raphael.js.

As a proof of concept, I wrote a little demo app presented here. It uses jQuery + Raphael for the interface, which is just a simple chip floorplan viewer. The boundaries of the various sections of the chip are drawn as rectilinear polygons (layout.js):

LT.prototype._drawBlock = function(g,block) {
  if ( block.path ) return;

  var linewidth = 1;
  if ( this.scale < 1.0 ) linewidth /= this.scale;
   var p = g.path({stroke:'white', 'stroke-width':linewidth+"px"});
   block.path = p;

  var boundary = block.boundary.split(':');
  for ( var i=0, n=boundary.length; i<n; i++ ) {
    var pt = boundary[i].split(',');
    if ( i == 0 ) p.moveTo(pt[0],pt[1]);
    else p.lineTo(pt[0],pt[1]);
  }
  p.andClose();

  var m = block.m;
  if ( m ) p.matrix(m[0],m[1],m[2],m[3],m[4],m[5]);

  p[0].onmouseover = function() { p.attr({fill:'red'}); };
  p[0].onmouseout = function() { p.attr({fill:''}); };

};

All the block boundaries are given in chip coordinates, so the whole view has to be scaled to fit in the canvas. All the polygons are drawn as a group, and then the group is translated and scaled to fit (layout.js):

LT.prototype.draw = function() {
  this.g = this.paper.group();

  // Calculate dimensions
  this.bbox.w = this.bbox.x2 - this.bbox.x1;
  this.bbox.h = this.bbox.y2 - this.bbox.y1;
  this.elem_w = this.jq.width();
  this.elem_h = this.jq.height();

  this.scale_w = this.bbox.w / this.elem_w;
  this.scale_h = this.bbox.h / this.elem_h;
  this.scale = this.scale_w > this.scale_h ? this.scale_w : this.scale_h;
  this.scale = (1 / this.scale) * 0.90;

  // Draw block outlines
  for ( var i=0, n=this.blocks.length; i<n; i++ ) {
    var block = this.blocks[i];
    this._drawBlock(this.g,block);
  }

  // normalize all drawing to 0,0
  this.g.translate(-this.bbox.x1,-this.bbox.y1);

  // Scale to fit
  this.g.scale ( this.scale, this.scale );

  // center all drawing in the paper
  this.g.translate((this.elem_w - this.bbox.w)*.5,
           (this.elem_h - this.bbox.h)*.5);
};

The polygons are given onmouseover / onmouseout handlers to fill them red when hovered, but that is the extent of the interactivity in this example.

The data for the block boundaries is pulled from mysql on the server side. It is all driven by a CGI script written in ruby. I chose this as a simpler alternative to Rails or Merb or Camping—showing the full stack of operations (database queries, template formatting, etc) without much of the hidden ‘magic’ of those frameworks (index.cgi):

class LayoutTest
  def main
    init
    parse_opts
    connect_to_gallery
    read_blocks_info
    read_layout_info
    output_layout
  rescue => e
    fatals_to_browser e
  end

  protected

  def init
    @cgi = CGI.new('html3')
    print @cgi.header
  end

  def parse_opts
    @block = @cgi['block']
    @block = 'pcore' if @block.empty?
    @parent = @cgi['parent']
  end

  def read_layout_info
    @instance = {}
    @layout = {}

    instances = query("select * from layout_instance " +
                      "where `cell` = '#{@block}'")
    instances.each do |inst|
      @instance[inst['instance']] = inst
    end

    chillins = @children.map{|c| "'#{c}'"}.join(',')
    layouts = query("select * from layout_info " +
                    "where `cell` in ('#{@block}',#{chillins})")
    layouts.each do |lo|
      @layout[lo['cell']] = lo
    end

  end

  def query sql
    rows = []
    sth = @dbi.execute(sql)
    sth.fetch_hash do |row|
      rows << row.dup
    end
    sth.finish
    rows
  end
end

The HTML output is generated using ERB. It loads the necessary javascript libraries (jQuery, Raphael, and my own layout code), then generates the necessary calls to LT.block() to define each block to be shown. The raw block boundary data has to be transformed (translated + rotated / mirrored) to account for placement relative to the containing block—that’s all handled in layout.js). The template (index.html.erb):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <title>layout test: <%= @block %></title>
    <link rel="stylesheet" href="layout.css" type="text/css" charset="utf-8" media="screen,projection"></link>
  </head>
  <body>
    <h1>layout test: <%= @block %></h1>
<% if @parent %>
<a href="?block=<%= @parent %>"><%= @parent %></a>
<% end %>
<span id="info"></span>
<div id="layout">
</div>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="raphael.js"></script>
<script type="text/javascript" src="layout.js"></script>
<script type="text/javascript">
var layout = new LT("layout");
<%= layout_block %>
<% @children.each do |child| %>
<%= layout_child child %>
<% end %>
layout.draw();
</script>
  </body>
</html>

The full code is available here. Not very useful without a database to feed the data, but should serve as a reasonable example:

Uncategorized

Comments (2)

Permalink

SMTP port 25 being blocked by my ISP?

Argh. I just discovered that all my out-going email (and my wifes) from the past several days was not delivered. Debugging the issue took a bit of patience, here’s the summary.

All outgoing mail in my home network is relayed to my hosting provider, Dreamhost.com. I use exim4 as the mail-transfer agent, configured via the remote_smtp_smarthost option. It’s been working just fine that way for years…

Well, it looks like my ISP (Comcast) recently decided to start blocking SMTP (port 25) traffic, so all of a sudden, my home network can no longer talk to the SMTP server at Dreamhost.com. Unfortunately, I did not find that out for several days, after Kim and I both received a bunch of “Mail delivery failed messages” like this:

This message was created automatically by mail delivery software.

A message that you sent could not be delivered to one or more of its
recipients. This is a permanent error. The following address(es) failed:

  somebody@yahoo.com
    retry time not reached for any host after a long failure period

After a bit of googling and experimenting with some different exim4 config options, I found this solution, suggesting that SMTP traffic should be sent on an alternate port that Dreamhost set up, apparently for exactly this purpose.

I’m actually not using the ‘split’ config file option for exim4, so I added the port=587 line to my exim4.conf.template instead. Email finally seems to be working again…

Uncategorized

Comments (0)

Permalink

yology.org

I just started another site: http://yology.org/

This is intended to be a set of tools for online genealogy—primarily for publishing genealogies and encouraging collaboration. It is also intended to be open-source.

To start off, I’ll mostly just be writing thoughts and plans via the blog

Uncategorized

Comments (2)

Permalink

Sxipper 3.0 demo

At the November Denver/Boulder New Tech Meetup Dick Hardt demo’d a prototype of sxipper 3.0.

Sxipper leverages your social graph (Facebook, LinkedIn, etc), and shows how it ties in to the larger web in general. For example, he showed how you might browse to a company’s site, and skipper will call out to you that someone in your graph (friend or friend of friend) is somehow connected to the site. Very cool concept.

Seeing Dick also reminded me of seeing his Identity 2.0 presentation at OSCON’05—one of the best presentations I’ve ever seen, in terms of his style. Perhaps dated now, but worth checking out.

Uncategorized

Comments (0)

Permalink