Calculate the REAL scale factor and the angle of rotation from an Android Matrix

After performing transformations such as rotation of a bitmap about an arbitrary point, the scale is lost. Of course, this makes sense because the bitmap is rotated inside the same bounds.

To get the real scale now, along with the most reliable degree of rotation, I had to follow this method. Hope it saves the rest of you a night or two.

float[] v = new float[9];
matrix.getValues(v);
// translation is simple
float tx = v[Matrix.MTRANS_X];
float ty = v[Matrix.MTRANS_Y];

// calculate real scale
float scalex = values[Matrix.MSCALE_X];
float skewy = values[Matrix.MSKEW_Y];
float rScale = (float) Math.sqrt(scalex * scalex + skewy * skewy);

// calculate the degree of rotation
float rAngle = Math.round(Math.atan2(v[Matrix.MSKEW_X], v[Matrix.MSCALE_X]) * (180 / Math.PI));

Multi Touch in Android – Translate, Scale, and Rotate

Here’s a quick and easy implementation of Android’s multi touch feature – one finger to move, two to zoom, and three to rotate the image.

Assuming you have a basic understanding of 2D matrix transformations, the Matrix class in Android uses a 3×3 matrix to achieve all of the 2D transformations.

The source code and pre -built APK is available. See the end of this post.

Main activity – MultiTouch.java

package com.multitouch.example;

import android.app.Activity;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;

public class MultiTouch extends Activity implements OnTouchListener {

    // these matrices will be used to move and zoom image
    private Matrix matrix = new Matrix();
    private Matrix savedMatrix = new Matrix();
    // we can be in one of these 3 states
    private static final int NONE = 0;
    private static final int DRAG = 1;
    private static final int ZOOM = 2;
    private int mode = NONE;
    // remember some things for zooming
    private PointF start = new PointF();
    private PointF mid = new PointF();
    private float oldDist = 1f;
    private float d = 0f;
    private float newRot = 0f;
    private float[] lastEvent = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ImageView view = (ImageView) findViewById(R.id.imageView);
        view.setOnTouchListener(this);
    }

    public boolean onTouch(View v, MotionEvent event) {
        // handle touch events here
        ImageView view = (ImageView) v;
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                savedMatrix.set(matrix);
                start.set(event.getX(), event.getY());
                mode = DRAG;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                oldDist = spacing(event);
                if (oldDist > 10f) {
                    savedMatrix.set(matrix);
                    midPoint(mid, event);
                    mode = ZOOM;
                }
                lastEvent = new float[4];
                lastEvent[0] = event.getX(0);
                lastEvent[1] = event.getX(1);
                lastEvent[2] = event.getY(0);
                lastEvent[3] = event.getY(1);
                d = rotation(event);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode == DRAG) {
                    matrix.set(savedMatrix);
                    float dx = event.getX() - start.x;
                    float dy = event.getY() - start.y;
                    matrix.postTranslate(dx, dy);
                } else if (mode == ZOOM) {
                    float newDist = spacing(event);
                    if (newDist > 10f) {
                        matrix.set(savedMatrix);
                        float scale = (newDist / oldDist);
                        matrix.postScale(scale, scale, mid.x, mid.y);
                    }
                    if (lastEvent != null && event.getPointerCount() == 3) {
                        newRot = rotation(event);
                        float r = newRot - d;
                        float[] values = new float[9];
                        matrix.getValues(values);
                        float tx = values[2];
                        float ty = values[5];
                        float sx = values[0];
                        float xc = (view.getWidth() / 2) * sx;
                        float yc = (view.getHeight() / 2) * sx;
                        matrix.postRotate(r, tx + xc, ty + yc);
                    }
                }
                break;
        }

        view.setImageMatrix(matrix);
        return true;
    }

    /**
     * Determine the space between the first two fingers
     */
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return FloatMath.sqrt(x * x + y * y);
    }

    /**
     * Calculate the mid point of the first two fingers
     */
    private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }

    /**
     * Calculate the degree to be rotated by.
     *
     * @param event
     * @return Degrees
     */
    private float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        double radians = Math.atan2(delta_y, delta_x);
        return (float) Math.toDegrees(radians);
    }
}

An important function that is always used is postXx(). This function concats a new matrix of the type Xx to the existing matrix object. Using setXx() will reset the matrix’s Xx property.
Main layout – main.xml

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<ImageView android:id="@+id/imageView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="@drawable/butterfly"
android:scaleType="matrix" />

</FrameLayout>

Browse and download the source code on GitHub.

recursive get from an FTP server

As we all know, downloading a directory from an FTP server over FTP over the command line is not possible. Some, have found mget to be the holy grail, I certainly do not.

I was restoring my music backup from my NAS, and I didn’t want to do it the GUI way, so CLI is the other, obvious option.

Trying to do a recursive fetch using the FTP prompt, did not work whatsoever.

Here is the better solution: recursive wget.



$ wget -r -l 0 ftp://user:password@server/directory

wget will now recursively fetch that entire directory.

Note: In some cases, you would need to pass the –username and –password arguments to wget.

SEED hits the pit again #iknowvention

SEED continues to make mistakes.

So is today’s challenge:

#include
int main(int argc, char *argv[])
{
 printf("%d",4["seed"]);
 return 0;
} 

The answer for the above is 0, as the fourth element in the array “seed” does not exist. This is because of the index value, which starts from 0. Had it been 3, then it would print out the character code for d, and that would be the right answer.
However, none of the options had the value 0, they had some other values.

@SEEDInfotech, why make such obvious mistakes on your side? Didn’t you already learn from my last post? There simply is no room for such mistakes!
Isn’t there any moderator smart enough in your company?

@SEEDInfotech #iknowvention

SEED makes Blunder! #iknowvention

Alright, so I’m taking part in this SEED Infotech C programming contest, and here’s one of yesterday’s challenges:

main()
{
 if(printf("C for yourself how it works \n");
  main();
  }

One of the options were
a) C for yourself how it works infinate times

Now isn’t that catchy? Look at it clearly, there’s one pair of brackets missing!

@SEEDInfotech, how did you choose that as the right answer?

Hit http://seed-iknowvention.com/PlayIKnowVation.aspx, and then click on previous level’s correct answer.

/usr/local/bin/waitpid

A quick script to wait for a certain pid, then once that quits, execute a command.
You may ask, why not just do command1 && command2 ? Well, if command1 exits with a non-zero exit status value, command2 will not get executed. Hence, I’ve brewed a quick script for this purpose.

File: /usr/local/bin/waitpid

#!/bin/sh
# Find the pid of the required process either by using psaux | grep -i command
# or by pidof command
#set -x
if [ $# -ne 3 ]; then
	echo "Usage: waitpid [pid to wait for] [app name] [command]"
	echo "       [command] should be command to execute after pid is dead"
	exit 1
fi
pidr=$1
app=$2
while true; do
	pid=`pidof $app | grep -o $pidr`
	if [ "$pid" = "$pidr" ]; then
		# do nothing
		echo "pid does exist,"
		echo "waiting for 5 seconds before next check"
		sleep 5
		continue
	fi
	break
done
# if we've reached here it means that the PID is dead
echo "specified pid does NOT exist,"
echo "running command provided in 10 seconds"
echo ""
echo "Press Ctrl + c to stop"
sleep 10
$3

Coloured /var/log/messages at tty12

Reading logs could never become any more easier, at just a keystroke, you have your logs displayed where you want, in some fancy colour. They look great too.

CCZE colourized logs

TTY’s can be accessed by pressing Alt + Ctrl + F[1 – 12] simultaneously. In the following, you’ll get a decent, colourized log display of /var/log/messages when you press Alt + Ctrl + F12

First install ccze, most distributions have it in their repositories. CCZE is a robust and modular log colorizer with plugins for apm, exim, fetchmail, httpd, postfix, procmail, squid, syslog, ulogd, vsftpd, xferlog, and more. It brightens up the log view.

To quickly test it, try tail -f var/log/messages | ccze -A

The -A switch prevents ccze from starting itself in a curses window.

Create a file cclm in /usr/local/bin(you have to be root to be able to do so), with the following contents:

#!/bin/sh
file=&amp;amp;quot;/var/log/messages&amp;amp;quot;
where=&amp;amp;quot;/dev/tty12&amp;amp;quot;
tail -f $file | ccze -A &amp;amp;gt;&amp;amp;gt; $where

Add the following line to /etc/inittab
c12:123456:respawn:/sbin/agetty -n -l /usr/local/bin/cclm 38400 tty12 linux
That’s all that there is to be done, either reboot to get it working, or execute the following in a terminal with privileges:

/sbin/agetty -n -l /usr/local/bin/cclm 38400 tty12 linux

This can be used on any ttys’. The most obvious ones to use would be tty8 to tty12.

QEMU | Quick Networking for TCP/UDP

Networking with a guest in QEMU is often a headache, TUN/TAP, seriously? Too hectic, let’s stick to the basics, TCP/UDP based. Instead of setting up a really complex set of configuration files, wouldn’t it be easy to just emulate the network card, DHCP the guest, and let it work right out of the box?

Well certainly yes. QEMU can do all this very easily.

Cut the chase, hit the code:

#!/bin/sh
qemu="qemu-system-x86_64"
cpu_args="-cpu qemu64 -smp 2"
mem_args="-m 128M"
drive="-hda /media/fowlmanordisk1/devel/virtual/red.tvway"
net_args="-net user -net nic,model=rtl8139"
redirs="-redir tcp:8022::22"
${qemu} ${cpu_args} ${mem_args} ${drive} ${net_args} ${redirs} -nographic &

is one of my virtual servers’ running on Ubuntu server. Take a look at the network arguments:
-net user -net nic,model=rtl8139
Pretty simple? The QEMU emulator runs an inbuilt DHCP server, if the guest recognises the network card, and requests the configuration from the DHCP server, it acquires the required IP address, and viola! Instant access to the outside world. The file /etc/resolv.conf still needs to be configured to your preferable DNS servers. OpenDNS is usually a good solution.

Notice the -redir argument, it specifies that an issue to port 8022 on the outside be mapped to port 22 on the inside(guest). So basically you could ssh localhost -p 8022 and get access to your guest machine.

The -redir is as follows(from the QEMU manual pages):

-redir [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport
         Redirect incoming TCP or UDP connections to the host port
         hostport to the guest IP address guestaddr on guest port
         guestport. If guestaddr is not specified, its value is x.x.x.15
         (default first address given by the built-in DHCP server). By
         specifying hostaddr, the rule can be bound to a specific host
         interface. If no connection type is set, TCP is used. This
         option can be given multiple times.

Enjoy virtual machines with QEMU.

visual basic 6 revisited – linux – wine

Earlier this year, I had written an article on running Visual Basic 6 on linux under wine, this is an update for it, the prior one is deprecated

Getting Visual Basic 6 to work on linux is pretty easy, not much trouble, all the basic things work, as of what I’ve tested.

Here’s how you get that damn thing to work:

Copy over the contents of OS/SYSTEM/ from the CD root to your wine system32 directory

$ cp -r /media/cdrom/OS/SYSTEM/* ~/.wine/drive_c/windows/system32/

Since we are only concerned about Visual Basic 6, copy over the folder VB98 from the CD root to your Program Files

$ cp -r /media/cdrom/VB98/ ~/.wine/drive_c/Program\ Files/
# for the sake of convenience, let's rename this folder as Visual Basic 6
$ mv ~/.wine/drive_c/Program\ Files/VB98/ ~/.wine/drive_c/Program\ Files/Visual\ Basic\ 6/

Register the two dynamically linked libraries essential to run Visual Basic 6 smoothly

$ cd ~/.wine/drive_c/windows/system32/
$ wine regsvr32 comcat.dll
$ wine regsvr32 MSSTDFMT.DLL

Easy, eh? Your all done, now, let’s create an optional launch command with the following contents
File: vbasic

#!/bin/bash
cd ~/.wine/drive_c/Program\ Files/Visual\ Basic\ 6/
wine VB6.EXE

Make our launcher executable and place it in the right place

$ chmod +x vbasic
# following command must be issued as root
$ mv vbasic /usr/local/bin/

Now you can just issue the command vbasic, and all should work well, using this launcher, you can create entries for your panel, etc.

Deployed with a fresh install of wine version 1.2-rc2

embedded gentoo [uclibc] | nothing beats this

A few uclibc embedded gentoo facts:

  • the compilation of the box takes around 15 minutes
  • at boot up, takes less than 3 megabytes of RAM
  • disk space: 17 megabytes
  • boots in under 8 seconds on a pentium3

link to stage3 tarballs archive
HTOP - Displaying System Statistics
# this is my make.conf, it should be the same in the stage3, if installing anything in the stage3, and even before updating, comment the line INSTALL_MASK=”*.h HACKING.gz TODO.gz”

CFLAGS="-Os -mtune=i386 -pipe"
CXXFLAGS="-Os -mtune=i386 -pipe"
CHOST="i386-gentoo-linux-uclibc"

FEATURES="strip"
MAKEOPTS="-j3"
GENTOO_MIRRORS="http://mirror.bytemark.co.uk/gentoo/ http://www.ibiblio.org/pub/Linux/distributions/gentoo"

USE="-ipv6 -python3 -cracklib -minimal"

LINGUAS="en"
VIDEO_CARDS=""
ACCEPT_KEYWORDS="~x86"
INSTALL_MASK="*.h HACKING.gz TODO.gz"

# download my stage3, from the previous post links, and then prepare to chroot

mount -o bind /dev stage3-*/dev
mount -o bind /proc stage3-*/proc
chroot stage3-*

# update the system, and create the necessary path, if you come across any errors, post them here, and expect a reply soon

emerge -avuDN world
mkdir /mounted

# begin the installation
# install necessary packages

ROOT=/mounted/ emerge -auvND baselayout uclibc bash dropbear pam udev iptables coreutils nano util-linux shadow kbd net-tools grep procps gzip sed findutils mawk htop
mkdir /mounted/proc
mkdir /mounted/dev

Continue reading “embedded gentoo [uclibc] | nothing beats this”

noCD? |boot from usb| Sabayon Linux

I had a pen-drive lying around, and so I decided to try out a new distribution of linux, Sabayon Linux.

Why Sabayon?

  • On a base install, everything is there and just works perfectly
  • For a newbie, the hard part is often getting playback codecs, well, Sabayon has them pre-installed
  • Sabayon has focused on the base theme too, it looks great
  • Entropy, their package manager, is really awesome, ‘man entropy’ to see more

Let’s mount and copy the contents of the live image, it can be downloaded here. As super user, do

mkdir /media/sabayon
mount -o loop /path/to/iso/file /media/sabayon

Plug in you pen-drive, wait for the device to settle down first, then copy whatever is important to you from the pen-drive to a hard-disk, and format it after you’ve successfully backed up your previous data

mkfs.ext3 /dev/sdyx

Replace sdyx as per your needs, i. e. the partition where you will be booting the live image from, in my case it was sdc1. You can choose any filesystem type you want to, but let’s just stick to ext3 for now.
Now copy the contents of the live image which you previously mounted to this drive after mounting it

mkdir /media/target
mount /dev/sdyx /media/target
cp -r /media/sabayon/* /media/target/

The parameter ‘-r’ indicates that it should copy recursively, and not just the files present in the top level directory
Now we’ll need to setup that device to boot correctly, you may not want to end up with a few errors and be done with it, do you?
Continue reading “noCD? |boot from usb| Sabayon Linux”

Restore Broken Package DB on Sabayon

System database not found or corrupted, running in safe mode using temporary, empty repository

phoenix jude # equo rescue resurrect
&gt;&gt; ####### ATTENTION: The installed package database will be resurrected, this will take a LOT of time.
&gt;&gt; ####### ATTENTION: Please use this function ONLY if you are using an Entropy-aware distribution.
     Can I continue ? [Yes/No] yes
     Are you REALLY sure ? [Yes/No] yes
     Do you even know what you're doing ? [Yes/No] yes
&gt;&gt;  @@ Creating backup of the previous database, if exists.
&gt;&gt;  @@ Previous database copied to file /var/lib/entropy/client/database/amd64/equo.db.backup.15103
&gt;&gt;   Initializing the new database at /var/lib/entropy/client/database/amd64/equo.db
&gt;&gt;   Database reinitialized correctly at /var/lib/entropy/client/database/amd64/equo.db
&gt;&gt;   Found 420663 files on the system. Assigning packages...
&gt;&gt;   Matching in repository: Sabayon Linux Official Repository
&gt;&gt;   Found 0 packages. Filling database...
&gt;&gt;   Database resurrected successfully.
&gt;&gt;   Now generating reverse dependencies metadata...
&gt;&gt;   Now indexing tables...
&gt;&gt;   Database reinitialized successfully.
&gt;&gt;   Keep in mind that virtual packages couldn't be matched. They don't own any files.
phoenix jude # 

fascinating and too simple!
For one thing, I have no clue why did it say “Found 0 packages.”, because just after this I was successful in doing an equo upgrade and it worked beautifully.
equo has become a teenager…

Easily Sort Your Music Library

Here is a very simple script to sort your whole music library according to their artists,

#!/bin/bash
# sortMusic v0.4
# make sure this script in in the music directory and you execute this script from there
# know issue: will display many errors, but it does it fine, those errors are not to be bothered about
# still needs a workaround
echo "$(ls -l *.mp3 | wc -l) Files will be sorted according to their Artists"
echo "Make sure all your song artists are correct, will wait for 3 seconds..."
sleep 3
for i in ./*; do
	artist=$(mp3info "$i" | sed -n 's/Artist: *//p')
	if [ -d "$artist" ]; then
		mv "$i" "$artist"
	else
		mkdir "$artist"
		mv "$i" "$artist"
	fi
done
# To move all the incorrect id3 artists
mkdir "Z Singles Z"
mv *.mp3 "Z Singles Z"

# That was very simple, wasn't it?

Just make a file called sortMusic-v0.4 in your main music folder, paste the above code into that file, then do $ chmod +x sortMusic-v0.4 and then do a $ ./sortMusic-v0.4 in the music folder. Please download mp3info as this uses that package. Please note that this is very case-sensitive, so if your artists are same but differ in case, it will make a new directory for that one, so please take care until I have a workaround for this.

NON-Standards Compliant C++ with Mumbai University

Mumbai University fails to keep up with the ever-evolving C++ syntax. Making Turbo C/C++ as their prescribed IDE, Turbo C/C++ is ancient and will not compile with the present syntax of C++. Here is the simplest example:

#include <iostream.h>
#include <conio.h>
void main()
{
	cout << "Hello";
}

The above code, when compiled with g++ 4.3.4, will give the following errors, but with Turbo C/C++ will compile and run just fine:

firstApp.cpp:1:22: error: iostream.h: No such file or directory
firstApp.cpp:2:19: error: conio.h: No such file or directory
firstApp.cpp:4: error: '::main' must return 'int'
firstApp.cpp: In function 'int main()':
firstApp.cpp:6: error: 'cout' was not declared in this scope

Here is the same code, but following the correct syntax:

#include <iostream>
using namespace std;
int main()
{
	cout << "Hello";
	return 42;
}

will compile and run just fine.

Argggg!!! This never ends! They even introspect on Valid XHTML and CSS standards!