go back to the main page

The Kfandango Handbook

Jaime Soffer <jsoffer@(this site)>


Revision 1.01.00

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".

Kfandango is a tridimensional CAD application coded in C/C++ and extensible by Python.


Table of Contents

1. Introduction
2. Command Reference
The main Kfandango window
The File Menu
The Edit Menu
The View Menu
3. Developer's Guide to Kfandango
Architecture
The Python interpreter
The GL widget pointer
The Python extension system
primitives.hpp
primitives.cpp
Python API
Deep hack
4. Credits and License
A. Installation
How to obtain Kfandango
Requirements
Compilation and Installation
Configuration

Chapter 1. Introduction

Kfandango is a tridimensional CAD application coded in C/C++ and extensible by Python. Please report any problems or feature requests directly to the author.

Warning

This application is currently useless for anything remotely serious except development. Do not attempt to build bridges, skyscrappers or nuclear installations based on data output by it. Should be considered a feature-incomplete (somewhat) stable alpha version. It's very useful for squiggling, programming fractal sequences and to describe static geometry, nonetheless.

The purpose of this application, once finished, should be similar to the one of Autodesk's Autocad (TM). The basic architecture is based on it and on GNU emacs: creating an optimized core and allowing a high level language (AutoLISP or elisp) in this case Python to perform operations with its low level interfaces.

Currently what's ready are the basic primitives line, triangle, line strip and triangle strip. All of them are accesible from the external scripting, making possible to draw about anything with moderate effort.

What's lacking is the capability to modify these drawings. An entity, once drawn, will stay the way it is (can't be hidden or deleted) until the whole drawing is destroyed. The only editing feature already coded in is the ability to move an already placed point.

As for the auxiliar functions, currently it's possible to snap only to the end of a line or to an intersection.

Chapter 2. Command Reference

The main Kfandango window

The File Menu

File->New (Ctrl-n)

Creates a new document

File->Restart (F5)

Calls the dotfile again. If the dotfile doesn't include a "clear()" command its effect will accumulate.

File->Save (Ctrl-s)

Saves the document (Unimplemented)

File->Quit (Ctrl-q)

Quits Kfandango

The Edit Menu

Edit->Lines (Ctrl-l)

When active, draw lines clicking the left mouse button anywhere on the GL area. A continous line will be drawn from the first point until the action is deactivated.

Edit->Triangles (Ctrl-t)

When active, draw triangles clicking the left mouse button anywhere on the GL area. A triangle will be drawn for each three clicks, facing toward if the clicks were done in counterclockwise order.

Edit->Find intersections

When active, selects the intersection between two line segments clicking the left mouse button close to them.

Edit->Select entity

When active, a left mouse button click selects the nearest entity.

The View Menu

View->Lock rotation (F2)

Locks the rotation angles, making impossible to rotate the view dragging the mouse while clicking the left mouse button.

View->Reset rotation (Ctrl-r)

Resets to zero the rotation angles, restoring the original view.

Chapter 3. Developer's Guide to Kfandango

Extending Kfandango is a joy to behold. Just read through the next chapter to learn how!

Architecture

The Python interpreter

Kfandango architecture is structured the following way. The main KApplication is a instance of a class derived of KMainWindow. It constructs a GL widget and a KLineEdit inside of a QVBox. All this is usual on a KDE application.

The most important technical difference between this and a conventional KDE application is the presence of a Python interpreter. This is implemented as a QPyObject derived from a QObject class. The constructor of this derived class runs Py_Initialize and the init* functions defined on primitives.h. The destructor, of course, runs Py_Finalize.

The Qt magic of this object works the following way: it has an slot called processExternalCommand who receives a QString instance as its argument. It simply runs PyRun_SimpleString on it. Connecting the slot of qpo, the standard Python interpreter, to a signal from any KDE object who produces a QString allows transparent Python execution.

The GL widget pointer

There is one "call to the tooth fairy" in this application. It does mean that I have broke a fundamental design rule in exactly one place: the GL widget, an instance of KFandangoWidget is a global pointer. Therefore it should be dealed with special care.

The purpose of taking such a bold measure is to allow a direct participation between the Python extension and the core. Since all the functions in primitives.cc have direct access to the api using a live pointer declared extern the interfacing is very simple. For example, to create a line in the core the extension should call

  gw->pushLine(x1, y1, z1, x2, y2, z2);

The disadvantage of this method is an increased risk. gw (the name of the global pointer) should be fully constructed before of qpo (the Python interpreter), since any call to Python can run a GL function. gw should be last to be destroyed, too, and this may be hard to predict; one should trust on the Qt/KDE autodeleting method. Of course any irresponsible usage of the pointer by a programmer will present undefined behavior, and by "undefined" I mean the application will most probably crash.

The Python extension system

primitives.hpp

Here has to be placed a prototype for the new expanded function. It has the basic structure

PyObject* name(PyObject *self, PyObject *args);

where name is the internal name of the C function that will provide the funcionality.

primitives.cpp

Here is where the extension is done. Three things are required: an entry on a PyMethodDef array, the function implementation and optionally an initialization function (which has to be run on the QPyObject::QPyObject constructor) containing a Py_InitModule instance, if a new module is created.

The entry on the PyMethodDef array has the following structure:

  {
    "pyfunction",
    name,
    METH_VARARGS,
    "Example of a Python extension. My hovercraft is full of eels!"
  },

where the first, quoted value is the name the function will have when called in Python, the second one is the internal name as defined on primitives.hpp, the third one never needs to be changed unless keywords are added to the arguments (look at the line_from function implementation, and at the 1.8 chapter of the Python extension manual, "Keyword Parameters for Extension Functions") and the last one is a description which will be stored as modulename.pyfunction.__doc__ inside Python.

The function implementation has a specific structure. First, define the variables which will hold the parameters of the python function. If some parameter requires to be optional and with a default value define it here.

  float x, y;
  float side = 3.f;

The following piece of code is where Python does its job. The quoted string is a code for python to understand which parameters will the function receive. Any parameter following the pipe character is defined as optional. The addresses following it are the variables where Python will save the values it receives from the function.

  if (!PyArg_ParseTuple(args, "ff|f", &x, &y, &side)){
    return NULL;
  }

On this example the values x and y are mandatory floating point values and the parameter side is optional and defaults to 3.0; if the user inputs something that doesn't correspond to the "ff|f" string, PyArg_ParseTupleAndKeywords will return false, the function will send a NULL back to Python, it will emit an exception and everybody will be happy and safe. Of course, the call will fail.

Look in (Some appendix: TODO) for a full list of keywords to use on the coded parameters string.

The next part is the only one who will be coded as a trivial C function. Use the variables which were just filled by Python to do some processing. For example, draw an square with center x, y and side length side.

  gw->newStrip(x-(side/2.0), y-(side/2.0), 0.f, 1); // begin a line strip
  gw->pushToStrip(x+(side/2.0), y-(side/2.0), 0.f); // line to bottom, right
  gw->pushToStrip(x+(side/2.0), y+(side/2.0), 0.f); // top, right
  gw->pushToStrip(x-(side/2.0), y+(side/2.0), 0.f); // top, left
  gw->pushToStrip(x-(side/2.0), y-(side/2.0), 0.f); // close it at bottom, left

Next, the return value. Here are two options: return nothing or return a value. To return nothing:

  Py_INCREF(Py_None); return(Py_None);

It's very important to do Py_INCREF instead of returning directly, or Python will lose count of its references, overflow an stack and die after a few dozens of calls.

To return a value, using the same coded string system of before:

  return Py_BuildValue("[fff]", x, y, side);

returns a tuple containing the input values. Be careful of selecting the right types.

Python API

Table of Contents

Drawing primitives - Functions that create lines or triangles
mousebutton - Callback called each time a mouse button is pressed
clearcalled - Callback called each time the drawing is cleared
ratio - Returns the width/height ratio of the GL area
clicks - Returns a tuple containing the last clicked points
select_entity - Finds the closer entity to a point
set_selected - Sets an entity as active
get_selected - Gets the index of the currently active entity
findint - Attempts to find an intersection
axis - Determines the rotation axis of the view
reset - Resets all the rotation angles
lock - Makes the rotation angles read only
rot_style - Switches between axis constrained rotation and two-axis free style
zoom - Sets the values of the viewport's borders
color - Color manipulation functions
lpos - Moves a light
glEnable/Disable wrappers - Functions that activate or deactivate GL features
select - Turns on/off displaying entities as selected
getsnap - Finds a close endline vertex
snap_ratio - Modifies the maximum distance to the cursor when finding a snap
write - Pipes some text to the mock stdout
clear - Fully clears screen and memory
move - Replaces the position of the active vertex

Name

Drawing primitives ? Functions that create lines or triangles

Synopsis

idorudraw.beginLinesEntity(l);
int l;

idorudraw.beginTrianglesEntity(l);
int l;

idorudraw.line(x1, y1, x2, y2, z1, z2);
float x1;
float y1;
float x2;
float y2;
float z1;
float z2;

idorudraw.triangle(x1, y1, x2, y2, x3, y3, z1, z2, z3);
float x1;
float y1;
float x2;
float y2;
float x3;
float y3;
float z1;
float z2;
float z3;

idorudraw.line_from(x, y, z, l);
float x;
float y;
float z;
int l;

idorudraw.triangle_from(x1, y1, x2, y2, z1, z2, l);
float x1;
float y1;
float x2;
float y2;
float z1;
float z2;
int l;

idorudraw.line_to(x, y, z);
float x;
float y;
float z;

idorudraw.triangle_to(x, y, z);
float x;
float y;
float z;

Inputs

l

The index of the layer to which the future entity will belong (default = 1).

x, y, z, xn, yn, zn

3D coordinates of a point (the 'z' values default to 0)

Description

The begin*Entity, line and triangle functions draw unconnected primitives.

The line_from, line_to, triangle_from and triangle_to functions draw line and triangle strips.

Usage

To draw unconnected primitives call first a begin*Entity function passing a layer index to it; then call as many related primitives (lines or triangles) as required for the entity. Call any drawing initialization function to finish the entity and start a new one.

To draw a connected primitive (a strip) call first a *_from function passing to it a point to begin a line strip, a line to begin a triangle strip, and, in both cases, the layer to which the strip will belong. To suspend a strip call any drawing initialization function.

Future considerations

Originally it was possible to use nested primitive initialization (for example, calling beginTrianglesEntity, then line_from, triangle and later line_to on that order) but it's currently not possible and will generate an error. This must be fixed in the future.


Name

mousebutton ? Callback called each time a mouse button is pressed

Synopsis

float fandango_input.mousebutton();

Description

The mousebutton function does nothing by default. It is automatically called by KFandangoWidget::mousePressEvent and should be overriden by an user defined function.

Usage

Create a function to call each time a mouse button is pressed, then assignate it to fandango_input.mousebutton. The following example explains how to set the mouse to change the light position every click, and how to restore it.

### Defined on click_aux.py
def clp(l=0):
    position = cadget.clicks()
    lpos(position[2][0], position[2][1],  position[2][2], l)

### Every click from now on will set the light position to the 
### current cursor position

fandango_input.mousebutton = clp

### Restore the default click action (none)

fandango_input.mousebutton = None

Future considerations

Currently this function reacts to the left button clicks only; future versions will have left, middle and right button handlers.


Name

clearcalled ? Callback called each time the drawing is cleared

Synopsis

fandango_input.clearcalled();

Description

The clearcalled function does nothing by default. It is automatically called by KFandangoWidget::clear and should be overriden by an user defined function.

Usage

Define a function just as on fandango_input.mousebutton. This will be called after all the memory pages are deleted and recreated on KFandangoWidget::clear. The following example explains how to set up the application to zoom automatically after calling clear().

def rezoom():
    qzoom(15)

fandango_input.mousebutton = rezoom

Notes

I don't remember what was it implemented for.


Name

ratio ? Returns the width/height ratio of the GL area

Synopsis

cadget.ratio();

Description

Simply returns the widget ratio. Useful to calculate the exact corners of a zoomed view.

Usage

Use the returned value to find a good value for the corners when doing a zoom.

### Defined on fandango.py
def qzoom(n, x = 0.0, y = 0.0):
    zoom((-n * cadget.ratio())+x, n+y, -n+y)

Name

clicks ? Returns a tuple containing the last clicked points

Synopsis

[[fff][fff][fff]] cadget.clicks();

Description

Returns a [[xyz][xyz][xyz]] tuple containing the coordinates of the last three clicked points.

Usage

Assignate the return of this function to a variable, then use its contents to perform any desired operation. The following example displays how to draw a triangle from the three latest clicks.

### Defined on click_aux.py
import idorudraw; D = idorudraw

def ct(l = 1):
    D.beginTrianglesEntity(l)
    corners = cadget.clicks()
    D.triangle( corners[0][0], corners[0][1], # x1, y1, 
                corners[1][0], corners[1][1], # x2, y2,
                corners[2][0], corners[2][1], # x3, y3,
                corners[0][2], corners[1][2], corners[2][2] ) # z1, 2, 3

Notes

The clicks are stored in order. corners[0] stores a point older than corners[1]. The initial state of the array is all zeroes.


Name

select_entity ? Finds the closer entity to a point

Synopsis

int cadget.select_entity(x, y, z);
float x;
float y;
float z;

Inputs

x, y, z

Coordinates of a 3d point.

Description

Finds an entity given a point near to it. The return value is the internal index of the entity.

Usage

Find a point using cadget.clicks or input one manually. Call the function with it as parameter and assign the output to a variable. Use the variable as a handler to perform operations over the found entity.

### Defined on click_aux.py

# After clicking on a point call this function and the closest
# line based entity and its closest vertex will be selected
def cs():
    select(1)
    c = cadget.clicks()
    cadget.set_selected(cadget.select_entity(c[2][0], c[2][1], c[2][2]))

Name

set_selected ? Sets an entity as active

Synopsis

cadget.set_selected(i);
int i;

Inputs

i

The index of the entity to set as active.

Description

Selects the entity. Setting an entity as active sets its active vertex too, allowing to move it with fandangoedit.move.

Usage

Find an entity by using cadget.select_entity, then call this function to make it active.

### Defined on click_aux.py

# After clicking on a point call this function and the closest
# line based entity and its closest vertex will be selected
def cs():
    select(1)
    c = cadget.clicks()
    cadget.set_selected(cadget.select_entity(c[2][0], c[2][1], c[2][2]))

# Will move the currently selected point to the last click
def cm():
    c = cadget.clicks()
    fandangoedit.move(c[2][0], c[2][1], c[2][2])

Name

get_selected ? Gets the index of the currently active entity

Synopsis

int cadget.get_selected();

Description

Simply returns the index of the currently active entity. Maybe useful to store a cache of previously selected entities.


Name

findint ? Attempts to find an intersection

Synopsis

glstate.findint(x, y, z);
float x;
float y;
float z;

[fff] glstate.getint();

Inputs

x, y, z

Coordinates of a 3d point.

Description

glstate.getint returns a [xyz] tuple containing the coordinates of the intersection between two lines. glstate.findint should have been called before with a point as input, which should be close to the expected intersection. If it is not, findint may either do nothing or find an undefined intersection somewhere else.

getint may be called as much times as needed, and its return will only change after a sucessful call to findint.

Usage

Retrieve a point by using cadget.clicks and pass its coordinates. Receive the resulting point on a variable. The following example is a crude attempt of drawing lines between intersection snaps.

### Get the first point (assumes the screen is filled with
### intersecting lines). Click near an intersection before calling
### the following: 

c = cadget.clicks()
findint(c[2][0], c[2][1], c[2][2])
c = getint()
line_from(c[0], c[1], c[2])

### Continue the strip, clicking near to intersections before calling
### the following.

c = cadget.clicks()
findint(c[2][0], c[2][1], c[2][2])
c = getint()
line_to(c[0], c[1], c[2])

### The whole process can be trivially automated (see, for example,
### cint() in fandango.py).

Future considerations

This function should return the found intersection's coordinates, but doesn't. To retrieve them glstate.getint should be called. This functionality is expected to improve.


Name

axis ? Determines the rotation axis of the view

Synopsis

glstate.axis(x, y, z);
float x;
float y;
float z;

Inputs

x, y, z

Components of a 3D vector

Description

Sets a rotation axis with origin in the center of the physical widget and pointing to center+[x,y,z]. Holding the left mouse button and pulling up and down rotates the view around this axis.

Notes

There are currently two rotation modes, to be set with glstate.rot_style(); passing to it 1 selects axial rotation and this function will set the axis. Passing to it 0 changes to a more free style rotation, and the selected axis will be ignored.


Name

reset ? Resets all the rotation angles

Synopsis

glstate.reset();

Description

The rotation angle of the view can be changed by holding the left mouse button and pulling around. This function resets the angles to zero.

Notes

Calling this function resets the view simultaneously for both rotation styles.


Name

lock ? Makes the rotation angles read only

Synopsis

glstate.lock(b);
bool b;

Inputs

b

A true/false value

Description

When actived by passing it a true value the mouse can't rotate the view. Can be deactived by passing 0 to it.


Name

rot_style ? Switches between axis constrained rotation and two-axis free style

Synopsis

glstate.rot_style(b);
bool b;

Inputs

b

A true/false value

Description

A value of 1 (default) sets axial rotation: the view will rotate around of the center by an axis determined by glstate.rot_style.

A value of 0 sets a more free rotation style, where left-right rotates the whole view around the y axis and top-down rotates around a rotated x axis.


Name

zoom ? Sets the values of the viewport's borders

Synopsis

glstate.zoom(l, t, b);
float l;
float t;
float b;

Inputs

l

Left border

t

Top border

b

Bottom border

Description

The GL widget viewport has two metrics, one in pixels and another in GL units. This function allows to match the GL metrics with the real screen pixels.

Setting the GL value who should be exactly on the left, top and bottom pixels, regardless of the resizing of the widget, allows full flexibility for the geometry. The right side value is automatically calculated on the call to this function based on the width/height ratio.

Resizing the widget causes the following effect: vertical resizing changes the scale of the view preserving the top and bottom values, and horizontal resizing crops the view by the sides, preserving the center.

Usage

This is mostly a low level function, and should be used to build upon it more user friendly ones. The basical usage is to determine first the top and bottom limits of the required viewport, then either calculate the center and substract enough value to determine the left side or simply give a good guess.

The following example is the actual implementation of qzoom, which resolves the border values for a view of exactly n*2 units of height with center on a given point.

### Defined on fandango.py
def qzoom(n, x = 0.0, y = 0.0):
    zoom((-n * cadget.ratio())+x, n+y, -n+y)

cadget.ratio determines the current ratio of the widget; it simply is substracted from the center. The other values are passed literally.

Future considerations

It may be useful to implement a mode to allow width-resizing to be pinned on a border instead of the center.


Name

color, clearcolor, lcolor ? Color manipulation functions

Synopsis

glstate.color(r, g, b, layer, a);
float r;
float g;
float b;
int layer;
float a;

glstate.clearcolor(r, g, b);
float r;
float g;
float b;

glstate.lcolor(r, g, b, light, a);
float r;
float g;
float b;
int light;
float a;

Inputs

r, g, b, a

RGBA components mapped between 0.0 and 1.0

layer, light

Index of the layer or the light to colorize

Description

color defines the color of all the entities on a determined layer.

clearcolor determines the color of the background.

lcolor sets the color of a determined light.

Usage

Any time a drawing entity is created, be it by line_from, triangle_from, beginLineEntity or beginTrianglesEntity, a layer must be assignated to them. If it is not, they belong to layer 1.

Calling color with a determined layer as argument changes the color of all the entities belonging to that layer.

 
### From my personal $(HOME)/.fandango
def emacs_colors():
    clearcolor(0.1843, 0.3098, 0.3098)
    lcolor(0.92, 0.95, 1.000000, 0)
    color(0.9607, 0.8705, 0.7019, 1, 0.5) ## Translucent
    color(1, 0.4983, 0.1411, 2)
    color(0.5294, 0.8078, 0.9803, 3)
    color(0, 1, 1, 4)
    color(0.8549, 0.4392, 0.8392, 255, 0.5) ## Crosshair; translucent
emacs_colors()

Layers list

255

Crosshair

254

It was the in-widget text color; unused

253

Hilighted items

1

Default layer

Notes

The only active light on the default compilation is light number 0. To activate more, edit KFandangoWidget::initializeGL and duplicate the lines calling setLight and initLight with numbers between 1 and MAX_LIGHTS-1. Recompile.

Future considerations

Employing the last layers to apply colors to UI elements is a mess. Modifying the NUM_LAYERS constant would break a few things.


Name

lpos ? Moves a light

Synopsis

glstate.lpos(x, y, z, light);
float x;
float y;
float z;
int light;

Inputs

x, y, z

A 3D point

Description

Changes the position of the designed light to [x, y, z].

Notes

The only active light on the default compilation is light number 0. To activate more, edit KFandangoWidget::initializeGL and duplicate the lines calling setLight and initLight with numbers between 1 and MAX_LIGHTS-1. Recompile.


Name

glEnable/Disable wrappers ? Functions that activate or deactivate GL features

Synopsis

glstate.aa(b);
bool b;

glstate.blend(b);
bool b;

glstate.texture(b);
bool b;

glstate.light(b);
bool b;

glstate.cull(b);
bool b;

Inputs

b

True is on, False is off.

Description

These functions wrap the functionality of glEnable and glDisable with the following parameters:

aa

GL_LINE_SMOOTH

blend

GL_BLEND

texture

GL_TEXTURE_2D

light

GL_LIGHTING

cull

GL_CULL_FACE

Usage

Simply call any of these with a true or false parameter to enable or disable the GL parameter.


Name

select ? Turns on/off displaying entities as selected

Synopsis

glstate.select(b);
bool b;

Inputs

b

True is on, False is off.

Description

When active, the selected entity will be highlighted and surrounded by its bounding box. Turn it off to avoid cluttering the drawing area when not needed.


Name

getsnap, getsnapvertex ? Finds a close endline vertex

Synopsis

bool glstate.getsnap(x, y, z);
bool x;
bool y;
bool z;

[fff] glstate.getsnapvertex();

Inputs

x, y, z

A 3D point near to a line end.

Description

glstate.getsnap simply finds the nearest line end to the given point. If a line end could not be found the return value is False. The found vertex should be retrieved with glstate.getsnapvertex.

Usage

Retrieve a vertex using cadget.clicks. Pass that vertex to glstate.getsnap, and check the return value. If True, use glstate.getsnapvertex to retrieve the ENDline snap.

Future considerations

The names are confuse and must be changed.


Name

snap_ratio ? Modifies the maximum distance to the cursor when finding a snap

Synopsis

glstate.snap_ratio(ratio);
float ratio;

Inputs

ratio

A distance between two pixels

Description

The functions who attempt to find an endline vertex or an intersection are too expensive to call them over the full drawing, and some algorithms try to determine if the input point is "close enough" to an entity before performing the test.

snap_ratio modifies the "close" distance. A value too small (1, 2...) requires a very precise input (and is probably faster), and a large value (10, 20, 800...) is more forgiving but requires more time to process. The default value is 5.

Future considerations

The function used to find snaps is not perfectly bounded. Even if an input is not close to a snap that doesn't mean one will not be found, probably not the expected one. It will be needed to check the (currenty unimplemented) return value of the find* function.


Name

write ? Pipes some text to the mock stdout

Synopsis

fandango_stdout.write(s);
string s;

Inputs

s

A text string

Description

The conventional Python interpreter sends the messages printed by print to the console. Since this is a GUI application such approach would be cumbersome.

This function replaces the standard output of Python and pipes the text through a Qt signal, making it accesible to all the widgets.

On the current implementation anything printed by Python will be displayed on the status bar.

Description

Warning

This function should not be used directly.

The correct way to use it is simply to replace the builtin sys.stdout method. This is already performed on fandango.py and most probably will not need to be modified.

### From fandango.py
import fandango_stdout
sys.stdout = fandango_stdout

Future considerations

On the current implementation stderr is not being handled. It would be trivial to use the same method for it too; but Python sends its errors on several lines and many are empty, making this approach almost useless.


Name

clear ? Fully clears screen and memory

Synopsis

idorudraw.clear();

Description

The clear function deletes all the iMem pointers and allocates them again.

All the information is lost.


Name

move ? Replaces the position of the active vertex

Synopsis

fandangoedit.move(x, y, z);
float x;
float y;
float z;

Inputs

x, y, z

New coordinates for a 3d point.

Description

If select mode is on, in a selected entity there is a selected vertex, which is marked by a small yellow square.

To select the entity fandangoedit.move should have been called, and a vertex close enough to the arguments gets selected.

Calling move replaces the position of this selected vertex.

Deep hack

This section is dedicated to the inner workings of the application. It's almost useless for anybody not interested on improving the application's core.

Table of Contents

pushPoint - Adds a point to the memory page list who calls it.
pushEntity - Begins a drawing entity from the given data.

Name

pushPoint ? Adds a point to the memory page list who calls it.

Synopsis

void iMem::pushPoint(x, y, z);
float x;
float y;
float z;

Inputs

x, y, z

Coordinates of a 3d point to put into memory.

Description

pushPoint is the lowest level function on the Kfandango API. It adds a point to the memory structure stored by the head of a iMem linked list.

The actual effect of this function variates depending on the array_type variable on the linked list.

Usage

pushPoint should not be used directly. It is instead called from newStrip, newTriStrip, pushLine, pushTriangle, pushToStrip and pushToTriStrip on the KFandangoWidget class.

Its usage adds a point to a memory array apt to be passed to glDrawArrays. The actual effect of the call is derived from the type of array passed as defined on the initialization of a iMem constructor.

The possible values and their effect are listed next.

  • GL_LINES: The odd indexed points mark the beginning of lines, the even indexed ones complete them.

  • GL_TRIANGLES: Each point defines a vertex of a triangle. For each three points a triangle is drawn. The front face of the triangle is defined by the winding order of the points who form it.

  • GL_LINE_STRIP: The first time the function is called after defining a new entity it marks a beginning point. Each time after it a line is drawn between the last point and the current.

  • GL_TRIANGLE_STRIP: As the GL_LINE_STRIP case, but requires two calls to be initialized. Each call after these draws a triangle between the two latest calls and the current. The front of the strip is determined by the winding order of the first three calls.

In all the cases creating a new entity by a call of pushEntity resets the counts and interrupts any strip (if it applies).

Since the correct amount of calls variates between array types, it's important that the functions which call it do so the right amount of times. The functions that initialize strips and those which complete them call it once. The function that draws a line calls it twice, and the one that draws a triangle calls it three times. A wrong count could result in unsynchronization of the memory page characterized by 3d drawing artifacts (geometry appearing where it should not, lines joining unrelated points, etc).


Name

pushEntity ? Begins a drawing entity from the given data.

Synopsis

void entityMarshall::pushEntity (type, data, normals, texture, layer);
glEnum type;
float* data;
float* normals;
float* texture;
unsigned int layer;

Inputs

type

One of GL_LINES, GL_TRIANGLES, GL_LINE_STRIP or GL_TRIANGLE_STRIP.

data

A pointer to a memory array containing the vertex of the entity to initialize.

normals

A pointer to the already calculated normal values for this entity. Should be NULL for unlit entities (line based).

texture

A pointer to the UV texture coordinates for this entity. NULL for line based entities.

layer

The index of a layercard structure to define the color and other properties of this entity (barely implemented).

Details

The pushEntity function should not be used directly, and the values passed to it should not be manually generated either.

This function is currently called by an entity marshall in the iMem::beginEntity function, which is called only by iMem::pushPoint.

What this function actually does it to set the input values to an entry on a entityCard struct entry. The entity will be retrieved from there to be rendered.

Discrepance with real code

  • The line based entities are not enforced to default their normals and texture pointers to NULL.

  • The layercard structure does not exist. The only property honored to this point is the color of the entity.

Chapter 4. Credits and License

Kfandango

Program copyright 2002, 2003 Jaime Soffer <jsoffer@(this site)>

Contributors:

Documentation copyright 2003 Jaime Soffer <jsoffer@(this site)>

This documentation is licensed under the terms of the GNU Free Documentation License.

This program is licensed under the terms of the GNU General Public License.

Appendix A. Installation

How to obtain Kfandango

Kfandango is not part of the KDE project; but can be found in the CVS archive and files section of the sourceforge project elcad (oddly enough).

Requirements

In order to successfully use Kfandango, you need KDE 3.x with support for OpenGL compiled in QT. It also worked correctly on KDE 2.2 and probably can be built for this older version replacing the admin directory and recreating the configure script.

Python 2.x is required.

A 3D video accelerator is highly recommended.

Kfandango uses about 10 megs of memory to begin to run, and will increase its size to hold the drawing entities.

Links to all the required libraries can be found on The Kfandango home page.

Compilation and Installation

In order to compile and install Kfandango on your system, type the following in the base directory of the distribution:

% ./configure
% make
% make install

Since Kfandango uses autoconf and automake you should have not trouble compiling it. Should you run into problems please report them directly to the author.

Configuration

The user level configuration is done on the .fandango archive, which should be at the home directory of the user. It is a python script, and, if present, its contents will be executed once by python's execfile function before the app starts.