From: Ian Jackson Date: Sat, 16 Sep 2023 11:05:22 +0000 (+0100) Subject: Move a lot of files into the utils tree X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=reprap-play.git;a=commitdiff_plain;h=ebfe6ab1e6d15fa695d8eade7ae74dbee3c2ff99 Move a lot of files into the utils tree Signed-off-by: Ian Jackson --- diff --git a/commitid.scad.pl b/commitid.scad.pl deleted file mode 100755 index 62ae510..0000000 --- a/commitid.scad.pl +++ /dev/null @@ -1,917 +0,0 @@ -#!/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 @_; }; - -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.8) * - ($Commitid_scale ? $Commitid_scale : 1.0); -function Commitid_depth() = - ($Commitid_depth ? $Commitid_depth : Commitid_pixelsz()/2) * - ($Commitid_depth_scale ? $Commitid_depth_scale : 1.0); -function Commitid__scale() = - Commitid_pixelsz() / 0.2; -END - -sub chrmodname ($) { - my ($chr) = @_; - my $chrx = sprintf '%#x', ord $chr; - 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 (;;) { - $_ = // die; - last if %cellmap && !m/\S/; - next unless m/\S/; - chomp; - s{^(.) }{}; - $cellmap{$1} = $_; - } - my %chrpolys; - # $chrs{$chr}[$poly] = $poly - # $poly->{E} = [ "012345012345", ... ] - # $poly->{Holes} = $poly2 - while () { - next unless m/\S/; - chomp; - my @chrs = split / /, $_; - !~ m/\S/ or die; - foreach my $row (reverse 0..4) { - $_ = ; - chomp; - s{^}{ }; - $_ .= ' ' x 8; - m{\S/\S} and die; - s{/(?=\s)}{L}g; - s{/(?=\S)}{r}g; - s{\\(?=\s)}{l}g; - s{\\(?=\S)}{R}g; - p "// $_\n"; - foreach my $chr (@chrs) { - s{^ }{} or die "$chr $_ ?"; - foreach my $col (0..2) { - my @verts; - if (s{^ }{}) { - } elsif (s{^\S}{}) { - my $f = $cellmap{$&}; - die unless $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 "$_ ?"; - } - } - } - die "$_ ?" if m{\S}; - } - } - - 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 (@$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 "convexity=4);\n"; - } - p "}\n"; - $demo .= $chr; - } - @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, < 00 20 22 02 11 -< 00 20 11 22 02 - -0 1 2 3 4 5 6 7 8 9 - -/#\ r /#\ ##\ # # ### /#/ ### /#\ /#\ -# # /# # # # # # # # # # # # -# # # /#/ ##< \## ##\ ##\ // >#< \## -# # # # # # # # # # # # # -\#/ /#\ ### ##/ # ##/ \#/ # \#/ ##/ - -a b c d e f - - # # /## - # /## # /#\ # -/## ##\ # /## #r# ### -# # # # # # # #/ # -\## ##/ \## \## \#/ # - -+ * - - # # - # \#/ -### ### - # /#\ - # # diff --git a/commitid.scad.pl b/commitid.scad.pl new file mode 120000 index 0000000..f19082c --- /dev/null +++ b/commitid.scad.pl @@ -0,0 +1 @@ +diziet-utils/commitid.scad.pl \ No newline at end of file diff --git a/diziet-utils/commitid.scad.pl b/diziet-utils/commitid.scad.pl new file mode 100755 index 0000000..62ae510 --- /dev/null +++ b/diziet-utils/commitid.scad.pl @@ -0,0 +1,917 @@ +#!/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 @_; }; + +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.8) * + ($Commitid_scale ? $Commitid_scale : 1.0); +function Commitid_depth() = + ($Commitid_depth ? $Commitid_depth : Commitid_pixelsz()/2) * + ($Commitid_depth_scale ? $Commitid_depth_scale : 1.0); +function Commitid__scale() = + Commitid_pixelsz() / 0.2; +END + +sub chrmodname ($) { + my ($chr) = @_; + my $chrx = sprintf '%#x', ord $chr; + 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 (;;) { + $_ = // die; + last if %cellmap && !m/\S/; + next unless m/\S/; + chomp; + s{^(.) }{}; + $cellmap{$1} = $_; + } + my %chrpolys; + # $chrs{$chr}[$poly] = $poly + # $poly->{E} = [ "012345012345", ... ] + # $poly->{Holes} = $poly2 + while () { + next unless m/\S/; + chomp; + my @chrs = split / /, $_; + !~ m/\S/ or die; + foreach my $row (reverse 0..4) { + $_ = ; + chomp; + s{^}{ }; + $_ .= ' ' x 8; + m{\S/\S} and die; + s{/(?=\s)}{L}g; + s{/(?=\S)}{r}g; + s{\\(?=\s)}{l}g; + s{\\(?=\S)}{R}g; + p "// $_\n"; + foreach my $chr (@chrs) { + s{^ }{} or die "$chr $_ ?"; + foreach my $col (0..2) { + my @verts; + if (s{^ }{}) { + } elsif (s{^\S}{}) { + my $f = $cellmap{$&}; + die unless $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 "$_ ?"; + } + } + } + die "$_ ?" if m{\S}; + } + } + + 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 (@$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 "convexity=4);\n"; + } + p "}\n"; + $demo .= $chr; + } + @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, < 00 20 22 02 11 +< 00 20 11 22 02 + +0 1 2 3 4 5 6 7 8 9 + +/#\ r /#\ ##\ # # ### /#/ ### /#\ /#\ +# # /# # # # # # # # # # # # +# # # /#/ ##< \## ##\ ##\ // >#< \## +# # # # # # # # # # # # # +\#/ /#\ ### ##/ # ##/ \#/ # \#/ ##/ + +a b c d e f + + # # /## + # /## # /#\ # +/## ##\ # /## #r# ### +# # # # # # # #/ # +\## ##/ \## \## \#/ # + ++ * + + # # + # \#/ +### ### + # /#\ + # # diff --git a/diziet-utils/funcs.scad.cpp b/diziet-utils/funcs.scad.cpp new file mode 100644 index 0000000..c34d157 --- /dev/null +++ b/diziet-utils/funcs.scad.cpp @@ -0,0 +1,86 @@ +// -*- C -*- + +function vecdiff2d(a,b) = [b[0]-a[0], b[1]-a[1]]; +function vecdiff(a,b) = [b[0]-a[0], b[1]-a[1], b[2]-a[2]]; + +#define dsq(i) (a[i]-b[i])*(a[i]-b[i]) +function dist2d(a,b) = sqrt(dsq(0) + dsq(1)); +function dist(a,b) = sqrt(dsq(0) + dsq(1) + dsq(2)); +#undef dsq + +#define vsq(i) (v[i]*v[i]) +function vectorlen2d(v) = sqrt(vsq(0) + vsq(1)); +function vectorlen(v) = sqrt(vsq(0) + vsq(1) + vsq(2)); +#undef vsq + +function unitvector2d(v) = v / vectorlen2d(v); +function unitvector(v) = v / vectorlen(v); + +function atan2vector(v) = atan2(v[1], v[0]); + +// | m[0][0] m[0][1] | +// | m[1][0] m[1][1] | +function determinant2(m) = (m[0][0] * m[1][1] - m[0][1] * m[1][0]); + +function clockwise2d(v) = [v[1], -v[0]]; + +function rotate2d(theta, v) = [ v[0] * cos(theta) - v[1] * sin(theta), + v[1] * cos(theta) + v[0] * sin(theta) ]; + +// intersection of lines p1..p2 and p3..p4 +function line_intersection_2d(p1,p2,p3,p4) = [ + // from https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line + #define XY12 determinant2([p1,p2]) + #define XY34 determinant2([p3,p4]) + #define XU12 QU(0,1,2) + #define XU34 QU(0,3,4) + #define YU12 QU(1,1,2) + #define YU34 QU(1,3,4) + #define QU(c,i,j) determinant2([[ p##i[c], 1 ], \ + [ p##j[c], 1 ]]) + #define DENOM \ + determinant2([[ XU12, YU12 ], \ + [ XU34, YU34 ]]) + //---- + determinant2([[ XY12, XU12 ], + [ XY34, XU34 ]]) / DENOM, + determinant2([[ XY12, YU12 ], + [ XY34, YU34 ]]) / DENOM, + ]; + #undef XY12 + #undef XY34 + #undef XU12 + #undef XU34 + #undef YU12 + #undef YU34 + #undef QU + #undef DENOM + + +function circle_point(c, r, alpha) = [ c[0] + r * cos(alpha), + c[1] + r * sin(alpha) ]; + +#define d (dist2d(a,c)) +#define alpha (atan2(a[1]-c[1],a[0]-c[0])) +#define gamma (asin(r / d)) +#define beta (alpha + 90 - gamma) + +function tangent_intersect_beta(c,r,a) = + beta; + +function tangent_intersect_b(c,r,a) = + circle_point(c, r, beta); +#undef d +#undef alpha +#undef gamma +#undef beta + +function tangents_intersect_beta(cbig,rbig,csmall,rsmall) = + tangent_intersect_beta(cbig,rbig-rsmall,csmall); + +function reflect_in_y(p) = [-p[0], p[1]]; + +function angle_map_range(in,base) = + in < base ? in + 360 : + in >= base + 360 ? in - 360 : + in; diff --git a/diziet-utils/reprap-objects.make b/diziet-utils/reprap-objects.make new file mode 100644 index 0000000..33e92d6 --- /dev/null +++ b/diziet-utils/reprap-objects.make @@ -0,0 +1,86 @@ +# reprap-objects Makefile, reuseable parts +# +# Build scripts for various 3D designs +# Copyright 2012-2016 Ian Jackson +# +# This work is free software: 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 work 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 work. If not, see . + + +HRR=/home/reprap +SLIC3R=$(HRR)/Slic3r/bin/slic3r +M4=m4 +SKEINFORGE=python $(HRR)/reprappro-software.git/skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py + +CWD := $(shell pwd) +PLAY ?= $(CWD) + +AUTO_TOPLEVELS := $(foreach m,$(USING_AUTOS),$(shell $(PLAY)/toplevel-find $m)) + +AUTO_INCS += funcs.scad + +default: autoincs scads + +$(shell set -xe; $(PLAY)/commitid.scad.pl >commitid.scad.tmp; cmp commitid.scad.tmp commitid.scad || mv -f commitid.scad.tmp commitid.scad ) + +autoincs: $(AUTO_INCS) $(AUTO_STLS_INCS) +scads: $(addsuffix .auto.scad, $(AUTO_TOPLEVELS)) +stls: $(addsuffix .auto.stl, $(AUTO_TOPLEVELS)) + +%.auto.scads: %.scad + $(MAKE) $(addsuffix .auto.scad, $(shell $(PLAY)/toplevel-find $*)) +%.auto.stls: + $(MAKE) $(addsuffix .auto.stl, $(shell $(PLAY)/toplevel-find $*)) + +-include .*.d + +%.stl: %.scad $(AUTO_INCS) + openscad -d .$@.d.tmp -o $*.tmp.stl $< + @rm -f $@ + @sed -e 's/\.tmp\.stl:/.stl:/' <.$@.d.tmp >.$@.d + @rm .$@.d.tmp + mv -f $*.tmp.stl $@ + +AUTOBASE=$(shell echo $(1) | perl -pe 's/,\w+\.auto$$//') + +%: %.cpp + cpp -nostdinc -P <$< >$@.tmp && mv -f $@.tmp $@ + +funcs.scad: + +#%.gcode: %.stl +# $(SKEINFORGE) $< + +%.gcode: manual-gcode-generator %.m-g + $(PLAY)/$^ >$@.tmp && mv -f $@.tmp $@ + +%.dxf: %.eps + pstoedit -dt -f "dxf: -polyaslines -mm" $< $@ + +%: %.pl + ./$< >$@.tmp && mv -f $@.tmp $@ + +%: %.m4 + $(M4) -P >$@.tmp $< && mv -f $@.tmp $@ + +.PRECIOUS: %.auto.scad +%.auto.scad: $(PLAY)/toplevel-make Makefile $(PLAY)/toplevel-find + @echo ' write $@' + $< $@ >$@.tmp + @mv -f $@.tmp $@ + +.PRECIOUS: %.stl %.gcode %.eps %.dxf + +clean: + rm -f *~ *.stl *.auto.scad *.gcode .*.d $(AUTO_INCS) + diff --git a/diziet-utils/toplevel-find b/diziet-utils/toplevel-find new file mode 100755 index 0000000..c9174c9 --- /dev/null +++ b/diziet-utils/toplevel-find @@ -0,0 +1,28 @@ +#!/usr/bin/perl -w +use strict; +use IO::File; + +@ARGV==1 or die; +my $base = $ARGV[0]; +$base =~ m/^\-/ and die; + +sub read_file ($); +sub read_file ($) { + my ($fn) = @_; + my $f = new IO::File "$fn", '<' or die "$fn $!"; + while (<$f>) { + if (m#^//// toplevels-from:#) { + defined($_ = <$f>) or die $!; + m#^include\s+\<(\S+)>\s*$# or die; + read_file($1); + next; + } + next unless m#^\s*(?:////\s?)?module\s+(\w+)\b.*////toplevel\b#; + print "$base,$1\n" or die $!; + } + $f->error and die "$fn $!"; +} + +read_file("$base.scad"); +close STDOUT or die $!; + diff --git a/diziet-utils/toplevel-make b/diziet-utils/toplevel-make new file mode 100755 index 0000000..2df668b --- /dev/null +++ b/diziet-utils/toplevel-make @@ -0,0 +1,10 @@ +#!/usr/bin/perl -w +use strict; +die unless @ARGV==1; +die unless $ARGV[0] =~ m/^(.+),(\w+)(?:\..*)?/; +my ($base,$obj) = ($1,$2); +print < +$obj(); +END +close STDOUT or die $!; diff --git a/diziet-utils/utils.scad b/diziet-utils/utils.scad new file mode 100644 index 0000000..e440b5f --- /dev/null +++ b/diziet-utils/utils.scad @@ -0,0 +1,51 @@ +// -*- C -*- + +// suitable for masking things within radius sqrt(2) only +module FArcSegment_mask(beta) { + for (i=[0 : 0.75 : 3]) { + rotate(i*beta/4) + polygon([[0, 0], + [1, 0], + [cos(beta/4), sin(beta/4)]]); + } +} + +module FArcSegment(xc,yc,inrad,outrad,alpha,delta) { + translate([xc,yc]) { + intersection() { + difference() { + circle(r=outrad, $fn=70); + circle(r=inrad, $fn=70); + } + rotate(alpha) scale(outrad*2) { + FArcSegment_mask(delta); + } + } + } +} + +module rectfromto(a,b) { + ab = b - a; + translate([min(a[0], b[0]), min(a[1], b[1])]) + square([abs(ab[0]), abs(ab[1])]); +} +module circleat(c,r) { translate(c) circle(r); } +module linextr(z0,z1, convexity=20) { + translate([0,0,z0]) + linear_extrude(height=z1-z0, convexity=convexity) + children(); +} + +module linextr_x_yz(x0,x1, convexity=20) { // XY turn into YZ + rotate([90,0,0]) + rotate([0,90,0]) + linextr(x0,x1, convexity=convexity) + children(); +} + +module linextr_y_xz(y0,y1, convexity=20) { // XY turn into YZ + rotate([0,0,180]) + rotate([90,0,0]) + linextr(y0,y1, convexity=convexity) + children(); +} diff --git a/funcs.scad.cpp b/funcs.scad.cpp deleted file mode 100644 index c34d157..0000000 --- a/funcs.scad.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// -*- C -*- - -function vecdiff2d(a,b) = [b[0]-a[0], b[1]-a[1]]; -function vecdiff(a,b) = [b[0]-a[0], b[1]-a[1], b[2]-a[2]]; - -#define dsq(i) (a[i]-b[i])*(a[i]-b[i]) -function dist2d(a,b) = sqrt(dsq(0) + dsq(1)); -function dist(a,b) = sqrt(dsq(0) + dsq(1) + dsq(2)); -#undef dsq - -#define vsq(i) (v[i]*v[i]) -function vectorlen2d(v) = sqrt(vsq(0) + vsq(1)); -function vectorlen(v) = sqrt(vsq(0) + vsq(1) + vsq(2)); -#undef vsq - -function unitvector2d(v) = v / vectorlen2d(v); -function unitvector(v) = v / vectorlen(v); - -function atan2vector(v) = atan2(v[1], v[0]); - -// | m[0][0] m[0][1] | -// | m[1][0] m[1][1] | -function determinant2(m) = (m[0][0] * m[1][1] - m[0][1] * m[1][0]); - -function clockwise2d(v) = [v[1], -v[0]]; - -function rotate2d(theta, v) = [ v[0] * cos(theta) - v[1] * sin(theta), - v[1] * cos(theta) + v[0] * sin(theta) ]; - -// intersection of lines p1..p2 and p3..p4 -function line_intersection_2d(p1,p2,p3,p4) = [ - // from https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line - #define XY12 determinant2([p1,p2]) - #define XY34 determinant2([p3,p4]) - #define XU12 QU(0,1,2) - #define XU34 QU(0,3,4) - #define YU12 QU(1,1,2) - #define YU34 QU(1,3,4) - #define QU(c,i,j) determinant2([[ p##i[c], 1 ], \ - [ p##j[c], 1 ]]) - #define DENOM \ - determinant2([[ XU12, YU12 ], \ - [ XU34, YU34 ]]) - //---- - determinant2([[ XY12, XU12 ], - [ XY34, XU34 ]]) / DENOM, - determinant2([[ XY12, YU12 ], - [ XY34, YU34 ]]) / DENOM, - ]; - #undef XY12 - #undef XY34 - #undef XU12 - #undef XU34 - #undef YU12 - #undef YU34 - #undef QU - #undef DENOM - - -function circle_point(c, r, alpha) = [ c[0] + r * cos(alpha), - c[1] + r * sin(alpha) ]; - -#define d (dist2d(a,c)) -#define alpha (atan2(a[1]-c[1],a[0]-c[0])) -#define gamma (asin(r / d)) -#define beta (alpha + 90 - gamma) - -function tangent_intersect_beta(c,r,a) = - beta; - -function tangent_intersect_b(c,r,a) = - circle_point(c, r, beta); -#undef d -#undef alpha -#undef gamma -#undef beta - -function tangents_intersect_beta(cbig,rbig,csmall,rsmall) = - tangent_intersect_beta(cbig,rbig-rsmall,csmall); - -function reflect_in_y(p) = [-p[0], p[1]]; - -function angle_map_range(in,base) = - in < base ? in + 360 : - in >= base + 360 ? in - 360 : - in; diff --git a/funcs.scad.cpp b/funcs.scad.cpp new file mode 120000 index 0000000..d6ea199 --- /dev/null +++ b/funcs.scad.cpp @@ -0,0 +1 @@ +diziet-utils/funcs.scad.cpp \ No newline at end of file diff --git a/reprap-objects.make b/reprap-objects.make deleted file mode 100644 index 33e92d6..0000000 --- a/reprap-objects.make +++ /dev/null @@ -1,86 +0,0 @@ -# reprap-objects Makefile, reuseable parts -# -# Build scripts for various 3D designs -# Copyright 2012-2016 Ian Jackson -# -# This work is free software: 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 work 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 work. If not, see . - - -HRR=/home/reprap -SLIC3R=$(HRR)/Slic3r/bin/slic3r -M4=m4 -SKEINFORGE=python $(HRR)/reprappro-software.git/skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py - -CWD := $(shell pwd) -PLAY ?= $(CWD) - -AUTO_TOPLEVELS := $(foreach m,$(USING_AUTOS),$(shell $(PLAY)/toplevel-find $m)) - -AUTO_INCS += funcs.scad - -default: autoincs scads - -$(shell set -xe; $(PLAY)/commitid.scad.pl >commitid.scad.tmp; cmp commitid.scad.tmp commitid.scad || mv -f commitid.scad.tmp commitid.scad ) - -autoincs: $(AUTO_INCS) $(AUTO_STLS_INCS) -scads: $(addsuffix .auto.scad, $(AUTO_TOPLEVELS)) -stls: $(addsuffix .auto.stl, $(AUTO_TOPLEVELS)) - -%.auto.scads: %.scad - $(MAKE) $(addsuffix .auto.scad, $(shell $(PLAY)/toplevel-find $*)) -%.auto.stls: - $(MAKE) $(addsuffix .auto.stl, $(shell $(PLAY)/toplevel-find $*)) - --include .*.d - -%.stl: %.scad $(AUTO_INCS) - openscad -d .$@.d.tmp -o $*.tmp.stl $< - @rm -f $@ - @sed -e 's/\.tmp\.stl:/.stl:/' <.$@.d.tmp >.$@.d - @rm .$@.d.tmp - mv -f $*.tmp.stl $@ - -AUTOBASE=$(shell echo $(1) | perl -pe 's/,\w+\.auto$$//') - -%: %.cpp - cpp -nostdinc -P <$< >$@.tmp && mv -f $@.tmp $@ - -funcs.scad: - -#%.gcode: %.stl -# $(SKEINFORGE) $< - -%.gcode: manual-gcode-generator %.m-g - $(PLAY)/$^ >$@.tmp && mv -f $@.tmp $@ - -%.dxf: %.eps - pstoedit -dt -f "dxf: -polyaslines -mm" $< $@ - -%: %.pl - ./$< >$@.tmp && mv -f $@.tmp $@ - -%: %.m4 - $(M4) -P >$@.tmp $< && mv -f $@.tmp $@ - -.PRECIOUS: %.auto.scad -%.auto.scad: $(PLAY)/toplevel-make Makefile $(PLAY)/toplevel-find - @echo ' write $@' - $< $@ >$@.tmp - @mv -f $@.tmp $@ - -.PRECIOUS: %.stl %.gcode %.eps %.dxf - -clean: - rm -f *~ *.stl *.auto.scad *.gcode .*.d $(AUTO_INCS) - diff --git a/reprap-objects.make b/reprap-objects.make new file mode 120000 index 0000000..a25571f --- /dev/null +++ b/reprap-objects.make @@ -0,0 +1 @@ +diziet-utils/reprap-objects.make \ No newline at end of file diff --git a/toplevel-find b/toplevel-find deleted file mode 100755 index c9174c9..0000000 --- a/toplevel-find +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/perl -w -use strict; -use IO::File; - -@ARGV==1 or die; -my $base = $ARGV[0]; -$base =~ m/^\-/ and die; - -sub read_file ($); -sub read_file ($) { - my ($fn) = @_; - my $f = new IO::File "$fn", '<' or die "$fn $!"; - while (<$f>) { - if (m#^//// toplevels-from:#) { - defined($_ = <$f>) or die $!; - m#^include\s+\<(\S+)>\s*$# or die; - read_file($1); - next; - } - next unless m#^\s*(?:////\s?)?module\s+(\w+)\b.*////toplevel\b#; - print "$base,$1\n" or die $!; - } - $f->error and die "$fn $!"; -} - -read_file("$base.scad"); -close STDOUT or die $!; - diff --git a/toplevel-find b/toplevel-find new file mode 120000 index 0000000..eacdaff --- /dev/null +++ b/toplevel-find @@ -0,0 +1 @@ +diziet-utils/toplevel-find \ No newline at end of file diff --git a/toplevel-make b/toplevel-make deleted file mode 100755 index 2df668b..0000000 --- a/toplevel-make +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/perl -w -use strict; -die unless @ARGV==1; -die unless $ARGV[0] =~ m/^(.+),(\w+)(?:\..*)?/; -my ($base,$obj) = ($1,$2); -print < -$obj(); -END -close STDOUT or die $!; diff --git a/toplevel-make b/toplevel-make new file mode 120000 index 0000000..ec6ed59 --- /dev/null +++ b/toplevel-make @@ -0,0 +1 @@ +diziet-utils/toplevel-make \ No newline at end of file diff --git a/utils.scad b/utils.scad deleted file mode 100644 index e440b5f..0000000 --- a/utils.scad +++ /dev/null @@ -1,51 +0,0 @@ -// -*- C -*- - -// suitable for masking things within radius sqrt(2) only -module FArcSegment_mask(beta) { - for (i=[0 : 0.75 : 3]) { - rotate(i*beta/4) - polygon([[0, 0], - [1, 0], - [cos(beta/4), sin(beta/4)]]); - } -} - -module FArcSegment(xc,yc,inrad,outrad,alpha,delta) { - translate([xc,yc]) { - intersection() { - difference() { - circle(r=outrad, $fn=70); - circle(r=inrad, $fn=70); - } - rotate(alpha) scale(outrad*2) { - FArcSegment_mask(delta); - } - } - } -} - -module rectfromto(a,b) { - ab = b - a; - translate([min(a[0], b[0]), min(a[1], b[1])]) - square([abs(ab[0]), abs(ab[1])]); -} -module circleat(c,r) { translate(c) circle(r); } -module linextr(z0,z1, convexity=20) { - translate([0,0,z0]) - linear_extrude(height=z1-z0, convexity=convexity) - children(); -} - -module linextr_x_yz(x0,x1, convexity=20) { // XY turn into YZ - rotate([90,0,0]) - rotate([0,90,0]) - linextr(x0,x1, convexity=convexity) - children(); -} - -module linextr_y_xz(y0,y1, convexity=20) { // XY turn into YZ - rotate([0,0,180]) - rotate([90,0,0]) - linextr(y0,y1, convexity=convexity) - children(); -} diff --git a/utils.scad b/utils.scad new file mode 120000 index 0000000..b1ed200 --- /dev/null +++ b/utils.scad @@ -0,0 +1 @@ +diziet-utils/utils.scad \ No newline at end of file