31 January 2009

Homer vs The Bible

Many years ago I engaged in a weekly debate with some Christians on the Lewis Trilemma - the liar/lunatic/son of god question. Both sides fought nobly and I came out at the end of it with my atheism reinforced. The Christians (who were all charming people btw) probably came out with their religiosity reinforced, so it was pretty zero-sum in the end.

Many, many arguments were advanced by the Christian side to support their beliefs, but by the end of the year their reasoning boiled entirely down to this: When I read the Bible, I hear the Voice of God speaking to me.

That's all. The final, irreducible argument. It doesn't leave much hope for people, like me, with a little god-deafness problem. It also doesn't leave much hope for them, if they happen to hear the Voice of God saying different things to each of them, as appears to happen a little too often. The rascal!

Anyway, along the way we had some entertaining digressions. For example, if you consider ancient Greek texts, Homer's Iliad, or the New Testament, you will notice that there are only a few extant ancient copies of Homer, with a large number and variety of errors. The New Testament, on the other hand, has a larger number of extant ancient copies, with far fewer transcription errors. This, apparently, demonstrates the accuracy and reliability of the New Testament. See http://www.clemson.edu/spurgeon/books/apology/Chapter4.html for an example of this kind of reasoning.

Homer was around a long time before the NT, and his/her/their works were primarily transmitted orally. The surviving documents are older, so it is unsurprising that there are fewer of them. Most of all, part of the fun of reciting a good story is embellishing it, and the early transcribers of Homer doubtless succumbed to this desire. The transcribers of the Good News however had entirely different motives and risked hell were their labours imperfect.

In other words, both Homer and the Bible come with a Creative-Commons license - but the Bible invokes the "No Derivative Works" clause. It doesn't make the Bible right any more than a commercial license makes a software product superior.

26 January 2009

How to draw an ellipse

Pretty much every drawing tool and library comes with an ellipse-drawing function, so you never even need to think about how it's done. Until, one day, you're the one writing the drawing tool.

Formulae and sample code abound for calculating the outline of an ellipse. The problem is when you try to draw a nice-looking ellipse, the edge needs to be smooth, and this means half-colouring some of the pixels at the edge.

The first thing is to remember that a pixel doesn't represent a point, it represents a square. So there are two things to calculate: for any given ideal euclidean point, whether that point is inside the ellipse; and for any given square, its "insideness", ie how much of that square is covered by the ellipse. So a pixel is drawn 100% opaque if it is completely inside the ellipse, and 100% transparent if it is completely outside. Otherwise, it is drawn with a transparency proportional to its insideness.

So, if all four corners of a pixel are "inside" the ellipse, consider the pixel is 100% covered by the ellipse. If all four corners are not "inside", consider the pixel 0% covered. Otherwise, subdivide the pixel into four sub-pixels, and calculate the insideness of each sub-pixel, recursively. The percent coverage for any given pixel is the average coverage for all its sub-pixels. Below a certain threshold, don't recurse; just use the values for the corner points.

To calculate whether a single point is inside the ellipse, use this function:


/**
 * determines whether a point p,q is inside an ellipse
 * specified by (x,y,a,b)
 *
 * returns 1 if inside, 0 if outside
 *
 * @param x x-coordinate of centre of ellipse
 * @param y y-coordinate of centre of ellipse
 * @param a horizonatal radius
 * @param b vertical radius
 * @param p x-coordinate of point to consider
 * @param q y-coordinate of point to consider
 */
insideEllipse = function(x, y, a, b, p, q) {
  var dx = (p - x) / a;
  var dy = (q - y) / b;
  var distance = dx * dx + dy * dy;
  return (distance < 1.0) ? 1 : 0;
};

To calculate the insideness of a square area, use this:


/**
 * determines what proportion of a square (p,q) - (p+side, q+side) is covered by this
 * ellipse. If side < threshold, returns an approximate result.
 *
 * returns: a value in the range [0.0, 1.0]
 *
 * @param p x-coordinate of point to consider
 * @param q y-coordinate of point to consider
 * @param side the length of the edge of the square to sample
 * @param threshold do not recurse if side is less than this value
 */
insideness = function(x, y, a, b, p, q, side, threshold) {
  var i1 = insideEllipse(x, y, a, b, p, q);
  var i2 = insideEllipse(x, y, a, b, p + side, q);
  var i3 = insideEllipse(x, y, a, b, p + side, q + side);
  var i4 = insideEllipse(x, y, a, b, p, q + side);
  var total = i1 + i2 + i3 + i4;
  if (total == 4 || total == 0 || side < threshold) {
    return total / 4.0;
  }

  side = side / 2;
  var j1 = insideness(x, y, a, b, p, q, side, threshold);
  var j2 = insideness(x, y, a, b, p + side, q, side, threshold);
  var j3 = insideness(x, y, a, b, p + side, q + side, side, threshold);
  var j4 = insideness(x, y, a, b, p, q + side, side, threshold);
  return (j1 + j2 + j3 + j4) / 4.0;
};

Enjoy.

20 January 2009

Parisharing

If you live in Paris, or would like to visit Paris, you might be interested in Parisharing, a radical new authentic tourism concept. It's still new, and you can help by taking the relevant survey:

From the site:

30 million people dream of visiting Paris, if only for a few days. 30 thousand who live in Paris dream of traveling elsewhere, if only for a few days.

PariSharing is the meeting of those dreams.

If you hope to visit Paris, this will be your chance to rent a real Parisian apartment at an unbeatable price while enjoying an authentic Parisian lifestyle.

If you live in Paris, this is your chance to increase your earnings and offer yourself a nicer or longer vacation.

There you have it - Parisharing will offer visitors an authentic and affordable experience of Paris, and at the same give residents of Paris an additional incentive to leave the city for vacation. Is that a win-win situation or what?

How Do You Find Me?

When you hit google's cache of a page it conveniently highlights all of your search terms. Some web sites have this neat trick of highlighting your search terms when you've come from a search, even though you're not in google any more. The "referer" (sic) http header gives them the necessary information. Thanks to the amazing technology presented in this article, you, too, can do the same thing and dazzle your visitors.

On top of that, if you're not using Google Analytics, you will surely want to know what search terms people are using to get to your site. Why not Google Analytics? Because They Know Too Much Already!

Here's a lump of java code you can stick into your web app for extracting google search term information. There are three important methods:

public String getSearchTerm(HttpServletRequest req) - return the URL-decoded search term. Given a referrer http://google.com/search?q=awesome+icon+editor, return "awesome icon editor". This String is what you would use for highlighting content on your page to give visitors the creepy feeling that you know what they're thinking.

public boolean isSearching(HttpServletRequest req) - true if getSearchTerm returns a non-empty String

public static Object[] google(String referrer) - returns an array of length 2; google()[0] is the same as getSearchTerm; google()[1] is the search results page number. This tells you how many times your user clicked "next" on google's search results before they got to your site. This is useful information - it tells you how desperately your user wants your app, and how irrelevant google considers your site. Yes, the truth hurts, but you need to know before you can do anything about it. I'm sorry. The page number comes from the "start" parameter. So, with this referrer string http://google.com/search?q=awesome+icon+editor&start=80, the page number would be 8.

If you're using Freemarker, you can bind this object to a global variable, so unless you abhor globalisation you will know directly within your template whether you're being googled.

So, the code. Free for private and commercial use. Don't be afraid to link back here. Enjoy.

ExternalSearchHelper.java

/*
  copyright conan dalton 2009, license http://creativecommons.org/licenses/by-sa/3.0/ 
*/
import org.apache.commons.lang.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.net.URLDecoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ExternalSearchHelper {
  static final Pattern itsGoogle = Pattern.compile("http://[^/]*google[^/]+/.*[&\\?]q=([^&]+).*");
  static final Pattern itsGoogle2 = Pattern.compile("http://[^/]*google[^/]+/.*[&\\?]q=([^&]+).*&start=([^&]+).*");

  public String getSearchTerm(HttpServletRequest req) {
    String referrer = req.getHeader("Referer");
    if (referrer == null || referrer.length() == 0) {
      return "";
    }

    return (String) google(referrer)[0];
  }

  public boolean isSearching(HttpServletRequest req) {
    // re-implement this if you don't want to depend on apache-commons
    return StringUtils.isNotBlank(getSearchTerm(req)); 
  }

  public static Object[] google(String referrer) {
    Object[] result = new Object[2];
    if (referrer == null || referrer.length() == 0) {
      return result;
    }

    Matcher m2 = itsGoogle2.matcher(referrer);
    if (m2.matches()) {
      result[0] = decode(m2.group(1));
      result[1] = new Integer(Integer.parseInt(m2.group(2)) / 10);
      return result;
    }

    Matcher m = itsGoogle.matcher(referrer);
    if (m.matches()) {
      result[0] = decode(m.group(1));
      result[1] = 0;
    }

    System.out.println("search term " + result[0]);
    return result;
  }

  private static String decode(String s) {
    return URLDecoder.decode(s);
  }
}

15 January 2009

On the Latest Intellij

I downloaded Intellij 8 the other day and as usual it was full of wonderful goodies:

  • Freemarker support, at last - syntax colouring, and freemarker stacktrace navigation from the console: awesome! [UPDATE] and navigation between templates included with the #include directive
  • Git support, at last!
  • Better javascript syntax highlighting
  • Hibernate: uses reverse-data-flow to find some strings which will become HQL queries, and highlights appropriately. Wow. I mean: wow, that must have been hard work, especially for something that isn't really all that essential ...
  • Little "close" button on each tab (the way web browsers do it), so I don't have to right-click and choose "close tab" any more. This is great. One of those little details.
  • Recognises associated source code when I update a dependency and its source - which I do a lot because some code for some projects is split into a separate project. Previously it would get all confused and not find the source any more.
I'm sure there's heaps more stuff, this is just what I noticed from the subset of features I use daily. Still missing:
  • Option to silence "Open in new frame" dialog when opening a new project. This is really annoying: most annoying dialogs come with a "don't ask this again" option - but not this one. Go away, pesky dialog!
Anyway, bravo, jetbrains.