Skip to content

Commit

Permalink
Get release ready for CPAN
Browse files Browse the repository at this point in the history
  • Loading branch information
Max Maischein committed Jun 4, 2011
1 parent dca1632 commit d48428e
Show file tree
Hide file tree
Showing 12 changed files with 437 additions and 9 deletions.
4 changes: 4 additions & 0 deletions Changes
@@ -0,0 +1,4 @@
TO DO:

0.01 20110603
. Released on an unsuspecting world
16 changes: 16 additions & 0 deletions MANIFEST
@@ -0,0 +1,16 @@
.gitignore
bin/random-poissondisc.pl
Changes
lib/Random/PoissonDisc.pm
Makefile.PL
MANIFEST This list of files
MANIFEST.skip
README
t/01-random-unit-vector.t
t/99-changes.t
t/99-examples.t
t/99-manifest.t
t/99-pod.t
t/99-todo.t
t/99-unix-text.t
t/99-versions.t
17 changes: 17 additions & 0 deletions MANIFEST.skip
@@ -0,0 +1,17 @@
.cvsignore$
^.git/
^.lwpcookies
^.releaserc
^blib/
^Random-PoissonDisc-.*
CVS/
^pm_to_blib
.tar.gz$
.old$
^Makefile$
^cvstest$
^blibdirs$
.bak$
^cover_db/
^db/
^documents/
121 changes: 121 additions & 0 deletions README
@@ -0,0 +1,121 @@
NAME
Random::PoissonDisc - distribute points aesthetically in R^n

SYNOPSIS
my $points = Random::PoissonDisc->points(
dimensions => [100,100],
r => $r,
);
print join( ",", @$_),"\n"
for @$points;

This module allows relatively fast (O(N)) generation of random points in
*n*-dimensional space with a distance of at least `r' between each
other. This distribution results in aesthetic so called "blue noise".

The algorithm was adapted from a sketch by Robert Bridson in
http://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf.

DATA REPRESENTATION
All vectors (or points) are represented as anonymous arrays of numbers.
All have the same dimension as the cardinality of the `dimensions' array
passed in the `->points' method.

USER INTERFACE
`Random::PoissonDisc->points( %options )'
Returns a reference to an array of points.

Acceptable options are:

* `<r'> - minimum distance between points.

Default is 10 units.

* `<dimensions'> - number of dimensions and respective value range as
an arrayref.

Default is

[ 100, 100 ]

meaning all points will be in R^2 , with each coordinate in the
range [0, 100).

* `<candidates'> - Number of candidates to inspect before deciding
that no ew neighbours can be placed around a point.

Default is 30.

This number may or may not need to be tweaked if you go further up
in dimensionality beyond 3 dimensions. The more candidates you
inspect the longer the algorithm will run for generating a number of
points.

In the algorithm description, this constant is named *k*.

INTERNAL SUBROUTINES
These subroutines are used for the algorithm. If you want to port this
module to PDL or any other vector library, you will likely have to
rewrite these.

`rnd( $low, $high )'
print rnd( 0, 1 );

Returns a uniform distributed random number in `[ $low, $high )'.

`grid_coords( $grid_size, $point )'
Returns the string representing the coordinates of the grid cell in
which `$point' falls.

`norm( @vector )'
print norm( 1,1 ); # 1.4142

Returns the Euclidean length of the vector, passed in as array.

`vdist( $l, $r )'
print vdist( [1,0], [0,1] ); # 1.4142

Returns the Euclidean distance between two points (or vectors)

`neighbour_points( $size, $point, $grid )'
my @neighbours = neighbour_points( $size, $p, \%grid )

Returns the points from the grid that have a distance between 0 and 2r
around `$point'. These points are the candidates to check when trying to
insert a new random point into the space.

`random_unit_vector( $dimensions )'
print join ",", @{ random_unit_vector( 2 ) };

Returns a vector of unit lenght poiting in a random uniform distributed
*n*-dimensional direction angle and returns a unit vector pointing in
that direction

The algorithm used is outlined in Knuth, _The Art of Computer
Programming_, vol. 2, 3rd. ed., section 3.4.1.E.6. but has not been
verified formally or mathematically by the module author.

TODO
The module does not use PDL or any other vector library.

REPOSITORY
The public repository of this module is
http://github.com/Corion/random-poissondisc.

SUPPORT
The public support forum of this module is http://perlmonks.org/.

BUG TRACKER
Please report bugs in this module via the RT CPAN bug queue at
https://rt.cpan.org/Public/Dist/Display.html?Name=Random-PoissonDisc or
via mail to random-poissondisc@rt.cpan.org.

AUTHOR
Max Maischein `corion@cpan.org'

COPYRIGHT (c)
Copyright 2011 by Max Maischein `corion@cpan.org'.

LICENSE
This module is released under the same terms as Perl itself.

44 changes: 35 additions & 9 deletions lib/Random/PoissonDisc.pm
Expand Up @@ -85,26 +85,24 @@ In the algorithm description, this constant is named I<k>.

sub points {
my ($class,%options) = @_;
# XXX Allow the grid to be passed in

$options{candidates} ||= 30;
$options{dimensions} ||= [100,100]; # do we only create integral points?
$options{r} ||= 10;
#$options{max} ||= 10; # we want to fill the space instead?!
#$options{max} ||= 10; # we want to fill the space instead?!
$options{ grid } ||= {};

my $grid_size = $options{ r } / sqrt( 0+@{$options{dimensions}});

my @result;
my @work;
my %grid; # well, a fakey grid, but as long as we use only integer
# coordinates for the grid, using a hash and normalized point coordinates is convenient

# Create a first random point somewhere in our cube:
my $p = [map { rnd(0,$_) } @{ $options{ dimensions }}];
push @result, $p;
push @work, $p;
my $c = grid_coords($grid_size, $p);
$grid{ $c } = $p;
$options{ grid }->{ $c } = $p;

while (@work) {
my $origin = splice @work, int rnd(0,$#work), 1;
Expand All @@ -131,8 +129,8 @@ sub points {
# check discs by using the grid
# Here we should check the "neighbours" in the grid too
my $c = grid_coords($grid_size, $p);
if (! $grid{ $c }) {
my @n = neighbour_points($grid_size, $p, \%grid);
if (! $options{ grid }->{ $c }) {
my @n = neighbour_points($grid_size, $p, $options{ grid });
for my $neighbour (@n) {
if( vdist($neighbour, $p) < $options{ r }) {
next CANDIDATE;
Expand All @@ -142,7 +140,7 @@ sub points {
# not already in grid, no close neighbours, add it
push @result, $p;
push @work, $p;
$grid{ $c } = $p;
$options{ grid }->{ $c } = $p;
#warn "$candidate Taking";
} else {
#warn "$candidate Occupied";
Expand Down Expand Up @@ -297,4 +295,32 @@ sub random_unit_vector {
The module does not use L<PDL> or any other
vector library.
=cut
=head1 REPOSITORY
The public repository of this module is
L<http://github.com/Corion/random-poissondisc>.
=head1 SUPPORT
The public support forum of this module is
L<http://perlmonks.org/>.
=head1 BUG TRACKER
Please report bugs in this module via the RT CPAN bug queue at
L<https://rt.cpan.org/Public/Dist/Display.html?Name=Random-PoissonDisc>
or via mail to L<random-poissondisc@rt.cpan.org>.
=head1 AUTHOR
Max Maischein C<corion@cpan.org>
=head1 COPYRIGHT (c)
Copyright 2011 by Max Maischein C<corion@cpan.org>.
=head1 LICENSE
This module is released under the same terms as Perl itself.
=cut
28 changes: 28 additions & 0 deletions t/99-changes.t
@@ -0,0 +1,28 @@
#!perl -w
use warnings;
use strict;
use File::Find;
use Test::More tests => 2;

=head1 PURPOSE
This test ensures that the Changes file
mentions the current version and that a
release date is mentioned as well
=cut

my $module = 'WWW::Mechanize::Firefox';

(my $file = $module) =~ s!::!/!g;
require "$file.pm";

my $version = sprintf '%0.2f', $module->VERSION;
diag "Checking for version " . $version;

my $changes = do { local $/; open my $fh, 'Changes' or die $!; <$fh> };

ok $changes =~ /^(.*$version.*)$/m, "We find version $version";
my $changes_line = $1;
ok $changes_line =~ /$version\s+20\d{6}/, "We find a release date on the same line"
or diag $changes_line;
16 changes: 16 additions & 0 deletions t/99-examples.t
@@ -0,0 +1,16 @@
#!perl -w

use warnings;
use strict;
use Test::More;
use File::Find;

plan 'no_plan';

sub check {
return if (! m{\.pl \z}xms);
my $output = `"$^X" -c $_ 2>&1`;
like( $output, qr/$_ syntax OK/, "$_ compiles" )
}

find({wanted => \&check, no_chdir => 1}, 'examples');
31 changes: 31 additions & 0 deletions t/99-manifest.t
@@ -0,0 +1,31 @@
use strict;
use Test::More;

# Check that MANIFEST and MANIFEST.skip are sane :

use File::Find;
use File::Spec;

my @files = qw( MANIFEST MANIFEST.skip );
plan tests => scalar @files * 4
+1 # MANIFEST existence check
;

for my $file (@files) {
ok(-f $file, "$file exists");
open F, "<$file"
or die "Couldn't open $file : $!";
my @lines = <F>;
is_deeply([grep(/^$/, @lines)],[], "No empty lines in $file");
is_deeply([grep(/^\s+$/, @lines)],[], "No whitespace-only lines in $file");
is_deeply([grep(/^\s*\S\s+$/, @lines)],[],"No trailing whitespace on lines in $file");

if ($file eq 'MANIFEST') {
chomp @lines;
is_deeply([grep { s/\s.*//; ! -f } @lines], [], "All files in $file exist")
or do { diag "$_ is mentioned in $file but doesn't exist on disk" for grep { ! -f } @lines };
};

close F;
};

36 changes: 36 additions & 0 deletions t/99-pod.t
@@ -0,0 +1,36 @@
use Test::More;

# Check our Pod
# The test was provided by Andy Lester,
# who stole it from Brian D. Foy
# Thanks to both !

use File::Spec;
use File::Find;
use strict;

eval {
require Test::Pod;
Test::Pod->import;
};

my @files;

if ($@) {
plan skip_all => "Test::Pod required for testing POD";
}
elsif ($Test::Pod::VERSION < 0.95) {
plan skip_all => "Test::Pod 0.95 required for testing POD";
}
else {
my $blib = File::Spec->catfile(qw(blib lib));
find(\&wanted, grep { -d } ($blib, 'bin'));
plan tests => scalar @files;
foreach my $file (@files) {
pod_file_ok($file);
}
}

sub wanted {
push @files, $File::Find::name if /\.p(l|m|od)$/;
}

0 comments on commit d48428e

Please sign in to comment.