2011-04-30 Map Drawing Using Polygons

I’m currently working on randomly generating islands using the ideas presented in Polygonal Map Generation by Amit. Check out his Flash demo! I am nowhere as far, yet. I’m writing my code in Perl and producing SVG output.

See below for source code used. I’d install it on a public server, but unfortunately there are quite some dependencies…


#! /usr/bin/perl -w
# Copyright (C) 2011  Alex Schroeder <>
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <>.

use strict;
use CGI qw(:standard);
use SVG;
use Math::Geometry::Voronoi;
use Class::Struct;
use Math::Fractal::Noisemaker;
use List::Util qw(min max);
use Data::Dumper;

my $points = 3000;
my $width  = 1000;
my $height =  550;
my $center_x = $width / 2;
my $center_y = $height / 2;
my $radius = 500;

my %color = (beach => '#a09077',
	     ocean => '#44447a',);

struct World => { points => '@',
		  centroids => '@',
		  voronoi => '$',
		  height => '@',

sub add_random_points {
  my ($world) = @_;
  for (my $i = 0; $i < $points; $i++) {
    push(@{$world->points}, [rand($width), rand($height)]);
  # print(join("\n", map {join(",", $_->[0], $_->[1])} @{$world->points}));
  return $world;

sub add_voronoi {
  my ($world) = @_;
  $world->voronoi(Math::Geometry::Voronoi->new(points => $world->points));

sub add_centroids {
  my ($world) = @_;
  $world->centroids([]); # clear
  foreach my $polygon ($world->voronoi->polygons) {
    push(@{$world->centroids}, centroid($polygon));

sub centroid {
  my ($cx, $cy) = (0, 0);
  my $A = 0;
  my $polygon = shift;
  my ($point_index, @points) = @$polygon; # see Math::Geometry::Voronoi
  my $point = $points[$#points];
  my ($x0, $y0) = ($point->[0], $point->[1]);
  for $point (@points) {
    my ($x1, $y1) = ($point->[0], $point->[1]);
    $cx += ($x0 + $x1) * ($x0 * $y1 - $x1 * $y0);
    $cy += ($y0 + $y1) * ($x0 * $y1 - $x1 * $y0);
    $A += ($x0 * $y1 - $x1 * $y0);
    ($x0, $y0) = ($x1, $y1);
  $A /= 2;
  $cx /= 6 * $A;
  $cy /= 6 * $A;
  return [$cx, $cy, $point_index];

sub add_height {
  my $world = shift;
  $Math::Fractal::Noisemaker::QUIET = 1;
  my $grid = Math::Fractal::Noisemaker::square();
  $world->height([]); # clear
  my $scale = max($height, $width); # grid is a square
  foreach my $point (@{$world->points}) {
    my $x = int($point->[0]*255/$scale);
    my $y = int($point->[1]*255/$scale);
    my $h = 0; # we must not skip any points!
    $h = $grid->[$x]->get($y) / 255
      unless $x < 0 or $y < 0 or $x > 255 or $y > 255;
    push(@{$world->height}, $h);

sub raise_point {
  my ($world, $x, $y, $radius) = @_;
  my $i = 0;
  foreach my $point (@{$world->points}) {
    my $dx = $point->[0] - $x;
    my $dy = $point->[1] - $y;
    my $d = sqrt($dx * $dx + $dy * $dy);
    my $v = max(0, $world->height->[$i] - $d / $radius);
    $world->height($i, $v);

sub svg {
  my $world = shift;
  my $svg = new SVG(-width => $width,
		    -height => $height, );
  foreach my $polygon ($world->voronoi->polygons) {
    my ($point_index, @points) = @$polygon; # see Math::Geometry::Voronoi
    my $x = $world->points->[$point_index]->[0];
    my $y = $world->points->[$point_index]->[1];
    next if $x < 0 or $y < 0 or $x > $width or $y > $height;
    my $z = int($world->height->[$point_index] * 255);
    my $color = $z == 0 ? $color{ocean} : "rgb($z,$z,$z)";
    my $path = join(",", map { map { int } @$_ } @points);
    $svg->polygon(points => $path,
		  fill => $color,
		  style => { 'stroke-width' => 1,
			     'stroke' => 'black'});
  return $svg->xmlify();

sub response {
  print header(-type=>'image/svg+xml');
  print shift;

sub main {
  if (path_info eq '/source') {
    seek DATA, 0, 0;
    print "Content-type: text/plain; charset=UTF-8\r\n\r\n", <DATA>;
  } else {
    srand(param('seed') || time);
    my $world = new World;
    add_random_points($world, $points);
    for (my $i = 2; $i--; ) {
      # Lloyd Relaxation
    # skip corner improvement
    # skip Delaunay triangulation
    raise_point($world, $center_x, $center_y, $radius);
    # draw

main ();


Add Comment

2010-05-10 Javascript

I still haven’t learnt Javascript. Right now I’m trying to integrate svg-edit and Oddmuse. This requires some Javascript voodoo that escapes me.

If you upload a SVG file to, you’ll be able to look at it. I’m using an iframe to do that.

If you then click on Edit this page and then on Edit image in the browser, you’ll get the SVG editor.

What I’m lacking is loading and saving the image!

If you look around the svg-edit project’s pages, you’ll note example code like the following:


What I don’t understand is where this code runs and how svgEditor is set.

If I simply add the following to the top of the page, inside the header, then Firebug will tell me that the svgEditor object does not exist.

    <script type="text/javascript">

What now? Is this a variable “inside” the iframe? Is this something I need to initialize by calling a function? Which one?

Update: Perhaps I’m running into some cross-site scripting protection. The site runs on, the SVG editor runs on Using Firebug, I run document.getElementsByName('svgEditor')[0].getSVGDocument().setUserData('fd') and get an error message. warning


Comments on 2010-05-10 Javascript

This site is great!

– Kate 2010-05-13 10:38 UTC

It looks like you’re not including the javascript library that provides the SVG editing functions.

Harald 2010-05-13 12:32 UTC

Hah, I’m seeing the light. Copying the script over to localhost has fixed this. I’ve come a long way. Now I need MIME encoding in Javascript, and probably a cool Save dialog – or a little rewrite.

AlexSchroeder 2010-05-13 20:41 UTC

AlexSchroeder 2010-05-13 23:20 UTC

Add Comment

2009-09-19 SVG, CSS, and Inkscape

I have a problem with Inkscape and I think it’s related to the handling or loss of CSS information. I have a script that creates an SVG map which looks fine in Firefox – check it out and hit the submit button.

When I import that SVG into another SVG in order to add more layers, everything looks more or less ok. One thing that’s already wrong is that the hex coordinates are missing. When I save the file, close Inkscape, open Inkscape again, and reopen the new file, things are messed up:

  1. Coordinates are still missing
  2. Font sizes are wrong for the coordinates
  3. Symbols (text elements) around planets have some sort of white border

Do you think Inkscape will have an easier time handling the SVG if I don’t use CSS to specificy stroke width, opacity, and the like?

I’m using Inkscape 0.46 at the moment.


Add Comment

2009-09-11 Traveller Map Based on UWP

There is an amazing Traveller Map API available out there. Specifically, there’s a service that will produce a PDF based on your UWP list. But you know how it is. I bet half the Traveller referees are trying to code up subsector generators and mappers. Me too!

Here’s my SVG Mapper for Traveller. It comes with example data so you can figure out what format it expects by looking at it. Just click the Submit button! Remember, you need a browser that knows how to deal with SVG files – Firefox will do.

Source code is available. I got to reuse some of my abandoned Old School Hex Mapper. And figuring out the trigonometry stuff is fun. :)


Comments on 2009-09-11 Traveller Map Based on UWP

Very nice! :-)

It looks very nice, and has a simple interface. This is a very good tool to quickly make a subsector.

You are very generous to release this with the GNU General Public License. This means that I might use it together with my random subsectror generator (that is also written in perl).

I have been thinking of doing my own mapper, (other than the ascii one I have made). My plan was to use GD or ImageMagick.

BeRKA 2009-09-11 05:51 UTC

Heh. I come from a FreeSoftware background. Sharing’s the ethical thing to do. ;)

Is your random subsector generator based on the Mongoose rules, or are they the same as for previous editions? I noticed that the generator I had used for my subsector produced strange trade codes and had to write a Perl script to fix them.

Also, Mongoose Traveller doesn’t say how UWP codes for the various based look like. They just provide Scout and Navy as examples. Do you know of a site that defines it?

AlexSchroeder 2009-09-11 07:21 UTC

My generator is based on previous editions? You can select to use book-3 or book-6 rules, that are slightly different when calculating hydrographics. My trade codes are from book-7.

Base codes can differ depending on the version you use. One common version is found on traveller map.

For Naval and Scout base “2” is also commonly used instead of “A”.

BeRKA 2009-09-11 08:25 UTC

Oh wow. Thanks for the link. This was new to me: “special codes deal with the presence of more than one type of base within the same system in order to maintain a single base code letter per system.”

I think I’m going to focus on the rules I have or I’ll go mad. :)

AlexSchroeder 2009-09-11 08:36 UTC


greywulf 2009-09-11 09:35 UTC

Add Comment

2008-09-12 Corporate Wikis

There was a lot of talk about corporate wikis, measuring corporate wiki participation, and some wikis “competing” for use in the corporate area.

Oddmuse is definitely not a “corporate wiki” in this respect. I think the problem is the process of decision making: Someone starts with a list of features and goes to collect more items for that list by talking to other “stakeholders” in the company. This is what you end up with:

Corporate wikis are a waste of time

Yeah, I’m being mean. :)

Damn, I forgot WYSIWYG, blog integration, CMS, mailing list integration, bug tracking system integration, calendar integration, … there’s so much out there!


Comments on 2008-09-12 Corporate Wikis

You’re clearly right. I experienced similar behaviour in the corporate world. They are coming with a monster (sometime needing a Java client or worst a Win32 fat client) to meet all the requirements from the “stakeholders”… and at the end, no one is using the system due its complexity. Contribution must be simple and easy. Why starting a Java/large application to update one single line in a documentation? That’s inefficient and frustrating (you contribute one time… and after you avoid to use the monster). It will take time before corporation fully understand that Internet-style and minimalist Wikis is the way to go.

‘’A good Wiki is a Wiki where you forget about its user interface when you are using it’’.

Alexandre Dulaunoy 2008-09-13 09:28 UTC

All we need to figure out now is how to communicat our message to decision makers. :)

AlexSchroeder 2008-09-14 00:38 UTC

Maybe we could somehow adapt RITA for this purpose?

RadomirDopieralski 2008-09-21 22:30 UTC

Add Comment