manticore/Manticore/Mantis.pm

287 lines
7.6 KiB
Perl

package Manticore::Mantis;
use strict;
use warnings;
use Math::Trig;
use Manticore::Layer;
our %font;
my %curvefunctions = (
curve => sub {
my $ratio = shift;
return ($ratio, 1-$ratio, $ratio)
},
smooth => sub {
my $ratio = shift;
my $sr = 0;
if($ratio < 0) {
$sr = 0
} elsif($ratio > 1) {
$sr = 1
} else {
my $sq = $ratio*$ratio;
$sr = -2*$ratio*$sq+3*$sq
}
return ($ratio, 1-$ratio, $sr);
},
circ => sub {
# On a right angled curve point that has both ends at the same location we get a perfect circle
# formula derived on a sheet of paper
my $ratio = shift;
my $pirat = $ratio*pi/2;
my $s = sin $pirat;
my $c = cos $pirat;
my $ratV = ($c + $s*($s-1)/$c);
my $ratU = ($s - $c*(1-$c)/$s);
my $dxU = $s*($s-1)/$c;
my $dyU = 1-$s;
my $dxV = 1-$c;
my $dyV = $c*(1-$c)/$s;
my $l2 = sqrt($dxU*$dxU + $dyU*$dyU);
my $l1 = sqrt($dxV*$dxV + $dyV*$dyV);
#print "[[ $ratio -> $ratU $ratV $l1 $l2 ]]\n";
return
(
$ratU,
$ratV,
$l1/($l1+$l2)
#$ratio
)
},
);
my $re_curves = join '|', keys %curvefunctions;
$re_curves = qr((?:$re_curves));
# split into ===-separated blocks
sub splitIntoBlocks {
my $inp = shift;
my @blocks = ([]);
for my $line(@$inp) {
if($line=~m#^===#) {
push @blocks, [$line]
} else {
push @{$blocks[-1]}, $line
}
}
return \@blocks
}
# returns a layer read from a file
sub loadFile {
my $fn = shift;
my $fh = undef;
open($fh, '<', $fn) or die "Could not read file '$fn', because: $!";
# my @blocks = ([]);
# while(my $line = <$fh>) {
# chomp $line;
# if($line=~m#^===#) {
# push @blocks, [$line]
# } else {
# push @{$blocks[-1]}, $line
# }
# }
my $blocks = splitIntoBlocks([map {chomp} <$fh>]);
my ($head) = @{shift @$blocks};
my @blocks = map {loadBlock($_)} @$blocks;
if(1==@blocks) {
return [$head, $blocks[0]]
} else {
return [$head, Manticore::Layer->fromText('traverse', '', \@blocks)]
}
}
# format
# === name
# cont
# cont
# !=== name
# !cont
# !=== name
# !cont
sub loadBlock {
my $block = shift;
my @block = @$block;
my @this = ();
while(@block and $block[0]!~m#^!#) {
push @this, shift @block
}
s#^!## for @block;
my $subblocks = splitIntoBlocks(\@block);
shift @$subblocks;
my @children = map {loadBlock($_)} @$subblocks;
my $head = shift @this;
if($head=~m#===\s*(\S+)#) {
$head = $1
} else {
$head = "traverse"
}
Manticore::Layer->fromText($head, (join "\n", @this), \@children);
}
sub fontLoader {
my %p = @_;
my $lambdaw2a = $p{lambdaw2a} || 0.46;
my $lambdaw2b = $p{lambdaw2b} || 0.28;
%font = (
W2=>{
data => [
[[0+3*$lambdaw2a,0+6*$lambdaw2a], [1+3*$lambdaw2b,0+6*$lambdaw2b], [4,6], [3,6]],
[[3,6], [4,6], [5.5,3.2], [4.5,3.2]],
[[4.5,3.2], [5.5,3.2], [7,6], [6,6]],
[[6,6], [7,6], [10,0], [9,0]],
]
},
);
if(my $dir = $p{dir}) {
my $dh = undef;
opendir($dh, $dir) or die "Could not read directory '$dir', because: $!";
while(my $fn = readdir($dh)) {
next unless $fn=~m#(.*)\.mant$#;
my $gly = $1;
my $fullfile = "$dir/$fn";
#my $fh = undef;
#open($fh, '<', $fullfile) or die "Could not read file '$fullfile', because: $!";
#my @blocks = ([]);
#while(my $line = <$fh>) {
# if($line=~m#^===#) {
# push @blocks, []
# } else {
# push @{$blocks[-1]}, $line
# }
#}
#my ($head) = @{shift @blocks};
#if($head=~m#^(\S+)#) {
# die "Head glyphem identifier does not equal file name in '$fullfile' ('$1' vs. '$gly')" unless $1 eq $gly
#} else {
# die "Malformed head in file $fullfile"
#}
#$font{$gly}{data} = [map {rowText2data(join '', @$_)} @blocks]
# TODO above is old code, below is new code, but not working yet
$font{$gly}{data} = loadFile($fullfile);
}
}
}
sub font { return %font }
# return display data out of a glyphem name
sub glyphemDisplay {
my $gly = shift;
#return [map {[map {@$_} @$_]} @{$font{$gly}{data}}];
my $data = glyphemData($gly);
return undef unless defined $data;
return [map {rowData2display($_)} @$data]
}
# return definition data out of a glyphem name
sub glyphemData {
my $gly = shift;
return $font{$gly}{data};
}
# Glyphems are stored per line and in the modes
# text -- the textual representation
# data -- the complete data representation
# display -- display mode as flattened coordinate list
# Conversion subs are
# rowData2text, rowText2data, rowData2display
# as Display is information rendered into linear vectors there is no rowDisplay2data
sub rowData2text {
my $fg = shift;
my @text = ();
for my $i(0..$#$fg) {
my $point = $fg->[$i];
if('ARRAY' eq ref $point) {
$point = "$point->[0],$point->[1]"
} elsif('HASH' eq ref $point) {
$point = "$point->{fun}($point->{par})";
}
push @text, $point;
}
my $astext = '';
#join ' ', @text;
for(0..$#text) {
$astext .= $text[$_];
if($_ != $#text) {
$astext .= (5 != $_ % 6) ? " " : "\n"
}
}
return $astext;
}
# generate a data row out of a content block
sub rowText2data {
my $content = shift;
my @blocks = ();
$content=~s#(-?[0-9\.]+,-?[0-9\.]+|$re_curves\([0-9]+\))#push @blocks,$1;""#ge;
if($content!~m#^\s*$#) {
warn "Leftover characters: $content"
#print "<<@blocks>> [[$content]]\n"
}
my @lblock = map {
my $ret = undef;
if(m#(.*),(.*)#) {
$ret = [$1,$2]
} elsif(m#([a-z]+)\(([0-9]+)\)#) {
$ret = {fun=>$1, par=>$2}
} else {
die "Illegal data state (\$content was preparsed but that did let pass the illegal string '$_')";
}
$ret
} @blocks;
return \@lblock;
}
sub rowData2display {
my $i = shift;
#return [map {@$_} @$i]
my @dis = @$i;
my @ret = ();
for(0..$#dis) {
my $pre = $dis[$_-4];
my $xy0 = $dis[$_-3];
my $here = $dis[$_-2];
my $xy1 = $dis[$_-1];
my $post = $dis[$_];
if('ARRAY' eq ref $here) {
if('ARRAY' eq ref $xy0 and 'ARRAY' eq ref $xy1) {
push @ret, @$here; # normal point context, we don't do anything fancy
} else {
# intentionally empty as we use this point for context of the function and not for something else
}
} elsif('HASH' eq ref $here) {
if('ARRAY' eq ref $xy0 and 'ARRAY' eq ref $xy1 and 'ARRAY' eq ref $post and 'ARRAY' eq ref $pre) {
my $func = $curvefunctions{$here->{fun}};
my $count = $here->{par};
# Old code state: function is curve, as we do not support anything else so far
# TODO New code state: func contains a function ratio -> ratio in line 1 -> ratio in line 2 -> ratio between lines
#print "===\n";
for my $lambda(1..$count) {
my $preratio = $lambda/($count+1);
#my $s = sin($ratio*pi/2);
#my $c = cos($ratio*pi/2);
my ($ratio, $r1, $rel1) = $func->($preratio);
my $rel2 = 1-$rel1;
#my $r1 = 1-$ratio;
my $xypre = [$pre->[0]*(1-$ratio) + $xy0->[0]*$ratio, $pre->[1]*(1-$ratio) + $xy0->[1]*$ratio];
my $xypost = [$post->[0]*(1-$r1) + $xy1->[0]*$r1, $post->[1]*(1-$r1) + $xy1->[1]*$r1];
my $pushy = [$xypre->[0]*$rel2 + $xypost->[0]*$rel1, $xypre->[1]*$rel2 + $xypost->[1]*$rel1,];
#print "<<$ratio $r1 -- [@$pre !! @$xy0] [@$xy1 !! @$post]\n [@$xypre] [@$xypost]\n [@$pushy]>>\n";
push @ret, @$pushy;
}
} else {
die "Illegal data, probably two functions too close to each other"
}
} else {
die "illegal data: neither ARRAY nor HASH";
}
}
return \@ret
}
1;