I'm the first to admit that my math skills are limited. "Normal" math is no problem, nor is writing software that deals with stuff like basic accounting or sensor data conversion. But i'm completely stumped by linear algebra and matrix multiplication.

I own a Boox Note Air, and there is python code that can render notes from the old export format. I'm currently in the process of reverse engineering the new format(*), and i think i found nearly all the data i need to write a new renderer in Perl. From what i can tell, the export format has changed, but the internal representation of the data in the software (and the rendering process) has stayed pretty much the same.

Unfortunately, the points that define a drawn line aren't saved as coordinates per se, but need to go through some matrix math before the screen coordinates are obtained. I think that's so that you can scale or rotate a line without loosing the original coordinates or something along that.

The old program has this python code:

if matrix is None: # Compatibility with older note format matrix = np.eye(3,3) else: matrix = np.asarray(json.loads(matrix)["values"], +dtype=np.float32).reshape(3,3) d = np.frombuffer(points, dtype=np.float32) d = d.byteswap() d = d.reshape(-1, 6) pressure = (d[:,2] / pressure_norm) ** pressure_pow # Projection matrix points = d[:, :2] points = np.concatenate((points, np.ones([points.shape +[0],1])), axis=1) points = points @ matrix.T points = points[:, :2]

As i said, i don't understand enough about matrices (or python for that matter) to translate that code to Perl. Can anyone help me on that?

Here is the code i have so far for reading the basic SQLite databases, Testfiles and some of my guesses on the format of the binary "points" blobs:

I generated a number of test files for the basic drawing system. The tarball is available on my webserver and it also includes the PNG export of all pages generated by the Android app.

The ShapeDatabase.db holds the list of all notes (and a list of their pages) as well as virtual directories and stuff. I deleted everything except the test files.

The next step is to open the SQLite database corresponding to the UID of the note. This then holds the data for all the shapes (and images and text) on each of the pages of that note. Each shape also comes with a transformation matrix (for resize and rotation i guess) plus a lot of flags (if the shape is visible and whatnot).

The binary "point cloud" file then holds the points lists for each drawn shape.

My current code for finding the proper UIDs is the following proof-of-concept mess. I started implementing to do the actual drawing, but got stuck while reverse-engineering the math:

#!/usr/bin/env perl use v5.38; use strict; use warnings; use DBI; use DBD::SQLite; use Data::Dumper; use JSON::XS; use Carp; my $basedir = 'Testfiles'; if(!defined($ARGV[0])) { croak("No note name given!"); } #my ($WIDTH, $HEIGHT) = (XXXXXXX my $notename = $ARGV[0]; my ($noteuid, $pages) = getUID($basedir, $notename); print "Note has UID ", $noteuid, " with ", scalar @{$pages}, " pages: +", join(', ', @{$pages}), "\n"; my $pagecount = 0; foreach my $page (@{$pages}) { my $pagefname = '' . $notename . '_page' . $pagecount . '.png'; $pagefname =~ s/\ /_/g; renderPage($basedir, $noteuid, $page, $pagefname); } sub renderPage($basedir, $noteuid, $page, $pagefname) { print "******* $pagefname *******\n"; my $fname = $basedir . '/' . $noteuid . '.db'; if(!-f $fname) { croak("File $fname not found!"); } my $dbh = DBI->connect("dbi:SQLite:dbname=$fname","","") or croak( +$!); my $selsth = $dbh->prepare("SELECT * FROM NewShapeModel WHERE docu +mentUniqueId = ? AND pageUniqueId = ? ORDER BY id") or croak($dbh->errstr); if(!$selsth->execute($noteuid, $page)) { print $dbh->errstr, "\n"; $dbh->disconnect; croak("DB Error"); } my @lines; while((my $line = $selsth->fetchrow_hashref)) { # de-JSON certain fields foreach my $key (qw[boundingRect matrixValues shapeLineStyle s +hapeCreateArgs]) { if(!defined($line->{$key}) || $line->{$key} eq '') { $line->{$key} = {}; } else { my $tmp = decode_json $line->{$key}; $line->{$key} = $tmp; } push @lines, $line; } } #$img = GD::Image->new($self->{width}, $self->{height}); #$imgblack = $self->{img}->colorAllocate(0, 0, 0); #$imgwhite = $self->{img}->colorAllocate(255, 255, 255); return; } sub getUID($basedir, $name) { my $fname = $basedir . '/ShapeDatabase.db'; if(!-f $fname) { croak($fname . " not found"); } my $dbh = DBI->connect("dbi:SQLite:dbname=$fname","","") or croak( +$!); my $selsth = $dbh->prepare("SELECT * FROM NoteModel WHERE title = +?") or croak($dbh->errstr); if(!$selsth->execute($name)) { print $dbh->errstr, "\n"; $dbh->disconnect; croak("DB Error"); } my $line = $selsth->fetchrow_hashref; $selsth->finish; $dbh->disconnect; #print Dumper($line); if(defined($line) && defined($line->{uniqueId})) { my $pages = []; if(defined($line->{pageNameList})) { my $json = $line->{pageNameList}; #print "JSON: $json\n"; my $decoded = decode_json $json; #print "Decoded: ", Dumper($decoded); if(!defined($decoded->{pageNameList})) { print "BLI\n"; } else { print "BLA\n"; $pages = $decoded->{pageNameList}; } } print Dumper($pages); return ($line->{uniqueId}, $pages); } croak("Note not found in ". $fname); }

Some observations/best guesses about the points cloud format:

(*) Reverse engineering the SQLite databases and the binary points lists is comparatively easy. All it takes is a few basic tools, a desktop calculator, half a dozen pages of notes in my absolutely awful handwriting, and the assumption that a good software dev is a lazy bum who just serializes their internal data representation to a binary file...

PerlMonks XP is useless? Not anymore: XPD - Do more with your PerlMonks XP

In reply to Translating python math to Perl by cavac

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.