/*
 *  This EAGLE User Language Program converts a board
 *  or a schematic into a DXF file. The drawing appears in
 *  black and white. 
 *
 *  DXF syntax generated according to the specifications given in
 *  the book
 *
 *     "Der DXF-Standard"
 *     Author: Dietmar Rudolph
 *     Publisher: Dr. L. Rossipaul Verlagsgesellschaft m.b.H., Muenchen, 1993
 *     ISBN 3-87686-246-9
 *
 */

//
// The following switches allow us to customize the generated DXF file:
//

enum { NO, YES };

int UseWireWidths   = YES;  // YES will generate wires, arcs and circles
                            // as polygons showing their real widths.
                            // This, however, can cause the DXF file to
                            // become very large!
                            // Set this to NO if you do not need the real
                            // widths.
int FillAreas       = YES;  // YES will fill wires, arcs etc. You must also
                            // set UseWireWidths to YES for this to work

//
// Some tools we need later:
//

real DxfAngle(real a)
{
  return a >= 360 ? a - 360 : a; // 0 <= DXF-Angle < 360
}

int tx2 = 0, ty2 = 0;

void GetTextPoint(UL_TEXT T)
{
  int w, ex, ey;

  ex = tx2 = T.x;
  ey = ty2 = T.y;
  T.wires(W) {
    tx2 = (W.x1 > ex) ? max(W.x1, max(W.x2, tx2)) : min(W.x1, min(W.x2, tx2));
    ty2 = (W.y1 > ey) ? max(W.y1, max(W.y2, ty2)) : min(W.y1, min(W.y2, ty2));
    w = W.width;
    }
  w /= 2;
  tx2 += (tx2 > ex) ? w : -w;
  ty2 += (ty2 > ey) ? w : -w;
}

int LayerActive[] = {1};

//
// Level 0: DXF code generating functions:
//

void DxfString(int code, string value)
{
  printf("%3d\n%s\n", code, value);
}

void DxfInt(int code, int value)
{
  printf("%3d\n%d\n", code, value);
}

void DxfReal(int code, real value)
{
  printf("%3d\n%1.3f\n", code, value);
}

//
// Level 1: DXF group functions:
//

void DxfCoordinate(int n, real x, real y)
{
  DxfReal(10 + n, x);
  DxfReal(20 + n, y);
  DxfReal(30 + n, 0.0);
}

void DxfSection(string name)
{
  DxfString(0, "SECTION");
  DxfString(2, name);
}

void DxfEndSection(void)
{
  DxfString(0, "ENDSEC");
}

void DxfTable(string name, int number)
{
  DxfString(0, "TABLE");
  DxfString(2, name);
  DxfInt(70, number);
}

void DxfEndTable(void)
{
  DxfString(0, "ENDTAB");
}

void DxfBlock(string name)
{
  DxfString(0, "BLOCK");
  DxfInt(8, 0);
  DxfString(2, name);
  DxfInt(70, 64);
  DxfCoordinate(0, 0.0, 0.0);
  DxfString(3, name);
}

void DxfEndBlock(void)
{
  DxfString(0, "ENDBLK");
  DxfInt(8, 0);
}

void DxfVariable(string name)
{
  DxfString(9, "$" + name);
}

void DxfTrailer(void)
{
  DxfString(0, "EOF");
}

void DxfPolyline(int layer, real width)
{
  DxfString(0, "POLYLINE");
  DxfInt(8, layer);
  DxfInt(66, 1);
  DxfCoordinate(0, 0.0, 0.0);
  DxfReal(40, width);
  DxfReal(41, width);
  DxfInt(70, width ? 0 : 1);
}

void DxfVertex(int layer, real x, real y)
{
  DxfString(0, "VERTEX");
  DxfInt(8, layer);
  DxfCoordinate(0, x, y);
}

void DxfVertexRound(int layer, real x, real y, real r)
{
  DxfString(0, "VERTEX");
  DxfInt(8, layer);
  DxfCoordinate(0, x, y);
  DxfReal(42, r);
}

void DxfSeqEnd(void)
{
  DxfString(0, "SEQEND");
  DxfInt(8, 0);
}

void DxfInsert(int layer, string name, real x, real y, real dx, real dy)
{
  if (LayerActive[layer]) {
     DxfString(0, "INSERT");
     DxfInt(8, layer);
     DxfString(2, name);
     DxfCoordinate(0, x, y);
     DxfReal(41, dx);
     DxfReal(42, dy);
     DxfReal(43, 1.0);
     }
}

void DxfPoint(int layer, real x, real y)
{
  if (LayerActive[layer]) {
     DxfString(0, "POINT");
     DxfInt(8, layer);
     DxfCoordinate(0, x, y);
     }
}

void DxfLine(int layer, real x1, real y1, real x2, real y2)
{
  if (LayerActive[layer]) {
     DxfString(0, "LINE");
     DxfInt(8, layer);
     DxfCoordinate(0, x1, y1);
     DxfCoordinate(1, x2, y2);
     }
}

void DxfCircle(int layer, real x, real y, real r)
{
  if (LayerActive[layer]) {
     DxfString(0, "CIRCLE");
     DxfInt(8, layer);
     DxfCoordinate(0, x, y);
     DxfReal(40, r);
     }
}

void DxfArc(int layer, real x, real y, real r, real a1, real a2)
{
  if (LayerActive[layer]) {
     DxfString(0, "ARC");
     DxfInt(8, layer);
     DxfCoordinate(0, x, y);
     DxfReal(40, r);
     DxfReal(50, a1);
     DxfReal(51, a2);
     }
}

void DxfSolid(int layer, real x1, real y1, real x2, real y2, real x3, real y3, real x4, real y4)
{
  if (LayerActive[layer]) {
     DxfString(0, "SOLID");
     DxfInt(8, layer);
     DxfCoordinate(0, x1, y1);
     DxfCoordinate(1, x2, y2);
     DxfCoordinate(2, x3, y3);
     DxfCoordinate(3, x4, y4);
     }
}

void DxfText(int layer, real x, real y, real size, string value, real angle, int mirror)
{
  if (LayerActive[layer]) {
     DxfString(0, "TEXT");
     DxfInt(8, layer);
     DxfCoordinate(0, x, y);
     DxfReal(40, size);
     DxfString(1, value);
     if (angle)
        DxfReal(50, angle);
     if (mirror)
        DxfInt(71, 2);
     }
}

void DxfLayer(int number, int color)
{
  int Color[] = {  7/*Black*/,   7/*Black*/,     7/*Black*/,
                   7/*Black*/,    7/*Black*/,      7/*Black*/,
                   7/*Black*/,   7/*Black*/,    7/*Black*/,
                   7/*Black*/,   7/*Black*/,   7/*Black*/,
                   7/*Black*/,    7/*Black*/, 7/*Black*/,
                   7/*Black*/};

  DxfString(0, "LAYER");
  DxfInt(2, number);
  DxfInt(70, 64);
  DxfInt(62, Color[color]);
  DxfString(6, "CONTINUOUS");
}

void DxfLineTypes(void)
{
  DxfString(0, "LTYPE");
  DxfString(2, "CONTINUOUS");
  DxfInt(70, 64);
  DxfString(3, "Solid line");
  DxfInt(72, 65);
  DxfInt(73, 0);
  DxfInt(40, 0);
}

void DxfVersion(void)
{
  DxfVariable("ACADVER");
  DxfString(1, "AC1009");
  DxfVariable("FILLMODE");
  DxfInt(70, FillAreas ? 1 : 0);
}

//
// Level 1: Block definitions for EAGLE primitives:
//

void DxfOctagonBlock(string name, int layer, real w, real x1, real y1, real x2, real y2)
{
  DxfBlock(name);
    DxfPolyline(layer, w);
      DxfVertex(layer, -x1, -y1);
      DxfVertex(layer, -x1,  y1);
      DxfVertex(layer, -x2,  y2);
      DxfVertex(layer,  x2,  y2);
      DxfVertex(layer,  x1,  y1);
      DxfVertex(layer,  x1, -y1);
      DxfVertex(layer,  x2, -y2);
      DxfVertex(layer, -x2, -y2);
      if (FillAreas) {
         DxfVertex(layer, -x1, -y1);
         DxfVertex(layer, -x1,  y1);
         }
      DxfSeqEnd();
    DxfEndBlock();
}

void DxfBoardBlocks(void)
{
  real f = 1.0 / ((sqrt(2) + 1) * 2);
  real x = 0.5;
  real y = f;
  real w  = FillAreas ? 0.5  : 0;
  real d  = FillAreas ? 0.5  : 1;

  real x1 = x - w / 4;
  real y1 = (y - w / 4 / sqrt(2)) * 0.5;
  real x2 = x - (x - (y - w / 4 / sqrt(2))) * 0.5;
  real y2 = x * 0.5 - w / 4;

  string pv[] = { "P", "V" };
  int l[] = {LAYER_PADS, LAYER_VIAS};

  DxfOctagonBlock("XLONGOCT", LAYER_PADS, w / 2, x1, y1, x2, y2);
  DxfOctagonBlock("YLONGOCT", LAYER_PADS, w / 2, y2, x2, y1, x1);

  for (int i = 0; i < 2; ++i) {
      DxfOctagonBlock("OCTAGON" + pv[i], l[i], w, x * d, y * d, y * d, x * d);

      DxfBlock("SQUARE" + pv[i]);
        DxfSolid(l[i], -0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5);
        DxfEndBlock();

      DxfBlock("ROUND" + pv[i]);
        if (FillAreas) {
           DxfPolyline(l[i], 0.5);
             DxfVertexRound(l[i], 0, -0.25, 1);
             DxfVertexRound(l[i], 0,  0.25, 1);
             DxfVertex(l[i], 0, -0.25);
             DxfSeqEnd();
           }
        else
           DxfCircle(l[i], 0, 0, 0.5);
        DxfEndBlock();
      }
}

//
// Level 2: Low level EAGLE to DXF conversion functions:
//

void Layer(UL_LAYER L)
{
  if (L.visible)
     DxfLayer(L.number, L.color);
  LayerActive[L.number] = L.visible;
}

void Area(UL_AREA A)
{
  DxfVariable("EXTMIN");
  DxfCoordinate(0, u2mm(A.x1), u2mm(A.y1));
  DxfVariable("EXTMAX");
  DxfCoordinate(0, u2mm(A.x2), u2mm(A.y2));
}

void Wire(UL_WIRE W)
{
  if (LayerActive[W.layer]) {
     if (UseWireWidths && W.width > 0) {

        real dx = u2mm(W.x2 - W.x1);
        real dy = u2mm(W.y2 - W.y1);
        real l  = sqrt(dx * dx + dy * dy);

        if (l > 0) {
           if (FillAreas)
              l *= 2;

           real w2 = u2mm(W.width / 2);
           real dxl = dx / l;
           real dyl = dy / l;

           real x1 = u2mm(W.x1) + dyl * w2;
           real y1 = u2mm(W.y1) - dxl * w2;
           real x2 = u2mm(W.x2) + dyl * w2;
           real y2 = u2mm(W.y2) - dxl * w2;
           real x3 = u2mm(W.x2) - dyl * w2;
           real y3 = u2mm(W.y2) + dxl * w2;
           real x4 = u2mm(W.x1) - dyl * w2;
           real y4 = u2mm(W.y1) + dxl * w2;

           if (FillAreas) {
              DxfPolyline(W.layer, u2mm(W.width));
                DxfVertex(W.layer, u2mm(W.x1), u2mm(W.y1));
                DxfVertex(W.layer, u2mm(W.x2), u2mm(W.y2));
                DxfSeqEnd();
              DxfPolyline(W.layer, u2mm(W.width) / 2);
                DxfVertexRound(W.layer, x2, y2, 1);
                DxfVertex(W.layer, x3, y3);
                DxfSeqEnd();
              DxfPolyline(W.layer, u2mm(W.width) / 2);
                DxfVertexRound(W.layer, x4, y4, 1);
                DxfVertex(W.layer, x1, y1);
                DxfSeqEnd();
              }
           else {
              DxfPolyline(W.layer, 0);
                DxfVertex(W.layer, x1, y1);
                DxfVertexRound(W.layer, x2, y2, 1);
                DxfVertex(W.layer, x3, y3);
                DxfVertexRound(W.layer, x4, y4, 1);
                DxfSeqEnd();
              }
           }
        }
     else
        DxfLine(W.layer, u2mm(W.x1), u2mm(W.y1), u2mm(W.x2), u2mm(W.y2));
     }
}

void Circle(UL_CIRCLE C)
{
  if (LayerActive[C.layer]) {
     if (UseWireWidths) {
        if (C.width > 0) {
           if (FillAreas) {
              DxfPolyline(C.layer, u2mm(C.width));
                DxfVertexRound(C.layer, u2mm(C.x), u2mm(C.y - C.radius), 1);
                DxfVertexRound(C.layer, u2mm(C.x), u2mm(C.y + C.radius), 1);
                DxfVertex(C.layer, u2mm(C.x), u2mm(C.y - C.radius));
                DxfSeqEnd();
              }
           else {
              DxfCircle(C.layer, u2mm(C.x), u2mm(C.y), u2mm(C.radius - C.width / 2));
              DxfCircle(C.layer, u2mm(C.x), u2mm(C.y), u2mm(C.radius + C.width / 2));
              }
           }
        else if (FillAreas) {
           DxfPolyline(C.layer, u2mm(C.radius));
             DxfVertexRound(C.layer, u2mm(C.x), u2mm(C.y - C.radius / 2), 1);
             DxfVertexRound(C.layer, u2mm(C.x), u2mm(C.y + C.radius / 2), 1);
             DxfVertex(C.layer, u2mm(C.x), u2mm(C.y - C.radius / 2));
             DxfSeqEnd();
           }
        return;
        }
     DxfCircle(C.layer, u2mm(C.x), u2mm(C.y), u2mm(C.radius));
     }
}

void Arc(UL_ARC A)
{
  if (LayerActive[A.layer]) {
     if (UseWireWidths && A.width > 0) {

        real a1 = A.angle1 / 180 * PI;
        real a2 = A.angle2 / 180 * PI;
        real ra = tan((A.angle2 - A.angle1) / 180 * PI / 4);

        if (FillAreas) {
           DxfPolyline(A.layer, u2mm(A.width));
             DxfVertexRound(A.layer, u2mm(A.x1), u2mm(A.y1), ra);
             DxfVertex(A.layer, u2mm(A.x2), u2mm(A.y2));
             DxfSeqEnd();
           }
        else {

           real w2 = u2mm(A.width / 2);
           real x1 = u2mm(A.x1) - w2 * cos(a1);
           real y1 = u2mm(A.y1) - w2 * sin(a1);
           real x2 = u2mm(A.x1) + w2 * cos(a1);
           real y2 = u2mm(A.y1) + w2 * sin(a1);
           real x3 = u2mm(A.x2) + w2 * cos(a2);
           real y3 = u2mm(A.y2) + w2 * sin(a2);
           real x4 = u2mm(A.x2) - w2 * cos(a2);
           real y4 = u2mm(A.y2) - w2 * sin(a2);

           DxfPolyline(A.layer, 0);
             DxfVertex(A.layer, x1, y1);
             DxfVertexRound(A.layer, x2, y2, ra);
             DxfVertex(A.layer, x3, y3);
             DxfVertexRound(A.layer, x4, y4, -ra);
             DxfSeqEnd();
           }
        }
     else
        DxfArc(A.layer,
               u2mm(A.xc), u2mm(A.yc),
               u2mm(A.radius),
               DxfAngle(A.angle1), DxfAngle(A.angle2));
     }
}

void Rectangle(UL_RECTANGLE R)
{
  DxfSolid(R.layer,
           u2mm(R.x1), u2mm(R.y1),
           u2mm(R.x2), u2mm(R.y1),
           u2mm(R.x1), u2mm(R.y2),
           u2mm(R.x2), u2mm(R.y2));
}

void Hole(UL_HOLE H)
{
  DxfCircle(LAYER_DIMENSION, u2mm(H.x), u2mm(H.y), u2mm(H.drill / 2));
}

void Via(UL_VIA V)
{
  string Shape[] = { "SQUAREV", "ROUNDV", "OCTAGONV" };

  DxfInsert(LAYER_VIAS, Shape[V.shape], u2mm(V.x), u2mm(V.y), u2mm(V.diameter), u2mm(V.diameter));
  DxfCircle(LAYER_DRILLS, u2mm(V.x), u2mm(V.y), u2mm(V.drill / 2));
}

void Pad(UL_PAD P)
{
  string Shape[] = { "SQUAREP", "ROUNDP", "OCTAGONP", "XLONGOCT", "YLONGOCT" };

  DxfInsert(LAYER_PADS, Shape[P.shape], u2mm(P.x), u2mm(P.y), u2mm(P.diameter), u2mm(P.diameter));
  DxfCircle(LAYER_DRILLS, u2mm(P.x), u2mm(P.y), u2mm(P.drill / 2));
}

void Smd(UL_SMD S)
{
  DxfSolid(S.layer, u2mm(S.x - S.dx / 2), u2mm(S.y - S.dy / 2),
                    u2mm(S.x + S.dx / 2), u2mm(S.y - S.dy / 2),
                    u2mm(S.x - S.dx / 2), u2mm(S.y + S.dy / 2),
                    u2mm(S.x + S.dx / 2), u2mm(S.y + S.dy / 2));
}

void Junction(UL_JUNCTION J)
{
  DxfCircle(LAYER_NETS,
            u2mm(J.x), u2mm(J.y),
            u2mm(J.diameter / 2));
}

void Polygon(UL_POLYGON P)
{
  P.wires(W) Wire(W);
}

void Text(UL_TEXT T)
{
  // Although DXF offers several parameters to fine tune a text, most
  // DXF aware programs do not process these parameters correctly.
  // Therefore we make a very basic approach here.
  int x = T.x;
  int y = T.y;
  int angle = T.angle; // makes it int for easier processing!

  if (T.mirror) {
     switch (angle) {
       case   0:
       case  90: if (!board) {
                    GetTextPoint(T);
                    x = tx2;
                    }
                 break;
       case 180:
       case 270: GetTextPoint(T);
                 if (board)
                    x = tx2;
                 y = ty2;
                 break;
       }
     }
  else {
     switch (angle) {
       case 180:
       case 270: GetTextPoint(T);
                 x = tx2;
                 y = ty2;
                 break;
       }
     }
  if (sheet || !T.mirror)
     angle = (angle == 90 || angle == 270) ?  90 : 0;
  else if (T.mirror)
     angle = (angle == 90 || angle == 270) ? 270 : 0;
  DxfText(T.layer,
          u2mm(x), u2mm(y),
          u2mm(T.size), T.value, angle, board ? T.mirror : 0);
}

//
// Level 3: High level EAGLE decomposing functions:
//

void Package(UL_PACKAGE P)
{
  P.polygons(P) Polygon(P);
  P.wires(W) Wire(W);
  P.texts(T) Text(T);
  P.arcs(A) Arc(A);
  P.circles(C) Circle(C);
  P.rectangles(R) Rectangle(R);
  P.holes(H) Hole(H);
  P.contacts(C) {
    if (C.pad) Pad(C.pad);
    else       Smd(C.smd);
    }
}

void Element(UL_ELEMENT E)
{
  int layer;

  switch (E.mirror) {
    case 0: layer = LAYER_TORIGINS; break; // not mirrored
    case 1: layer = LAYER_BORIGINS; break; // mirrored
    }
  DxfPoint(layer, u2mm(E.x), u2mm(E.y));
  Package(E.package);
  E.texts(T) Text(T);
}

void Signal(UL_SIGNAL S)
{
  S.polygons(P) Polygon(P);
  S.wires(W) Wire(W);
  S.vias(V) Via(V);
}

void Board(UL_BOARD B)
{
  B.polygons(P) Polygon(P);
  B.wires(W) Wire(W);
  B.texts(T) Text(T);
  B.arcs(A) Arc(A);
  B.circles(C) Circle(C);
  B.rectangles(R) Rectangle(R);
  B.holes(H) Hole(H);
  B.elements(E) Element(E);
  B.signals(S) Signal(S);
}

void Pin(UL_PIN P)
{
  P.wires(W) Wire(W);
  P.circles(C) Circle(C);
  P.texts(T) Text(T);
}

void Symbol(UL_SYMBOL S)
{
  S.polygons(P) Polygon(P);
  S.wires(W) Wire(W);
  S.texts(T) Text(T);
  S.pins(P) Pin(P);
  S.arcs(A) Arc(A);
  S.circles(C) Circle(C);
  S.rectangles(R) Rectangle(R);
}

void Instance(UL_INSTANCE I)
{
  Symbol(I.gate.symbol);
  I.texts(T) Text(T);
}

void Part(UL_PART P)
{
  P.instances(I) Instance(I);
}

void Segment(UL_SEGMENT S)
{
  S.wires(W) Wire(W);
  S.junctions(J) Junction(J);
  S.texts(T) Text(T);
}

void Bus(UL_BUS B)
{
  B.segments(S) Segment(S);
}

void Net(UL_NET N)
{
  N.segments(S) Segment(S);
}

void Sheet(UL_SHEET S)
{
  S.polygons(P) Polygon(P);
  S.wires(W) Wire(W);
  S.texts(T) Text(T);
  S.arcs(A) Arc(A);
  S.circles(C) Circle(C);
  S.rectangles(R) Rectangle(R);
  S.parts(P) Part(P);
  S.busses(B) Bus(B);
  S.nets(N) Net(N);
}

//
// Main program:
//

string OutputFileName = "";

if (board)
   board(B) OutputFileName = B.name;
else if (schematic)
   schematic(SCH) OutputFileName = SCH.name;

if (OutputFileName) {
   output(filesetext(OutputFileName, ".dxf")) {
     DxfSection("HEADER");
       DxfVersion();
       if (board) board(B) Area(B.area);
       else sheet(S) Area(S.area);
       DxfEndSection();
     DxfSection("TABLES");
       DxfTable("LTYPE", 1);
         DxfLineTypes();
         DxfEndTable();
       DxfTable("LAYER", 255);
         if (board) board(B) B.layers(L) Layer(L);
         else schematic(SCH) SCH.layers(L) Layer(L);
         DxfEndTable();
       DxfEndSection();
     if (board) {
        DxfSection("BLOCKS");
          DxfBoardBlocks();
          DxfEndSection();
        }
     DxfSection("ENTITIES");
       if (board) board(B) Board(B);
       else sheet(S) Sheet(S);
       DxfEndSection();
     DxfTrailer();
     }
   }
