Graph-Layout-Aesthetic

 view release on metacpan or  search on metacpan

Aesthetic.xs  view on Meta::CPAN


    state->forces = 0;
    state->dimensions = nr_dimensions;
    state->graph = gr;
    state->graph_sv = newRV(topology);
    /* so it's out of sequence with things that are 0 */
    state->sequence = 1;

    state->temperature = 1e2;
    state->end_temperature = 1e-3;
    state->iterations = 1000;

    state->paused = 0;

    RETVAL = NEWSV(1, 0);
    sv_setref_pv(RETVAL, class, (void*) state);
  OUTPUT:
    RETVAL

void
paused(aglo_state state, aglo_boolean new_paused=0)

Aesthetic.xs  view on Meta::CPAN

            av_push(av, force->force_sv);
            av_push(av, newSVnv(force->weight));
        }
        break;
      default:
        break;
    }


void
init_gloss(aglo_state state, aglo_real temperature, aglo_real end_temperature, aglo_signed iterations, aglo_real randomize_size=1)
  PPCODE:
    if (temperature <= 0)
        croak("Temperature %"NVff" should be > 0", (NV) temperature);
    if (end_temperature <= 0)
        croak("End_temperature %"NVff" should be > 0", (NV) end_temperature);
    if (temperature < end_temperature)
        warn("Temperature %"NVff" should probably be >= end_temperature %"NVff,
              (NV) temperature, (NV) end_temperature);
    if (iterations < 0)
        croak("Iterations %"IVdf" should be >= 0", (IV) iterations);
    state->temperature = temperature;
    state->end_temperature = end_temperature;
    state->iterations = iterations;
    if (randomize_size > 0) aglo_randomize(aTHX_ state, randomize_size);

void
_gloss(aglo_state state, aglo_real pause_time=1e50)
  PREINIT:
    time_t now;
    aglo_real lambda;
  PPCODE:
    if (state->iterations <= 0) croak("No more iterations left");
    lambda = pow(state->temperature / state->end_temperature, 1.0 / state->iterations);
    state->paused = 0;
    while (state->iterations > 0 && !state->paused) {
        aglo_step(aTHX_ state, state->temperature,
                  state->temperature < 1e-5 ? state->temperature : 1e-5);

        state->temperature /= lambda;
        state->iterations--;

        if (items > 1) {
            time(&now);
            if (pause_time <= now) break;
        }
    }
    /* We lose the value anyways on exception in gradient,
       but restore is just a nicity, not part of the API */

void

Aesthetic.xs  view on Meta::CPAN


aglo_real
stress(aglo_state state)
  CODE:
    calculate_aesth_forces(aTHX_ state);
    RETVAL = aglo_point_mag(state->dimensions*state->graph->vertices, state->gradient);
  OUTPUT:
    RETVAL

aglo_unsigned
iterations(aglo_state state, aglo_signed iterations=0)
  CODE:
    RETVAL = state->iterations;
    if (items > 1) {
        if (iterations < 0)
            croak("Iterations %"IVdf" should be >= 0", (IV) iterations);
        state->iterations = iterations;
    }
  OUTPUT:
    RETVAL

SV *
topology(aglo_state state)
  CODE:
    SvREFCNT_inc(state->graph_sv);
    RETVAL = state->graph_sv;
  OUTPUT:

SP+E.paper.ps  view on Meta::CPAN

np 189 557 12 0.00 360.00 arc fil 0 setgray np 189 557 12 0.00
360.00 arc st 3 setlinewidth 0.500 setgray np 226 388 12 0.00
360.00 arc fil 0 setgray np 226 388 12 0.00 360.00 arc st 123
898 a Fb(-knr)18 b(1)h(-kmel)f(1)h(-bt)g(25)123 948 y Ft(2.1)26
b(No)n(de)13 b(r)n(epulsion)e(and)h(e)n(dge)h(minimization)25
b(\(0.46s\))p 1124 211 892 2 v 1123 260 2 50 v 1149 245 a Fm(ar)n(gument)p
1357 260 V 50 w(c)n(o)n(oling)15 b(sche)n(dule)g(c)n(ontr)n(ol)p
2014 260 V 1124 261 892 2 v 1123 309 2 50 v 1149 295 a Fc(-bt)p
1357 309 V 162 w Fn(b)q(eginning)i(temp)q(erature)p 2014 309
V 1123 359 V 1149 344 a Fc(-it)p 1357 359 V 162 w Fn(n)o(um)o(b)q(er)e(of)g
(iterations)p 2014 359 V 1124 361 892 2 v 1124 419 V 1123 467
2 50 v 1149 452 a Fm(ar)n(gument)p 1357 467 V 50 w(aesthetic)h(function)p
2014 467 V 1124 469 892 2 v 1123 517 2 50 v 1149 502 a Fc(-knr)p
1357 517 V 138 w Fn(no)q(de/no)q(de)g(repulsion)p 2014 517
V 1123 567 V 1149 552 a Fc(-kner)p 1357 567 V 114 w Fn(no)q(de/edge)f
(repulsion)p 2014 567 V 1123 617 V 1149 602 a Fc(-kcp)p 1357
617 V 138 w Fn(cen)o(trip)q(etal)h(repulsion)p 2014 617 V 1123
666 V 1149 651 a Fc(-kmel)p 1357 666 V 114 w Fn(edge)f(length)h(minimization)
p 2014 666 V 1123 716 V 1149 701 a Fc(-kmei)p 1357 716 V 114
w Fn(edge)f(in)o(tersection)h(minimization)p 2014 716 V 1124
718 892 2 v 1149 776 a Fm(the)g(numb)n(er)g(fol)r(lowing)g(e)n(ach)g(such)h

SP+E.paper.ps  view on Meta::CPAN

Fk(\))36 b Fs( )924 797 y Ff(\024)961 830 y Fk(\001\()p Fr(x)p
Fk(\))p 951 846 107 2 v 951 880 a Fs(j)p Fk(\001\()p Fr(x)p
Fk(\))p Fs(j)1062 797 y Ff(\025)1105 854 y Fs(\001)21 b Fk(min)q(\()p
Fs(j)p Fk(\001\()p Fr(x)p Fk(\))p Fs(j)p Fi(;)6 b(t)p Fk(\))265
966 y(4.)20 b(Add)14 b(resulting)h(sum)e(v)o(ector)g(to)g(the)h(curren)o(t)f
(state,)g(pro)q(ducing)i(new)e(state.)879 1053 y Fr(x)37 b
Fs( )g Fr(x)21 b Fk(+)h(\001)1142 1036 y Fu(0)1153 1053 y Fk(\()p
Fr(x)p Fk(\))265 1141 y(5.)e(Calculate)15 b(the)e(new)g Fi(t)p
Fk(.)925 1186 y Fi(t)36 b Fs( )h Fk(co)q(ol)q(\()p Fi(t)p Fk(\))265
1257 y(6.)20 b(Rep)q(eat)14 b(un)o(til)h(the)e(desired)h(n)o(um)o(b)q(er)g
(of)f(iterations)i(has)e(b)q(een)h(completed.)p 1858 1264 2
835 v 186 1310 2 46 v 1858 1310 V 187 1311 1673 2 v 770 1375
a Ft(Figur)n(e)f(1.)g(The)g(A)o(GLO)h(algorithm)228 1581 y
Fn(Both)k(algorithms)g(use)g(a)g(simple)i(geometric)e(co)q(oling)h(sc)o
(hedule)g(iden)o(tical)h(to)e(that)f(used)i(in)187 1631 y(the)c(basic)h(sim)o
(ulated)g(annealing)h(algorithm:)d Fh(cool)q Fn(\()p Fh(t)p
Fn(\))e(=)h Fh(t=\025)p Fn(,)h(where)573 1767 y Fh(\025)57
b Fn(=)751 1707 y Ff(\022)786 1736 y Fm(b)n(e)n(ginning)14
b(temp)n(er)n(atur)n(e)p 786 1756 433 2 v 813 1798 a(ending)h(temp)n(er)n
(atur)n(e)1223 1707 y Ff(\023)1254 1717 y Fn(1)6 b Fg(=)f Fm(iter)n(ations)
187 1884 y Fn(and)20 b(the)h(default)g(v)m(alues)g(for)f(the)g(temp)q
(erature)g(parameters)g(are)g(100)f(and)h(0.001,)f(and)h(the)187
1934 y(default)c(n)o(um)o(b)q(er)h(of)e(iterations)h(is)h(1000.)d(Better)i
(co)q(oling)h(sc)o(hedules)g(should)g(b)q(e)g(p)q(ossible)g(\(see)187
1983 y(Section)f(4.2\).)228 2033 y(Our)k Fj(fdp)f Fn(algorithm)g(di\013ers)g
(from)f(F)l(ruc)o(h)o(terman)g(&)i(Reingold)1380 2017 y Fx(10)1436
2033 y Fn(in)f(sev)o(eral)h(w)o(a)o(ys.)d(First,)187 2083 y
Fj(fdp)h Fn(uses)h(a)f(simple)i(sum)e(of)g(t)o(w)o(o)f(sp)q(eci\014c)j
(aesthetic)f(functions)g(\(no)q(de/no)q(de)g(repulsion)h(and)187
2133 y(edge)f(length)h(minimization\).)h(A)o(GLO)e(generalizes)i
Fj(fdp)e Fn(b)o(y)g(using)h(a)f(w)o(eigh)o(ted)g(sum)h(of)e(aes-)187
2183 y(thetic)i(functions.)g(Second,)g(w)o(e)f(do)g(temp)q(erature)h
(clipping)i(of)d(the)g(mo)o(v)o(emen)o(t)g(v)o(ectors)f(in)j(a)187

bin/gloss.pl  view on Meta::CPAN

use Graph::Layout::Aesthetic::Monitor::GnuPlot;

my $VERSION = "0.01";

Getopt::Long::Configure ("bundling_override");

my %weight;
GetOptions('help|?|h'	=> \my $help,
           man		=> \my $man,
           version	=> \my $version,
           "it=o"	=> \my $iterations,
           "bt=f"	=> \my $begin_temp,
           "et=f"	=> \my $end_temp,
           "m!"		=> \my $monitor,
           "mr=o"	=> \my $monitor_rate,
           "s!"		=> \my $sleep,
           "d=o"	=> \my $dimensions,
           "stress!"	=> \my $stress,
           "edges!"	=> \my $edges,
           "cin=s"	=> \my $coord_infile,
           "knr=f"	=> \$weight{NodeRepulsion},

bin/gloss.pl  view on Meta::CPAN

}
warn("No aesthetics specified, so you'll get random placement\n") unless $n;

$aglo->all_coordinates(read_coordinates($coord_infile)) if
    defined $coord_infile;

$monitor &&= Graph::Layout::Aesthetic::Monitor::GnuPlot->new();
my $start = time;
$aglo->gloss(begin_temperature	=> $begin_temp,
             end_temperature	=> $end_temp,
             iterations		=> $iterations,
             monitor_delay	=> $monitor_rate,
             monitor		=> $monitor,
             hold		=> defined $coord_infile);
# $aglo->normalize;
my $elapsed = time() - $start;

if ($edges) {
    print "BEGIN_STATE\n";
    for ($aglo->increasing_edges) {
        for (@$_) {

bin/gloss.pl  view on Meta::CPAN


=over

=item B<-edges>

display edges instead of coordinates. This is the output format the
old gloss program gave.

=item B<-it integer>

Number of iterations, defaults to 1000

=item B<-bt float>

Beginning temperature, defaults to 100

=item B<-et float>

Ending temperature, defaults to 0.001

=item B<-m>

include/aglo.h  view on Meta::CPAN

typedef aglo_real  *aglo_point;
typedef const aglo_real *aglo_const_point;
typedef aglo_real *aglo_gradient;

typedef struct aglo_state {
    aglo_graph graph;		/* Topology */
    SV *graph_sv;		/* perl object reference for graph */
    struct use_force *forces;
    aglo_real		temperature;
    aglo_real		end_temperature;
    aglo_unsigned	iterations;
    aglo_unsigned	dimensions;	/* e.g. 2 means 2-dimensional space */
    aglo_signed		sequence;	/* bumped when state changed */
    aglo_signed		centroid_sequence;
    aglo_gradient	gradient, force_gradient;
    aglo_boolean	paused;
    aglo_point		cached_centroid;
    aglo_point		point[1];	/* State vector, must be last */
} *aglo_state;

typedef void aglo_aesth_gradient_fx(pTHX_ aglo_state state,

lib/Graph/Layout/Aesthetic.pm  view on Meta::CPAN


sub gloss {
    my ($aglo, %params) = @_;

    my $beg_t = delete $params{begin_temperature};
    $beg_t = 1e2 if !defined $beg_t;

    my $end_t = delete $params{end_temperature};
    $end_t = 1e-3 if !defined $end_t;

    my $iter = delete $params{iterations};
    $iter = 1e3 if !defined $iter;
    croak "Iterations should be >= 0" if $iter < 0;
    croak "Iterations should be an integer" if $iter != int($iter);

    my $mon_delay = delete $params{monitor_delay};
    $mon_delay = 2 if !defined $mon_delay;
    croak "MonitorDelay should be >= 0" if $mon_delay < 0;

    my $monitor = delete $params{monitor};
    my $hold = delete $params{hold};

lib/Graph/Layout/Aesthetic.pm  view on Meta::CPAN

    if ($monitor) {
        my $code;
        # In case the monitor call already says to pause
        $aglo->paused(0);
        if (ref($monitor) eq "CODE") {
            $monitor->($aglo);
            $code = 1;
        } else {
            $monitor->plot($aglo);
        }
        while ($aglo->iterations > 0 && !$aglo->paused) {
            $aglo->_gloss(time() + $mon_delay);
            if ($code) {
                $monitor->($aglo);
            } else {
                $monitor->plot($aglo);
            }
        }
    } elsif ($aglo->iterations > 0) {
        $aglo->_gloss;
    }
}

sub pause {
    return shift->paused(1);
}

sub coordinates_to_graph {
    my ($aglo, $graph, %params) = @_;

lib/Graph/Layout/Aesthetic.pm  view on Meta::CPAN

  @edges = $aglo->increasing_edges;
  $edges = $aglo->increasing_edges;

  $aglo->zero;
  $aglo->randomize(?$size?);
  $aglo->jitter(?$distance?);
  ($min, $max) = $aglo->frame;
  ($min, $max) = $aglo->iso_frame;
  $aglo->normalize;

  $aglo->init_gloss($temperature, $end_temperature, $iterations ?,$randomize_size?);
  $aglo->step(?$temperature ?, $jitter_size??);
  $aglo->pause;
  $paused = $aglo->paused;
  $aglo->_gloss($end_time);
  # Valid parameter keys/value pairs are:
  #   begin_temperature => $temperature
  #   end_temperature   => $end_temperature
  #   iterations        => $iterations
  #   hold              => $boolean
  #   monitor           => $monitor
  #   monitor_delay     => $seconds
  $aglo->gloss(%parameters);
  @gradient = $aglo->gradient;
  $gradient = $aglo->gradient;
  $stress = $aglo->stress;

  $old_temperature   = $aglo->temperature(?$new_temperature ?, $warn??);;
  $old_end_temperature = $aglo->end_temperature(?$new_end_temperature ?,
    						$warn??);
  $old_iterations = $aglo->iterations(?$new_iterations?);

  $aglo->coordinates_to_graph($graph, %parameters);
  # Valid parameter keys/value pairs are:
  #   id_attribute  => $string
  #   id_attribute  => \%name_to_num
  #   pos_attribute => $string
  #   pos_attribute => \@strings
  #   min_attribute => $string
  #   min_attribute => \@strings
  #   max_attribute => $string
  #   max_attribute => \@strings

  Graph::Layout::Aesthetic->gloss_graph($graph, %parameters)
  # Valid parameter keys/value pairs are:
  #   literal           => $boolean
  #   nr_dimensions     => $integer
  #   forces            => \%forces
  #   begin_temperature => $temperature
  #   end_temperature   => $end_temperature
  #   iterations        => $iterations
  #   hold              => false or 1
  #   monitor           => $monitor
  #   monitor_delay     => $seconds
  #   pos_attribute     => $string
  #   pos_attribute     => \@strings

=head1 DESCRIPTION

A Graph::Layout::Aesthetic object represents a state in the process of laying
out a graph. The idea is that the state is repeatedly modified until an

lib/Graph/Layout/Aesthetic.pm  view on Meta::CPAN

L<finished|Graph::Layout::Aesthetic::Topology/finish>
L<Graph::Layout::Aesthetic::Topology|Graph::Layout::Aesthetic::Topology> object
$topology. The position space for each vertex is assumed to have $nr_dimensions
dimensions (defaults to 2 if not given).

Vertices start out without any particular positions (they may not even be valid
numbers). You'll have to do some initial placement yourself (notice that
L<gloss|"gloss"> already has a L<randomize|"randomize"> built in).

The initial temperature is 1e2, the end_temperature is 1e-3 and the default
number of iterations is 1000.

=item X<add_force>$aglo->add_force($force_name ?, $weight?)

The steps on the Graph::Layout::Aesthetic state are caused by applying forces
to the vertices that will supposedly make the graph layout more pleasing.
Each application of this method will add a new force named $force_name with
weight $weight (defaults to 1) working on the graph vertices.

$force_name has to be a name potentially known to
L<Graph::Layout::Aesthetic::Force|Graph::Layout::Aesthetic::Force>. Because

lib/Graph/Layout/Aesthetic.pm  view on Meta::CPAN

Calculates the smallest enclosing box of all coordinates where all box sizes
are the same. The box will be symmetrically placed around the box that would be
returned by L<frame|"frame">. $min will be a reference to minimal values for
each coordinate, and $max a reference to maximum values.

=item X<normalize>$aglo->normalize

Scales and moves all points so that the new iso_frame has minimum [0, 0, ..]
and maximum [1, 1,...].

=item X<init_gloss>$aglo->init_gloss($temperature, $end_temperature, $iterations?, $randomize_size?)

Sets new values for current (starting) $temperature, $end_temperature and
$iterations. If $randomize_size is bigger than 0 (the default is 1) it also
does a:

    $aglo->randomize($randomize_size);

This method is used before a new or restarted L<_gloss|"_gloss"> or to change
the initial default values.

=item X<step>$aglo->step(?$temperature ?, $jitter_size??)

Do a single step at temperature $temperature (defaults to $aglo->temperature)

lib/Graph/Layout/Aesthetic.pm  view on Meta::CPAN

=item $aglo->jitter($jitter_size) if $jitter_size;

=item Calculate the weighted sum of all aesthetic forces

=item Limit the size of the displacement the combined force would like to cause to the $temperature if it's bigger (but keep the direction).

=item Apply the calculated displacement to all vertices

=back

Notice it doesn't change L<iterations|"iterations"> or
L<temperature|"temperature">.

=item X<pause>$aglo->pause

Normally the layout methods L<_gloss|"_gloss"> and L<gloss|"gloss"> run until
there are no more iteration to be done. By calling this method you set a flag
telling them to stop as soon as a new step would be taken. The only chance
you get to do this is normally during the
L<gloss monitor calbback|"gloss_monitor"> or during a
L<force gradient call|Graph::Layout::Aesthetic::Force/aesth_gradient>.

lib/Graph/Layout/Aesthetic.pm  view on Meta::CPAN

The pause flag is implictly cleared at object construction or when you enter
L<_gloss|"_gloss"> or L<gloss|"gloss">.

=item X<paused>$paused = $aglo->paused

Return true if layout is paused, false otherwise.

=item X<_gloss>$aglo->_gloss(?$end_time?)

A convenience method that repeatedly applies the L<step|"step"> method
until $end_time is reached, the target number of iterations is reached or
the layout gets L<paused|"pause">.
After each step it changes the temperature in the direction of the
end temperature. $end_time defaults to some huge number, so if it's not given
the loop will be completely controlled by the target number of iterations.
Giving 0 as $end_time will do only one step (including lowering of temperature
and iterations left).

_gloss basically equivalent to:

    if ($aglo->iterations <= 0) croak("No more iterations left");
    my $lambda = ($aglo->temperature / $aglo->end_temperature) ** (1 / $aglo->iterations);
    $aglo->paused(0);
    while ($aglo->iterations > 0 && !$aglo->paused) {
        $aglo->step;

        $aglo->temperature($aglo->temperature / lambda);
        $aglo->iterations( $aglo->iterations() - 1);

        return if $end_time <= time();
    }

=item X<gloss>$aglo->gloss(%parameters)

This method does a complete graph layout. It first sets temperatures and
iterations from the given %parameters (using L<init_gloss|"init_gloss">) and
L<randomizes|"randomize"> all coordinates (unless requested not to). It then
iterates as many times as requested (using L<_gloss|"_gloss">) or until the
layout gets L<paused|"pause">.

If requested it can also monitor the progress of the layout. This means that
at the start and at regular times thereafter (including the end) a given
callback gets called which can then do things like display the current
configuration. A simple monitor based on L<gnuplot|gnuplot(1)> comes with this
package, see L<Graph::Layout::Aesthetic::Monitor::GnuPlot|Graph::Layout::Aesthetic::Monitor::GnuPlot>.

lib/Graph/Layout/Aesthetic.pm  view on Meta::CPAN

=over

=item X<gloss_begin_temperature>begin_temperature => $temperature

Starting temperature, defaults to 100

=item X<gloss_end_temperature>end_temperature => $end_temperature

Ending temperature, defaults to 1e-3

=item X<gloss_iterations>iterations => $iterations

Number of iterations requested, defaults to 1000.

=item X<gloss_hold>hold => $boolean

If given a false value (the default), all vertex coordinates will be
randomized before the iterations start. If given a true value, the
vertex coordinates will remain what they were before this method call
(supposedly because you did some explicit placement before or want to
further optimize a previously generated layout).

=item X<gloss_monitor>monitor => $monitor

If given, $monitor will be called once initially and then periodically (and
once for the final configuration) like this if it's a CODE reference:

    $monitor->($aglo);

lib/Graph/Layout/Aesthetic.pm  view on Meta::CPAN

=item X<end_temperature>$old_end_temperature = $aglo->end_temperature(?$new_end_temperature ?, $warn??)

If given a $new_end_temperature argument, it will set the end_temperature to
that. The end_temperature will remain unchanged otherwise. If $warn is true
(the default), it will complain if you set the end_temperature higher than the
current temperature.

In all cases it will return the end_temperature as it was just before this
call.

=item X<iterations>$old_iterations = $aglo->iterations(?$new_iterations?);

If given a $new_iterations argument, it will set the remaining iterations to
that. The number of iterations will remain unchanged otherwise.

In all cases it will return the number of iterations left as it was just before
this call.

=item X<coordinates_to_graph>$aglo->coordinates_to_graph($graph, %parameters)

Copies the current state coordinates of $aglo to vertex attributes of the
standard L<Graph|Graph> object $graph. It also stores
L<the containing frame|"frame"> as global graph attributes.

%parameters are key/value pairs. Recognized are:

lib/Graph/Layout/Aesthetic.pm  view on Meta::CPAN

=item X<gloss_graph_begin_temperature>begin_temperature => $temperature

Starting temperature, defaults to 100.
The same as the L<gloss|"gloss"> L<begin_temperature parameter|"gloss_begin_temperature">.

=item X<gloss_graph_end_temperature>end_temperature => $end_temperature

Ending temperature, defaults to 1e-3
The same as the L<gloss|"gloss"> L<end_temperature parameter|"gloss_end_temperature">.

=item X<gloss_graph_iterations>iterations => $iterations

Number of iterations requested, defaults to 1000.
The same as the L<gloss|"gloss"> L<iterations parameter|"gloss_iterations">.

=item X<gloss_graph_hold>hold => $boolean

The same as the L<gloss|"gloss"> L<hold parameter|"gloss_hold"> meaning
no randomization is done if given a true value (default is false). In the case
the boolean is 1, the initial coordinates are retrieved from the same
attributes as where the results will be set (see the
L<pos_attribute parameter|"gloss_graph_pos_attribute">.

If the true value is not 1, it will behave in the same sort of way as the

lib/Graph/Layout/Aesthetic/Include.pm  view on Meta::CPAN

typedef aglo_real  *aglo_point;
typedef const aglo_real *aglo_const_point;
typedef aglo_real *aglo_gradient;

typedef struct aglo_state {
    aglo_graph graph;		/* Topology */
    SV *graph_sv;		/* perl object reference for graph */
    struct use_force *forces;
    aglo_real		temperature;
    aglo_real		end_temperature;
    aglo_unsigned	iterations;
    aglo_unsigned	dimensions;	/* e.g. 2 means 2-dimensional space */
    aglo_signed		sequence;	/* bumped when state changed */
    aglo_signed		centroid_sequence;
    aglo_gradient	gradient, force_gradient;
    aglo_boolean	paused;
    aglo_point		cached_centroid;
    aglo_point		point[1];	/* State vector, must be last */
} *aglo_state;

typedef void aglo_aesth_gradient_fx(pTHX_ aglo_state state,

t/02_Aesthetic.t  view on Meta::CPAN

like($@, qr!Topology hasn.t been finished at !, "Topology must be finished");
$topo3->finish;
eval { Graph::Layout::Aesthetic->new($topo3, -1) };
    like($@, qr!Nr_dimensions must not be negative at!, "Positive dimensionality");

my $aglo = Graph::Layout::Aesthetic->new($topo3);
isa_ok($aglo, "Graph::Layout::Aesthetic", "Created into the right class");
is($aglo->nr_dimensions, 2, "Defaults to two dimensions");
nearly($aglo->temperature, 1e2, "Default temperature is 1e2");
nearly($aglo->end_temperature, 1e-3, "Default end_temperature is 1e-3");
is($aglo->iterations, 1e3, "Default temperature is 1e3");
is($aglo->topology, $topo3, "Topology is available");
$topo3 = undef;
is($topo_destroys, 0, "aglo keeps topology alive");
$force_destroys = 0;
my $force = Graph::Layout::Aesthetic::Force::NodeRepulsion->new;
is($force_destroys, 0, "The force is alive");
$aglo->_add_force($force);
is($force_destroys, 0, "The force is alive");
$force = undef;
is($force_destroys, 0, "The force is alive");

t/02_Aesthetic.t  view on Meta::CPAN

$aglo->normalize;
is_deeply(scalar $aglo->all_coordinates, [[1/4, 1/4], [3/4, 0], [1/2, 1]],
          "Right normalization");

# Checking init_gloss
$aglo = Graph::Layout::Aesthetic->new($topo3);
$aglo->zero;
$aglo->init_gloss(2, 1, 3, -1);
is($aglo->temperature, 2, "Temperature set");
is($aglo->end_temperature, 1, "End temperature set");
is($aglo->iterations, 3, "Iterations set");
is_deeply(scalar $aglo->all_coordinates, [[0, 0], [0, 0], [0, 0]],
          "Vertices unmoved");
# Randomize if given
is_random($aglo, sub {
    $_[0]->zero;
    $_[0]->init_gloss(2, 1, 3, $_[1]);
});
# Default randomize is 1
is_random($aglo, sub {
    $_[0]->zero;

t/02_Aesthetic.t  view on Meta::CPAN

# Default jitter is 1e-5
is_random($aglo, sub {
    $_[0]->zero;
    $_[0]->step;
    my @coords = $aglo->all_coordinates;
    for (@coords) {
        $_ *= $_[1]/1e-5 for @$_;
    }
    $aglo->all_coordinates(@coords);
});
is($aglo->iterations, 1000, "Iterations unchanged by step");
is($aglo->temperature, 1e2, "Temperature unchanged by step");
# Default jitter is 1e-5, but temperature restricted
is_random($aglo, sub {
    $_[0]->zero;
    $_[0]->step(1e-6);
    my @coords = $aglo->all_coordinates;
    for (@coords) {
        $_ *= $_[1]/1e-6 for @$_;
    }
    $aglo->all_coordinates(@coords);

t/02_Aesthetic.t  view on Meta::CPAN

$aglo->add_force("min_edge_length");
$aglo->all_coordinates([0, -1], [0, 1]);
is_deeply(scalar $aglo->gradient, [[0, 4], [0, -4]], "Quadratic attraction");
nearly($aglo->stress, 4*sqrt 2, "Stressed out");

# Checking _gloss
$aglo = Graph::Layout::Aesthetic->new($topo2);
$aglo->add_force("min_edge_length");
$aglo->all_coordinates([0, -1], [0, 1]);
$aglo->_gloss(0);
is($aglo->iterations, 999, "Iterations lowered");
nearly($aglo->temperature, 100 / (100/1e-3)**(1/1000), "Temperature lowered");
nearly($aglo->end_temperature, 1e-3, "End_temperature unchanged");
{
    local $EPS = 1e-4;
    @coords = $aglo->all_coordinates;
    nearly($coords[0][0], 0);
    nearly($coords[0][1], 3);
    nearly($coords[1][0], 0);
    nearly($coords[1][1],-3);
}
$aglo->all_coordinates([0, -1], [0, 1]);
$aglo->init_gloss(100, 1, 1, 0);
$aglo->_gloss();
is($aglo->iterations, 0, "Iterations lowered");
nearly($aglo->temperature, 1, "Temperature lowered");
nearly($aglo->end_temperature, 1, "End_temperature unchanged");
{
    local $EPS = 1e-4;
    @coords = $aglo->all_coordinates;
    nearly($coords[0][0], 0);
    nearly($coords[0][1], 3);
    nearly($coords[1][0], 0);
    nearly($coords[1][1],-3);
}
$aglo->end_temperature(1e-3);
eval { $aglo->_gloss() };
like($@, qr!No more iterations left at !, "Can't iterate beyond 0");
is($aglo->iterations, 0, "Iterations unchanged");
nearly($aglo->temperature, 1, "Temperature unchanged");

$aglo->add_force("node_repulsion");
$aglo->all_coordinates([0, -1], [0, 1]);
$aglo->init_gloss(100, 1e-3, 1000, 0);
$aglo->_gloss(time);
is($aglo->iterations, 999, "Time based finish");
$aglo->_gloss(10000+time);
is($aglo->iterations, 0, "Enough time to finish");
@coords = $aglo->all_coordinates;
my $distance = sqrt(($coords[0][0]-$coords[1][0])**2 +
                    ($coords[0][1]-$coords[1][1])**2);
{
    local $EPS = 2e-2;
    nearly($distance, 1, "Balance");
    nearly($aglo->stress, 0, "Balance");
}
$aglo->all_coordinates([0, -1], [0, 1]);
$aglo->init_gloss(100, 1e-3, 1000, 0);
$aglo->_gloss();
is($aglo->iterations, 0, "Enough time to finish");
@coords = $aglo->all_coordinates;
$distance = sqrt(($coords[0][0]-$coords[1][0])**2 +
                 ($coords[0][1]-$coords[1][1])**2);
{
    local $EPS = 2e-2;
    nearly($distance, 1, "Balance");
    nearly($aglo->stress, 0, "Balance");
}

# Checking gloss
$aglo = Graph::Layout::Aesthetic->new($topo3);
is_random($aglo, sub {
    # One-step gloss without forces and hold is just a randomize
    $aglo->all_coordinates([3, 6], [7, 4], [5, 10]);
    $aglo->gloss(iterations => 1);
    my @coords = $aglo->all_coordinates;
    for (@coords) {
        $_ *= $_[1] for @$_;
    }
    $aglo->all_coordinates(@coords);
});

$aglo = Graph::Layout::Aesthetic->new($topo2);
$aglo->add_force("min_edge_length");
$aglo->all_coordinates([0, -1], [0, 1]);
$aglo->end_temperature(1);
$aglo->gloss(iterations => 1, hold => 1);
is($aglo->iterations, 0, "Iterations lowered");
nearly($aglo->temperature, 1e-3, "Temperature lowered");
nearly($aglo->end_temperature, 1e-3, "End_temperature unchanged");
{
    local $EPS = 1e-4;
    @coords = $aglo->all_coordinates;
    nearly($coords[0][0], 0);
    nearly($coords[0][1], 3);
    nearly($coords[1][0], 0);
    nearly($coords[1][1],-3);
}

$aglo->all_coordinates([0, -1], [0, 1]);
$aglo->gloss(begin_temperature => 4, iterations => 1, hold => 1);
@coords = $aglo->all_coordinates;
{
    local $EPS = 1e-4;
    nearly($coords[0][0], 0);
    nearly($coords[0][1], -1+4/sqrt 2, "Temperature restricted");
    nearly($coords[1][0], 0);
    nearly($coords[1][1], 1-4/sqrt 2, "Temperature restricted");
}

$aglo->add_force("node_repulsion");
$aglo->all_coordinates([0, -1], [0, 1]);
$aglo->gloss(hold => 1, end_temperature => 2e-3);
is($aglo->iterations, 0, "Enough time to finish");
nearly($aglo->temperature,     2e-3, "Temperature lowered");
nearly($aglo->end_temperature, 2e-3, "End_temperature reached");
@coords = $aglo->all_coordinates;
$distance = sqrt(($coords[0][0]-$coords[1][0])**2 +
                    ($coords[0][1]-$coords[1][1])**2);
{
    local $EPS = 2e-2;
    nearly($distance, 1, "Balance");
    nearly($aglo->stress, 0, "Balance");
}
eval { $aglo->gloss(foo => 5) };
like($@, qr!Unknown parameter foo at !, "Right error message");
# Accepts iterations 0
$aglo->gloss(iterations => 0);
$aglo->gloss(iterations => 0, monitor => sub {});

# Test monitor CODE ref
$count = 0;
$aglo->gloss(monitor_delay => 10000, monitor => sub { $count++ });
is($count, 2, "Begin and end call");

# Test monitor object
$count = 0;
$aglo->gloss(monitor_delay => 10000, monitor => TestMonitor->new);
is($count, 2, "Begin and end call");

t/02_Aesthetic.t  view on Meta::CPAN

nearly($aglo->end_temperature(200, 1), 500, "Combined get, but set below end");
is(@warnings, 1, "One warning");
like($warnings[0], qr!Temperature 100\.0+ should probably be >= end_temperature 200\.0+ at !, "Proper warning");
@warnings = ();
nearly($aglo->end_temperature(300), 200, "Combined get, but set below end");
is(@warnings, 1, "One warning");
like($warnings[0], qr!Temperature 100\.0+ should probably be >= end_temperature 300\.0+ at !, "Proper warning");
@warnings = ();
nearly($aglo->end_temperature, 300, "Combined get, but set below end");

# Checking iterations
$aglo = Graph::Layout::Aesthetic->new($topo3);
is($aglo->iterations, 1000, "Proper default");
$aglo->iterations(100);
is($aglo->iterations, 100, "Settable");
is($aglo->iterations(200), 100, "Combined get/set");
is($aglo->iterations, 200, "Value changed");
eval { $aglo->iterations(-1) };
like($@, qr!Iterations -1 should be >= 0 at !, "Proper error message");
is($aglo->iterations, 200, "Value unchanged on error");

# Checking pause
$aglo = Graph::Layout::Aesthetic->new($topo3);
is($aglo->paused, "", "The default is unpaused");
is($aglo->paused, "", "Paused without args doesn't change a false state");
$aglo->pause;
is($aglo->paused, 1, "Pause sets the pause flag");
is($aglo->paused, 1, "Paused without args doesn't change a false state");
$aglo->pause;
is($aglo->paused, 1, "Pause is idempotent");

t/02_Aesthetic.t  view on Meta::CPAN

is($aglo->paused(undef), 1, "Combined assign returns old value");
is($aglo->paused(0), "", "Undef is false");
is($aglo->paused(""), "", "0 is false");
is($aglo->paused, "", "\"\" is false");

# The corresponding tests for _gloss will be done in the perl force tester
# since we need a gradient callback for them
for my $start_pause (0, 1) {
    my $count = 0;
    $aglo->paused($start_pause);
    $aglo->gloss(iterations => 3,
                 monitor_delay => 0,
                 monitor => sub {
                     is($aglo->paused, "");
                     $count++;
                 });
    is($count, 4, "Done all counts");
    is($aglo->paused, "");

    for $_ (0..3) {
        my $count = 0;
        $aglo->paused($start_pause);
        $aglo->gloss(iterations => 3,
                     monitor_delay => 0,
                     monitor => sub {
                         my $aglo = shift;
                         is($aglo->paused, "");
                         $aglo->pause if $count == $_;
                         $count++;
                     });
        is($count, $_+1, "Done only start event");
        is($aglo->iterations, 3-$_, "No iterations done");
        is($aglo->paused, 1);
    }
}

can_ok("Graph::Layout::Aesthetic", qw(coordinates_to_graph gloss_graph));

my $graph_class = eval "use Graph; 1" ? "Graph" : undef;

if ($graph_class && $Graph::VERSION >= 0.50) {
    # Check coordinates_to_graph

t/02_Aesthetic.t  view on Meta::CPAN

                                                 pos_attribute => ["x", "y"],
                                                 hold => 1) };
    like($@,
         qr!^Attribute 'x' for vertex 'foo\d' doesn.t exist at !,
         "Copy attempt from layout_pos if hold is 1");

    ok(!$g->has_vertex_attribute("layout_pos", "foo0"),
       "No layout_pos attribute yet");
    ok(!$g->has_graph_attribute("layout_min"), "No layout_min attribute yet");
    ok(!$g->has_graph_attribute("layout_max"), "No layout_max attribute yet");
    Graph::Layout::Aesthetic->gloss_graph($g, forces => {}, iterations => 1);
    ok($g->has_vertex_attribute("foo0", "layout_pos"), "Pos attribute now");
    is(@{$g->get_vertex_attribute("foo0", "layout_pos")}, 2,
       "Default 2 dimensions");
    ok($g->has_graph_attribute("layout_min"), "Min attribute now");
    is(@{$g->get_graph_attribute("layout_min")}, 2, "Default 2 dimensions");
    ok($g->has_graph_attribute("layout_max"), "Max attribute now");
    is(@{$g->get_graph_attribute("layout_max")}, 2, "Default 2 dimensions");
    Graph::Layout::Aesthetic->gloss_graph($g,
                                          forces => {},
                                          iterations => 1,
                                          nr_dimensions => 3);
    is(@{$g->get_vertex_attribute("foo0", "layout_pos")}, 3,
       "Three dimensions if requested");
    is(@{$g->get_graph_attribute("layout_min")}, 3,
       "Three dimensions if requested");
    is(@{$g->get_graph_attribute("layout_max")}, 3,
       "Three dimensions if requested");

    # At 0 iterations gloss_graph should behave like init_gloss
    $g->set_vertex_attribute("foo0", "layout_pos", [2, 2]);
    $g->set_graph_attribute("layout_min", [3, 3]);
    $g->set_graph_attribute("layout_max", [4, 4]);
    Graph::Layout::Aesthetic->gloss_graph($g,
                                          forces => {},
                                          iterations => 0);
    for my $pos ($g->get_vertex_attribute("foo0", "layout_pos"),
                 $g->get_graph_attribute("layout_min"),
                 $g->get_graph_attribute("layout_max")) {
        ok(-1 < $pos->[0]);
        ok($pos->[0] < 1);
        ok(-1 < $pos->[1]);
        ok($pos->[1] < 1);
    }

    $g->set_vertex_attribute("foo0", "layout_pos", [2, 3]);
    $g->set_graph_attribute("layout_min", "abba");
    $g->set_graph_attribute("layout_max", {z => 4});
    Graph::Layout::Aesthetic->gloss_graph($g,
                                          forces => {},
                                          iterations => 0,
                                          hold => 1,
                                          min_attribute => undef,
                                          max_attribute => undef);
    is_deeply($g->get_vertex_attribute("foo0", "layout_pos"), [2, 3]);
    is($g->get_graph_attribute("layout_min"), "abba");
    is_deeply($g->get_graph_attribute("layout_max"), {z => 4});

    $g->set_vertex_attribute("foo0", "layout_pos", [4, 5]);
    ok(!$g->has_vertex_attribute("foo0", "www"), "No www attribute yet");
    Graph::Layout::Aesthetic->gloss_graph($g,
                                          forces => {},
                                          iterations => 0,
                                          pos_attribute => "www",
                                          hold => "layout_pos");
    is_deeply($g->get_vertex_attribute("foo0", "www"), [4, 5]);

    g_is_random($g, sub {
        Graph::Layout::Aesthetic->gloss_graph($_[0],
                                              forces => {},
                                              iterations => 0);
    });

    Graph::Layout::Aesthetic->gloss_graph($g,
                                          forces => {
                                              min_edge_length => 1,
                                              node_repulsion  => 1,
                                          });
    $coords[0] = $g->get_vertex_attribute("foo0", "layout_pos");
    $coords[1] = $g->get_vertex_attribute("foo1", "layout_pos");
    $distance = sqrt(($coords[0][0]-$coords[1][0])**2 +

t/02_Aesthetic.t  view on Meta::CPAN

    }

    $count = 0;
    Graph::Layout::Aesthetic->gloss_graph($g,
                                          forces => {
                                              min_edge_length => 1/4,
                                              node_repulsion  => 2,
                                          },
                                          monitor => sub { $count++},
                                          monitor_delay => 0);
    is($count, 1001, "Default number of iterations");
    $coords[0] = $g->get_vertex_attribute("foo0", "layout_pos");
    $coords[1] = $g->get_vertex_attribute("foo1", "layout_pos");
    $distance = sqrt(($coords[0][0]-$coords[1][0])**2 +
                     ($coords[0][1]-$coords[1][1])**2);
    {
        local $EPS = 2e-2;
        nearly($distance, 2, "Balance");
    }

    if (eval { require Graph::Directed }) {

t/04_Force.t  view on Meta::CPAN

$aglo->_add_force($force);
for my $start_pause (0, 1) {
    for $_ (0..3) {
        $when = $_;
        $pause_count = 0;
        $aglo->paused($start_pause);
        $aglo->init_gloss(1000, 1, 3);
        $aglo->_gloss;
        if ($_ == 3) {
            is($pause_count, 3, "Done only start event");
            is($aglo->iterations, 0, "No iterations done");
            is($aglo->paused, "");
        } else {
            is($pause_count, $_+1, "Done only start event");
            is($aglo->iterations, 2-$_, "No iterations done");
            is($aglo->paused, 1);
        }
    }
}
$force = undef;
$aglo = undef;

# Final tests
is($balanced, 0, "Every setup has a cleanup and vice versa");

t/05_GnuPlot.t  view on Meta::CPAN


0 1
1 0

1 0
0 0

e
!, "Proper plot commands");
    my $from = time;
    $aglo->gloss(iterations	=> 1,
                 hold		=> 1,
                 monitor	=> $monitor,
                 monitor_delay	=> 0);
    tail(qr!set xrange \[ -0.050* : 1.050* \]
set yrange \[ -0.050* : 1.050* \]
set title "Time=\d+\s+Temp=100.0*"
plot "-"
0 0
1 1



( run in 1.457 second using v1.01-cache-2.11-cpan-71847e10f99 )