=pod =head1 NAME Pedigree::MarriageNode - a marriage in a pedigree =head1 SYNOPSIS use Pedigree::MarriageNode; $node = new Pedigree::MarriageNode(I<%params>); $FSpouse = $node->FSpouse(); $MSpouse = $node->MSpouse(); $consang = $self->isConsanguinic(); $area = $node->SetArea(); $node->CalcAbsCoord(I<$x>, I<$y>); $node->DrawNode(I<$xidst>, I<$ydist>, I<$belowtextfont>, I<$abovetextfont>, I<@fieldsfornode>); $node->DrawConnections(); =head1 DESCRIPTION This package contains data about a marriage. =cut #################################################################### # Define the package # #################################################################### package Pedigree::MarriageNode; use Pedigree; use strict; our @ISA=('Pedigree::Node'); #################################################################### # new # #################################################################### =pod =over 4 =item B(I<%params>); Construct a new node from the given parameters. =cut sub new { my ($class,%params)=@_; my $self=$class->SUPER::new(%params); if (!ref($self)) { # Bad node return 0; } bless ($self,$class); # # Normally marriage nodes are not consanguinic # if (!exists($self->{'Consanguinic'})) { $self->{'Consanguinic'} = 0; } # After we constructed the node, we want to move the kids # from the parent nodes to the marriage node my $selfId = $self->{'Id'}; if (exists($self->{'FSpouse'}) && exists($self->{'MSpouse'})) { my $fspouse = $self->{'FSpouse'}; my $fspouseId = $fspouse->Id(); my $mspouse = $self->{'MSpouse'}; my $mspouseId = $mspouse->Id(); foreach my $kidId (keys %{$self->{'kids_by_parent_id'}->{$fspouseId}}) { if ($main::DEBUG) { print STDERR "Checking kid $kidId for $selfId\n"; } if (exists ($self->{'kids_by_parent_id'}->{$mspouseId}->{$kidId})) { if ($main::DEBUG) { print STDERR "Moving $kidId from $fspouseId and $mspouseId to ". "$selfId\n"; } delete $self->{'kids_by_parent_id'}->{$mspouseId}->{$kidId}; delete $self->{'kids_by_parent_id'}->{$fspouseId}->{$kidId}; $self->{'kids_by_parent_id'}->{$selfId}->{$kidId}=1; } } } return $self; } #################################################################### # FSpouse # #################################################################### =pod =item B(); Get female spouse of a node. =cut sub FSpouse { my $self = shift; return $self->{'FSpouse'}; } #################################################################### # MSpouse # #################################################################### =pod =item B(); Get female spouse of a node. =cut sub MSpouse { my $self = shift; return $self->{'MSpouse'}; } #################################################################### # isConsanguinic # #################################################################### =pod =item B(); Check whether the node is consanguinic =cut sub isConsanguinic { my $self = shift; return $self->{'Consanguinic'}; } #################################################################### # SetArea # #################################################################### =pod =item B(); Calculate relative coordinates for all nodes, that are descendants of the given node I the spouses that form the marriage. We create a Pedigree::Area(3) around the given node and recursively apply the function to all descendants. The subroutine returns the reference to the created area. =cut sub SetArea { my $self = shift; my $area = $self->SUPER::SetArea(); # # Female is to the right, male is to the left unless we have # Sort Order set for anybody. If it is set, the order # is OPPOSITE to the SortOrder # my ($left,$right) = ($self->MSpouse(), $self->FSpouse()); if ($left->SortOrder() <=> $right->SortOrder()) { ($left,$right) = sort {$a->SortOrder() <=> $b->SortOrder()} ($left,$right); } my ($rightRoot,$gen) = @{$right->FindRoot(0,-1)}; $rightRoot->SetRelY($gen); my $rightArea=$rightRoot->SetArea(); $area->AddRight($rightArea); my ($leftRoot,$gen) = @{$left->FindRoot(0,1)}; $leftRoot->SetRelY($gen); my $leftArea=$leftRoot->SetArea(); $area->AddLeft($leftArea); $self->{'Area'}=$area; if ($main::DEBUG) { print STDERR "Setting area for marriage node ",$self->Id(),"\n"; for (my $y=$area->GetYmin(); $y<=$area->GetYmax(); $y++) { print STDERR "\t$y: ", $area->GetXmin($y), ", ", $area->GetXmax($y), "\n"; } } return $area; } #################################################################### # CalcAbsCoor # #################################################################### =pod =item B(I<$x>, $); Set the absolute coordinates of the given node, if the absolute coordinates of the parent node are (I<$x>, I<$y>), and recursively do this for all descendants of this node, and right and left clumps. =cut sub CalcAbsCoor { my $self=shift; my ($x,$y) = @_; $self->SUPER::CalcAbsCoor($x, $y); $x += $self->GetRelX(); $y += $self->GetRelY(); my ($FRoot,undef) = @{$self->FSpouse()->FindRoot(0)}; $FRoot->CalcAbsCoor($x, $y); my ($MRoot,undef) = @{$self->MSpouse()->FindRoot(0)}; $MRoot->CalcAbsCoor($x, $y); return 0; } #################################################################### # DrawNode # #################################################################### =pod =item B(I<$xdist>, I<$ydist>, I<$belowtextfont>, I<$abovetextfont>, I<@fieldsfornode>); Output the command to draw this node. The parameters are distances between the nodes (in cm). =cut sub DrawNode { my $self=shift; my ($xdist, $ydist, $belowtextfont, $abovetextfont, @fieldsfornode) = @_; my $result = '\rput('.($xdist*$self->GetAbsX()).", ". ($ydist*$self->GetAbsY()).'){\pnode{'. $self->Id()."}}\n"; return $result; } #################################################################### # DrawConnections # #################################################################### =pod =item B(); Draw the connections from the given node to its descendants and the spouses =cut sub DrawConnections { my $self = shift; my $xdist = shift; my $ydist = shift; my $result = $self->SUPER::DrawConnections($xdist, $ydist); my $Id=$self->Id(); my $style=""; if ($self->isConsanguinic()) { $style='doubleline=true, '; } foreach my $spouse ($self->FSpouse(), $self->MSpouse()) { if (!ref($spouse)) { next; } my $sId=$spouse->Id(); # Check whether spouse nodes are adjacent to the marriage node. # We do this check only for non-consanguinic unions if (!($self->isConsanguinic()) && (abs($self->GetIndexX() - $spouse->GetIndexX()) > 1)) { my ($nodeA,$nodeB) = sort {$a->GetIndexX() <=> $b->GetIndexX()} ($spouse, $self); my $IdA=$nodeA->Id(); my $IdB=$nodeB->Id(); $result .= "\\ncloop[$style angleA=0, angleB=180, loopsize=". 0.4*$ydist . ', arm=' . 0.4*$xdist . ']{'.$IdA.'}{'.$IdB.'}'."\n"; } else { $result .= "\\ncline[$style]{".$Id.'}{'.$sId.'}'."\n"; } } return $result; } #################################################################### # THE END # #################################################################### =pod =back =head1 ENVIRONMENT The calling program should define B<$main::DEBUG> and set it to 0 or 1. =head1 SEE ALSO pedigree(1), Pedigree(3) =head1 AUTHOR Boris Veytsman, Leila Akhmadeeva, 2006, 2007 =cut 1;