1. Foreword

Welcome to djnn, the software development framework that will help you produce innovative user interfaces throughout efficient design and development processes.

djnn is an extensible environment, and we continuously develop extensions for supporting richer user interfaces. The current version includes the core system of djnn, the graphical module that implements the core of the SVG model, finite state machines for describing discrete behaviours, data-flow bricks for describing continuous behaviours, and initial support for input management.

This document is a guide to using the Djnn Java API. The introduction chapter provides background and practical information about djnn in general, this API in particular, and how to use the available documentation. The other chapters introduce the concepts used in djnn and describe how to use the services provided to build your own user interfaces.

This document is not an exhaustive list of classes and functions, structured by modules. the Djnn Java API Reference Manual (not available yet). This document rather takes on the endeavour of introducing you to the concepts of djnn programming through a progressive approach, focusing initially on the most common task: building a graphical user interface.

The copyright holder on this book is Ecole Nationale de l’Aviation Civile (2007-2014). This book is part of the Djnn Java API, and it is made available to you under the same terms as the said software. Please refer to the documents you received when obtaining the software.

2. Introduction

to be written

3. Setting up Djnn

This chapter describes how to install the Djnn Java API, how is organised, and how you can compile your own code.

3.1. Introduction

Before going into further details about how to program with the Djnn Java API, we need to settle a few practical issues. This is the goal of this chapter.

3.2. Prerequisite

The Java Djnn API is a wrapping of the Djnn C libraries based on JNA. The installation of the djnn C libraries corresponding to your operating system is required. To complete this installation, please refer to the documentation of the C library. Once this library is properly installed on your system, you need to download the JNA library version 3.5.2 or higher and to add it to your Java project. Then, you can add the Djnn Java library to your project. If you experience a JNA library linking error while running your program, you may have to specify the installation path of the Djnn C libraries:

On a linux system:

export LD_LIBRARY_PATH=/path/to/your/djnn/lib

On a macos X system:

export DYLD_LIBRARY_PATH=/path/to/your/djnn/lib

3.3. MacOsX specific

In order to run your Djnn Java program on the MacOs X operating system you must use the following option:

java -XstartOnFirstThread yourProgram

3.4. API structure

3.4.1. Djnn modules

The Djnn environment is organised around the concept of module. A Djnn application uses the core module, plus optional modules depending on the desired interaction modalities: 2D graphics, speech, etc. The Java Djnn API has a special class, called Application, that you have to instantiate at the beginning of your program. The constructor of this class load the core and the base modules then you can load other modules by calling the initModule method. The argument of this method is an enum type that gives you the list of the available modules.

Application app = new Application ();
app.initModule (Module.GUI);

Note that the Application class has also a special method called startMainloop. You have to call this method at the end of your program if you want to run an interactive application.

3.4.2. Djnn Java packages

The Java Djnn API is organised around classical Java packages corresponding to the various Djnn modules. The list of the packages and classes can be found in the Javadoc.

3.5. Compiling your own Djnn code

3.5.1. Compiling an application

Let us use a simple example, which assembles three Djnn components in a larger component in order to emit a beep every second:

import net.djnn.java.core.Application;
import net.djnn.java.core.Binding;
import net.djnn.java.core.Component;
import net.djnn.java.core.Element;
import net.djnn.java.core.Module;
import net.djnn.java.sound.Beep;

public class TestBeep {
  public static void main (String[] args) {
    Application app = new Application ();
    app.initModule (Module.SOUND);
    Component root = new Component (null, "root");
    Clock cl = new Clock(root, "cl", 1000);
    Beep beep = new Beep (root, "beep", 1);
    new Binding (root, null, cl, beep);
    root.run ();
    app.startMainloop ();
  }
}

This example uses components from the Sound and Base modules, and of course it uses the Djnn core. The instruction to compile it looks like the following:

javac -classpath /path/to/djnn.jar TestBeep.java

And this is the instruction to run your program:

java -classpath /path/to/djnn.jar TestBeep

Building interactive applications

1. The application tree

This chapter describes how to build an interactive application by creating, running and evolving an djnn tree. It introduces the concept of component and a few basic component types, shows how to build the application tree with components and how to refer to components in the tree. It ends with a simple description of the execution of an interactive application.

1.1. Introduction to the Djnn tree

In chapter Setting up Djnn you have seen how to compile and run a simple Djnn application. We will now see how to build more complex applications.

1.1.1. A tree of components

The central structure of an djnn application is a tree of components. Components are the nodes of the tree. If you know XML, the concept will be familiar to you: in the same way that an XML tree is made of XML elements, an djnn tree is made of djnn components. Actually, we will see later that djnn components can be created from XML elements, and stored as XML elements.

All user interaction modules provided by djnn, starting with the GUI module, come as a set of djnn components that you can use to build your application tree. For displaying a rectangle in a window, you will use a Rectangle component and a Frame component, for instance. For emitting sounds you can use a Beep component. For managing touch input you will need to deal with Pointer components. And for defining the behavior of your application you will use control components such as Binding, Connector or FSM components.

1.1.2. Composite components

Some components can contain children, and this is how you build your application tree. "Component" has the same meaning here as in the expression "component programming". Composite components are there to help you structure your application, provide reusability, and ensure encapsulation. You build an interactive component (some say an interactor) by assembling components into a composite component. The resulting interactor is a component that you or others can reuse to build more complex interactors, software components, or applications. In the end, your application itself is a component. It can take part in larger scale components made of multiple program, or you can reuse it later as a sub-component of a larger application.

Whereas other djnn components are mostly in charge of implementing the basic blocks of user interfaces, djnn composite components are mostly in charge of software engineering issues. Components implement encapsulation and parameterisation through a namespace system. All names exported by children are only visible to their parent and siblings. These names can in turn be exported (with an optional renaming) by the parent component to its own siblings and parents.

1.1.3. Tree traversals

Finally, a word about execution. For the time being, let us consider that the execution of the application is a series of traversals of the tree. During these traversals, all active djnn components are triggered. Being triggered causes the components to implement what they are made for: displaying a rectangle, managing a window, emitting a sound, etc. There are several types of traversals of the tree during its lifetime. Initialisation, rendering and control flows triggered by events are example of such traversals. If you are familiar with 3D graphics, you might like to understand the djnn tree as an extended scene graph. If you are more interested in the theory of languages and compilation, you might prefer to interpret it as the abstract tree of a language, because it is oriented towards structuring software as much as rendering graphics.

Rendering as a traversal of the tree

In the rest of this chapter, we will see in more detail how you can build an djnn tree for your application, and what happens when you run the application.

1.2. Building the tree

1.2.1. Creating components

The most simple way of building an application tree is to create new djnn components by assembling existing components. The following example creates a label component, made of two rectangles and a text. It uses constructors that each create a fresh component.

import fr.i_star.java.core.Component;
import fr.i_star.java.core.Element;
import fr.i_star.java.gui.Rectangle;
import fr.i_star.java.gui.Text;
import fr.i_star.java.gui.style.FillColor;
import fr.i_star.java.gui.style.TextAnchor;

public class Label extends Component {
        public Label(Element parent, String name, String text,  double x, double y) {
                super(parent, name);
                Element bkg = new Component(null, null);
                new Rectangle(bkg, "r1", x, y, 150, 50, 5, 5);
                new Rectangle(bkg, "r2", x + 5, y + 5, 140, 40, 5, 5);
                this.addChild(bkg, "bkg");
                new FillColor(this, "c", 0, 0, 0);
                new TextAnchor(this, "ta", TextAnchor.Anchor.MIDDLE);
                new Text(this, "label", x + 70, y + 30, text);
        }
}

This example shows two ways of assembling components. Each component class has a constructor that allows you to specify the parent of the component you are creating, and the name under which the new component will be known by its parent. This is how the text is added to the label. These two arguments are available in all constructors, and default to null. When no parent is specified the components can be added to a parent later, using the addChild method. This is how the bkg component is added to the label component.

Giving names to components is not mandatory. However, you will need it if you want to address a given component, for instance for setting its properties from a resource file or binding actions to events occuring in that component.

1.2.2. Loading components

We have seen how Djnn components can be created with Java code, using the constructor of component classes and passing it the appropriate parameters. Here is another way of creating components: loading them from XML data. Each builtin type of djnn components has an XML equivalent, and each djnn module comes with an XML parser that can read the corresponding XML elements. Therefore, you can load individual components from XML files (or from the Web) and use them to build the djnn tree. Or you can load more complex components that constitute a ready-made sub-tree. The following example loads a given graphical component from a SVG file, a whole component from another XML file, and uses them to build a tree.

Element svg = Component.load("mySvgFile.svg");
Element xml = Component.load("myXMLFile.xml");
Component root = new Component (null, "root");
root.addChild(svg.findElement("layer1"), "layer1");
root.addChild(xml.findElement("behavior"), "behavior");
root.run();

1.2.3. Cloning components

To be written

1.2.4. Models

In your programs you might wish to create or load components in the sole purpose of cloning them in the future or running them upon given events, which means you do not want them to be run like the others. You can do this by putting them in components that will never be run. This is done by creating a new tree root (a component with no parent). Or, in some cases, you can pass an extra argument named model to the constructor, before the parent and identifier; this means that the created component is to be handled as a model and should not be run when its parent is run. See for instance the first argument to new Beep() below, which means that beep is a model and should not be run when c is run (note for experts programmers: this is similar to the defun declaration in Lisp or the sub declaration in Perl).

Component c = new Component (null, null);
Element beep = new Beep (c, "beep", 1)
c.run(); /* no beep */

Many non-persistent components (that is, components whose rendering spontaneously finishes just after starting) are similar to functions in functional or imperative languages and we will see examples where they are useful as models, not because we want to clone them but because we want to activate them explicitly and not when their parent is activated.

1.3. References in the tree

When building the tree, or later when implementing the application behaviour, it is often necessary to refer to existing components. djnn provides several ways of doing that.

1.3.1. Java references

When programming in Java, the most efficient and natural-looking way of referring to a component is to use reference to a class instance. That is what we have done in all examples so far, like with b and r in the following code:

Element b, r;
b = new Component (0, "border");
r = new Rectangle (b, "rectangle", 10, 10, 150, 50, 0, 0);

As you see, all such reference have type Element, which is the most basic type of a node in the application tree. We will use such references at many places in this manual. Please be aware though that it this not a portable way of referring to Djnn components: it does not transpose to components stored in XML files, for instance. For this, you need to use path references or alternative referencing systems.

1.3.2. Path references

When building the tree, or later when implementing the application behaviour, it is often necessary to refer to existing components. djnn provides several ways of doing that.

c = new Component (0, "root");
new Rectangle (c, "r", ...);
new Beep (c, "b", 1);
p = c.findElement ("r/press");
b = c.findElement ("b");
new Binding (c, "binding", p, b);

1.3.3. URIs

djnn supports the creation of your own naming systems, including URIs. This is especially useful for referring to components situated in other programs, possibly on other computers:

Java code

1.3.4. Extended path references

djnn provides supports for the Xpointer standard defined by the Web consortium. To be completed.

1.4. Modifying the tree

To be written.

1.5. Executing your application

1.5.1. Execution phases

Let us come back to the simple complete application from chapter Setting up Djnn. The execution of the main method can be decomposed in four phases:

public class TestBeep {
  public static void main (String[] args) {
    /* phase 0: initializing Djnn */
    Application app = new Application ();
    app.initModule (Module.SOUND);

    /* phase 1: creating an application tree */
    Component root = new Component (null, "root");
    Clock cl = new Clock(root, "cl", 1000);
    Beep beep = new Beep (root, "beep", 1);
    new Binding (root, null, cl, beep);

    /* phase 2: initial rendering of the tree */
    root.run ();

    /* phase 3: waiting for interactions */
    app.startMainloop ();
  }
}

The initialization phase is mandatory but it is not really part of your application; this phase is even automated in other Djnn APIs. This leaves us with three phases:

  • the construction of the tree;

  • the initial rendering of the tree or a portion of the tree, by a call to the run method of the class Component;

  • the runtime phase, in which the application tree waits for interactions. During this phases, the tree changes in reaction to end-user’s actions, or to any other external events. These changes are then interpreted into visible modifications (or any other perceptible change) of the UI through new renderings of the UI tree.

These phases are described in more details in in the following sections.

1.5.2. Initializing the tree

to be written

1.5.3. Initial rendering

The djnn tree is just a representation of your application and its user interface. Building it ensures that its components are ready to be activated, but it has no real effect. To really start interacting with the end-user, the tree must first be interpreted into perceptible effects: pixels on the screens, sounds, etc. This phase is called the initial rendering of the tree.

Rendering and execution

What does "rendering" mean? In djnn, rendering means executing. The term comes from computer graphics, and djnn extends it to all kinds of components. In computer graphics, images are built in two phases: first, a digital model of objects is created in memory, then the model is "rendered" as pixels on the screen.

Rendering is the operation by which a model is interpreted into its intended effect: displaying a shape on the screen, producing a sound, or just modifying data in the application. Being rendered (or interpreted) is the main role of components when they are executed, and every type of components encapsulates a given type of rendering. Chapters Building native components and Building modules explain how new component types can be created to create new effects.

Formally speaking, the execution of a tree is a sequence of activations of the components in the tree. When activated, composite components execute their children and atomic components trigger whatever rendering they are made for. Once your djnn tree is ready, you need to activate the part (or parts) of the tree that you want to be rendered when you launch your program. Usually you will run the root component of your application, which in turn will activate all its children, but you can decide to run only particular parts of it.

Rendering, or executing, a component is obtained by calling the run method of the class Component.

When this is applied to a container component, all its sub-components are executed in depth-first, left to right order; this means that the execution of the second child does not start before the first child and all its children have started executing. This ensures the tree traversal shown in the Introduction chapter.

You can control the initial rendering of the tree by choosing which components to execute. In a simple program, you will create a root component, fill it with children, then start it. But you can be more selective. For instance in the following code, component c2 and its children are not activated, which means that only one window will appear on the screen.

c = new Component (null, null);
c1 = new Component (c, null);
new Frame (c1, null, ...);
c2 = new Component (c, null);
new Frame (c2, null, ...);
c1.run ();

In order to have both windows appear, one should run c2. This is done by either calling c2.run () instead of c1.run (), or calling c.run () later during the execution of the application.

Running and stopping components

to be written.

Component order

The order in which components are executed or rendered is often important. Let us imagine, for instance, a hypothetical Morse module and the following code:

c = new Component (null, null);
c1 = new Component (c, null);
c2 = new Component (c, null);
new MorseLetter (c2, null, "M");
new MorseLetter (c2, null, "A");
new MorseLetter (c2, null, "Y");
new MorseLetter (c1, null, "S");
new MorseLetter (c1, null, "O");
new MorseLetter (c1, null, "S");
new MorseLetter (c2, null, "D");
new MorseLetter (c2, null, "A");
new MorseLetter (c2, null, "Y");
istRunComponent (c);

This example would emit the string ... --- ... -- .- -.-- -.. .- -.--, that is SOS MAYDAY. This illustrates how rendering works: the structure of the tree influences the rendering order. The tree is traversed in depth-first, left-to-right order. A component inserted before (at the left of) another component will be rendered before that other component.

Rendering context

Now let us come back to graphical user interfaces and examine how the rendering of a component can influence the rendering of other components in the tree. Consider the following example with two windows and two texts.

c = new Component (null, null);
new Frame (c, "f1", ...);
new Text (c, "t1", 50, 50, "Window 1");
new Frame (c, "f2", ...);
new Text (c, "t2", 50, 50, "Window 2");
c.run ();

The four components in c are rendered in the order f1, t1, f2, t2, and the rendering of Frames influences the rendering of texts: text t1 is rendered in the first window, and text t2 in the second window.

This shows that the rendering of a component can affect the rendering of the components that are rendered after it. Actually, this is only true within the same component: components can influence the rendering of components that appear at their right in the same component, as well as their children.

1.5.4. Waiting for interactions

Although djnn makes it possible to write pure computation programs and sequential dialogues controlled by the computer through the command line, most applications you will write will wait for user input or external events during an indeterminate amount of time. The "system relay loop" component makes this possible in two different ways:

  • when running, it engages your program into a waiting loop or equivalent, until you decide to stop it,

  • it routes events traditionnally detected by the operating system: clock events, network events, and input events when appropriate.

After the initial rendering, interactive applications wait for user events, clock events, network events or any other events that will trigger their response, usually in a perceptible way. This is called the runtime phase. During this phase, the control of the application resides with the operating system (or with a main loop, depending what operating system you are using) until the application decides to quit in response to an event.

See section System relay in chapter Execution model for more details.

2. Defining basic behaviors

to be written.

3. Designing components

to be written

4. The base module: behaviour bricks

to be written

Interacting with the user and the environment

1. The graphics module

An essential part of Djnn is its graphical user interface (GUI) module. The GUI module is a combination of graphics, drawing surface management, and input management. We focus here on graphics and a small subset of surface management. In this chapter you will learn the basics of drawing with Djnn: basic graphical components, object grouping, transformations, adding color and gradients, text capabilities, clipping. This chapter also explains how to load SVG files to populate the Djnn tree.

1.1. Getting started

The example below is one of the simplest applications with a GUI you can write. It does not do much, but the code demonstrates the basic code of every djnn program that involves a GUI:

  • load the appropriate modules

  • instantiate a frame

  • execute the application tree

  • wait for interaction

import fr.i_star.java.core.Application;
import fr.i_star.java.core.Component;
import fr.i_star.java.core.Module;
import fr.i_star.java.gui.Frame;

public class MyFrame {
  public static void main (String[] args) {
    Application app = new Application ();
    app.initModule (Module.GUI);
    Component root = new Component (null, "root");
    new Frame(root, "f", "f", 0, 0, 500, 500);
    root.run ();
    app.startMainloop ();
  }
}

The first two lines of the main method initialize the required modules. The forth line creates a Frame, that is a drawing surface that will appear on the display. Every program with a GUI must have at least one frame. The fifth line runs the frame, thus making it appear. The sixth line ensures that the program does not leave and the frame stays on the display.

1.2. Windowing

The GUI module offers basic support for windowing: creating rectangular windows and sub-windows, managing the cursor. In a later chapter, a more complex model for windowing and display surfaces will be proposed. However, the current model should prove enough for most graphical applications.

1.2.1. Frames

Frames are djnn components that each represent a window on the computer screen. The window is managed by the operating system.

A frame defines an initial coordinate system. The (0, 0) coordinates represent the upper-left corner of the frame. The numeric value ot the x-abscissa increases from left to right, and the numeric value of the y-ordinate increases from top to bottom. This initial coordinate system is used to place graphical objects.

A frame applies to (contains) all the graphical components that are located after it in the same parent component. When two or more Frames are in the same parent, the first Frame applies to all components until the second Frame, and so on. For instance, to create two rectangles in two windows, write:

Application app = new Application ();
app.initModule (Module.GUI);
Component root = new Component (null, "root");
new Frame (root, "frame1", "f1", 0, ...);
new Rectangle (c, "rect1", 10, 10, ...);
new Frame (c, "frame2", "f2", 100, 0, ...);
new Rectangle (c, "rect2", 10, 10, ...);

You may find it surprising that a Frame is not a container in djnn. This is a deliberate choice: being aimed at multimodal user interfaces, djnn does not give a privileged place to graphics and windowing. The privileged place is occupied by Components. To build an object made of a window containing graphical objects, create a Component, put a Frame in it, then put graphical components.

1.2.2. Creating sub-windows

(to be written)

Note that an alternative to Frames for creating sub-windows is the use of Clips, described later in this chapter. With clips you can to obtain similar visual results, and more.

1.2.3. Cursors

Cursors are djnn components that allow you to control the mouse cursor. Like a graphical component, a cursor applies to the current Frame. When several cursors are specified for the same frame, the last one will apply.

Note: Cursors have only been tested on Windows.

1.3. Graphical shapes

Graphical shapes are provided as components that you add to the tree. The set of graphical components is similar to those from the SVG standard.

1.3.1. Basic shapes

djnn contains the following set of basic shapes: circles (Circle), ellipses (Ellipse), straight lines (Line), rectangles with optional round corners (Rectangle).

You can add a basic shape to your program by calling the constructor of this shape. Thus, new Rectangle () will create a new rectangle, and new Circle () will create a new circle. The Djnn libraries Reference Manual gives all the arguments you can give to specify the geometry of those graphical components, in addition to the usual arguments for Components.

Shapes only describe the geometry of graphical objects. When used alone, they are displayed with default visual attributes.

1.3.2. Curves

Besides basic shapes, djnn can also draw arbitrary shapes with both straight line and cubic Bézier curves, with components Polygon and Path.

A polygon is a series of line segments. A path is a series of segments that can be straight lines, cubic Bézier curves or quadratic Bézier curves. A cubic Bézier curve is a curved segment that is defined with two end points and two control points. Each control point determines the shape of the curve by controlling one of the end point tangent vector.

Example of cubic Bézier curves

Note
Mathematically speaking, the basic shapes from the previous section are equivalent to a curve that would construct the same shape.

1.3.3. Images

You can display a pixmap image with an Image component. djnn supports bitmap, gif, jpeg and png files.

If the image file supports transparency (not alpha-blending, only full transparency), djnn will render its transparent areas.

Note
You can apply the full set of transformations to an image. However, the result may sometimes be hard to read.

1.3.4. Texts

For displaying a text, you need to use an Text component.

Upcoming JAVA code

A

1.4. Graphical style

You can control the appearance of graphical components by specifying how they should be rendered. This is done by using graphical components collectively named graphical style. If you have used UI programming toolkits in the past, it is important to note that with djnn you cannot specify the graphical style as an argument of a shape constructor, but that it is made of independent components. It reflects the fact that each style component modifies the graphical context within which all following shapes are drawn. Thus, if you put a red FillColor in the djnn tree, each following shape will be rendered with a red filling until a new FillColor or a NoFill is encountered in the tree.

1.4.1. Fill and stroke

Shapes and text can be filled (ie apply paint to the interior of the shape) and stroked (ie apply paint along the outline of the shape). A shape may be filled with a simple color (FillColor), a linear gradient (LinearGradient) or a radial gradient (RadialGradient), see Gradients for more details.

The outline can only be painted with a simple color (OutlineColor) but it can be customized with the following components:

  • width (OutlineWitdh)

  • cap style (OutlineCap)

  • join style (OutlineJoin)

  • miter limit (OutlineMiterLimit)

  • dash (DashArray, DashOffet)

1.4.2. Opacity

djnn provides a way to assign opacity values to fill and stroke so that the underlying graphics show through when you draw your shapes or images.

You can set the opacity of an object by adding an opacity component for the fill (FillOpacity) or the stroke (OutlineOpacity).

Example: fill opacity 100% (default) and 50%

Upcoming JAVA code

Opacity is a number between 0.0 and 1.0. Whereas a new fill or stroke colour encountered in the tree replaces the current fill or stroke colour in the graphical context, opacities are compounded by multiplying the current value with the new one.

1.4.3. Gradients

Perception is based on contrast, and color contrast is the easiest to create. Designers often want to use gradients rather than mere color juxtaposition. Gradients consists of continuously smooth color transition along a vector from one color to another, possibly followed by additional transitions along the same vector to other colors. djnn provides 2 types of gradients:

  • linear gradient (LinearGradient),

  • radial gradient (RadialGradient),

As in SVG, it is possible to add stops for each kind of gradient.

upcoming JAVA code

Example of gradients

1.4.4. Patterns

Filling with patterns is not yet implemented

1.4.5. Tiled images

Tiling is not yet available.

1.4.6. Fonts

For specifying the font used when rendering text, you can use the same font descriptions as in SVG. This includes font family (Helvetica, Arial, …), font size (12, 24, …), font style (italic, oblique) and font weight (bold, normal, lighter, bolder or a number in the [0, 100] range).

Upcoming JAVA code

1.5. Geometrical transformations

Within djnn, each graphical context has a coordinate system inherited from its ancestor, the root coordinate system being that of the frame. The coordinates of a graphical shape are always relative to those of its context, and the coordinate system of a graphical context is always relative to that of its parent context. By default, a graphical context has an identity transformation attached to it, so that the coordinate system of the parent applies by default.

You can modify the current coordinate system, and thus change the way graphical objects are rendered, by adding geometrical transformations. All geometrical transformations are compounded in sequence; this can be interpreted as each transformation creating a new context for the interpretation of the following transformations and shapes. See below for a note concerning the importance of the order.

djnn offers usual transformations: translation, rotation and scaling. It also offers full homographies (also known as projective transformations), that is general 2D transformations expressed with a 3x3 matrix.

It must be emphasized that transformations act on the relation between two coordinate systems and they do not modify the coordinates of objects themselves, just the way they are rendered. You will see later how this allows you to have the same graphical objects rendered at two different scales in two different parts of your application tree. To figure out what is happening when you apply a transformation, you can imagine that the rendering engine draws each object inside a transparent sheet. To apply a translation, it moves the sheet of the given object; to apply a rotation, it spins this sheet. Similarly for scaling it modifies the sheet of the given component without touching the others sheets.

1.5.1. Translations

A translation has the effect of moving the graphical objects along the X and Y axes of their graphical context.

upcoming JAVA code

1.5.2. Rotations

A translation has the effect of moving the graphical objects along the X and Y axes of their graphical context.

upcoming JAVA code

1.5.3. Scaling

A scaling scales up or down the graphical objects along the X and Y axis of their graphical context. The centre of the scaling can be specified too, it is expressed by its coordinates in the graphical context that applies to the scaling.

upcoming JAVA code

1.5.4. Homographies

You can also perform more complex transformations, such as a mirror effect, by directly manipulating the matrix of an homography. The new coordinates (x2, y2) are derived from an orignal point (x1, y1) as follows:

| x2 |   | a c e |   | x1 |   | a.x1 + c.y1 + e |
| y2 | = | b d f | x | y1 | = | b.x1 + d.y1 + f |
| 1  |   | 0 0 1 |   | 1  |   |       1         |

For example, you can permute the X and Y coordinates with the following transformation matrix

| x2 |   | 0 1 0 |   | x1 |   | y1 |
| y2 | = | 1 0 0 | x | y1 | = | x1 |
| 1  |   | 0 0 1 |   | 1  |   | 1  |

This is expressed as follows:

upcoming JAVA code

It gives the following effect:

Permute X and Y axes

1.5.5. Order of transformations

Each graphical context has a 3x3 transformation matrix. When the object is created, this matrix is set to identity. Later, when a translation (similarly rotation, scaling, skewing) is added to a graphical context, its transformation matrix is left-multiplied by a translation (similarly rotation, scaling, skewing) matrix.

In a composite transformation/sequence of transformations, the order of individual transformation is important because composite transformations are built from right to left (i.e. when a new transformation is applied the current transformation matrix is left-multiply by the matrix of the transformation).

For example, if you first rotate then translate, you get a different result than if you translate, then rotate. Indeed, the matrix produced by the product (translation x rotation) is almost always different from the matrix obtained by the product (rotation x translation)

A rotation of alpha degrees followed by a translation by (dx, dy):

         | cos(alpha)  -sin(alpha)  dx |
Matrix = | sin(alpha)   cos(alpha)  dy |
         |     0            0       1  |

A translation by (dx, dy) followed by a rotation of alpha degrees:

         | cos(alpha)  -sin(alpha)  cos(alpha).dx - sin(alpha).dy |
Matrix = | sin(alpha)   cos(alpha)  sin(alpha).dx + cos(alpha).dy |
         |     0            0                     1               |

1.6. Structuring graphics

1.6.1. Grouping graphics

It is often useful to bundle graphical components together so that they can be manipulated easily as a whole. The main usages are:

  • bundling components together so they can be cloned, hidden, moved and more as a whole,

  • bundling several components together so that they form a new single component composed of several simpler one reacting as a whole to events,

  • interposing a new coordinate system in a hierarchy of components. This can be very useful to manage panning, zooming and other kind of viewing transformation. See Coordinate transformations for an explanation of the transformation system,

  • composing attributes such as opacity with those of their children components (see Opacity for more details),

Grouping graphics in djnn is obtained by creating an empty component and putting graphical components in it. In the example below, we introduce a component named OutlineColor that makes all graphical components in the same group red.

upcoming JAVA code

1.6.2. Clipping your graphics

to be written

1.7. Using SVG files

The GUI module is able to create a tree of graphical components from a file formatted in the SVG format. This means that instead of instantiating graphical objects, resources and transformations with component constructors, you can load them from a SVG file.

When applied to an SVG file, the function istLoadElement or creates a subtree of graphical components (objects, resources, transformations). You can then use the root of this subtree as a component. Or, taking advantage of the fact that SVG elements can be named, use it as a library of graphical models, using subroutines istFindElement and istCloneElement to instantiate the parts of the file you are interested in. See chapter Creating models and instantiating for more details.

2. Animation

to be written

3. Display surfaces

to be written

4. The input module

to be written

5. The GUI module

Chapter summary

This chapter describes the Djnn GUI library, which implements the classical combination of graphics and certain input devices that was implemented for decades by graphical toolkits: clicking on graphical objects, enter, leave, etc.

Chapter contents

5.1. Introduction

to be written

6. The files module

to be written

7. The network module

to be written

8. The MacBook module

to be written

Beyond the application tree

1. Extension mechanisms

to be written

2. Building native components

to be written

3. Building modules

to be written

4. Parsing XML files

to be written

5. Using the main loop

to be written