λόγος and the Rise of Turtle Graphics

Logos é uma palavra forte de origem grega, ela pode significar razão, palavra ou discurso. Os próprios gregos foram responsáveis pela Trigonometria, cuja etimologia é a composição das palavras trigōnon (triângulo) e metron (medida) – algo como o estudo das medidas do triângulo (3 lados e 3 ângulos). Eles também foram responsáveis pelas inserções da Geometria (γεωμετρία = terra + medida) Euclidiana. Ambas as disciplinas são ramificações da Matemática, sendo que elas, juntamente com Análise Vetorial e Quaternion são os elementos fundamentais da Matemática para Computação Gráfica.

clip_image002

Escola de Atenas e os Elementos de Euclides

Se juntarmos todos estes ingredientes, misturarmos e adicionarmos algumas palavras mágicas (spells) – dentro de contexto da Computação, obviamente. Podemos extrair um produto fundamental composto por um software gráfico e educacional cujo “logo” (de logotipo) é uma tartaruga.

Sim, é sobre isto mesmo que estou falando, da linguagem de programação Logo. Que possui uma fundação, que a promove: Logo Foundation. O mais interessante, é que Logo é influenciada pela linguagem de John McCarthy, e ele dispensa apresentação. Fora isto, existe outra ferramenta educacional criada pela Microsoft, o Small Basic, que promove a linguagem Basic.

O Basic aquele que deixou Gates e Allen famosos. E também foi minha primeira linguagem de programação (que com o passar do tempo houve uma relação entre amor e ódio, hoje existe muito respeito para com ela). O ambiente do Microsoft Small Basic possui um objeto chamado Turtle, que permite você fazer a mesma coisa que um ambiente Logo, só que em Basic – muito recomendado para a garotada, ou para os profissionais que ainda acham que programar para SharePoint (ou qualquer outro produto) é programar. (Nada pessoal, com SharePoint foi apenas uma escolha aleatória baseado em popularidade e falácia, poderia ser BizTalk, sei lá Smile with tongue out).

The rise of Turtle Graphics Xbox

Há duas semanas, no mesmo dia que o Fates Warning tocou no Brasil, tive a oportunidade de ministrar uma palestra sobre Scala. E uma das minhas ideias era criar um exemplo especial fora do contexto do Scala. (O Fates Warning é uma das minhas bandas favoritas desde a adolescência). Flirt male

O exemplo especial foi construir um ambiente de Turtle Graphics para Windows usando C++, Direct2D, Java e Scala. E estendi este exemplo para minha segunda linguagem favorita, o F#. Ou seja, programei o engine e seu modelo de scripting compatíveis com a JVM e com a CLR.

clip_image002[4]

Na verdade, todo este trabalho surgiu pela oportunidade da palestra (Palestra Fundamentos de Scala). No entanto, criar este ambiente era uma idéia antiga, impulsionada pelo livro que está ai em cima: An Integrated Introduction to Computer Graphics and Geometric Modeling de Ronald Goldman. Neste livro, ele começa dizendo algo do tipo: “vamos começar aprender Computação Gráfica e sua Matemática usando o Turtle Graphics”. Assim, ele sugere o uso do Logo ou que você crie seu ambiente. Bem, eu optei pelo segundo, seguindo meu instinto de programador C++. Devil

Após fazer um rápido protótipo com Excel para validar as equações trigonométricas, li rapidamente a documentação do Direct2D para extrair o que eu precisava codificar e sai codificando. Com engine finalizado, parti para a parte do scripting e fiz uma coisa inédita – usei Java Native Interfaces (JNI), para expor o código nativo para Scala – sinceramente, foi bem tranquilo e divertido – e para aqueles que gostariam de saber: Sim, estou programando em Java, afinal (agora) sou um programador sem fronteiras! A mesma coisa eu fiz para o F#, que no caso pode usar P/Invoke diretamente.

Camada de interoperabilidade do scripting com o Java:

import java.nio.ByteBuffer;

final class JNITurtle
{
    private native ByteBuffer create();
    private native void rotate(ByteBuffer hTurtle, float angle);
    private native void resize(ByteBuffer hApp, float size);
    private native void move(ByteBuffer hApp, int distance);
    private native void speed(ByteBuffer hApp, int value);
    private native void destroy(ByteBuffer hApp);

    private boolean disposed;
    private ByteBuffer hTurtle;

    JNITurtle()
    {
        disposed = false;
        hTurtle = create();
    }

    static
    {
        System.loadLibrary("Turtle");
    }

    public void rotate(float angle)
    {
        rotate(hTurtle, angle);
    }

    public void resize(float size)
    {
        resize(hTurtle, size);
    }

    public void move(int distance)
    {
        move(hTurtle, distance);
    }

    public void speed(int value)
    {
        speed(hTurtle, value);
    }

    public void dispose()
    {
        if(!disposed)
        {
           disposed = true;
           destroy(hTurtle);
        }
    }

    protected void finalize()
    {
        dispose();
    }
}

Camada de interoperabilidade do scripting com o F#:

namespace global

open System
open System.Runtime.InteropServices

module private API =
    [<DllImport(@"Turtle.dll", CallingConvention = CallingConvention.Cdecl)>]
    extern nativeint create()
    [<DllImport(@"Turtle.dll", CallingConvention = CallingConvention.Cdecl)>]
    extern void rotate(nativeint hApp, float32 angle)
    [<DllImport(@"Turtle.dll", CallingConvention = CallingConvention.Cdecl)>]
    extern void resize(nativeint hApp, float32 size)
    [<DllImport(@"Turtle.dll", CallingConvention = CallingConvention.Cdecl)>]
    extern void move(nativeint hApp, int distance)
    [<DllImport(@"Turtle.dll", CallingConvention = CallingConvention.Cdecl)>]
    extern void speed(nativeint hApp, int distance)
    [<DllImport(@"Turtle.dll", CallingConvention = CallingConvention.Cdecl)>]
    extern void destroy(nativeint hApp)

[<Sealed>]
type private PInvokeTurtle() =
    let mutable disposed = false
    let mutable hTurtle = API.create()

    override this.Finalize() = this.close()

    member this.rotate(angle: float32) = API.rotate(hTurtle, angle)

    member this.resize(size: float32) = API.resize(hTurtle, size)

    member this.move(distance: int) = API.move(hTurtle, distance)

    member this.speed(value: int) = API.speed(hTurtle, value)

    member private this.close() = 
        if not disposed then 
            disposed <- true
            API.destroy(hTurtle)

    interface IDisposable with
        member this.Dispose() = GC.SuppressFinalize(this); this.close()

Estes trechos de código interagem com a fachada que exporta o modelo de scripting: jnimain.cpp e dllmain.cpp do código nativo. Tanto o JNITurtle quanto o PInvokeTurtle implementam suas estratégias para liberação de recursos via dispose pattern.

jnimain.cpp:

#include "scriptable.hpp"

#include <jni.h>

extern "C" 
{
    JNIEXPORT jobject JNICALL Java_JNITurtle_create(JNIEnv* env, jobject)
    {
        return env->NewDirectByteBuffer(scriptable::create(), 0);
    }

    JNIEXPORT void JNICALL Java_JNITurtle_rotate(JNIEnv* env, jobject, jobject hTurtle, jfloat angle)
    {
        auto turtlePtr = reinterpret_cast<scriptable::TurtlePtr>(env->GetDirectBufferAddress(hTurtle));
        scriptable::rotate(turtlePtr, angle);
    }

    JNIEXPORT void JNICALL Java_JNITurtle_resize(JNIEnv* env, jobject, jobject hTurtle, jfloat size)
    {
        auto turtlePtr = reinterpret_cast<scriptable::TurtlePtr>(env->GetDirectBufferAddress(hTurtle));
        scriptable::resize(turtlePtr, size);
    }

    JNIEXPORT void JNICALL Java_JNITurtle_move(JNIEnv* env, jobject, jobject hTurtle, jint distance)
    {
        auto turtlePtr = reinterpret_cast<scriptable::TurtlePtr>(env->GetDirectBufferAddress(hTurtle));
        scriptable::move(turtlePtr, distance);
    }

    JNIEXPORT void JNICALL Java_JNITurtle_speed(JNIEnv* env, jobject, jobject hTurtle, jint speed)
    {
        auto turtlePtr = reinterpret_cast<scriptable::TurtlePtr>(env->GetDirectBufferAddress(hTurtle));
        scriptable::speed(turtlePtr, speed);
    }

    JNIEXPORT void JNICALL Java_JNITurtle_destroy(JNIEnv* env, jobject, jobject hTurtle)
    {
        auto turtlePtr = reinterpret_cast<scriptable::TurtlePtr>(env->GetDirectBufferAddress(hTurtle));
        scriptable::destroy(turtlePtr);
    }
}

dllmain.cpp:

#include "scriptable.hpp"

extern "C"
{
    __declspec(dllexport) scriptable::TurtlePtr create()
    {
        return scriptable::create();
    }

    __declspec(dllexport) void rotate(scriptable::TurtlePtr hTurtle, float angle)
    {
        scriptable::rotate(hTurtle, angle);
    }

    __declspec(dllexport) void resize(scriptable::TurtlePtr hTurtle, float size)
    {
        scriptable::resize(hTurtle, size);
    }

    __declspec(dllexport) void move(scriptable::TurtlePtr hTurtle, int distance)
    {
        scriptable::move(hTurtle, distance);
    }

    __declspec(dllexport) void speed(scriptable::TurtlePtr hTurtle, int value)
    {
        scriptable::speed(hTurtle, value);
    }

    __declspec(dllexport) void destroy(scriptable::TurtlePtr hTurtle)
    {
        scriptable::destroy(hTurtle);
    }
}

Você poderá carregar o Turtle.dll (o engine do Turtle Graphics) nos respectivos ambientes REPL (F# e Scala) usando uma fachada de código gerenciado, no meu caso TurtleFSharp.dll e turtle.jar.

clip_image002[7]

clip_image004

E com isto, produzir uma sequência de riscos variados, ou seja, desenhos:

clip_image006

clip_image008

Aqui um exemplo de script em Scala:

clip_image010

Se tiver curiosidade. A Turtle.dll, o engine do meu Turtle Graphics, é composto das seguintes dependências:

clip_image012

D2D1.DLL é o Direct2D, MSCRV100.DLL é a C Runtime Library (CRT) do Visual C++ 10 e MSVCP100.DLL é a Standard C++ Library (SCL) do Visual C++ 10. O resto já é conhecido do sistema operacional Windows.

Se quiser digerir a parte que interage com o Direct2D, você encontrará no program.cpp definições como:

Construtor e os recursos a serem utilizados (estes caras que precisarão ser liberados pela ação de dispose):

clip_image002[9]

Inicializador da Janela:

clip_image004[6]

O message loop:

clip_image006[5]

Método que desenha o caminho da tartaruga:

clip_image008[5]

O “renderizador”:

clip_image010[6]

clip_image012[5]

Responsável por carregar o “cursor” da tartaruga:

clip_image014

As funções matemáticas de auxilio:

clip_image016

E o componente de script:

clip_image018

Enfim, vai encontrar algumas pérolas. Hot smile

Faça o download do código fonte e binário do Turtle Graphics e assim como eu divirta-se. Na verdade, acho que você vai preferir mesmo o Small Basic ou o Logo, mas isto não é mais problema meu – mas o meu funciona nos REPLs do Scala e do F# (e C#, quando o Roslyn estiver ok). Winking smile

About Fabio Galuppo

I'm Software Engineer and Professional Trainer. I love Programming and Rock'n'Roll.
This entry was posted in C++, Computer Graphics, Direct2D, F#, Functional Programming, Math, Scala, Windows SDK. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s