/* Hello, Emacs, this is -*-C-*-
 * $Id: dxf.trm,v 1.16 2006/07/21 02:35:45 sfeam Exp $
 *
 */

/* GNUPLOT - dxf.trm */

/*[
 * Copyright 1991 - 1993, 1998, 2004
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the complete modified source code.  Modifications are to
 * be distributed as patches to the released version.  Permission to
 * distribute binaries produced by compiling modified sources is granted,
 * provided you
 *   1. distribute the corresponding source modifications from the
 *    released version in the form of a patch file along with the binaries,
 *   2. add special version identification to distinguish your version
 *    in addition to the base release version number,
 *   3. provide your name and address as the primary contact for the
 *    support of your modified version, and
 *   4. retain our contact information in regard to use of the base
 *    software.
 * Permission to distribute the released version of the source code along
 * with corresponding source modifications in the form of a patch file is
 * granted with same provisions 2 through 4 for binary distributions.
 *
 * This software is provided "as is" without express or implied warranty
 * to the extent permitted by applicable law.
]*/

/*
 * This file is included by ../term.c.
 *
 * This terminal driver supports:
 *   AutoCad (Release 10.x) dxf file format (import with AutoCad dxfin command)
 *
 *
 * AUTHOR
 *   Florian Hiss  (fhis1231@w204zrz.zrz.tu-berlin.de)
 *
 * send your comments or suggestions to (gnuplot-info@lists.sourceforge.net).
*/

/*
 * adapted to the new terminal layout by Stefan Bodewig (Dec. 1995)
 */

#include "driver.h"

#ifdef TERM_REGISTER
register_term(dxf)
#endif

#ifdef TERM_PROTO
TERM_PUBLIC void DXF_init __PROTO((void));
TERM_PUBLIC void DXF_graphics __PROTO((void));
TERM_PUBLIC void DXF_text __PROTO((void));
TERM_PUBLIC void DXF_linetype __PROTO((int linetype));
TERM_PUBLIC void DXF_move __PROTO((unsigned int x, unsigned int y));
TERM_PUBLIC void DXF_vector __PROTO((unsigned int ux, unsigned int uy));
TERM_PUBLIC void DXF_put_text __PROTO((unsigned int x, unsigned int y,
				       const char str[]));
TERM_PUBLIC int DXF_text_angle __PROTO((int ang));
TERM_PUBLIC int DXF_justify_text __PROTO((enum JUSTIFY mode));
TERM_PUBLIC void DXF_reset __PROTO((void));

#define DXF_XMAX (120.0 * DXF_UNIT)
#define DXF_YMAX (80.0 * DXF_UNIT)
#if 0 /* HBB 20030626: old version */
#define DXF_HTIC (0.01 * DXF_XMAX)	/* 1.0 percent */
#define DXF_VTIC (0.01 * DXF_YMAX)	/* 1.0 percent */
#else
/* HBB 20030626: make them have the same length in DXF_UNITs ! */
# define DXF_HTIC (2.0 * DXF_UNIT)
# define DXF_VTIC (2.0 * DXF_UNIT)
#endif
#define DXF_HCHAR (0.014 * DXF_XMAX)	/* 1.4 percent */
#define DXF_VCHAR (0.026 * DXF_YMAX)	/* 2.6 percent */
#endif /* TERM_PROTO */

#ifndef TERM_PROTO_ONLY
#ifdef TERM_BODY

#define DXF_UNIT 60.0
#define LINEWIDTH 0.0351	/* default line width is 1 pt */

/* 120 (autocad units) wide by 80 (autocad units) high (default)
 * use the GNUPLOT 'set size' command to change the defaults */
/* actual text height */
#define DXF_TEXTHEIGHT (0.7 * DXF_VCHAR)

/* actual text width, only a guess, we don't know the width of
 * a character of given height of the AutoCad STANDARD text font,
 * so change it if you like */
#define DXF_TEXTWIDTH (0.7 * DXF_HCHAR)

/* number of line types we support. see below  */
#define DXF_LINE_TYPES 7

/* number of layers used for the drawing. see below */
#define MAX_LAYER 7

/* line type scaling */
#define LT_SCALE 1

static unsigned int DXF_posx;
static unsigned int DXF_posy;
/* linetype is mapped to a layer. see below. */
static unsigned int dxf_linetype;
static enum JUSTIFY dxf_justify = LEFT;
static float dxf_angle = 0.0;	/* 0 is horizontal, 90.0 is vertical */

/* text style used in the entire drawing */
static const char *text_style = "STANDARD";

/* text always resides on layer 0 */
#define TEXT_LAYER 0

/* each linetype resides on its own layer. each layer has its own color.
 * this avoids difficulties that AutoCad has with proper scaling of
 * the linetypes.
 * change the colors according to your needs */
static const char *layer_name[] ={ "0", "1", "2", "3", "4", "5", "6" };

/* the colours are white, red, yellow, green, cyan, blue, magenta.
 * change them according to your needs.
 * when using a black and white plotting device the colours map to different
 * line thicknesses. see description of AutoCad print / plot command */
static const char *layer_colour[] = { "7", "1", "2", "3", "4", "5", "6" };

/* support line types AutoCad has to offer by default. */
static const char *layer_lines[] = {
    "CONTINUOUS", "DASHED", "HIDDEN", "CENTER", "PHANTOM", "DOT", "DASHDOT"
};

static TBOOLEAN vector_was_last = FALSE;

TERM_PUBLIC void
DXF_init()
{
    DXF_posx = DXF_posy = 0;
    dxf_linetype = 0;
    dxf_angle = 0.0;
    vector_was_last = FALSE;
}

TERM_PUBLIC void
DXF_graphics()
{
    register struct termentry *t = term;
    int i;
    static char GPFAR dxfi1[] = "\
999\n\
%% GNUPLOT: dxf file for AutoCad\n\
  0\nSECTION\n  2\nHEADER\n\
  9\n$EXTMIN\n\
 10\n0.000\n 20\n0.000\n\
  9\n$EXTMAX\n\
 10\n%-6.3f\n 20\n%-6.3f\n\
  9\n$LIMMIN\n\
 10\n0.000\n 20\n0.000\n\
  9\n$LIMMAX\n\
 10\n%-6.3f\n 20\n%-6.3f\n\
  9\n$TEXTSTYLE\n  7\n%s\n\
  9\n$TEXTSIZE\n 40\n%-6.3f\n\
  9\n$PLINEWID\n 40\n%-6.4f\n\
  9\n$LTSCALE\n  40\n%-6.3f\n\
  9\n$COORDS\n 70\n  1\n\
  9\n$CELTYPE\n 6\nBYLAYER\n\
  9\n$CLAYER\n  8\n0\n\
  9\n$CECOLOR\n 62\n   %s\n\
  9\n$MENU\n  1\nacad\n\
  0\nENDSEC\n\
  0\nSECTION\n  2\nTABLES\n";
    static char GPFAR dxfi2[] = "\
0\nTABLE\n  2\nLTYPE\n 70\n    %d\n\
0\nLTYPE\n  2\nCONTINUOUS\n 70\n    64\n\
  3\nSolid line\n 72\n    65\n 73\n      0\n 40\n0.0\n\
  0\nLTYPE\n  2\nDASHED\n 70\n    64\n\
  3\n__ __ __ __ __ __ __ __ __ __ __ __ __ __ __\n\
 72\n    65\n 73\n     2\n 40\n0.75\n 49\n0.5\n 49\n-0.25\n\
  0\nLTYPE\n  2\nHIDDEN\n 70\n    64\n\
  3\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _\n\
 72\n    65\n 73\n     2\n 40\n0.375\n 49\n0.25\n 49\n-0.125\n\
  0\nLTYPE\n  2\nCENTER\n 70\n    64\n\
  3\n____ _ ____ _ ____ _ ____ _ ____ _ ____ _ ____\n\
 72\n    65\n 73\n     4\n 40\n2.0\n 49\n1.25\n 49\n-0.25\n\
 49\n0.25\n 49\n-0.25\n\
  0\nLTYPE\n  2\nPHANTOM\n 70\n    64\n\
  3\n_____ _ _ _____ _ _ _____ _ _ _____ _ _ ____\n\
 72\n    65\n 73\n     6\n 40\n2.5\n 49\n1.25\n\
 49\n-0.25\n 49\n0.25\n 49\n-0.25\n 49\n0.25\n 49\n-0.25\n\
  0\nLTYPE\n  2\nDOT\n 70\n    64\n\
  3\n...............................................\n\
 72\n    65\n 73\n     2\n 40\n0.25\n 49\n0.0\n 49\n-0.25\n\
  0\nLTYPE\n  2\nDASHDOT\n 70\n    64\n\
  3\n__ . __ . __ . __ . __ . __ . __ . __ . __ . __\n\
 72\n    65\n 73\n     4\n 40\n1.0\n 49\n0.5\n 49\n-0.25\n\
 49\n0.0\n 49\n-0.25\n\
  0\nENDTAB\n";

    fprintf(gpoutfile, dxfi1,
	    t->xmax / DXF_UNIT, t->ymax / DXF_UNIT,
	    t->xmax / DXF_UNIT, t->ymax / DXF_UNIT,
	    text_style,
	    DXF_TEXTHEIGHT / DXF_UNIT,
	    LINEWIDTH,
	    (double) LT_SCALE,
	    layer_colour[0]);
    /* the linetype table */
    fprintf(gpoutfile, dxfi2, DXF_LINE_TYPES);
    /* the layer table */
    fprintf(gpoutfile, "  0\nTABLE\n  2\nLAYER\n 70\n   %-d\n", MAX_LAYER);
    for (i = 1; i <= MAX_LAYER; i++)
	fprintf(gpoutfile, "  0\nLAYER\n  2\n%s\n 70\n   64\n62\n   %s\n  6\n%s\n", layer_name[i - 1], layer_colour[i - 1], layer_lines[i - 1]);

    /* no blocks for insertion */
    /* start the entity section */
    fputs("  0\nENDTAB\n0\nENDSEC\n\
  0\nSECTION\n  2\nBLOCKS\n  0\nENDSEC\n\
  0\nSECTION\n\
  2\nENTITIES\n",
	  gpoutfile);
}

TERM_PUBLIC void
DXF_text()
{
    if (vector_was_last)
	fputs("  0\nSEQEND\n", gpoutfile);
    fputs("  0\nENDSEC\n  0\nEOF\n", gpoutfile);
}

TERM_PUBLIC void
DXF_linetype(int linetype)
{
    linetype = ABS(linetype);
    linetype = linetype % DXF_LINE_TYPES;
    dxf_linetype = linetype;
}

TERM_PUBLIC void
DXF_move(unsigned int x, unsigned int y)
{
    DXF_posx = x;
    DXF_posy = y;
    if (vector_was_last)
	fputs("  0\nSEQEND\n", gpoutfile);
    vector_was_last = FALSE;
    fprintf(gpoutfile, "\
  0\nPOLYLINE\n  8\n%s\n 66\n   1\n\
  6\n%s\n\
  0\nVERTEX\n  8\n%s\n\
  6\n%s\n\
 10\n%-6.3f\n 20\n%-6.3f\n 30\n0.000\n",
	    layer_name[dxf_linetype],
	    layer_lines[dxf_linetype],
	    layer_name[dxf_linetype],
	    layer_lines[dxf_linetype],
	    DXF_posx / DXF_UNIT, DXF_posy / DXF_UNIT);

}

TERM_PUBLIC void
DXF_vector(unsigned int ux, unsigned int uy)
{
    DXF_posx = ux;
    DXF_posy = uy;
    vector_was_last = TRUE;

    fprintf(gpoutfile, "\
  0\nVERTEX\n  8\n%s\n\
  6\n%s\n\
  10\n%-6.3f\n  20\n%-6.3f\n  30\n0.000\n",
	    layer_name[dxf_linetype],
	    layer_lines[dxf_linetype],
	    DXF_posx / DXF_UNIT, DXF_posy / DXF_UNIT);
}

TERM_PUBLIC void
DXF_put_text(unsigned int x, unsigned int y, const char str[])
{
    int stl;
    float xleftpos, yleftpos, xrightpos, yrightpos;

    /* shut up gcc warnings  - SB */
    xleftpos = yleftpos = xrightpos = yrightpos = 1.0;	/* dummy */
    /* ignore empty strings */
    if (str[0] == NUL)
	return;

    stl = 0;
    while (str[stl] != NUL)
	++stl;			/* get string length */

    if (vector_was_last)
	fputs("  0\nSEQEND\n", gpoutfile);
    vector_was_last = FALSE;
    fprintf(gpoutfile, "  0\nTEXT\n  8\n%s\n", layer_name[TEXT_LAYER]);
    if (dxf_angle != 90.0) {
	switch (dxf_justify) {
	case LEFT:
	    xleftpos = (float) x;
	    yleftpos = (float) (y - DXF_VCHAR / 4.0);
	    xrightpos = (float) (x + stl * DXF_TEXTWIDTH);
	    yrightpos = yleftpos;
	    break;
	case RIGHT:
	    xleftpos = (float) (x - stl * DXF_TEXTWIDTH);
	    yleftpos = (float) (y - DXF_VCHAR / 4.0);
	    xrightpos = (float) x;
	    yrightpos = yleftpos;
	    break;
	case CENTRE:
	    xleftpos = (float) (x - stl * DXF_TEXTWIDTH / 2.0);
	    yleftpos = (float) (y - DXF_VCHAR / 4.0);
	    xrightpos = (float) x;	/* center point */
	    yrightpos = yleftpos;
	    break;
	}
    } else {
	switch (dxf_justify) {
	case LEFT:
	    xleftpos = (float) (x + DXF_VCHAR / 4.0);
	    yleftpos = (float) y;
	    xrightpos = xleftpos;
	    yrightpos = (float) (y + stl * DXF_TEXTWIDTH);
	    break;
	case RIGHT:
	    xleftpos = (float) (x + DXF_VCHAR / 4.0);
	    yleftpos = (float) (y - stl * DXF_HCHAR);
	    xrightpos = xleftpos;
	    yrightpos = (float) y;
	    break;
	case CENTRE:
	    xleftpos = (float) (x + DXF_VCHAR / 4.0);
	    yleftpos = (float) (y - stl * DXF_TEXTWIDTH / 2.0);
	    xrightpos = xleftpos;
	    yrightpos = (float) y;	/* center point */
	    break;
	}
    }

    fprintf(gpoutfile, "\
 10\n%-6.3f\n 20\n%-6.3f\n 30\n0.000\n\
 40\n%-6.3f\n  1\n%s\n 50\n%-6.3f\n\
  7\n%s\n",
	    xleftpos / DXF_UNIT, yleftpos / DXF_UNIT,
	    DXF_TEXTHEIGHT / DXF_UNIT, str, dxf_angle,
	    text_style);

    if (dxf_justify != LEFT) {
	fprintf(gpoutfile, " 72\n%d\n\
 11\n%-6.3f\n 21\n%-6.3f\n 31\n0.000\n",
		dxf_justify,
		xrightpos / DXF_UNIT, yrightpos / DXF_UNIT);
    }
}

TERM_PUBLIC int
DXF_text_angle(int ang)
{
    dxf_angle = (ang ? 90.0 : 0.0);
    return (TRUE);
}

TERM_PUBLIC int
DXF_justify_text(enum JUSTIFY mode)
{
    dxf_justify = mode;
    return (TRUE);
}

TERM_PUBLIC void
DXF_reset()
{
    DXF_posx = DXF_posy = 0;
}

#endif /* TERM_BODY */

#ifdef TERM_TABLE
TERM_TABLE_START(dxf_driver)
  "dxf", "dxf-file for AutoCad (default size 120x80)",
    DXF_XMAX, DXF_YMAX, DXF_VCHAR, DXF_HCHAR,
    DXF_VTIC, DXF_HTIC, options_null, DXF_init, DXF_reset,
    DXF_text, null_scale, DXF_graphics, DXF_move, DXF_vector,
    DXF_linetype, DXF_put_text, DXF_text_angle,
    DXF_justify_text, do_point, do_arrow, set_font_null
TERM_TABLE_END(dxf_driver)

#undef LAST_TERM
#define LAST_TERM dxf_driver

#endif /* TERM_TABLE */
#endif /* TERM_PROTO_ONLY */

#ifdef TERM_HELP
START_HELP(dxf)
"1 dxf",
"?commands set terminal dxf",
"?set terminal dxf",
"?set term dxf",
"?terminal dxf",
"?term dxf",
"?dxf",
" The `dxf` terminal driver creates pictures that can be imported into AutoCad",
" (Release 10.x).  It has no options of its own, but some features of its plots",
" may be modified by other means.  The default size is 120x80 AutoCad units,",
" which can be changed by `set size`.  `dxf` uses seven colors (white, red,",
" yellow, green, cyan, blue and magenta), which can be changed only by",
" modifying the source file.  If a black-and-white plotting device is used, the",
" colors are mapped to differing line thicknesses.  See the description of the",
" AutoCad print/plot command."
END_HELP(dxf)
#endif /* TERM_HELP */
