cavac has asked for the wisdom of the Perl Monks concerning the following question:
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...
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Translating python math to Perl
by bliako (Abbot) on Aug 25, 2023 at 11:00 UTC | |
by NERDVANA (Priest) on Aug 25, 2023 at 20:24 UTC | |
by bliako (Abbot) on Aug 29, 2023 at 07:20 UTC | |
Re: Translating python math to Perl
by NERDVANA (Priest) on Aug 25, 2023 at 21:06 UTC | |
by cavac (Prior) on Aug 28, 2023 at 07:55 UTC | |
by NERDVANA (Priest) on Sep 02, 2023 at 07:58 UTC | |
by etj (Priest) on Feb 06, 2024 at 18:39 UTC | |
by NERDVANA (Priest) on Feb 07, 2024 at 02:13 UTC | |
| |
by NERDVANA (Priest) on Aug 29, 2023 at 20:16 UTC | |
by etj (Priest) on Feb 06, 2024 at 19:57 UTC | |
Re: Translating python math to Perl
by Anonymous Monk on Aug 25, 2023 at 22:34 UTC | |
Re: Translating python math to Perl
by harangzsolt33 (Deacon) on Sep 04, 2023 at 02:58 UTC |