App-Physics-ParticleMotion

 view release on metacpan or  search on metacpan

lib/App/Physics/ParticleMotion.pm  view on Meta::CPAN

	#my $cloth = $top->Cloth;
	$cloth->configure( -scrollregion => [-10000, -10000, 10000, 10000] );
	$cloth->configure( -height => 600, -width => 800 );
	$cloth->pack( -fill => 'both', -expand => 1, -side => 'top' );
	$app->{cloth} = $cloth;
	
	# Math::Project3D is meant to project arbitrary functions. If we
	# want to project discrete data, we just use the current contents
	# of three variables as functions.
	$proj->new_function( 'x,y,z', '$x', '$y', '$z' );
	$app->{axis} = [];
	
	# Run the sub for the first time.
	_draw_axis($app);
	
	# Print starting coordinates to file if required
	if ( defined $out_file ) {
	    foreach my $p_no ( 0 .. $#particles ) {
	        my @proj = @y[ $dimensions * $p_no .. $dimensions * ( $p_no + 1 ) - 1 ];
	        push @proj, (0) x ( 3 - scalar(@proj) ) if @proj != 3;
    	    print( $out_filehandle ( $p_no + 1 ) . " 0 @proj\n" );
	    }
	}
	
	# Run the TK MainLoop
	MainLoop;
}


# main routine that doesn't end.
sub _draw {
	my $app = shift;
	$app->{started} = 0;
	
	my $trace = $app->{trace};
	my $cloth = $app->{cloth};
	my $epsilon = $app->{epsilon};
	my @functions = @{$app->{functions}};
	my @particles = @{$app->{particles}};
	my $particles = scalar @particles;
	my $dimensions = $app->{dimensions};
	my $out_file = $app->{out_file};
	my $out_filehandle = $app->{out_filehandle};
	my $displayscale = $app->{displayscale};
	my $time_warp = $app->{time_warp};
	my @y = @{$app->{y}};
	my $proj = $app->{proj};

	# Starting time and time steps. $dt will be adjusted by the integrator
	my $t = 0;
	my $dt = 0.1;

    # @prevlines holds line objects from the previous iterations.
    my @prevlines = ();

    # Previous values for line drawing
    my @prev_x = ();
    my @prev_y = ();

    # Start time of the simulation for speed adjustment on fast systems
    my $timeref = time();

    # main loop
    while (1) {

        # Delete old lines if we don't want to keep traces.
        # Change their color otherwise
        if ( not $trace ) {
            $cloth->delete($_) foreach @prevlines;
            @prevlines = ();
        }
        else {
            $_->configure( -state => 'disabled' ) foreach @prevlines;

            # Won't have to keep the objects around for modification:
            @prevlines = ();
        }

        # Integrate the next step. (I'm open for speed improvements!)
        ( $t, $dt, @y ) = Math::RungeKutta::rk4_auto(
            \@y,
            sub {
                my $t    = shift;
                my @dydt = @_[ @_ / 2 .. $#_ ];
                foreach ( 0 .. $#functions ) {
                    push @dydt, $functions[$_]->( $t, @_ );
                }
                return @dydt;
            },
            $t,
            $dt,
            $epsilon
        );

        # Project and draw
        foreach my $p_no ( 0 .. $particles - 1 ) {
            my @proj =
              @y[ $dimensions * $p_no .. $dimensions * ( $p_no + 1 ) - 1 ];
            push @proj, (0) x ( 3 - scalar(@proj) ) if @proj != 3;

            # File output
            print( $out_filehandle ( $p_no + 1 ) . " $t @proj\n" )
              if defined $out_file;

            my ( $x, $y ) = $proj->project(@proj);

            if ( defined $prev_x[$p_no] ) {
                my $coords =
                  [ _transform( $app, $prev_x[$p_no], $prev_y[$p_no], $x, $y ) ];
                @$coords = map { int $_ } @$coords;
                $coords->[2] -= 1 while abs( $coords->[0] - $coords->[2] ) < 1;
                $coords->[3] -= 1 while abs( $coords->[1] - $coords->[3] ) < 1;

                my $line = $cloth->Line(
                    -coords       => $coords,
                    -fill         => $particles[$p_no]{color},
                    -disabledfill => $particles[$p_no]{colort},
                );
                push @prevlines, $line;    # if not $trace;
            }
            $prev_x[$p_no] = $x;
	        $prev_y[$p_no] = $y;
        }

        # Speed control.
        DoOneEvent(DONT_WAIT);
        my $endtime = $timeref + $t / $time_warp;
        while ( $endtime > time() ) {
            sleep(0.01);
            DoOneEvent(DONT_WAIT);
        }
    }
}





# transform calculates the window coordinates from the
# relative coordinates of the projected plane
sub _transform {
	my $app = shift;
	my $cloth = $app->{cloth};
	my $displayscale = $app->{displayscale};
    my $y_max_half = $cloth->cget('-height') / 2;
    my $x_max_half = $cloth->cget('-width') / 2;
    my @res;
    while (@_) {
        push @res, $x_max_half + shift(@_) * $displayscale,
          $y_max_half - shift(@_) * $displayscale;
    }
    return @res;
}

# draw_axis draws the n axis' as Tk::Cloth::Line objects.
sub _draw_axis {
	my $app = shift;
	my $axis = $app->{axis};
	my $proj = $app->{proj};
	my $dimensions = $app->{dimensions};
	my $axiscolor = $app->{axiscolor};
	my $cloth = $app->{cloth};
	my $displayscale = $app->{displayscale};
    my $max = 20000;
    $_->delete() foreach @$axis;
    foreach my $dim ( 0 .. $dimensions - 1 ) {
        my ( $x1, $y1 ) =
        	$proj->project( map { $_ == $dim ? -$max / $displayscale : 0 }
             0 .. 2 );
        my ( $x2, $y2 ) =
        	$proj->project( map { $_ == $dim ? $max / $displayscale : 0 }
             0 .. 2 );
		push @$axis,
	        $cloth->Line(
              -coords => [ _transform( $app, $x1, $y1, $x2, $y2 ) ],
              -fill   => $axiscolor,
        	);
    }
}


1;
__END__

=head1 NAME

App::Physics::ParticleMotion - Simulations from Differential Equations



( run in 2.143 seconds using v1.01-cache-2.11-cpan-d7f47b0818f )