Multitouch Tisch mit FTIR TchnikSunday, 28-Dec-2014, 21:29

By Simon

Das hier ist das Ergebnis einer einwöchigen Projektarbeit. Wir haben einen Multi-Touch fähigen Bildschirm in einen Tisch eingebaut. Den Bildschirm haben wir aus ein paar wenigen Materialien selbst konstruiert:

  • ein altes LCD Panel
  • Plexiglas
  • Infrarot LEDs
  • Eine Infrarot Kamera
  • Lampen als Hintergrundbeleuchtung

Die Technik die wir dabei verwendet haben nennt sich FTIR - Frustrated Total Internal Reflection.

Es funktioniert indem wir Infrarotlicht von der Seite durch das Plexiglas senden. Wenn dann jemand seinen Finger auf die Scheibe hält wird das Licht nach unten abgelenkt.

FTIR Diagramm

Wenn das Licht nach unten abgelengkt kann die Infrarotkamera kleine weiße Punkte erkennen. Sogenannte Blobs

FTIR Touch

Damit kann man letztendlich erkennen wo sich die Finger gerade auf dem Bildschirm befinden und somit auch Anwendungen dafür programmieren, die das volle Potenzial dieses Bildschirms nutzen Wir haben auch ein Making Of Video gedreht:

Comment on this article

Blogging SoftwareSunday, 28-Dec-2014, 21:29

By Simon

My all new Blog

I recently switched my hosting provider and I upgraded to an cheap dedicated server by ovh So I started migrating all my data and I decided to speed up my software stack a little bit by replacing apache with nginx. I was never quite satisfied with the speed of my old website, but - so I thought - with this new dedicated server everything should be faster.

When I finally had everything ready I notice that the loading time for the webpage is over 500ms just to load the HTML. Old graph

I decided this can’t be and since I always wanted to get started with Go I decided to use a nice webframework named revel and build my own blogging software.

This is the result

Of course most javascript and css resources are now cdn hosted, but the rendering and delivery of the html page happens in under 50ms New graph

I also optimized it a little bit more for mobile devices, the resulting website scores much better on Google’s PageSpeed Insights The blog code is just a few hundred lines of go code + lots of template code.

If you’re interested you can have a look at the code on github

Thanks to alle the people who wrote the components this software builds on:

And of course https://github.com/hermanschaaf/ironzebra for inspiration

Comment on this article

Android HttpClient – Download einer Datei mit NotificationSunday, 28-Dec-2014, 21:31

By Simon

Eine Sache für die ich etwas länger Suchen musste, ist das herunterladen einer Datei mit Überprüfung des Netzwerksstatus. Das sollte möglichst auch nicht auf dem Main Thread ablaufen da das Interface sonst einfriert. Um das ganze im Hintergrund ausführen zu können benötigen wir einen Thread, aber um im Interface darzustellen das der Download fertig ist (z.B. eine Notification anzeigen) muss nach dem Thread wieder Code im Main Thread ausgefürt werden. Am einfachsten erreicht man das mit der AsyncTask Klasse die in der Android Bibliothek zu Verfügung steht.

package org.graetzer;

/**
* Copyright 2011 Simon Grätzer simon@graetzer.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;

public class TestClass extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout);

        LoadTask task = new LoadTask(this);
        task.execute("http://google.de", "http://example.org");
    }
        // Ich verzichte in dieser Klasse darauf einen kontinuierlichen Fortschritt anzuzeigen, deswegen ist der zweite Parameter Void
        static class LoadTask extends AsyncTask<String, Void, List<File>> {
        private final Context context;
        private final ConnectivityManager cmanager;
        private ProgressDialog spinner;

        public LoadTask (Context ctx) {
            this.context = ctx;
            // Netzwerke abfragen
            this.cmanager = (ConnectivityManager) context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        }

        @Override
        protected void onPreExecute() {
                        // Anzeigen dass die Anwendung noch aktiv ist
                        spinner = ProgressDialog.show(context, "Loading...", "Load network data", true, false);
        }

        @Override
        protected List doInBackground(String... params) {
            List result = new ArrayList();
            int i = 0;
            for (String url : params) {
                try {
                    File file = cacheFile(i + "_cached");
                    if (download(url, file))
                        result.add(file);
                    i++;
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return result;
        }

        /**
         * Entferne die Aktivitätsanzeige und löse eine Notification aus
         */
        protected void onPostExecute(List result) {
            spinner.dismiss();
            int LOAD_ID = 1;
            if (result.size() > 0) {
                for (File file : result) {
                    String ns = Context.NOTIFICATION_SERVICE;
                    NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
                    int icon = R.drawable.appicon;
                    CharSequence tickerText = "Finished download";
                    long when = System.currentTimeMillis();
                    Context context = getApplicationContext();
                    CharSequence contentTitle = "The Download was Finished ";
                    CharSequence contentText = "Downloaded file was "+ file.getName();
                    Intent notificationIntent = new Intent(context, TestClass.class);
                    PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);

                    Notification notification = new Notification(icon, tickerText, when);
                    notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);

                    mNotificationManager.notify(LOAD_ID, notification);
                    LOAD_ID++;
                }
            } else {
                    // Zeige einen Fehler an
                AlertDialog.Builder builder = new Builder(context).setMessage("Error while downloading");
                AlertDialog alert = builder.create();
                alert.show();
            }

        }

        private File cacheFile(String name) throws IOException {
            return new File(context.getCacheDir(), name);
        }

        private boolean download(String url, File file) {
            NetworkInfo info = null;
            if (cmanager != null)
                info = cmanager.getActiveNetworkInfo();

            if (info != null && info.isAvailable()) {
                try {
                    HttpClient client = new DefaultHttpClient();
                    HttpGet get = new HttpGet(url);
                    HttpEntity entity = client.execute(get).getEntity();
                    if (entity != null) {
                        // Create the file or overwrite
                        OutputStream out = new BufferedOutputStream(
                                new FileOutputStream(file));
                        entity.writeTo(out);
                        entity.consumeContent();
                        out.close();
                    }
                    return true;
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
                  return false;
        }
    }
}

Comment on this article

Mac OSX Lion – Kein StartvolumeSunday, 28-Dec-2014, 21:45

By Simon

Gerade eben habe ich versucht meinem das neue OSX Lion aufzuspielen. Aber als die Abfrage nach dem Volume kommt, auf das Lion installiert werden soll, kann ich keine meiner partitionen auswählen. Als Begründung steht nur: Sie können dieses Volume nicht als Startvolume für ihren Computer verwenden.

Nach längerer Suche bin ich auf die Lösung gestoßen: Man muss einfach, z.B. mit dem Festplattendienstprogramm, die Gewünschte Partition um ca. 10GB verkleinern, damit Lion die Recovery Partition anlegen kann.

Comment on this article

Echtzeit Rendern mit Javascript und Canvas ElementSunday, 28-Dec-2014, 21:45

By Simon

Mir ist während der letzten Informatik-Vorlesung an der RWTH die Idee gekommen mich mal etwas genauer mit den Möglichkeiten von HTML 5 auseinanderzusetzen. Dabei habe ich mir vorgenommen, dass recht bekannte Spiel “Achtung die Kurve” mittels Canvas Element zu implementieren und nach ein paar Stunden ist eine kleine Techdemo dabei heraus gekommen. Wie ich dabei vorgegangen bin will ich hier kurz erläutern.

Wenn man so ein einfaches Multiplayer Spiel bauen möchte, dann muss man sich um die Spiellogik und das Rendering kümmern. Dazu benötigt man eine game-loop die in regelmäßigen Abständen die Logik und das Rendering ausführt.

Ein naiver Ansatz dafür wäre etwas in der Art:

function run(){
    setInterval(animate}, 1000 / 30);
}

function animate(){
    calculateLogic();
    render();
}
//...

Das Problem dabei ist das wir uns hier auf eine Framerate von 30fps verlassen, dass kann sich aber schnell ändern wenn der Computer langsam ist, das Intervall also z.B. erst wieder nach 500ms ausgeführt wird. Außerdem könnten andere Javascript Timer die Ausführung verzögern. Man muss also noch die Zeit die seit der letzten Ausführung vergangen ist beachten. So etwa in der Art:

function animate(){
    var timeSinceLastFrame = timeAtLastFrame - Date.now();
    var distance = speed*timeSinceLastFrame;
    timeAtLastFrame = Date.now();
}

Das ist aber auch nur suboptimal, weil man nun in der Gesamten Spiellogik die Zeit beachten muss, außerdem kann es bei manchen Algorithmen (z.B. in der Kollisionsberechnung) von Vorteil sein, wenn man weiß das diese nur alle X ms ausgeführt werden. Außerdem ist die setInterval Funktion in den meisten Browsern inzwischen überflüssig, es existiert die Funktion requestAnimationFrame.

Letztlich habe ich also das hier benutzt:

// Diese Funktion ruft nach einem gewissen Zeitabstand einen callback auf,
// der dann einen Schritt der Animation ausführt
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(fn) {setTimeout(fn, 1000/30);};

const frameTime = 1000/30;//30 fps
var lastFrame = new Date().getTime();
var leftover = 0.0;

function animate(timestamp) {
   if(this.status != runStatus.running)
      return;

   // Wir errechnen hier wie oft man die Spiellogik ausführen muss.
   var timePassed = timestamp - this.lastFrame + this.leftover;
   var catchUpCount = Math.floor(timePassed/frameTime);
   // Wir fangen die Ungenauigkeiten durch das Runden ab
   this.leftover = timePassed - catchUpCount*frameTime;
   this.lastFrame = timestamp;

   // Die Spiellogik ausführen
   for(var x = 0; x < catchUpCount; x++) {
     calculateLogic();
   }
   render();

   requestAnimationFrame(animate);
};

Die Spiellogik wird Zeitlich unabhängig von der genauen Framerate und dem Zeichnen des Spiels, indem wir berechnen wie viel Zeit seit dem letzten Ausführen vergangen ist und sie dann entsprechend oft ausführen. Jetzt muss man nur noch die Spiellogik und das Zeichnen implementieren.

Die etwas gekürzte Logik für den Player:

const w = 30/desiredFramerate;
const q = w*(Math.PI / 20);
Player.prototype.calculateNextFrame = function() {
    if (this.movement == move.left)
        this.setDirection(this.angle + q);
    else if (this.movement == move.right)
       this.setDirection(this.angle - q);

    this.x += this.deltaX*this.speed*3*w;
    this.y += this.deltaY*this.speed*3*w;
    this.path.push([this.x, this.y]);
}

Und der Zeichencode für einen Player:

Player.prototype.draw = function(ctx) {
    //Draw the path
    ctx.beginPath();
    ctx.lineWidth = this.radius * 2 + 0.5;
    ctx.strokeStyle = this.color;
    for(var i = 0; i < this.path.length - 1; i++) {
        if(this.path[i] != null) {
            var x = this.path[i][0];
            var y = this.path[i][1];
            ctx.lineTo(x, y);
        }
    }
    ctx.stroke();
    ctx.closePath();

    // Draw player head
    ctx.beginPath();
    ctx.fillStyle = "yellow";
    ctx.arc(this.x, this.y, this.radius + 0.5, 0, Math.PI * 2, true);
    ctx.fill();
    ctx.closePath();
};

Wer sich für den genauen Code interessiert, um z.B. zu sehen wie die Keyboard Events funktionieren: achtung.js

Comment on this article

Next »