287 lines
7.6 KiB
Perl
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;
|