templates/otter_wasm.ns.d.ts
save/lock
/library/*/*.usvg
+/library/*/*.coloured.svg
*.tmp
.tsconfig.*.json
stamp/*
BUNDLE_SOURCES ?= bundle-rust-sources
+ifndef INKSCAPE_EXTENSIONS
+INKSCAPE ?= inkscape
+INKSCAPE_EXTENSIONS := $(shell $(INKSCAPE) -x)
+endif
+RECOLOUR_SVG ?= $(INKSCAPE_EXTENSIONS)/color_replace.py
+
DEPLOY_ARCH=x86_64-unknown-linux-musl
DEPLOY_RELEASE=debug
DEPLOY_TARGET_DIR=$(TARGET_DIR)/$(addsuffix /,$(DEPLOY_ARCH))$(DEPLOY_RELEASE)
clean: clean-nailing
rm -f templates/script.js library/*/*.usvg stamp/*
+ rm -rf $(LIBRARY_CLEAN)
rm -rf target
$(NAILING_CARGO_JUST_RUN) rm -rf target
```
sudo apt install build-essential cpio git curl \
pkg-config libssl-dev \
- node-typescript
+ node-typescript inkscape
```
2. Install Rust. This is most easily done with rustup:
print DEBUG "file $lstem ";
- print $makefile <<END or die $!;
+ my $process_colour = sub ($$) {
+ my ($linput, $lprocessed) = @_;
+ print $makefile <<END or die $!;
LIBRARY_FILES += $lprocessed
-$lprocessed: $lupstream $licpath $input
+$lprocessed: $linput $licpath $input
\$(LIBRARY_PROCESS_SVG)
END
+ };
+
+ my $process_colours = sub {
+ my $colours = $gcfg->('colours');
+ if (!keys %$colours) {
+ $process_colour->($lupstream, $lprocessed, "");
+ return;
+ }
+ foreach my $colour (sort keys %$colours) {
+ my $cspec = $colours->{$colour};
+ my $abbrev = $cspec->{abbrev} or confess;
+ my $ncoloured = $lupstream;
+ $ncoloured =~ s/\.svg$/.coloured$&/;
+ my $outfile = $lprocessed;
+ $ncoloured =~ s/_c/$abbrev/ or confess "$outfile ?";
+ $outfile =~ s/_c/$abbrev/ or confess "$outfile ?";
+ my $coloured = $lupstream;
+
+ my $ci = 0;
+ my $cfp = '$<';
+ my $emitmap = sub {
+ my ($from, $to) = @_;
+ confess if $from =~ m/\W/ || $to =~ m/\W/;
+
+ my $nfp = "\$@.$ci.tmp";
+ $coloured = $ncoloured;
+ print $makefile <<END or die $! if $ci == 0;
+LIBRARY_CLEAN += $ncoloured
+$ncoloured: $lupstream Makefile
+END
+ $ci++;
+ print $makefile <<END or die $!;
+ \$(RECOLOUR_SVG) -f '$from' -t '$to' $cfp >$nfp
+END
+ $cfp = $nfp;
+ };
+
+ my %map = %{ $cspec->{map} // { } };
+ while (keys %map) {
+ my $from = (sort keys %map)[0];
+ my @maybe_cycle = ();
+ my $cycle;
+ for (;;) {
+ push @maybe_cycle, $from;
+ my $to = $map{$from};
+ if (!exists $map{$to}) {
+ last;
+ }
+ $from = $to;
+ if (grep { $_ eq $to } @maybe_cycle) {
+ $cycle = $to;
+ last;
+ }
+ }
+ my $emit_most = sub ($) {
+ my ($end) = @_;
+ $end //= $#maybe_cycle;
+ foreach my $i (@maybe_cycle[0..$end]) {
+ $emitmap->($i, $map{$i});
+ }
+ };
+ if (defined $cycle) {
+ my $temp = 'abcbfb'; # chosen at random
+ my $aside = $maybe_cycle[-1];
+ $emitmap->($aside, $temp);
+ $emit_most->($#maybe_cycle-1);
+ $emitmap->($temp, $map{$aside});
+ print $makefile <<END or die $!;
+# cycle: @maybe_cycle
+END
+ } else {
+ $emit_most->();
+ }
+ delete $map{$_} foreach @maybe_cycle;
+ }
+ if ($ci) {
+ # inkscape extensions have a tendency to write an empty
+ # file when they don't like their input or arguments
+ print $makefile <<END or die $!;
+ test -s $cfp
+ mv -f $cfp \$@
+END
+ }
+
+ $process_colour->($coloured, $outfile);
+ }
+ };
+
+ $process_colours->();
if (stat $lupstream) {
print DEBUG "already.\n";
#[serde(default)] centre: [f64; 2],
#[serde(default)] flip: bool,
#[serde(default="num_traits::identities::One::one")] scale: f64,
+ colours: Option<HashMap<String, RecolourData>>,
#[serde(flatten)] outline: Box<dyn OutlineDefn>,
}
+#[derive(Debug,Deserialize)]
+struct RecolourData {
+ abbrev: String,
+ // `map` not used by Rust
+}
+
#[derive(Debug)]
struct GroupData {
groupname: String,
DuplicateItem { item: String, group1: String, group2: String },
#[error("{:?}",&self)]
FilesListLineMissingWhitespace(usize),
+ #[error("{:?}",&self)]
+ MissingSubstituionToken(&'static str),
+ #[error("{:?}",&self)]
+ RepeatedSubstituionToken(&'static str),
}
impl LibraryLoadError {
d,
});
for fe in gdefn.files.0 {
+ let mut add1 = |item_name: &str, desc| {
+ let idata = ItemData {
+ group: group.clone(),
+ d: Arc::new(ItemDetails { desc }),
+ };
+ type H<'e,X,Y> = hash_map::Entry<'e,X,Y>;
+ match l.items.entry(item_name.to_owned()) {
+ H::Occupied(oe) => throw!(LLE::DuplicateItem {
+ item: item_name.to_owned(),
+ group1: oe.get().group.groupname.clone(),
+ group2: groupname.clone(),
+ }),
+ H::Vacant(ve) => {
+ debug!("loaded shape {} {}", libname, item_name);
+ ve.insert(idata);
+ }
+ };
+ Ok::<_,LLE>(())
+ };
+
let item_name = format!("{}{}{}", gdefn.item_prefix,
fe.item_spec, gdefn.item_suffix);
- let idata = ItemData {
- group: group.clone(),
- d: Arc::new(ItemDetails { desc: fe.desc.clone() }),
- };
- type H<'e,X,Y> = hash_map::Entry<'e,X,Y>;
- match l.items.entry(item_name.clone()) {
- H::Occupied(oe) => throw!(LLE::DuplicateItem {
- item: item_name.clone(),
- group1: oe.get().group.groupname.clone(),
- group2: groupname.clone(),
- }),
- H::Vacant(ve) => {
- debug!("loaded shape {} {}", libname, item_name);
- ve.insert(idata);
+
+ if let Some(colours) = &group.d.colours {
+ #[throws(LLE)]
+ fn subst(before: &str, needle: &'static str, replacement: &str)
+ -> String {
+ let mut matches = before.match_indices(needle);
+ let m1 = matches.next()
+ .ok_or(LLE::MissingSubstituionToken(needle))?;
+ if matches.next().is_some() {
+ Err(LLE::RepeatedSubstituionToken(needle))?;
+ }
+ before[0.. m1.0].to_owned()
+ + replacement
+ + &before[m1.0 + m1.1.len() ..]
}
- };
+ for (colour, recolourdata) in colours {
+ let t_item_name = subst(&item_name, "_c", &recolourdata.abbrev)?;
+ let t_desc = Html(subst(&fe.desc.0, "_colour", colour)?);
+ add1(&t_item_name, t_desc)?;
+ }
+ } else {
+ add1(&item_name, fe.desc.clone())?;
+ }
}
}
l