[comp.sys.amiga.tech] Revised DR2D specification

cunniff@hpfcso.HP.COM (Ross Cunniff) (12/13/89)

Here is a revised DR2D format that responds to some of the comments
posted here or e-mailed.  The changes include:

	* Object attributes (fill color, etc.) are now more IFF-like
	* It uses the FTXT FONS chunk for font information
	* Nested objects (groups) are now more IFF-like
	* ProVector specific chunks are no longer documented here
	* Arrowhead attributes have been added
	* Bounding box information is optional
	* The limit of 256 fill and edge colors has been removed
	  (it is now 65536)
	* Layer description information has been added
	* Line dash patterns are no longer hard coded (i.e. they
	  are configurable/modifiable)

The single change which some people have requested that is NOT here is:
coordinates are still in IEEE format.  The arguments for changing them are:

	* Hard to read and parse
		not true; see my previous response on this where I give
		a 20-line C routine to convert to fixed-point notation
	* Integers are more natural
		debatable; anyway, a program can decide to convert to
		integers if it wants to
	* Would require two passes over the file to decide which scaling
	  factor to use for integers
		not true, the DRHD chunk has bounding box information which
		can be used to determine a suitable scaling factor
	* Integers are more flexible; they have a greater precision; you
	  could have a project-wide scaling factor for them
		Integers do potentially have a greater precision; however,
		remember that this is an INTERCHANGE format.  Consider
		Project A, with a scaling factor of 1,000,000 and Project
		B with a scaling factor of 100.  Consider cut-and-paste
		between the projects.  If you cut from project B, you can
		have objects that WILL NOT FIT in the range of Project A.
		If you cut from Project A, you can have objects that will
		resolve to singularities in the range of Project B.  "Ah,"
		you say, "What if each object has its own scaling factor?"
		to which I would reply "Let's just take it all the way;
		each coordinate has its own scaling factor.  Oops, that
		is what a floating point number IS..."

Anyway, here is the revised format:

89/12/10

The following is the format of the DR2D IFF FORM file produced
by ProVector, a 2-dimensional structured graphics program.
Each chunk is described by first giving the chunk ID (both in
ASCII and in hex) and then giving the structure associated with
the chunk, as well as any extra definitions that make sense.
Coordinates are specified in IEEE format (see note 1 for a
description of the IEEE format).  It is assumed that these
'drawings' are on white 'paper'.

Please address questions and comments to:

			Ross Cunniff
			Taliesin, Inc.
			P.O. Box 1671
			Fort Collins, CO 80522
			(303) 484-7321



FORM (0x464F524D)	/* All drawings are a FORM */

	struct FORMstruct {
	    ULONG	ID;
	    ULONG	Size;		/* Total size of the file	*/
	};


DR2D (0x44523244)	/* ID of 2D drawing */


DRHD (0x44524844)	/* Drawing header */

	struct DRHDstruct {
	    ULONG	ID;
	    ULONG	Size;
	    IEEE	XMin, YMin,	/* Minimum and maximum */
			XMax, YMax;	/*   coordinates of the drawing area */
	};


/* The following are global project properties */

CMAP (0x434D4150)	/* Color map */	/* Same as ILBM CMAP */

	struct CMAPstruct {
	    ULONG	ID;
	    ULONG	Size;
	    UBYTE	ColorMap[Size];
	};


FONS (0x464F4E53)	/* Font for text */	/* Same as FTXT.FONS */

	struct FONSstruct {
	    ULONG	ID;
	    ULONG	Size;
	    UBYTE	FontID;			/* ID it's called */
	    UBYTE	Pad1;			/* Always 0 */
	    UBYTE	Proportional;		/* Is it proportional? */
	    UBYTE	Serif;			/* Has it serifs? */
	    CHAR	Name[Size-4];		/* The name of the font */
	};


DASH (0x44415348)	/* Line dash pattern for edges */

	struct DASHstruct {
	    ULONG	ID;
	    ULONG	Size;
	    USHORT	DashID;			/* Name of the dash pattern */
	    USHORT	NumDashes;		/* Should always be even */
	    IEEE	Dashes[NumDashes];	/* On-off pattern */
	};

	/* The numbers in the dash patterns are relative to the line
	   weight.  The dash pattern {1.0,0.0} will always yield
	   a dotted line with the dots as long as they are tall.
	   By convention, DashID 0 is reserved to mean 'No line pattern
	   at all', i.e. the edges are invisible.  This DASH pattern
	   should NOT appear in a DR2D FORM.  Again by convention, a
	   NumDashes of 0 means that the line is solid. */


AROW (0x41524F57)	/* An arrow-head pattern */

	#define ARROW_FIRST	0x01	/* Draw an arrow on the first point */
	#define ARROW_LAST	0x02	/* Draw an arrow on the last point */

	struct AROWstruct {
	    ULONG	ID;
	    ULONG	Size;
	    UBYTE	Flags;		/* Flags, from ARROW_*, above */
	    UBYTE	Pad0;		/* Should always 0 */
	    USHORT	ArrowID;	/* Name of the arrow head */
	    USHORT	NumPoints;
	    IEEE	ArrowPoints[NumPoints]; /* See note 2 */
	};


FILL (0x46494C4C)	/* Object-oriented fill pattern */
				/* See note 3 */

	struct FILLstruct {
	    ULONG	ID;
	    ULONG	Size;
	    USHORT	FillID;		/* ID of the fill */
	    USHORT	NumObjs;	/* Number of objects in the fill */
	};


LAYR (0x4C415952)	/* Define a layer */

	#define LF_ACTIVE	0x01	/* Active for editing */
	#define LF_DISPLAYED	0x02	/* Displayed on the screen */

	struct LAYRstruct {
	    ULONG	ID;
	    ULONG	Size;
	    USHORT	LayerID;	/* ID of the layer */
	    UBYTE	Flags;		/* Flags, from LF_*, above */
	    UBYTE	Pad0;		/* Always 0 */
	};


/* The following sets object attributes for the next object(s).  The
   attributes are in effect until the next ATTR chunk is found, or
   until the end of the enclosing FORM, whichever comes first. */

ATTR (0x41545452)	/* Object attributes */

	/* Various fill types */
	#define FT_NONE		0	/* No fill			*/
	#define FT_COLOR	1	/* Fill with color from palette */
	#define FT_OBJECTS	2	/* Fill with tiled objects	*/

	/* Join types */
	#define JT_NONE		0	/* Don't do line joins */
	#define JT_MITER	1	/* Mitered join */
	#define JT_BEVEL	2	/* Beveled join */
	#define JT_ROUND	3	/* Round join */

	struct ATTRstruct {
	    UBYTE	FillType;	/* One of FT_*, above	*/
	    UBYTE	JoinType;	/* One of JT_*, above	*/
	    UBYTE	EdgePattern;	/* ID of edge dash pattern*/
	    UBYTE	ArrowHeads;	/* ID of arrowhead to use */
	    USHORT	FillValue;	/* Color or object with which to fill */
	    USHORT	EdgeValue;	/* Edge color index	*/
	    USHORT	WhichLayer;	/* Which layer it's in	*/
	    IEEE	EdgeThick;	/* Line width		*/
	};

BBOX (0x42424F48)	/* Bounding box of the next object in the FORM */

	struct BBOXstruct {
	    ULONG	ID;
	    ULONG	Size;
	    IEEE	XMin, YMin,	/* Bounding box of obj. */
			XMax, YMax;	/* including line width	*/
	};

	/* This chunk is ONLY in effect for the NEXT object in the FORM.
	   Note that a BBOX may apply to a FILL or an AROW. */

/* The following are object definitions */

RAST (0x52415354)	/* Raster image */

	struct RASTstruct {
	    ULONG	ID;
	    ULONG	Size;
	    IEEE	XPos, YPos,		/* Virtual coords */
			XSize, YSize;		/* Virtual size */
	    SHORT	NumX, NumY, Depth;	/* Actual size */
	    UBYTE	Colors[3*(1<<Depth)];	/* Color map */
	    UBYTE	Closest[(1<<Depth)];	/* Closest equivs in CMAP */
#define RSIZE(x,y,d)	((x+15)/16)*y*d
	    USHORT	Rast[RSIZE(NumX,NumY,Depth)];	/* Lookups into Colors*/
	};


CPLY (0x43504C59)	/* Closed polygon */
OPLY (0x4F504C59)	/* Open polygon, i.e. line segments */

	struct POLYstruct {
	    ULONG	ID;
	    ULONG	Size;
	    USHORT	NumPoints;
	    IEEE	PolyPoints[2*NumPoints]; /* See note 2 */
	};



GRUP (0x47525550)	/* Group */	/* See note 3 */

	struct GROUPstruct {
	    ULONG	ID;
	    ULONG	Size;
	    USHORT	NumObjs;
	};

	/* NOTE that for GRUPs, the Layer information of the GRUP
	   FORM overrides the Layer information of the nested objects. */

TEXT (0x54455854)	/* A text string */

	struct TEXTstruct {
	    ULONG	ID;
	    ULONG	Size;
	    USHORT	WhichFont;	/* Which font to use */
	    IEEE	CharW, CharH,	/* W/H of an individual char	*/
			BaseX, BaseY,	/* Start of baseline */
			Rotation;	/* Angle of text (in radians) */
	    USHORT	NumChars;
	    CHAR	TextChars[NumChars];
	};


NOTE 1:	IEEE represents a single-precision IEEE floating point number.
	These numbers consist of 32 bits, arranged as follows:

	+---------------+---------------+---------------+---------------+
	|s e e e e e e e|e m m m m m m m|m m m m m m m m|m m m m m m m m|
	+---------------+---------------+---------------+---------------+
	 31	      24 23           16 15            8 7             0
	
	where
		s	is the sign bit of the entire number.  If this
			bit is '1', the number is negative.  If '0',
			the number is positive.
		e	is the 8-bit exponent, in excess-127 form.
			This is the power of two to which the mantissa
			should be raised before using the number
			in calculations.  Excess-127 means that
			127 is added to the exponent before packing
			it into the number. An exponent of 0 here means
			the real exponent is actually -127.  A value
			of 127 means the exponent is actually 0.
			A value of 255 means the exponenent is actually
			128.
		m	is the 23-bit mantissa.  A leading binary
			1 is assumed.  This means that the mantissa
			may vary from 1.0000000 to 1.999999...
	
	The value 0.0000000 is represented by all bits being cleared
	to zero.  Note that on many computers, the natural format
	of single-precision floating point numbers *IS* IEEE.  This
	is true of the Amiga.  There are system calls to transform
	IEEE numbers to and from Motorola FFP numbers, should your
	application require them.


NOTE 2: The algorithm for drawing polygons is as follows:

	typedef union {
	    IEEE num;
	    LONG bits;
	} Coord;

	#define INDICATOR	0xFFFFFFFF
	#define IND_SPLINE	0x00000001
	#define IND_MOVETO	0x00000002

	Coord	Temp0, Temp1;
	int	FirstPoint, i, Increment;

	/* Initialize the path */
	NewPath();
	FirstPoint = 1;

	/* Draw the path */
	i = 0;
	while( i < NumPoints ) {
	    Temp0.num = PolyPoints[2*i];	Temp1.num = PolyPoints[2*i + 1];
	    if( Temp0.bits == INDICATOR ) {
		/* Increment past the indicator */
		Increment = 1;
		if( Temp1.bits & IND_MOVETO ) {
		    /* Close and fill, if appropriate */
		    if( ID == CPLY ) {
			FillPath();
		    }
		    else {
			StrokePath();
		    }

		    /* Set up the new path */
		    NewPath();
		    FirstPoint = 1;
		}
		if( Temp1.bits & IND_SPLINE ) {
		    /* The next 4 points are BSpline control points */
		    if( FirstPoint )
			MoveTo(	PolyPoints[2*i + 2], PolyPoints[2*i + 3] );
		    else
			LineTo(	PolyPoints[2*i + 2], PolyPoints[2*i + 3] );
		    SplineTo(	PolyPoints[2*i + 4], PolyPoints[2*i + 5],
				PolyPoints[2*i + 6], PolyPoints[2*i + 7],
				PolyPoints[2*i + 8], PolyPoints[2*i + 9] );
		    FirstPoint = 0;
		    /* Increment past the control points */
		    Increment += 4;
		}
	    }
	    else {
		if( FirstPoint )
		    MoveTo(	PolyPoints[2*i], PolyPoints[2*i + 1] );
		else
		    LineTo(	PolyPoints[2*i], PolyPoints[2*i + 1] );
		FirstPoint = 0;

		/* Increment past the last endpoint */
		Increment = 1;
	    }

	    /* Add the increment */
	    i += Increment;
	}

	/* Close the last path */
	if( ID == CPLY ) {
	    FillPath();
	}
	else {
	    StrokePath();
	}

	Fills are according to the even-odd rule, so 'holes' in polygons
	are easy to create (desirable in many cases).  Most objects produced
	by ProVector (polygons, smoothed polygons, ellipses, rectangles,
	and even the rendering of indivdiual characters in fonts) are
	represented by this form.


NOTE 3: FILL and GRUP chunks are ONLY valid inside nested DR2D FORMs.
	Here is an example:

	FORM { DR2D		/* Top-level drawing... */
		DRHD { ... }	/* Confirmed by presence of DRHD chunk */
		CMAP { ... }	/* Various other things... */
		FONS { ... }
		FORM { DR2D		/* A nested form... */
			FILL { 1 }	/* Ah!  The fill-pattern table */
			CPLY { ... }	/* with only 1 object */
		}
		FORM { DR2D		/* Yet another nested form */
			GRUP { ..., 3 }	/* Ah! A group of 3 objects */
			TEXT { ... }
			CPLY { ... }
			OPLY { ... }
		}
	}

	All OBJECTS (i.e. CPLY, OPLY, TEXT, FORM { DR2D GRUP }, etc.)
	are allowed inside these nested FORMs, but global drawing
	attributes (i.e. CMAP, FILL, etc.) are not.


Here is a (symbolic) DR2D form:

	FORM { DR2D
		DRHD { 0.0, 0.0, 10.0, 8.0 }
		CMAP { 0,0,0, 255,255,255 }
		FONS { 1, 0, 1, 0, "Roman" }
		DASH { 1, 2, {1.0, 1.0} }
		ATTR { 0, 0, 0, 0, 0, 0, 0, 0.0 }
		BBOX { 2.0, 2.0, 8.0, 6.0 }
		FORM { DR2D
			GRUP { 2 }
			BBOX { 3.0, 4.0, 7.0, 5.0 }
			TEXT { 1, 0.5, 1.0, 3.0, 5.0, 0.0, 12, "Hello, World" }
			BBOX { 2.0, 2.0, 8.0, 6.0 }
			OPLY { 5, {2.0,2.0, 8.0,2.0, 8.0,6.0, 2.0,6.0, 2.0,2.0 }
		}
	}

This picture should look something like this:

		.................
		.		.
		. Hello, World! .
		.................

filbo@gorn.santa-cruz.ca.us (Bela Lubkin) (12/17/89)

In article <9310006@hpfcso.HP.COM> Ross Cunniff writes:
>Here is a revised DR2D format that responds to some of the comments
>posted here or e-mailed.  The changes include:
[...]
>The single change which some people have requested that is NOT here is:
>coordinates are still in IEEE format.  The arguments for changing them are:
>
>	* Hard to read and parse
>		not true; see my previous response on this where I give
>		a 20-line C routine to convert to fixed-point notation
[...]

Ross, I think you miss the point of this argument.  >*It is not always
POSSIBLE to insert an arbitrary 20 lines of C code*< into a program.
Where does this leave programmers who are writing in BASIC, Forth, Lisp,
ARexx, database languages, etc.?  There are some now, there will be more
later, programs and programming languages that have built-in tools for
manipulating IFF structures but which have no support either for IEEE
number formats OR bit-twiddling.  Up to now the IFF spec has been kept
free of floating point.  Whatever your arguments for it, they are not as
strong as that.

Here is one line of C code to translate from integer to IEEE:

  coordinate=icoordinate*scalefactor;

Bela Lubkin    * *    //  filbo@gorn.santa-cruz.ca.us  CI$: 73047,1112 (slow)
     @       * *     //  belal@sco.com  ..ucbvax!ucscc!{gorn!filbo,sco!belal}
R Pentomino    *   \X/  Filbo @ Pyrzqxgl +408-476-4633 and XBBS +408-476-4945