#!/usr/bin/perl -w

#############################################################################
# Cell - a cubic part of the world (aka a room or house or city)
#
# Copyright (C) 1999-2000 by Tels. All rights reserved.
#############################################################################

package CoW::Cell;
use vars qw($VERSION);
$VERSION = 1.00;    # Current version of this package
require  5.005;     # requires this Perl version or later

use base CoW::Thing;
use base Exporter;
@EXPORT_OK = qw/EMPTY/;
use strict;
use vars qw($AUTOLOAD);

use CoW::Dromed::Constants qw/CUBE AIR/;
use CoW::Coord qw/ALIGN_INSIDE/;
use CoW::Brush;

use constant EMPTY => -1;

#############################################################################
# private, initialize self 

my $id = 1;

sub _init
  {
  my $self = shift;
  my $args = shift;

  $self->SUPER::_init($args); 	# use things _init to make copy and checks
  # now imply some more checks
  $self->SUPER::_default( {
    shape => CUBE,
    id => $id,
    name => 'cell_'.$id,
    kind => AIR,
   } ); 
  $id++;
  return $self;
  }

##############################################################################
# public

sub split
  {
  # split yourself by inserting other cells
  my $self = shift;
  my $dir = shift || 0; $dir = 0 if $dir < 0; $dir = 2 if $dir > 2;
  my $count = shift || 2; $count = 2 if $count < 2;
  my $fit = shift || 0;					# default: dont fit
 
  $self->empty(); 
  # left overs are sizes 
  my $size = $_[0] || 0; 	# peek at first
  my $parts = 0; 		# no parts
  # if have parts, so get sum. get also all sizes
  my ($cell,@names,$remains,$sum,@sizes,$i);
  $sum = 0;
  if ($size >= 1)
    {
    $parts = 1;
    for ($i = 0; $i < $count; $i++)
      {
      $size = shift || 1; push @sizes,$size;
      $sum += $size;
      #print "size $i: $size\n";
      }
    }
  $names[0] = [ 'x','l','_parent::l,_parent::x' ];
  $names[1] = [ 'y','w','_parent::w,_parent::y' ];
  $names[2] = [ 'z','h','_parent::h,_parent::z' ];
  my $x = 0; $remains = 1; my ($c,$xc);
  for ($i = 0; $i < $count; $i++)
    {
    if ($sum != 0)
      {
      $size = $sizes[$i] / $sum;
      }
    else
      {
      $size = $remains / ($count-$i);
      }
    #print "split x: $x size: $size remains: $remains\n";
    $c = new CoW::Size ( rel => "_parent::$names[$dir]->[1]", val => $size);
    $xc = new CoW::Coord ( rel => "$names[$dir]->[2]", 
      val => $x - $size/2, align => ALIGN_INSIDE, );
    $self->insert ( CoW::Cell->new ( 
            "$names[$dir]->[0]" => $xc, "$names[$dir]->[1]" => $c ));
    $x += $size; $remains -= $size;
    } 
  }

sub grid
  {
  my $self = shift;
  my $x = shift || 1;
  my $y = shift || 1;
  my $z = shift || 1;
  $self->empty(); 

  my $cell;
  my $wc = new CoW::Size ( rel => '_parent::w', val => 1/$x);
  my $lc = new CoW::Size ( rel => '_parent::l', val => 1/$y);
  my $hc = new CoW::Size ( rel => '_parent::h', val => 1/$z);
  my ($xc,$yc,$zc,$zi,$xi,$yi);
  my $xs = 1 / $x; my $ys = 1 / $y; my $zs = 1 / $z;
  for ($zi = 0; $zi < $z; $zi++)
    {
    for ($yi = 0; $yi < $y; $yi++)
      {
      for ($xi = 0; $xi < $x; $xi++)
        {
        $xc = new CoW::Coord ( rel => '_parent::l,_parent::x', 
           val => $xs*$xi-$xs/2 );
        $yc = new CoW::Coord ( rel => '_parent::w,_parent::y', 
           val => $ys*$yi-$ys/2, );
        $zc = new CoW::Coord ( rel => '_parent::h,_parent::z', 
           val => $zs*$zi-$zs/2, );
        $self->insert ( CoW::Cell->new ( name => 'cell_$xi_$yi_$zi',
          w => $wc, l => $lc, h => $hc, 
          x => $xc, y => $yc, z => $zc,
          )); 
        }
      }
    }
  return $self; 
  }

1; 

__END__

#############################################################################

=pod

=head1 NAME

CoW::Cell -- a (cubic) part of the world

=head1 SYNOPSIS

	use CoW::Cell;

	$world = new CoW::Cell ( x => 0, y => 0, z => 0, 
         w => 128, l => 128, h => 64 );

	print $world->split ( 0, 3 );	# in x axis

=head1 REQUIRES

perl5.005, L<Bunch|Bunch>, L<Thing|Thing>, L<Constants::CoW|Constants::CoW>

=head1 EXPORTS

Exports nothing.

=head1 DESCRIPTION

This represents a (cubic) part of the world. This can be a room (like a 
kitchen, sitting room etc) or a house, or a city part etc. A I<Cell> can 
contain additional other cells or L<Things|Thing> or L<Brushes|Brush> or
L<Objects|Object>. 

=head1 FIELDS

=over 2

=item shape

The shape of the cell, currently only CUBE.

=item kind

The fill type of the cell. This can be AIR (the default), SOLID, WATER etc.
Additional, it can be EMPTY (nothing will be generated in that cell).
Upon generating, the cell will filled (except for EMPTY cells) with a 
brush of the size of the cell and this kind.

=back

=head1 METHODS

=head2 split

	$cell->split($direction, $fit, $amount, $size1, $size2,);

C<split> first removes all contained things, then splits the cell into a row of
new cells. 
Direction is the axis to split along, 0 for X, 1 for Y and 2 for Z.

Sizes < 1 will be taken as relative sizes, their sum should be 1.0
(or else not all cells will fit in or some space will be left over). 
If you set $fit != 0, then the last cell will be grown or shrunk so that the
sum of their sizes equals 1.0.

If the first size is >= 1.0, the the sizes will be taken as parts. This means
that first the sum of all parts is computed, then each cell gets a size
according to the given size. Missing sizes will be assumed to have a partsize
of 1.

	# Cell layout: |----|----|--------|
	# Parts:
	#   4     4     8      => 16 and 1/4, 1/4 and 2/4 as relative sizes
	# relative sizes:
	#   1/4  1/4   2/4

Both calls below have the same effect:

	$cell->split(IN_X,3,0, 4,4,8);
	$cell->split(IN_X,3,1, 1/4,1/4);	# last is done automatically

The following would create a reversed layout:

	$cell->split(IN_X,3,1, 2);	# last two 1's are assumed due to fit

Splitting the cell into different shapes than along one axis are not done yet,
except C<grid>.  But you can do it manually by inserting cells via C<insert()>.
	
=head2 grid

	$cell->grid($x,$y,$z);

C<grid> first removes all contained things, then splits the cell into a 3D
grid of new cells. The three parameters give the number of cells along the X,
Y and Z axis and default to 1. Each cell will have the same size. The cells
will be created in a nested order, first Z, then Y, then X. So, if you split a
cell into 2x2x2 cells, the following order will be created:
	

	Cell 0 is invisible in this picture: 

	          /-------/-------/
	         /       /	 /|
	        /       /       / |
	       /   4   /   6   /  |
	      /	      /       /   |
	     /-------/-------/   /|
	    / 	    /	    /| 6/ |
	   /   	   /       / | /  /
	  /   5   /   7   /  |/ 2/
	 /	 /       /   |  /
	|-------|-------| 7 /| /
	|	|	|  / |/
	|   1	|   7   | /  |
	|-------|-------|/3 /
	|   1 	|   3   |  /
	|	|	| /
        |-------|-------|/

The cells will be named according to their coordinates, so that cell '0' in the
example above will be 'cell_0_0_0' and cell 7 would be 'cell_1_1_1'.

Examples:

	$cell->grid(3,3);	# split room into 3x3 rooms
	$cell->grid(1,1,3);	# split a cell (tower etc) into three storeys

=head2 shape

Set/gets the brush shape (only CUBE yet).

=head1 BUGS

None known yet.

=head1 AUTHOR

Tels http://bloodgate.com 1999-2000.

=cut

