X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=reprap-play.git;a=blobdiff_plain;f=commitid.scad.pl;h=62ae5105e07259a2a0a2ec25ad2f69f8aec8b0a0;hp=dcebd26a5c1cd9bf853294d9ec3a63c1e86db7d6;hb=0dd084916e997047d879b074c71b5d40a311f747;hpb=f0820c19256946b5b9bf6af9543b6e60b15037d4 diff --git a/commitid.scad.pl b/commitid.scad.pl index dcebd26..62ae510 100755 --- a/commitid.scad.pl +++ b/commitid.scad.pl @@ -1,75 +1,287 @@ #!/usr/bin/perl -w + +# commitid.scad.pl - a program for annotating solid models with commit info +# Copyright (C)2016 Ian Jackson. See below. There is NO WARRANTY. + + +# USAGE +# ===== +# +# .../commitid.scad.pl [OPTION...] [STRING...] >commitid.scad.new \ +# && mv -f commitid.scad.new commitid.scad +# +# Run without arguments, commitid.scad.pl will output an openscad file +# which contains 2D and 3D models of the current git commit count and +# commit object id (commit hash), useful for identifying printed +# parts. +# +# See below for details. You probably want these two sections, as a +# quick starting point: +# General form of provided openscad modules +# Autoscaling modules +# +# We can also generate models of short mainly-numeric strings +# specified on the command line. +# +# +# Options: +# +# --git Generate git commit indications, as shown below +# (this is the default if no strings are requested with -t). +# Ie, produce the `Autoscaling modules' and `Specific layouts'. +# +# --git=objid +# Generate git commit indication based on commit object only +# (ie avoid counting commits). Ie, do not generate `Small' +# and `Full' layouts (and never select them for `Best'). +# +# -i Do not generate `+' dirty indication if git-untracked files +# are present (ie, missing .gitignore entries). The `*' +# dirty tree indication (for modified files) cannot be disabled. +# +# [-t[LAYOUT]] TEXT +# Generate a layout LAYOUT containing TEXT. TEXT can +# contain newlines (a final newline usually undesirable, as +# it will generate a blank line). If LAYOUT is not specified, +# generates Arg0, Arg1, Arg2, etc., for successive such +# TEXTs. The permissible character set in is TEXT is: +# space 0-9 a-f + * +# +# +# OPENSCAD INTERFACE +# ================== +# +# Dynamic variables for configuration +# ----------------------------------- +# +# We honour the following variables to control various scaling factors: +# +# default value notes +# $Commitid_pixelsz 0.8 \ multiplied together +# $Commitid_scale 1.0 / +# $Commitid_depth pixelsz/2 \ multiplied together +# $Commitid_depth_scale 1.0 / +# $Commitid_max_best_scale 2.0 limits XY scaling in *Best* +# +# FYI the font is nominally 3x5 pixels, with 1-pixel inter-line and +# inter-character gaps. (It's not strictly speaking a 3x5 bitmap +# font, size it contains partial pixels and diagonals.) +# +# +# Non-`module'-specific functions +# ------------------------------- +# +# We provide the following functions (which depend on the config +# variables, but not on anything else) and compute useful values: +# +# function Commitid_pixelsz() Actual size of each nominal pixel +# function Commitid_depth() Depth to use (the amount characters +# should be raised or sunken) +# +# General form of provided openscad modules +# ----------------------------------------- +# +# module Commitid_MODULE_2D(...) Collection of polygons forming characters +# module Commitid_MODULE(...) The above, extruded up and down in Z +# module Commitid_MODULE_M_2D(...) Mirror writing +# module Commitid_MODULE_M(...) 3D mirror writing +# function Commitid_MODULE_sz() A 2-vector giving the X,Y size +# +# Except for *Best* modules, the XY origin is in the bottom left +# corner without any margin. Likewise Commitid_MODULE_sz does not +# include any margin. +# +# For 3D versions, the model is 2*depth deep and the XY plane bisects +# the model. This means it's convenient to either add or subtract from +# a workpiece whose face is in the XY plane. +# +# The _M versions are provided to avoid doing inconvenient translation +# and rotation to get the flipped version in the right place. +# +# +# Autoscaling modules +# ------------------- +# +# These modules take a specification of the available XY space, and +# select and generate a suitable specific identification layout: +# +# module Commitid_BestCount_2D (max_sz, margin=Commitid_pixelsz()) +# module Commitid_BestCount (max_sz, margin=Commitid_pixelsz()) +# module Commitid_BestCount_M_2D(max_sz, margin=Commitid_pixelsz()) +# module Commitid_BestCount_M (max_sz, margin=Commitid_pixelsz()) +# module Commitid_BestObjid_2D (max_sz, margin=Commitid_pixelsz()) +# module Commitid_BestObjid (max_sz, margin=Commitid_pixelsz()) +# module Commitid_BestObjid_M_2D(max_sz, margin=Commitid_pixelsz()) +# module Commitid_BestObjid_M (max_sz, margin=Commitid_pixelsz()) +# +# max_sz should be [x,y]. +# +# BestCount includes (as much as it can of) the git commit count, +# ie the result of +# git rev-list --first-parent --count HEAD +# (and it may include some of the git revision ID too). +# +# BestObjid includes as much as it can of the git commit object hash, +# and never includes any of the count. +# +# All of these will autoscale and autorotate the selected model, and +# will include an internal margin of the specified size (by default, +# one pixel around each edge). If no margin is needed, pass margin=0. +# +# There are no `function Commitid_Best*_sz'. If they existed they +# would simply return max_sz. +# +# +# Output format +# ------------- +# +# In general the output, although it may be over multiple lines, +# is always in this order +# git commit object id (hash) +# dirty indicator +# git commit count +# +# Not all layouts have all these parts. The commit object id may +# sometimes be split over multiple lines, but the count will not be. +# If both commit id and commit count appear they will be separated +# by (at least) a newline, or a dirty indicator, or a space. +# +# The commit id is truncated to fit, from the right. +# +# The commit count is truncated from the _left_, leaving the least +# significant decimal digits. +# +# The dirty indicator can be +# +# * meaning the working tree contains differences from HEAD +# +# + meaning the working tree contains untracked files +# (ie files you have failed to `git add' and also failed +# to add to gitignore). (But see the -i option.) +# +# +# Specific layouts +# ---------------- +# +# If you want to control the exact layout (and make space for it in +# your design), you can use these: +# +# module Commitid_LAYOUT_2D() +# module Commitid_LAYOUT() +# module Commitid_LAYOUT_M_2D() +# module Commitid_LAYOUT_M() +# function Commitid_LAYOUT_sz() +# +# Here LAYOUT is one of the following (giving for example, `module +# Commitid_Full8_2D'). In the examples, we will assume that the tree +# is dirty, the commit count is 123456, and the commit object id +# starts abcdeffedbcaabcdef... In the examples `_' shows where a +# space would be printed. +# +# Small2 Small3 ... Small9 Small10 Small12 Small14 Small16 +# A single line containing as much of the count will fit, eg: +# Small5 3456* +# Small8 _*123456 +# The objectid is included if more than one character of of it +# will fit without makign the output ambiguous: +# Small9 ab*123456 +# +# Small2S Small4S ... Small16S +# Small3T Small9T Small12T +# Same as Small but split into two lines (S) +# or three lines (T). Eg: +# Small4S *4 Small6T _* +# 56 34 +# 56 +# Git2 Git3 ... Git9 Git10 Git12 Git14 Git16 +# Git4S Git6S ... Git16S +# Git6T Git9T Git12T +# Just the commit object hash, in one, two (S) or three (T) +# lines. E.g.: +# Git5 abcd* +# +# Full4 Full6 ... Full20: +# The commit object hash plus the commit count, on +# separate lines, eg: +# Full12 abcdef Full16 abcdeffe +# *23456 _*123456 +# +# Full6T Full9T ... Full30T +# As Full but the commit object id is split over two lines +# producing a 3-line layout, eg: +# Full9T abc Full21T abcdeff +# de* edbcaa* +# 456 _123456 +# +# Other LAYOUTs +# ------------- +# +# FontDemo +# +# A demonstration of the built-in 18-character font +# +# Arg0 Arg1, ... +# +# Strings passed on command line (without -t, or bare -t, +# rather than with -tLAYOUT). +# +# LAYOUT +# +# Generated by passing -tLAYOUT on the command line. +# + + +# COPYRIGHT, LICENCE AND LACK-OF-WARRANTY INFORMATION +# =================================================== +# +# This program is Free Software and a Free Cultural Work. +# +# You can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Alternatively, at your option: +# +# This work is licensed under the Creative Commons +# Attribution-ShareAlike 4.0 International License. +# +# There is NO WARRANTY. + + use strict; $SIG{__WARN__} = sub { die @_; }; -# xxx much of the comment below is TODO - -# We generate a physical indication of which commit was used. -# -# We provide for scaling factors with dynamic variables: -# $Commitid_pixelsz if not set, we use 0.4 } multiplied -# $Commitid_scale if not set, we use 1.0 } together -# $Commitid_depth if not set, we use xy pixel size from above -# $Commitid_depth_scale if not set, we use 1.0 (multiplies depth above) -# -# For each form we have -# -# module Commitid_Form_2D(.4) -# module Commitid_Form() -# -# And we provide -# -# function Commitid_pixelsz() // $Commitid_pixelsz * $Commitid_scale -# function Commitid_depth() // see above -# -# We can generate these forms: -# -# Tiny3: -# Tiny4: -# Tiny5: -# Tiny6: -# Tiny7: -# Tiny8: -# git rev-list --first-parent --count HEAD -# typically 3-4 characters but we allow for up to 6 -# eg -# Tiny4 1070 -# -# Tiny4Q: -# Tiny6Q: -# Tiny9Q: -# same but in two lines eg -# Tiny4Q 10 -# 70 -# -# Git4 -# Git6 -# Git8 -# git-rev-parse HEAD -# eg -# Git6 82f2a2 - -# Small4 -# Small6 -# Small8 -# git-rev-list --first-parent --count HEAD -# git-rev-parse HEAD -# eg -# Small6 1070 -# 82f2a2 +our $debug=0; + +if (@ARGV && $ARGV[0] =~ m/^-(D+)$/) { + $debug = length $1; + shift @ARGV; +} sub p { print @_ or die $!; } +sub p_debug { print STDERR @_ if $debug; } + p <<'END'; +// *** AUTOGENERATED - DO NOT EDIT *** // function Commitid_pixelsz() = - ($Commitid_pixelsz ? $Commitid_pixelsz : 0.4) * + ($Commitid_pixelsz ? $Commitid_pixelsz : 0.8) * ($Commitid_scale ? $Commitid_scale : 1.0); function Commitid_depth() = - ($Commitid_depth ? $Commitid_depth : Commitid_pixelsz()) * + ($Commitid_depth ? $Commitid_depth : Commitid_pixelsz()/2) * ($Commitid_depth_scale ? $Commitid_depth_scale : 1.0); function Commitid__scale() = - Commitid_pixelsz() / 0.1; + Commitid_pixelsz() / 0.2; END sub chrmodname ($) { @@ -78,6 +290,249 @@ sub chrmodname ($) { return "Commitid__chr_$chrx"; } +our $gtm_demo_i = -1; +our $gtm_demo_j; +our @gtm_demo_o; + +sub gentextmodule_demo_start_batch () { + $gtm_demo_j = 0; + $gtm_demo_i++; +} + +sub argl_formal (@) { join ', ', @_; } +sub argl_actual (@) { join ',', map { m/=/ ? $` : $_ } @_; } + +sub gen3dmodule ($@) { + my ($modb,$size,@argl) = (@_); + $size ||= "${modb}_sz()"; + p "module ${modb}_M_2D(".argl_formal(@argl)."){\n"; + p " translate([${size}[0],0])\n"; + p " mirror([1,0,0])\n"; + p " ${modb}_2D(".argl_actual(@argl).");\n"; + p "};\n"; + foreach my $mir ('','_M') { + my $mm = "${modb}${mir}"; + p "module ${mm}(".argl_formal(@argl)."){\n"; + p " d=Commitid_depth();\n"; + p " translate([0,0,-d]) linear_extrude(height=d*2)\n"; + p " ${mm}_2D(".argl_actual(@argl).");\n"; + p "}\n"; + } +} + +sub gentextmodule ($@) { + my ($form, @lines) = @_; + my $modb = "Commitid_$form"; + p "module ${modb}_2D(){\n"; + p " // |$_|\n" foreach @lines; + p " scale(Commitid__scale()){\n"; + my $y = @lines; + my $cols = 1; + foreach my $line (@lines) { + $y--; + my $x = 0; + foreach my $chr (split //, $line) { + p sprintf " translate([%d * 0.8, %d * 1.2]) %s();\n", + $x, $y, chrmodname $chr + if $chr =~ m/\S/; + $x++; + } + $cols = $x if $x > $cols; + } + p " }\n"; + p "}\n"; + gen3dmodule($modb,''); + + p sprintf "function %s_sz() = Commitid__scale() * 0.1 * [ %d, %d ];\n", + $modb, 2 * ($cols * 4 - 1), 2 * (@lines * 6 - 1); + + push @gtm_demo_o, <commitid-DEBUG-simplify-$chr.ps"; + print S "%!\n"; + print S "(Courier-Bold) findfont 15 scalefont setfont\n"; + + $prcount=0; +} + +sub debug_simplify_done () { + return unless $debug; + print S "showpage\n"; + close S or die $!; +} + +sub debug_simplify_pr ($$$) { + my ($chr,$polys,$why) = @_; + + return unless $debug; + + print STDERR "PR $chr $why\n"; + my $ct_x = 10000 * ($prcount % 6); + my $ct_y = 18000 * int($prcount / 6); + printf S "0 setgray\n"; + printf S "%d %d moveto\n", map {$_/100 + 10} $ct_x,$ct_y; + printf S "(%s) show\n", $why; + my $pr_recur; + + $pr_recur = sub { + my ($tpolys, @levels) = @_; + return unless @$tpolys; + foreach my $i (0..$#$tpolys) { + printf STDERR "P@levels %02d :", $i; + my $pinfo = $tpolys->[$i]; + my $p = $pinfo->{E}; + printf STDERR "@$p\n"; + my $lw = 5 - 4*($i / ($#$tpolys || 1)); + my $pp = sub { + my $spec = $p->[$_[0]]; + $spec =~ m/^\d{5}/; + sprintf "%d %d",map { $_/100 } + 1000 + $ct_x + $&, + 5000 + $ct_y + $'; + }; + printf S "%s setrgbcolor\n", (@levels==0 ? '0 0 0' : + @levels==1 ? '0 0 1' + : '1 1 0'); + foreach my $eai (0..$#$p) { + my $ebi = ($eai + 1) % @$p; + printf S <($eai), $pp->($ebi); + %f setlinewidth + %s moveto + %s lineto + stroke +END + } + $pr_recur->($pinfo->{Holes}, @levels, $i); + } + }; + $pr_recur->($polys,0); + + $prcount++; +} + +sub simplify ($$) { + my ($chr,$polys) = @_; + use Data::Dumper; + + return unless @$polys; + + my $count=0; + my $pr = sub { }; + + if ($debug) { + debug_simplify_begin($chr); + } + + $pr->("start"); + + AGAIN: while(1) { + my %edges; + my $found_hole; + + foreach my $pi (0..$#$polys) { + my $p = $polys->[$pi]{E}; + foreach my $ei (0..$#$p) { + my $e = $p->[$ei].$p->[($ei+1) % @$p]; + die if $edges{$e}; + $edges{$e} = [ $p, $pi, $ei ]; + } + } + p_debug "AGAIN $count\n"; + my $merge = sub { + my ($pa, $pai, $eai, $pb, $pbi, $ebi) = @_; + p_debug "# merging $pai:$eai.. $pbi:$ebi..\n"; + splice @$pa, $eai, 1, + ((@$pb)[$ebi+1..$#$pb], (@$pb)[0..$ebi-1]); + @$pb = ( ); + }; + foreach my $pai (0..$#$polys) { + my $painfo = $polys->[$pai]; + my $pa = $painfo->{E}; + foreach my $eai (0..$#$pa) { + my $ear = $pa->[ ($eai+1) % @$pa ].$pa->[$eai]; + my $ebi = $edges{$ear}; + next unless $ebi; + my ($pb,$pbi); + ($pb, $pbi, $ebi) = @$ebi; + # $pai:($eai+1)..$eai and $pbi:$ebi..($ebi+1) are identical + # so we want to remove them. + if ($pai==$pbi) { + # we're making a hole! we make an assumption: + # holes have fewer line segments than the + # outlines. This is almost always true because of + # the way we construct our figures. + if (($ebi - $eai + @$pa) % @$pa > @$pa/2) { + # We arrange that $eai..$ebi is the hole + ($ebi,$eai) = ($eai,$ebi); + } + p_debug "HOLE $eai $ebi\n"; + # we want to make the smallest hole, to avoid + # making a hole that itself needs simplifying + my $holesz = ($ebi - $eai + @$pa) % @$pa; + $found_hole = [ $pa,$pai,$eai, $ebi, $holesz ] + unless $found_hole && $found_hole->[4] < $holesz; + } else { + $merge->($pa,$pai,$eai,$pb,$pbi,$ebi); + debug_simplify_pr($chr,$polys,"after $count"); + next AGAIN; + } + } + # we process hole joining last, so that the whole of the + # edge of the hole must be part of the same polygon + if ($found_hole) { + p_debug "HOLE DOING @$found_hole\n"; + my ($pa,$pai,$eai,$ebi) = @$found_hole; + # simplify the indexing + @$pa = ((@$pa)[$eai..$#$pa], (@$pa)[0..$eai-1]); + $ebi -= $eai; $ebi += @$pa; $ebi %= @$pa; + $eai = 0; + push @{ $painfo->{Holes} }, { + E => [ (@$pa)[$eai+1..$ebi-1] ], + Holes => [ ], + }; + splice @$pa, $eai, $ebi-$eai+1; + debug_simplify_pr($chr,$polys,"hole $count"); + next AGAIN; + } + } + last; + } + + debug_simplify_done(); +} + +sub p_edgelist ($$$) { + my ($points,$vecs,$p) = @_; + my @vec; + foreach my $pt (@$p) { + $pt =~ s{\d{5}}{$&,}; + $pt =~ s{\b\d}{$&.}g; + push @$points, "[$pt]"; + push @vec, $#$points; + } + push @$vecs, \@vec; +} + sub parsefont () { my %cellmap; for (;;) { @@ -89,6 +544,9 @@ sub parsefont () { $cellmap{$1} = $_; } my %chrpolys; + # $chrs{$chr}[$poly] = $poly + # $poly->{E} = [ "012345012345", ... ] + # $poly->{Holes} = $poly2 while () { next unless m/\S/; chomp; @@ -113,9 +571,9 @@ sub parsefont () { } elsif (s{^\S}{}) { my $f = $cellmap{$&}; die unless $f; - $f =~ s/\b\d/ sprintf '%05d', $col*2000 + $&*1025 /ge; - $f =~ s/\d\b/ sprintf '%05d', $row*2000 + $&*1025 /ge; - push @{ $chrpolys{$chr} }, [ split / /, $f ]; + $f =~ s/\b\d/ sprintf '%05d', $col*2000 + $&*1000 /ge; + $f =~ s/\d\b/ sprintf '%05d', $row*2000 + $&*1000 /ge; + push @{ $chrpolys{$chr} }, { E => [ split / /, $f ] }; } else { die "$_ ?"; } @@ -125,40 +583,304 @@ sub parsefont () { } } - my @demo; + my $demo = ''; my $democols = 6; foreach my $chr (sort keys %chrpolys) { + + my $polys = $chrpolys{$chr}; + $_->{Holes} = [] foreach @$polys; + + simplify($chr,$polys); + my $mod = chrmodname $chr; p "module $mod () {\n"; - foreach my $poly (@{ $chrpolys{$chr} }) { - p " polygon(["; - my $delim = ""; - foreach my $pt (@$poly) { - p $delim; - $pt =~ s{\d{5}}{$&,}; - $pt =~ s{\b\d}{$&.}g; - p "[$pt]"; - $delim = ','; + foreach my $poly (@$polys) { + p " polygon("; + my $holes = $poly->{Holes}; + my (@points, @vecs); + p_edgelist(\@points, \@vecs, $poly->{E}); + foreach my $hole (@$holes) { + p_edgelist(\@points, \@vecs, $hole->{E}); + } + p "points=[".(join ",",@points)."],"; + if (@$holes) { + p ",paths=[".(join ",", + map { "[".(join ",",@$_)."]" } + @vecs)."],"; } - p "]);\n"; + p "convexity=4);\n"; } p "}\n"; - my $px = @demo % $democols; - my $py = int(@demo / $democols); - push @demo, " scale( Commitid__scale() ) translate([$px * 0.800, $py * 1.200]) - $mod ();\n"; + $demo .= $chr; } - p "module Commitid_FontDemo(){\n"; - p $_ foreach @demo; + @demo = reverse $demo =~ m{.{1,$democols}}go; +} + +parsefont(); + +our $do_git; # contains may chars 'c' (count) and/or 'o' (object) +our $do_git_untracked = 1; +our $argcounter; + +our @forms; +our %included; # 0 = not at all; 1 = truncated; 2 = full + +sub rjustt ($$$;$) { + # right justify and truncate (ie, pad and truncate at left) + # always includes prefix + # sets $included{$what} + my ($sz, $what, $whole, $prefix) = @_; + $prefix //= ''; + my $lw = length $whole; + my $spare = $sz - $lw - (length $prefix); + $included{$what}= 1 + ($spare > 0); + return + ($spare > 0 ? (' ' x $spare) : ''). + $prefix. + substr($whole, ($spare < 0 ? -$spare : 0)); +} + +sub ljustt ($$$;$) { + my ($sz, $what, $whole, $suffix) = @_; + $suffix //= ''; + $sz -= length $suffix; + $included{$what} = 1 + ($sz >= length $whole); + return sprintf "%-${sz}.${sz}s%s", $whole, $suffix; +} + +sub genform_prep() { + $included{$_}=0 foreach qw(Objid Count); +} + +sub genform ($@) { + my ($form, @lines) = @_; + gentextmodule($form, @lines); + my $f = { + Form => $form, + Chars => (length join '', @lines), + Lines => (scalar @lines), + Ambiguous => ($form =~ m/Full/ && !grep { m/\W/ } @lines), + Included => { %included }, + }; + push @forms, $f; +} + +sub genform_q ($$$) { + my ($form, $s, $lines) = @_; + $gtm_demo_j++; + my $l = length $s; + return if $l % $lines; + my $e = $l/$lines; + return if $e < 2; + $gtm_demo_j--; + genform($form, $s =~ m/.{$e}/g); +} + +sub genform_plusq ($$) { + my ($form, $s) = @_; + genform($form, $s); + genform_q("${form}S", $s, 2); + genform_q("${form}T", $s, 3); +} + +our @gcmd; + +sub gitrun_start () { + open F, "-|", @gcmd or die "$gcmd[0]: start: $!"; +} + +sub gitrun_done (;$) { + my ($errok) = @_; + $?=0; $!=0; + return if close F; + return if $errok; + die $! if $!; + die "@gcmd failed ($?)\n"; +} + +sub gitoutput (@) { + (@gcmd) = (qw(git), @_); + gitrun_start; + $_ = ; + gitrun_done; + defined or die "@gcmd produced no output"; + chomp or die "@gcmd produced no final newline"; + $_; +} + +sub do_git () { + return unless $do_git; + + @gcmd = qw(git status --porcelain); + push @gcmd, qw(--untracked=no) unless $do_git_untracked; + + my $git_dirty = ''; + gitrun_start; + while () { + if (m/^\?\?/ && $do_git_untracked) { + $git_dirty = '+'; + next; + } + $git_dirty = '*'; + last; + } + gitrun_done($git_dirty eq '*'); + + my $git_count; + my $git_object; + + if ($do_git =~ m/c/) { + $git_count = gitoutput qw(rev-list --first-parent --count HEAD); + } + if ($do_git =~ m/o/) { + $git_object = gitoutput qw(rev-parse HEAD); + } + print STDERR join ' ', map { $_ // '?' } + "-- commitid", $git_object, $git_dirty, $git_count, "--\n"; + + foreach my $sz (2..10, qw(12 14 16)) { + gentextmodule_demo_start_batch(); + + if (defined($git_count)) { + genform_prep(); + my $smallstr = rjustt($sz, 'Count', $git_count, $git_dirty); + my $forgitobj = $sz - length($git_count) - 1; + if (defined($git_object) && $forgitobj >= 2) { + $smallstr = ljustt($forgitobj, 'Objid', $git_object). + ($git_dirty || ' '). + $git_count; + } + genform_plusq("Small$sz", $smallstr); + } + + genform_prep(); + genform_plusq("Git$sz", ljustt($sz, 'Objid', $git_object, $git_dirty)) + if defined $git_object; + + if (defined $git_count && defined $git_object && $sz<=10) { + genform_prep(); + genform("Full".($sz*2), + ljustt($sz, 'Objid', $git_object), + rjustt($sz, 'Count', $git_count, $git_dirty)); + + genform_prep(); + my $e = $sz; + genform("Full".($e*3)."T", + ljustt($e*2, 'Objid', $git_object, $git_dirty) + =~ m/.{$e}/g, + rjustt($e, 'Count', $git_count)); + } + } +} + +sub do_some_best ($$) { + my ($bestwhat, $formre) = @_; + my $modname = "Best$bestwhat"; + my $fullmodname = "Commitid_${modname}_2D"; + my @argl = qw(max_sz margin=Commitid_pixelsz()); + p "module $fullmodname(".argl_formal(@argl).") {\n"; + my $mbs = '$Commitid_max_best_scale'; + p " sc_max = $mbs ? $mbs : 2;\n"; + p " sz = max_sz - 2*[margin,margin];\n"; + my @do; + foreach my $f ( + sort { + $b->{Included}{$bestwhat} <=> $a->{Included}{$bestwhat} or + $b->{Chars} <=> $a->{Chars} or + $a->{Lines} <=> $b->{Chars} + } + grep { + $_->{Form} =~ m/$formre/ && + !$_->{Ambiguous} + } + @forms + ) { + my $form = $f->{Form}; + p " sz_$form = Commitid_${form}_sz();\n"; + foreach my $rot (qw(0 1)) { + my $id = "${form}_r${rot}"; + p " sc_$id = min(sc_max"; + foreach my $xy (qw(0 1)) { + p ",sz[$xy]/sz_$form","[",(($xy xor $rot)+0),"]"; + } + p ");\n"; + push @do, " if (sc_$id >= 1.0"; + push @do, " && sc_$id >= sc_${form}_r1" if !$rot; + push @do, ") {\n"; + push @do, " translate([margin,margin]) scale(sc_$id)\n"; + push @do, " rotate(90) translate([0,-sz_$form"."[1]])\n" if $rot; + push @do, " Commitid_${form}_2D();\n"; + push @do, " } else"; + } + } + push @do, <#< \## # # # # # # # # # # # # # @@ -186,3 +908,10 @@ a b c d e f # # # # # # # #/ # \## ##/ \## \## \#/ # ++ * + + # # + # \#/ +### ### + # /#\ + # #