summaryrefslogtreecommitdiff
path: root/hyp2mat/lib
diff options
context:
space:
mode:
Diffstat (limited to 'hyp2mat/lib')
-rw-r--r--hyp2mat/lib/.gitignore11
-rw-r--r--hyp2mat/lib/Makefile.am69
-rw-r--r--hyp2mat/lib/clipper.cpp3700
-rw-r--r--hyp2mat/lib/clipper.hpp351
-rw-r--r--hyp2mat/lib/clipper.txt29
-rw-r--r--hyp2mat/lib/copper.cc382
-rw-r--r--hyp2mat/lib/crop.cc108
-rw-r--r--hyp2mat/lib/crop.h36
-rw-r--r--hyp2mat/lib/csxcad.cc376
-rw-r--r--hyp2mat/lib/csxcad.h53
-rw-r--r--hyp2mat/lib/draw.cc296
-rw-r--r--hyp2mat/lib/exec_board.cc251
-rw-r--r--hyp2mat/lib/exec_devices.cc89
-rw-r--r--hyp2mat/lib/exec_end.cc53
-rw-r--r--hyp2mat/lib/exec_net.cc502
-rw-r--r--hyp2mat/lib/exec_netclass.cc76
-rw-r--r--hyp2mat/lib/exec_padstack.cc269
-rw-r--r--hyp2mat/lib/exec_polygon.cc275
-rw-r--r--hyp2mat/lib/exec_stackup.cc446
-rw-r--r--hyp2mat/lib/exec_supplies.cc57
-rw-r--r--hyp2mat/lib/hyp2mat.h224
-rw-r--r--hyp2mat/lib/hyp2mat.pc.in12
-rw-r--r--hyp2mat/lib/hyperlynx.cc348
-rw-r--r--hyp2mat/lib/hyperlynx.h79
-rw-r--r--hyp2mat/lib/hypfile.cc378
-rw-r--r--hyp2mat/lib/hypfile.h333
-rw-r--r--hyp2mat/lib/misc.cc65
-rw-r--r--hyp2mat/lib/palette.cc120
-rw-r--r--hyp2mat/lib/palette.h55
-rw-r--r--hyp2mat/lib/parse.yy826
-rw-r--r--hyp2mat/lib/parse_param.h172
-rw-r--r--hyp2mat/lib/parser.h40
-rw-r--r--hyp2mat/lib/pcb.cc333
-rw-r--r--hyp2mat/lib/pdf.cc292
-rw-r--r--hyp2mat/lib/pdf.h59
-rw-r--r--hyp2mat/lib/polygon.cc335
-rw-r--r--hyp2mat/lib/polygon.h74
-rw-r--r--hyp2mat/lib/scan.ll371
38 files changed, 11545 insertions, 0 deletions
diff --git a/hyp2mat/lib/.gitignore b/hyp2mat/lib/.gitignore
new file mode 100644
index 0000000..17fd190
--- /dev/null
+++ b/hyp2mat/lib/.gitignore
@@ -0,0 +1,11 @@
+*.lo
+*.o
+.deps/
+.libs/
+config.h
+config.h.in~
+hyp2mat.pc
+libhyp2mat.la
+parse.cc
+parse.h
+scan.cc
diff --git a/hyp2mat/lib/Makefile.am b/hyp2mat/lib/Makefile.am
new file mode 100644
index 0000000..5e1000d
--- /dev/null
+++ b/hyp2mat/lib/Makefile.am
@@ -0,0 +1,69 @@
+# used by automake
+
+#
+# hyp2mat library
+#
+
+# if you wish to install libraries, headers, and package information
+if INSTALL_LIBRARY
+include_HEADERS=hyp2mat.h
+lib_LTLIBRARIES=libhyp2mat.la
+pkgconfig_DATA=hyp2mat.pc
+else
+# if you do not wish to install libraries, headers, and package information
+noinst_HEADERS=hyp2mat.h
+noinst_LTLIBRARIES=libhyp2mat.la
+noinst_DATA=hyp2mat.pc
+endif
+
+pkgconfigdir=$(datarootdir)/pkgconfig
+
+libhyp2mat_la_SOURCES=\
+ clipper.cpp \
+ clipper.hpp \
+ copper.cc \
+ crop.h \
+ crop.cc \
+ csxcad.cc \
+ csxcad.h \
+ draw.cc \
+ exec_board.cc \
+ exec_devices.cc \
+ exec_end.cc \
+ exec_net.cc \
+ exec_netclass.cc \
+ exec_padstack.cc \
+ exec_polygon.cc \
+ exec_stackup.cc \
+ exec_supplies.cc \
+ hyp2mat.h \
+ hyperlynx.cc \
+ hyperlynx.h \
+ hypfile.cc \
+ hypfile.h \
+ misc.cc \
+ palette.cc \
+ palette.h \
+ parse_param.h \
+ parser.h \
+ parse.yy \
+ polygon.h \
+ polygon.cc \
+ pcb.cc \
+ pdf.cc \
+ pdf.h \
+ scan.ll
+
+EXTRA_DIST=clipper.txt parse.h
+
+BUILT_SOURCES=parse.cc parse.h scan.cc
+
+scan.cc: scan.ll parse.h
+ ${LEX} -o $*.cc $<
+
+parse.h parse.cc: parse.yy
+ ${YACC} --output=$*.cc --defines=$*.h $<
+
+CLEANFILES=parse.cc parse.h scan.cc
+
+#not truncated
diff --git a/hyp2mat/lib/clipper.cpp b/hyp2mat/lib/clipper.cpp
new file mode 100644
index 0000000..02a7b70
--- /dev/null
+++ b/hyp2mat/lib/clipper.cpp
@@ -0,0 +1,3700 @@
+/*******************************************************************************
+* *
+* Author : Angus Johnson *
+* Version : 5.1.6 *
+* Date : 23 May 2013 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2013 *
+* *
+* License: *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt *
+* *
+* Attributions: *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping" *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
+* http://portal.acm.org/citation.cfm?id=129906 *
+* *
+* Computer graphics and geometric modeling: implementation and algorithms *
+* By Max K. Agoston *
+* Springer; 1 edition (January 4, 2005) *
+* http://books.google.com/books?q=vatti+clipping+agoston *
+* *
+* See also: *
+* "Polygon Offsetting by Computing Winding Numbers" *
+* Paper no. DETC2005-85513 pp. 565-575 *
+* ASME 2005 International Design Engineering Technical Conferences *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
+* September 24-28, 2005 , Long Beach, California, USA *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
+* *
+*******************************************************************************/
+
+/*******************************************************************************
+* *
+* This is a translation of the Delphi Clipper library and the naming style *
+* used has retained a Delphi flavour. *
+* *
+*******************************************************************************/
+
+#include "clipper.hpp"
+#include <cmath>
+#include <vector>
+#include <algorithm>
+#include <stdexcept>
+#include <cstring>
+#include <cstdlib>
+#include <ostream>
+
+namespace ClipperLib {
+
+static long64 const loRange = 0x3FFFFFFF;
+static long64 const hiRange = 0x3FFFFFFFFFFFFFFFLL;
+static double const pi = 3.141592653589793238;
+enum Direction { dRightToLeft, dLeftToRight };
+
+#define HORIZONTAL (-1.0E+40)
+#define TOLERANCE (1.0e-20)
+#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE))
+#define NEAR_EQUAL(a, b) NEAR_ZERO((a) - (b))
+
+const char coords_range_error[] = "Coordinate exceeds range bounds.";
+
+inline long64 Abs(long64 val)
+{
+ return val < 0 ? -val : val;
+}
+
+//------------------------------------------------------------------------------
+// PolyTree methods ...
+//------------------------------------------------------------------------------
+
+void PolyTree::Clear()
+{
+ for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i)
+ delete AllNodes[i];
+ AllNodes.resize(0);
+ Childs.resize(0);
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyTree::GetFirst() const
+{
+ if (!Childs.empty())
+ return Childs[0];
+ else
+ return 0;
+}
+//------------------------------------------------------------------------------
+
+int PolyTree::Total() const
+{
+ return AllNodes.size();
+}
+
+//------------------------------------------------------------------------------
+// PolyNode methods ...
+//------------------------------------------------------------------------------
+
+PolyNode::PolyNode(): Childs(), Parent(0), Index(0)
+{
+}
+//------------------------------------------------------------------------------
+
+int PolyNode::ChildCount() const
+{
+ return Childs.size();
+}
+//------------------------------------------------------------------------------
+
+void PolyNode::AddChild(PolyNode& child)
+{
+ unsigned cnt = Childs.size();
+ Childs.push_back(&child);
+ child.Parent = this;
+ child.Index = cnt;
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyNode::GetNext() const
+{
+ if (!Childs.empty())
+ return Childs[0];
+ else
+ return GetNextSiblingUp();
+}
+//------------------------------------------------------------------------------
+
+PolyNode* PolyNode::GetNextSiblingUp() const
+{
+ if (!Parent) //protects against PolyTree.GetNextSiblingUp()
+ return 0;
+ else if (Index == Parent->Childs.size() - 1)
+ return Parent->GetNextSiblingUp();
+ else
+ return Parent->Childs[Index + 1];
+}
+//------------------------------------------------------------------------------
+
+bool PolyNode::IsHole() const
+{
+ bool result = true;
+ PolyNode* node = Parent;
+ while (node)
+ {
+ result = !result;
+ node = node->Parent;
+ }
+ return result;
+}
+
+//------------------------------------------------------------------------------
+// Int128 class (enables safe math on signed 64bit integers)
+// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1
+// Int128 val2((long64)9223372036854775807);
+// Int128 val3 = val1 * val2;
+// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37)
+//------------------------------------------------------------------------------
+
+class Int128
+{
+ public:
+
+ ulong64 lo;
+ long64 hi;
+
+ Int128(long64 _lo = 0)
+ {
+ lo = (ulong64)_lo;
+ if (_lo < 0) hi = -1; else hi = 0;
+ }
+
+
+ Int128(const Int128 &val): lo(val.lo), hi(val.hi){}
+
+ Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){}
+
+ long64 operator = (const long64 &val)
+ {
+ lo = (ulong64)val;
+ if (val < 0) hi = -1; else hi = 0;
+ return val;
+ }
+
+ bool operator == (const Int128 &val) const
+ {return (hi == val.hi && lo == val.lo);}
+
+ bool operator != (const Int128 &val) const
+ { return !(*this == val);}
+
+ bool operator > (const Int128 &val) const
+ {
+ if (hi != val.hi)
+ return hi > val.hi;
+ else
+ return lo > val.lo;
+ }
+
+ bool operator < (const Int128 &val) const
+ {
+ if (hi != val.hi)
+ return hi < val.hi;
+ else
+ return lo < val.lo;
+ }
+
+ bool operator >= (const Int128 &val) const
+ { return !(*this < val);}
+
+ bool operator <= (const Int128 &val) const
+ { return !(*this > val);}
+
+ Int128& operator += (const Int128 &rhs)
+ {
+ hi += rhs.hi;
+ lo += rhs.lo;
+ if (lo < rhs.lo) hi++;
+ return *this;
+ }
+
+ Int128 operator + (const Int128 &rhs) const
+ {
+ Int128 result(*this);
+ result+= rhs;
+ return result;
+ }
+
+ Int128& operator -= (const Int128 &rhs)
+ {
+ *this += -rhs;
+ return *this;
+ }
+
+ Int128 operator - (const Int128 &rhs) const
+ {
+ Int128 result(*this);
+ result -= rhs;
+ return result;
+ }
+
+ Int128 operator-() const //unary negation
+ {
+ if (lo == 0)
+ return Int128(-hi,0);
+ else
+ return Int128(~hi,~lo +1);
+ }
+
+ Int128 operator/ (const Int128 &rhs) const
+ {
+ if (rhs.lo == 0 && rhs.hi == 0)
+ throw "Int128 operator/: divide by zero";
+
+ bool negate = (rhs.hi < 0) != (hi < 0);
+ Int128 dividend = *this;
+ Int128 divisor = rhs;
+ if (dividend.hi < 0) dividend = -dividend;
+ if (divisor.hi < 0) divisor = -divisor;
+
+ if (divisor < dividend)
+ {
+ Int128 result = Int128(0);
+ Int128 cntr = Int128(1);
+ while (divisor.hi >= 0 && !(divisor > dividend))
+ {
+ divisor.hi <<= 1;
+ if ((long64)divisor.lo < 0) divisor.hi++;
+ divisor.lo <<= 1;
+
+ cntr.hi <<= 1;
+ if ((long64)cntr.lo < 0) cntr.hi++;
+ cntr.lo <<= 1;
+ }
+ divisor.lo >>= 1;
+ if ((divisor.hi & 1) == 1)
+ divisor.lo |= 0x8000000000000000LL;
+ divisor.hi = (ulong64)divisor.hi >> 1;
+
+ cntr.lo >>= 1;
+ if ((cntr.hi & 1) == 1)
+ cntr.lo |= 0x8000000000000000LL;
+ cntr.hi >>= 1;
+
+ while (cntr.hi != 0 || cntr.lo != 0)
+ {
+ if (!(dividend < divisor))
+ {
+ dividend -= divisor;
+ result.hi |= cntr.hi;
+ result.lo |= cntr.lo;
+ }
+ divisor.lo >>= 1;
+ if ((divisor.hi & 1) == 1)
+ divisor.lo |= 0x8000000000000000LL;
+ divisor.hi >>= 1;
+
+ cntr.lo >>= 1;
+ if ((cntr.hi & 1) == 1)
+ cntr.lo |= 0x8000000000000000LL;
+ cntr.hi >>= 1;
+ }
+ if (negate) result = -result;
+ return result;
+ }
+ else if (rhs.hi == this->hi && rhs.lo == this->lo)
+ return Int128(1);
+ else
+ return Int128(0);
+ }
+
+ double AsDouble() const
+ {
+ const double shift64 = 18446744073709551616.0; //2^64
+ if (hi < 0)
+ {
+ if (lo == 0) return (double)hi * shift64;
+ else return -(double)(~lo + ~hi * shift64);
+ }
+ else
+ return (double)(lo + hi * shift64);
+ }
+};
+
+Int128 Int128Mul (long64 lhs, long64 rhs)
+{
+ bool negate = (lhs < 0) != (rhs < 0);
+
+ if (lhs < 0) lhs = -lhs;
+ ulong64 int1Hi = ulong64(lhs) >> 32;
+ ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF);
+
+ if (rhs < 0) rhs = -rhs;
+ ulong64 int2Hi = ulong64(rhs) >> 32;
+ ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF);
+
+ //nb: see comments in clipper.pas
+ ulong64 a = int1Hi * int2Hi;
+ ulong64 b = int1Lo * int2Lo;
+ ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi;
+
+ Int128 tmp;
+ tmp.hi = long64(a + (c >> 32));
+ tmp.lo = long64(c << 32);
+ tmp.lo += long64(b);
+ if (tmp.lo < b) tmp.hi++;
+ if (negate) tmp = -tmp;
+ return tmp;
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+bool FullRangeNeeded(const Polygon &pts)
+{
+ bool result = false;
+ for (Polygon::size_type i = 0; i < pts.size(); ++i)
+ {
+ if (Abs(pts[i].X) > hiRange || Abs(pts[i].Y) > hiRange)
+ throw coords_range_error;
+ else if (Abs(pts[i].X) > loRange || Abs(pts[i].Y) > loRange)
+ result = true;
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+bool Orientation(const Polygon &poly)
+{
+ return Area(poly) >= 0;
+}
+//------------------------------------------------------------------------------
+
+inline bool PointsEqual( const IntPoint &pt1, const IntPoint &pt2)
+{
+ return ( pt1.X == pt2.X && pt1.Y == pt2.Y );
+}
+//------------------------------------------------------------------------------
+
+double Area(const Polygon &poly)
+{
+ int highI = (int)poly.size() -1;
+ if (highI < 2) return 0;
+
+ if (FullRangeNeeded(poly)) {
+ Int128 a;
+ a = Int128Mul(poly[highI].X + poly[0].X, poly[0].Y - poly[highI].Y);
+ for (int i = 1; i <= highI; ++i)
+ a += Int128Mul(poly[i - 1].X + poly[i].X, poly[i].Y - poly[i -1].Y);
+ return a.AsDouble() / 2;
+ }
+ else
+ {
+ double a;
+ a = ((double)poly[highI].X + poly[0].X) * ((double)poly[0].Y - poly[highI].Y);
+ for (int i = 1; i <= highI; ++i)
+ a += ((double)poly[i - 1].X + poly[i].X) * ((double)poly[i].Y - poly[i - 1].Y);
+ return a / 2;
+ }
+}
+//------------------------------------------------------------------------------
+
+double Area(const OutRec &outRec, bool UseFullInt64Range)
+{
+ OutPt *op = outRec.pts;
+ if (!op) return 0;
+ if (UseFullInt64Range) {
+ Int128 a(0);
+ do {
+ a += Int128Mul(op->pt.X + op->prev->pt.X, op->prev->pt.Y - op->pt.Y);
+ op = op->next;
+ } while (op != outRec.pts);
+ return a.AsDouble() / 2;
+ }
+ else
+ {
+ double a = 0;
+ do {
+ a = a + (op->pt.X + op->prev->pt.X) * (op->prev->pt.Y - op->pt.Y);
+ op = op->next;
+ } while (op != outRec.pts);
+ return a / 2;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool PointIsVertex(const IntPoint &pt, OutPt *pp)
+{
+ OutPt *pp2 = pp;
+ do
+ {
+ if (PointsEqual(pp2->pt, pt)) return true;
+ pp2 = pp2->next;
+ }
+ while (pp2 != pp);
+ return false;
+}
+//------------------------------------------------------------------------------
+
+bool PointOnLineSegment(const IntPoint pt,
+ const IntPoint linePt1, const IntPoint linePt2, bool UseFullInt64Range)
+{
+ if (UseFullInt64Range)
+ return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) ||
+ ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) ||
+ (((pt.X > linePt1.X) == (pt.X < linePt2.X)) &&
+ ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) &&
+ ((Int128Mul((pt.X - linePt1.X), (linePt2.Y - linePt1.Y)) ==
+ Int128Mul((linePt2.X - linePt1.X), (pt.Y - linePt1.Y)))));
+ else
+ return ((pt.X == linePt1.X) && (pt.Y == linePt1.Y)) ||
+ ((pt.X == linePt2.X) && (pt.Y == linePt2.Y)) ||
+ (((pt.X > linePt1.X) == (pt.X < linePt2.X)) &&
+ ((pt.Y > linePt1.Y) == (pt.Y < linePt2.Y)) &&
+ ((pt.X - linePt1.X) * (linePt2.Y - linePt1.Y) ==
+ (linePt2.X - linePt1.X) * (pt.Y - linePt1.Y)));
+}
+//------------------------------------------------------------------------------
+
+bool PointOnPolygon(const IntPoint pt, OutPt *pp, bool UseFullInt64Range)
+{
+ OutPt *pp2 = pp;
+ while (true)
+ {
+ if (PointOnLineSegment(pt, pp2->pt, pp2->next->pt, UseFullInt64Range))
+ return true;
+ pp2 = pp2->next;
+ if (pp2 == pp) break;
+ }
+ return false;
+}
+//------------------------------------------------------------------------------
+
+bool PointInPolygon(const IntPoint &pt, OutPt *pp, bool UseFullInt64Range)
+{
+ OutPt *pp2 = pp;
+ bool result = false;
+ if (UseFullInt64Range) {
+ do
+ {
+ if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) ||
+ ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) &&
+ Int128(pt.X - pp2->pt.X) <
+ Int128Mul(pp2->prev->pt.X - pp2->pt.X, pt.Y - pp2->pt.Y) /
+ Int128(pp2->prev->pt.Y - pp2->pt.Y))
+ result = !result;
+ pp2 = pp2->next;
+ }
+ while (pp2 != pp);
+ }
+ else
+ {
+ do
+ {
+ if ((((pp2->pt.Y <= pt.Y) && (pt.Y < pp2->prev->pt.Y)) ||
+ ((pp2->prev->pt.Y <= pt.Y) && (pt.Y < pp2->pt.Y))) &&
+ (pt.X < (pp2->prev->pt.X - pp2->pt.X) * (pt.Y - pp2->pt.Y) /
+ (pp2->prev->pt.Y - pp2->pt.Y) + pp2->pt.X )) result = !result;
+ pp2 = pp2->next;
+ }
+ while (pp2 != pp);
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range)
+{
+ if (UseFullInt64Range)
+ return Int128Mul(e1.deltaY, e2.deltaX) == Int128Mul(e1.deltaX, e2.deltaY);
+ else return e1.deltaY * e2.deltaX == e1.deltaX * e2.deltaY;
+}
+//------------------------------------------------------------------------------
+
+bool SlopesEqual(const IntPoint pt1, const IntPoint pt2,
+ const IntPoint pt3, bool UseFullInt64Range)
+{
+ if (UseFullInt64Range)
+ return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y);
+ else return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y);
+}
+//------------------------------------------------------------------------------
+
+bool SlopesEqual(const IntPoint pt1, const IntPoint pt2,
+ const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range)
+{
+ if (UseFullInt64Range)
+ return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y);
+ else return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y);
+}
+//------------------------------------------------------------------------------
+
+double GetDx(const IntPoint pt1, const IntPoint pt2)
+{
+ return (pt1.Y == pt2.Y) ?
+ HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y);
+}
+//---------------------------------------------------------------------------
+
+void SetDx(TEdge &e)
+{
+ e.deltaX = (e.xtop - e.xbot);
+ e.deltaY = (e.ytop - e.ybot);
+
+ if (e.deltaY == 0) e.dx = HORIZONTAL;
+ else e.dx = (double)(e.deltaX) / e.deltaY;
+}
+//---------------------------------------------------------------------------
+
+void SwapSides(TEdge &edge1, TEdge &edge2)
+{
+ EdgeSide side = edge1.side;
+ edge1.side = edge2.side;
+ edge2.side = side;
+}
+//------------------------------------------------------------------------------
+
+void SwapPolyIndexes(TEdge &edge1, TEdge &edge2)
+{
+ int outIdx = edge1.outIdx;
+ edge1.outIdx = edge2.outIdx;
+ edge2.outIdx = outIdx;
+}
+//------------------------------------------------------------------------------
+
+inline long64 Round(double val)
+{
+ return (val < 0) ? static_cast<long64>(val - 0.5) : static_cast<long64>(val + 0.5);
+}
+//------------------------------------------------------------------------------
+
+long64 TopX(TEdge &edge, const long64 currentY)
+{
+ return ( currentY == edge.ytop ) ?
+ edge.xtop : edge.xbot + Round(edge.dx *(currentY - edge.ybot));
+}
+//------------------------------------------------------------------------------
+
+bool IntersectPoint(TEdge &edge1, TEdge &edge2,
+ IntPoint &ip, bool UseFullInt64Range)
+{
+ double b1, b2;
+ if (SlopesEqual(edge1, edge2, UseFullInt64Range))
+ {
+ if (edge2.ybot > edge1.ybot) ip.Y = edge2.ybot;
+ else ip.Y = edge1.ybot;
+ return false;
+ }
+ else if (NEAR_ZERO(edge1.dx))
+ {
+ ip.X = edge1.xbot;
+ if (NEAR_EQUAL(edge2.dx, HORIZONTAL))
+ ip.Y = edge2.ybot;
+ else
+ {
+ b2 = edge2.ybot - (edge2.xbot / edge2.dx);
+ ip.Y = Round(ip.X / edge2.dx + b2);
+ }
+ }
+ else if (NEAR_ZERO(edge2.dx))
+ {
+ ip.X = edge2.xbot;
+ if (NEAR_EQUAL(edge1.dx, HORIZONTAL))
+ ip.Y = edge1.ybot;
+ else
+ {
+ b1 = edge1.ybot - (edge1.xbot / edge1.dx);
+ ip.Y = Round(ip.X / edge1.dx + b1);
+ }
+ }
+ else
+ {
+ b1 = edge1.xbot - edge1.ybot * edge1.dx;
+ b2 = edge2.xbot - edge2.ybot * edge2.dx;
+ double q = (b2-b1) / (edge1.dx - edge2.dx);
+ ip.Y = Round(q);
+ if (std::fabs(edge1.dx) < std::fabs(edge2.dx))
+ ip.X = Round(edge1.dx * q + b1);
+ else
+ ip.X = Round(edge2.dx * q + b2);
+ }
+
+ if (ip.Y < edge1.ytop || ip.Y < edge2.ytop)
+ {
+ if (edge1.ytop > edge2.ytop)
+ {
+ ip.X = edge1.xtop;
+ ip.Y = edge1.ytop;
+ return TopX(edge2, edge1.ytop) < edge1.xtop;
+ }
+ else
+ {
+ ip.X = edge2.xtop;
+ ip.Y = edge2.ytop;
+ return TopX(edge1, edge2.ytop) > edge2.xtop;
+ }
+ }
+ else
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void ReversePolyPtLinks(OutPt *pp)
+{
+ if (!pp) return;
+ OutPt *pp1, *pp2;
+ pp1 = pp;
+ do {
+ pp2 = pp1->next;
+ pp1->next = pp1->prev;
+ pp1->prev = pp2;
+ pp1 = pp2;
+ } while( pp1 != pp );
+}
+//------------------------------------------------------------------------------
+
+void DisposeOutPts(OutPt*& pp)
+{
+ if (pp == 0) return;
+ pp->prev->next = 0;
+ while( pp )
+ {
+ OutPt *tmpPp = pp;
+ pp = pp->next;
+ delete tmpPp;
+ }
+}
+//------------------------------------------------------------------------------
+
+void InitEdge(TEdge *e, TEdge *eNext,
+ TEdge *ePrev, const IntPoint &pt, PolyType polyType)
+{
+ std::memset(e, 0, sizeof(TEdge));
+ e->next = eNext;
+ e->prev = ePrev;
+ e->xcurr = pt.X;
+ e->ycurr = pt.Y;
+ if (e->ycurr >= e->next->ycurr)
+ {
+ e->xbot = e->xcurr;
+ e->ybot = e->ycurr;
+ e->xtop = e->next->xcurr;
+ e->ytop = e->next->ycurr;
+ e->windDelta = 1;
+ } else
+ {
+ e->xtop = e->xcurr;
+ e->ytop = e->ycurr;
+ e->xbot = e->next->xcurr;
+ e->ybot = e->next->ycurr;
+ e->windDelta = -1;
+ }
+ SetDx(*e);
+ e->polyType = polyType;
+ e->outIdx = -1;
+}
+//------------------------------------------------------------------------------
+
+inline void SwapX(TEdge &e)
+{
+ //swap horizontal edges' top and bottom x's so they follow the natural
+ //progression of the bounds - ie so their xbots will align with the
+ //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
+ e.xcurr = e.xtop;
+ e.xtop = e.xbot;
+ e.xbot = e.xcurr;
+}
+//------------------------------------------------------------------------------
+
+void SwapPoints(IntPoint &pt1, IntPoint &pt2)
+{
+ IntPoint tmp = pt1;
+ pt1 = pt2;
+ pt2 = tmp;
+}
+//------------------------------------------------------------------------------
+
+bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a,
+ IntPoint pt2b, IntPoint &pt1, IntPoint &pt2)
+{
+ //precondition: segments are colinear.
+ if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y))
+ {
+ if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b);
+ if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b);
+ if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a;
+ if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b;
+ return pt1.X < pt2.X;
+ } else
+ {
+ if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b);
+ if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b);
+ if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a;
+ if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b;
+ return pt1.Y > pt2.Y;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2)
+{
+ OutPt *p = btmPt1->prev;
+ while (PointsEqual(p->pt, btmPt1->pt) && (p != btmPt1)) p = p->prev;
+ double dx1p = std::fabs(GetDx(btmPt1->pt, p->pt));
+ p = btmPt1->next;
+ while (PointsEqual(p->pt, btmPt1->pt) && (p != btmPt1)) p = p->next;
+ double dx1n = std::fabs(GetDx(btmPt1->pt, p->pt));
+
+ p = btmPt2->prev;
+ while (PointsEqual(p->pt, btmPt2->pt) && (p != btmPt2)) p = p->prev;
+ double dx2p = std::fabs(GetDx(btmPt2->pt, p->pt));
+ p = btmPt2->next;
+ while (PointsEqual(p->pt, btmPt2->pt) && (p != btmPt2)) p = p->next;
+ double dx2n = std::fabs(GetDx(btmPt2->pt, p->pt));
+ return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
+}
+//------------------------------------------------------------------------------
+
+OutPt* GetBottomPt(OutPt *pp)
+{
+ OutPt* dups = 0;
+ OutPt* p = pp->next;
+ while (p != pp)
+ {
+ if (p->pt.Y > pp->pt.Y)
+ {
+ pp = p;
+ dups = 0;
+ }
+ else if (p->pt.Y == pp->pt.Y && p->pt.X <= pp->pt.X)
+ {
+ if (p->pt.X < pp->pt.X)
+ {
+ dups = 0;
+ pp = p;
+ } else
+ {
+ if (p->next != pp && p->prev != pp) dups = p;
+ }
+ }
+ p = p->next;
+ }
+ if (dups)
+ {
+ //there appears to be at least 2 vertices at bottomPt so ...
+ while (dups != p)
+ {
+ if (!FirstIsBottomPt(p, dups)) pp = dups;
+ dups = dups->next;
+ while (!PointsEqual(dups->pt, pp->pt)) dups = dups->next;
+ }
+ }
+ return pp;
+}
+//------------------------------------------------------------------------------
+
+bool FindSegment(OutPt* &pp, bool UseFullInt64Range,
+ IntPoint &pt1, IntPoint &pt2)
+{
+ //outPt1 & outPt2 => the overlap segment (if the function returns true)
+ if (!pp) return false;
+ OutPt* pp2 = pp;
+ IntPoint pt1a = pt1, pt2a = pt2;
+ do
+ {
+ if (SlopesEqual(pt1a, pt2a, pp->pt, pp->prev->pt, UseFullInt64Range) &&
+ SlopesEqual(pt1a, pt2a, pp->pt, UseFullInt64Range) &&
+ GetOverlapSegment(pt1a, pt2a, pp->pt, pp->prev->pt, pt1, pt2))
+ return true;
+ pp = pp->next;
+ }
+ while (pp != pp2);
+ return false;
+}
+//------------------------------------------------------------------------------
+
+bool Pt3IsBetweenPt1AndPt2(const IntPoint pt1,
+ const IntPoint pt2, const IntPoint pt3)
+{
+ if (PointsEqual(pt1, pt3) || PointsEqual(pt2, pt3)) return true;
+ else if (pt1.X != pt2.X) return (pt1.X < pt3.X) == (pt3.X < pt2.X);
+ else return (pt1.Y < pt3.Y) == (pt3.Y < pt2.Y);
+}
+//------------------------------------------------------------------------------
+
+OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint pt)
+{
+ if (p1 == p2) throw "JoinError";
+ OutPt* result = new OutPt;
+ result->pt = pt;
+ if (p2 == p1->next)
+ {
+ p1->next = result;
+ p2->prev = result;
+ result->next = p2;
+ result->prev = p1;
+ } else
+ {
+ p2->next = result;
+ p1->prev = result;
+ result->next = p1;
+ result->prev = p2;
+ }
+ return result;
+}
+
+//------------------------------------------------------------------------------
+// ClipperBase class methods ...
+//------------------------------------------------------------------------------
+
+ClipperBase::ClipperBase() //constructor
+{
+ m_MinimaList = 0;
+ m_CurrentLM = 0;
+ m_UseFullRange = true;
+}
+//------------------------------------------------------------------------------
+
+ClipperBase::~ClipperBase() //destructor
+{
+ Clear();
+}
+//------------------------------------------------------------------------------
+
+void RangeTest(const IntPoint& pt, long64& maxrange)
+{
+ if (Abs(pt.X) > maxrange)
+ {
+ if (Abs(pt.X) > hiRange)
+ throw coords_range_error;
+ else maxrange = hiRange;
+ }
+ if (Abs(pt.Y) > maxrange)
+ {
+ if (Abs(pt.Y) > hiRange)
+ throw coords_range_error;
+ else maxrange = hiRange;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::AddPolygon(const Polygon &pg, PolyType polyType)
+{
+ int len = (int)pg.size();
+ if (len < 3) return false;
+
+ long64 maxVal;
+ if (m_UseFullRange) maxVal = hiRange; else maxVal = loRange;
+ RangeTest(pg[0], maxVal);
+
+ Polygon p(len);
+ p[0] = pg[0];
+ int j = 0;
+
+ for (int i = 0; i < len; ++i)
+ {
+ RangeTest(pg[i], maxVal);
+
+ if (i == 0 || PointsEqual(p[j], pg[i])) continue;
+ else if (j > 0 && SlopesEqual(p[j-1], p[j], pg[i], m_UseFullRange))
+ {
+ if (PointsEqual(p[j-1], pg[i])) j--;
+ } else j++;
+ p[j] = pg[i];
+ }
+ if (j < 2) return false;
+
+ len = j+1;
+ while (len > 2)
+ {
+ //nb: test for point equality before testing slopes ...
+ if (PointsEqual(p[j], p[0])) j--;
+ else if (PointsEqual(p[0], p[1]) ||
+ SlopesEqual(p[j], p[0], p[1], m_UseFullRange))
+ p[0] = p[j--];
+ else if (SlopesEqual(p[j-1], p[j], p[0], m_UseFullRange)) j--;
+ else if (SlopesEqual(p[0], p[1], p[2], m_UseFullRange))
+ {
+ for (int i = 2; i <= j; ++i) p[i-1] = p[i];
+ j--;
+ }
+ else break;
+ len--;
+ }
+ if (len < 3) return false;
+
+ //create a new edge array ...
+ TEdge *edges = new TEdge [len];
+ m_edges.push_back(edges);
+
+ //convert vertices to a double-linked-list of edges and initialize ...
+ edges[0].xcurr = p[0].X;
+ edges[0].ycurr = p[0].Y;
+ InitEdge(&edges[len-1], &edges[0], &edges[len-2], p[len-1], polyType);
+ for (int i = len-2; i > 0; --i)
+ InitEdge(&edges[i], &edges[i+1], &edges[i-1], p[i], polyType);
+ InitEdge(&edges[0], &edges[1], &edges[len-1], p[0], polyType);
+
+ //reset xcurr & ycurr and find 'eHighest' (given the Y axis coordinates
+ //increase downward so the 'highest' edge will have the smallest ytop) ...
+ TEdge *e = &edges[0];
+ TEdge *eHighest = e;
+ do
+ {
+ e->xcurr = e->xbot;
+ e->ycurr = e->ybot;
+ if (e->ytop < eHighest->ytop) eHighest = e;
+ e = e->next;
+ }
+ while ( e != &edges[0]);
+
+ //make sure eHighest is positioned so the following loop works safely ...
+ if (eHighest->windDelta > 0) eHighest = eHighest->next;
+ if (NEAR_EQUAL(eHighest->dx, HORIZONTAL)) eHighest = eHighest->next;
+
+ //finally insert each local minima ...
+ e = eHighest;
+ do {
+ e = AddBoundsToLML(e);
+ }
+ while( e != eHighest );
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::InsertLocalMinima(LocalMinima *newLm)
+{
+ if( ! m_MinimaList )
+ {
+ m_MinimaList = newLm;
+ }
+ else if( newLm->Y >= m_MinimaList->Y )
+ {
+ newLm->next = m_MinimaList;
+ m_MinimaList = newLm;
+ } else
+ {
+ LocalMinima* tmpLm = m_MinimaList;
+ while( tmpLm->next && ( newLm->Y < tmpLm->next->Y ) )
+ tmpLm = tmpLm->next;
+ newLm->next = tmpLm->next;
+ tmpLm->next = newLm;
+ }
+}
+//------------------------------------------------------------------------------
+
+TEdge* ClipperBase::AddBoundsToLML(TEdge *e)
+{
+ //Starting at the top of one bound we progress to the bottom where there's
+ //a local minima. We then go to the top of the next bound. These two bounds
+ //form the left and right (or right and left) bounds of the local minima.
+ e->nextInLML = 0;
+ e = e->next;
+ for (;;)
+ {
+ if (NEAR_EQUAL(e->dx, HORIZONTAL))
+ {
+ //nb: proceed through horizontals when approaching from their right,
+ // but break on horizontal minima if approaching from their left.
+ // This ensures 'local minima' are always on the left of horizontals.
+ if (e->next->ytop < e->ytop && e->next->xbot > e->prev->xbot) break;
+ if (e->xtop != e->prev->xbot) SwapX(*e);
+ e->nextInLML = e->prev;
+ }
+ else if (e->ycurr == e->prev->ycurr) break;
+ else e->nextInLML = e->prev;
+ e = e->next;
+ }
+
+ //e and e.prev are now at a local minima ...
+ LocalMinima* newLm = new LocalMinima;
+ newLm->next = 0;
+ newLm->Y = e->prev->ybot;
+
+ if ( NEAR_EQUAL(e->dx, HORIZONTAL) ) //horizontal edges never start a left bound
+ {
+ if (e->xbot != e->prev->xbot) SwapX(*e);
+ newLm->leftBound = e->prev;
+ newLm->rightBound = e;
+ } else if (e->dx < e->prev->dx)
+ {
+ newLm->leftBound = e->prev;
+ newLm->rightBound = e;
+ } else
+ {
+ newLm->leftBound = e;
+ newLm->rightBound = e->prev;
+ }
+ newLm->leftBound->side = esLeft;
+ newLm->rightBound->side = esRight;
+ InsertLocalMinima( newLm );
+
+ for (;;)
+ {
+ if ( e->next->ytop == e->ytop && !NEAR_EQUAL(e->next->dx, HORIZONTAL) ) break;
+ e->nextInLML = e->next;
+ e = e->next;
+ if ( NEAR_EQUAL(e->dx, HORIZONTAL) && e->xbot != e->prev->xtop) SwapX(*e);
+ }
+ return e->next;
+}
+//------------------------------------------------------------------------------
+
+bool ClipperBase::AddPolygons(const Polygons &ppg, PolyType polyType)
+{
+ bool result = false;
+ for (Polygons::size_type i = 0; i < ppg.size(); ++i)
+ if (AddPolygon(ppg[i], polyType)) result = true;
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::Clear()
+{
+ DisposeLocalMinimaList();
+ for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) delete [] m_edges[i];
+ m_edges.clear();
+ m_UseFullRange = false;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::Reset()
+{
+ m_CurrentLM = m_MinimaList;
+ if( !m_CurrentLM ) return; //ie nothing to process
+
+ //reset all edges ...
+ LocalMinima* lm = m_MinimaList;
+ while( lm )
+ {
+ TEdge* e = lm->leftBound;
+ while( e )
+ {
+ e->xcurr = e->xbot;
+ e->ycurr = e->ybot;
+ e->side = esLeft;
+ e->outIdx = -1;
+ e = e->nextInLML;
+ }
+ e = lm->rightBound;
+ while( e )
+ {
+ e->xcurr = e->xbot;
+ e->ycurr = e->ybot;
+ e->side = esRight;
+ e->outIdx = -1;
+ e = e->nextInLML;
+ }
+ lm = lm->next;
+ }
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::DisposeLocalMinimaList()
+{
+ while( m_MinimaList )
+ {
+ LocalMinima* tmpLm = m_MinimaList->next;
+ delete m_MinimaList;
+ m_MinimaList = tmpLm;
+ }
+ m_CurrentLM = 0;
+}
+//------------------------------------------------------------------------------
+
+void ClipperBase::PopLocalMinima()
+{
+ if( ! m_CurrentLM ) return;
+ m_CurrentLM = m_CurrentLM->next;
+}
+//------------------------------------------------------------------------------
+
+IntRect ClipperBase::GetBounds()
+{
+ IntRect result;
+ LocalMinima* lm = m_MinimaList;
+ if (!lm)
+ {
+ result.left = result.top = result.right = result.bottom = 0;
+ return result;
+ }
+ result.left = lm->leftBound->xbot;
+ result.top = lm->leftBound->ybot;
+ result.right = lm->leftBound->xbot;
+ result.bottom = lm->leftBound->ybot;
+ while (lm)
+ {
+ if (lm->leftBound->ybot > result.bottom)
+ result.bottom = lm->leftBound->ybot;
+ TEdge* e = lm->leftBound;
+ for (;;) {
+ TEdge* bottomE = e;
+ while (e->nextInLML)
+ {
+ if (e->xbot < result.left) result.left = e->xbot;
+ if (e->xbot > result.right) result.right = e->xbot;
+ e = e->nextInLML;
+ }
+ if (e->xbot < result.left) result.left = e->xbot;
+ if (e->xbot > result.right) result.right = e->xbot;
+ if (e->xtop < result.left) result.left = e->xtop;
+ if (e->xtop > result.right) result.right = e->xtop;
+ if (e->ytop < result.top) result.top = e->ytop;
+
+ if (bottomE == lm->leftBound) e = lm->rightBound;
+ else break;
+ }
+ lm = lm->next;
+ }
+ return result;
+}
+
+
+//------------------------------------------------------------------------------
+// TClipper methods ...
+//------------------------------------------------------------------------------
+
+Clipper::Clipper() : ClipperBase() //constructor
+{
+ m_Scanbeam = 0;
+ m_ActiveEdges = 0;
+ m_SortedEdges = 0;
+ m_IntersectNodes = 0;
+ m_ExecuteLocked = false;
+ m_UseFullRange = false;
+ m_ReverseOutput = false;
+ m_ForceSimple = false;
+}
+//------------------------------------------------------------------------------
+
+Clipper::~Clipper() //destructor
+{
+ Clear();
+ DisposeScanbeamList();
+}
+//------------------------------------------------------------------------------
+
+void Clipper::Clear()
+{
+ if (m_edges.empty()) return; //avoids problems with ClipperBase destructor
+ DisposeAllPolyPts();
+ ClipperBase::Clear();
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DisposeScanbeamList()
+{
+ while ( m_Scanbeam ) {
+ Scanbeam* sb2 = m_Scanbeam->next;
+ delete m_Scanbeam;
+ m_Scanbeam = sb2;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::Reset()
+{
+ ClipperBase::Reset();
+ m_Scanbeam = 0;
+ m_ActiveEdges = 0;
+ m_SortedEdges = 0;
+ DisposeAllPolyPts();
+ LocalMinima* lm = m_MinimaList;
+ while (lm)
+ {
+ InsertScanbeam(lm->Y);
+ lm = lm->next;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, Polygons &solution,
+ PolyFillType subjFillType, PolyFillType clipFillType)
+{
+ if( m_ExecuteLocked ) return false;
+ m_ExecuteLocked = true;
+ solution.resize(0);
+ m_SubjFillType = subjFillType;
+ m_ClipFillType = clipFillType;
+ m_ClipType = clipType;
+ m_UsingPolyTree = false;
+ bool succeeded = ExecuteInternal();
+ if (succeeded) BuildResult(solution);
+ m_ExecuteLocked = false;
+ return succeeded;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::Execute(ClipType clipType, PolyTree& polytree,
+ PolyFillType subjFillType, PolyFillType clipFillType)
+{
+ if( m_ExecuteLocked ) return false;
+ m_ExecuteLocked = true;
+ m_SubjFillType = subjFillType;
+ m_ClipFillType = clipFillType;
+ m_ClipType = clipType;
+ m_UsingPolyTree = true;
+ bool succeeded = ExecuteInternal();
+ if (succeeded) BuildResult2(polytree);
+ m_ExecuteLocked = false;
+ return succeeded;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixHoleLinkage(OutRec &outrec)
+{
+ //skip OutRecs that (a) contain outermost polygons or
+ //(b) already have the correct owner/child linkage ...
+ if (!outrec.FirstLeft ||
+ (outrec.isHole != outrec.FirstLeft->isHole &&
+ outrec.FirstLeft->pts)) return;
+
+ OutRec* orfl = outrec.FirstLeft;
+ while (orfl && ((orfl->isHole == outrec.isHole) || !orfl->pts))
+ orfl = orfl->FirstLeft;
+ outrec.FirstLeft = orfl;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::ExecuteInternal()
+{
+ bool succeeded;
+ try {
+ Reset();
+ if (!m_CurrentLM ) return true;
+ long64 botY = PopScanbeam();
+ do {
+ InsertLocalMinimaIntoAEL(botY);
+ ClearHorzJoins();
+ ProcessHorizontals();
+ long64 topY = PopScanbeam();
+ succeeded = ProcessIntersections(botY, topY);
+ if (!succeeded) break;
+ ProcessEdgesAtTopOfScanbeam(topY);
+ botY = topY;
+ } while(m_Scanbeam || m_CurrentLM);
+ }
+ catch(...) {
+ succeeded = false;
+ }
+
+ if (succeeded)
+ {
+ //tidy up output polygons and fix orientations where necessary ...
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec *outRec = m_PolyOuts[i];
+ if (!outRec->pts) continue;
+ FixupOutPolygon(*outRec);
+ if (!outRec->pts) continue;
+
+ if ((outRec->isHole ^ m_ReverseOutput) == (Area(*outRec, m_UseFullRange) > 0))
+ ReversePolyPtLinks(outRec->pts);
+ }
+
+ if (!m_Joins.empty()) JoinCommonEdges();
+ if (m_ForceSimple) DoSimplePolygons();
+ }
+
+ ClearJoins();
+ ClearHorzJoins();
+ return succeeded;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertScanbeam(const long64 Y)
+{
+ if( !m_Scanbeam )
+ {
+ m_Scanbeam = new Scanbeam;
+ m_Scanbeam->next = 0;
+ m_Scanbeam->Y = Y;
+ }
+ else if( Y > m_Scanbeam->Y )
+ {
+ Scanbeam* newSb = new Scanbeam;
+ newSb->Y = Y;
+ newSb->next = m_Scanbeam;
+ m_Scanbeam = newSb;
+ } else
+ {
+ Scanbeam* sb2 = m_Scanbeam;
+ while( sb2->next && ( Y <= sb2->next->Y ) ) sb2 = sb2->next;
+ if( Y == sb2->Y ) return; //ie ignores duplicates
+ Scanbeam* newSb = new Scanbeam;
+ newSb->Y = Y;
+ newSb->next = sb2->next;
+ sb2->next = newSb;
+ }
+}
+//------------------------------------------------------------------------------
+
+long64 Clipper::PopScanbeam()
+{
+ long64 Y = m_Scanbeam->Y;
+ Scanbeam* sb2 = m_Scanbeam;
+ m_Scanbeam = m_Scanbeam->next;
+ delete sb2;
+ return Y;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DisposeAllPolyPts(){
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ DisposeOutRec(i);
+ m_PolyOuts.clear();
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DisposeOutRec(PolyOutList::size_type index)
+{
+ OutRec *outRec = m_PolyOuts[index];
+ if (outRec->pts) DisposeOutPts(outRec->pts);
+ delete outRec;
+ m_PolyOuts[index] = 0;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SetWindingCount(TEdge &edge)
+{
+ TEdge *e = edge.prevInAEL;
+ //find the edge of the same polytype that immediately preceeds 'edge' in AEL
+ while ( e && e->polyType != edge.polyType ) e = e->prevInAEL;
+ if ( !e )
+ {
+ edge.windCnt = edge.windDelta;
+ edge.windCnt2 = 0;
+ e = m_ActiveEdges; //ie get ready to calc windCnt2
+ } else if ( IsEvenOddFillType(edge) )
+ {
+ //EvenOdd filling ...
+ edge.windCnt = 1;
+ edge.windCnt2 = e->windCnt2;
+ e = e->nextInAEL; //ie get ready to calc windCnt2
+ } else
+ {
+ //nonZero, Positive or Negative filling ...
+ if ( e->windCnt * e->windDelta < 0 )
+ {
+ if (Abs(e->windCnt) > 1)
+ {
+ if (e->windDelta * edge.windDelta < 0) edge.windCnt = e->windCnt;
+ else edge.windCnt = e->windCnt + edge.windDelta;
+ } else
+ edge.windCnt = e->windCnt + e->windDelta + edge.windDelta;
+ } else
+ {
+ if ( Abs(e->windCnt) > 1 && e->windDelta * edge.windDelta < 0)
+ edge.windCnt = e->windCnt;
+ else if ( e->windCnt + edge.windDelta == 0 )
+ edge.windCnt = e->windCnt;
+ else edge.windCnt = e->windCnt + edge.windDelta;
+ }
+ edge.windCnt2 = e->windCnt2;
+ e = e->nextInAEL; //ie get ready to calc windCnt2
+ }
+
+ //update windCnt2 ...
+ if ( IsEvenOddAltFillType(edge) )
+ {
+ //EvenOdd filling ...
+ while ( e != &edge )
+ {
+ edge.windCnt2 = (edge.windCnt2 == 0) ? 1 : 0;
+ e = e->nextInAEL;
+ }
+ } else
+ {
+ //nonZero, Positive or Negative filling ...
+ while ( e != &edge )
+ {
+ edge.windCnt2 += e->windDelta;
+ e = e->nextInAEL;
+ }
+ }
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsEvenOddFillType(const TEdge& edge) const
+{
+ if (edge.polyType == ptSubject)
+ return m_SubjFillType == pftEvenOdd; else
+ return m_ClipFillType == pftEvenOdd;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const
+{
+ if (edge.polyType == ptSubject)
+ return m_ClipFillType == pftEvenOdd; else
+ return m_SubjFillType == pftEvenOdd;
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsContributing(const TEdge& edge) const
+{
+ PolyFillType pft, pft2;
+ if (edge.polyType == ptSubject)
+ {
+ pft = m_SubjFillType;
+ pft2 = m_ClipFillType;
+ } else
+ {
+ pft = m_ClipFillType;
+ pft2 = m_SubjFillType;
+ }
+
+ switch(pft)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ if (Abs(edge.windCnt) != 1) return false;
+ break;
+ case pftPositive:
+ if (edge.windCnt != 1) return false;
+ break;
+ default: //pftNegative
+ if (edge.windCnt != -1) return false;
+ }
+
+ switch(m_ClipType)
+ {
+ case ctIntersection:
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.windCnt2 != 0);
+ case pftPositive:
+ return (edge.windCnt2 > 0);
+ default:
+ return (edge.windCnt2 < 0);
+ }
+ break;
+ case ctUnion:
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.windCnt2 == 0);
+ case pftPositive:
+ return (edge.windCnt2 <= 0);
+ default:
+ return (edge.windCnt2 >= 0);
+ }
+ break;
+ case ctDifference:
+ if (edge.polyType == ptSubject)
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.windCnt2 == 0);
+ case pftPositive:
+ return (edge.windCnt2 <= 0);
+ default:
+ return (edge.windCnt2 >= 0);
+ }
+ else
+ switch(pft2)
+ {
+ case pftEvenOdd:
+ case pftNonZero:
+ return (edge.windCnt2 != 0);
+ case pftPositive:
+ return (edge.windCnt2 > 0);
+ default:
+ return (edge.windCnt2 < 0);
+ }
+ break;
+ default:
+ return true;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt)
+{
+ TEdge *e, *prevE;
+ if( NEAR_EQUAL(e2->dx, HORIZONTAL) || ( e1->dx > e2->dx ) )
+ {
+ AddOutPt( e1, pt );
+ e2->outIdx = e1->outIdx;
+ e1->side = esLeft;
+ e2->side = esRight;
+ e = e1;
+ if (e->prevInAEL == e2)
+ prevE = e2->prevInAEL;
+ else
+ prevE = e->prevInAEL;
+ } else
+ {
+ AddOutPt( e2, pt );
+ e1->outIdx = e2->outIdx;
+ e1->side = esRight;
+ e2->side = esLeft;
+ e = e2;
+ if (e->prevInAEL == e1)
+ prevE = e1->prevInAEL;
+ else
+ prevE = e->prevInAEL;
+ }
+ if (prevE && prevE->outIdx >= 0 &&
+ (TopX(*prevE, pt.Y) == TopX(*e, pt.Y)) &&
+ SlopesEqual(*e, *prevE, m_UseFullRange))
+ AddJoin(e, prevE, -1, -1);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt)
+{
+ AddOutPt( e1, pt );
+ if( e1->outIdx == e2->outIdx )
+ {
+ e1->outIdx = -1;
+ e2->outIdx = -1;
+ }
+ else if (e1->outIdx < e2->outIdx)
+ AppendPolygon(e1, e2);
+ else
+ AppendPolygon(e2, e1);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddEdgeToSEL(TEdge *edge)
+{
+ //SEL pointers in PEdge are reused to build a list of horizontal edges.
+ //However, we don't need to worry about order with horizontal edge processing.
+ if( !m_SortedEdges )
+ {
+ m_SortedEdges = edge;
+ edge->prevInSEL = 0;
+ edge->nextInSEL = 0;
+ }
+ else
+ {
+ edge->nextInSEL = m_SortedEdges;
+ edge->prevInSEL = 0;
+ m_SortedEdges->prevInSEL = edge;
+ m_SortedEdges = edge;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::CopyAELToSEL()
+{
+ TEdge* e = m_ActiveEdges;
+ m_SortedEdges = e;
+ while ( e )
+ {
+ e->prevInSEL = e->prevInAEL;
+ e->nextInSEL = e->nextInAEL;
+ e = e->nextInAEL;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx, int e2OutIdx)
+{
+ JoinRec* jr = new JoinRec;
+ if (e1OutIdx >= 0)
+ jr->poly1Idx = e1OutIdx; else
+ jr->poly1Idx = e1->outIdx;
+ jr->pt1a = IntPoint(e1->xcurr, e1->ycurr);
+ jr->pt1b = IntPoint(e1->xtop, e1->ytop);
+ if (e2OutIdx >= 0)
+ jr->poly2Idx = e2OutIdx; else
+ jr->poly2Idx = e2->outIdx;
+ jr->pt2a = IntPoint(e2->xcurr, e2->ycurr);
+ jr->pt2b = IntPoint(e2->xtop, e2->ytop);
+ m_Joins.push_back(jr);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ClearJoins()
+{
+ for (JoinList::size_type i = 0; i < m_Joins.size(); i++)
+ delete m_Joins[i];
+ m_Joins.resize(0);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddHorzJoin(TEdge *e, int idx)
+{
+ HorzJoinRec* hj = new HorzJoinRec;
+ hj->edge = e;
+ hj->savedIdx = idx;
+ m_HorizJoins.push_back(hj);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ClearHorzJoins()
+{
+ for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); i++)
+ delete m_HorizJoins[i];
+ m_HorizJoins.resize(0);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertLocalMinimaIntoAEL(const long64 botY)
+{
+ while( m_CurrentLM && ( m_CurrentLM->Y == botY ) )
+ {
+ TEdge* lb = m_CurrentLM->leftBound;
+ TEdge* rb = m_CurrentLM->rightBound;
+
+ InsertEdgeIntoAEL( lb );
+ InsertScanbeam( lb->ytop );
+ InsertEdgeIntoAEL( rb );
+
+ if (IsEvenOddFillType(*lb))
+ {
+ lb->windDelta = 1;
+ rb->windDelta = 1;
+ }
+ else
+ {
+ rb->windDelta = -lb->windDelta;
+ }
+ SetWindingCount( *lb );
+ rb->windCnt = lb->windCnt;
+ rb->windCnt2 = lb->windCnt2;
+
+ if( NEAR_EQUAL(rb->dx, HORIZONTAL) )
+ {
+ //nb: only rightbounds can have a horizontal bottom edge
+ AddEdgeToSEL( rb );
+ InsertScanbeam( rb->nextInLML->ytop );
+ }
+ else
+ InsertScanbeam( rb->ytop );
+
+ if( IsContributing(*lb) )
+ AddLocalMinPoly( lb, rb, IntPoint(lb->xcurr, m_CurrentLM->Y) );
+
+ //if any output polygons share an edge, they'll need joining later ...
+ if (rb->outIdx >= 0 && NEAR_EQUAL(rb->dx, HORIZONTAL))
+ {
+ for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i)
+ {
+ IntPoint pt, pt2; //returned by GetOverlapSegment() but unused here.
+ HorzJoinRec* hj = m_HorizJoins[i];
+ //if horizontals rb and hj.edge overlap, flag for joining later ...
+ if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot),
+ IntPoint(hj->edge->xtop, hj->edge->ytop),
+ IntPoint(rb->xbot, rb->ybot),
+ IntPoint(rb->xtop, rb->ytop), pt, pt2))
+ AddJoin(hj->edge, rb, hj->savedIdx);
+ }
+ }
+
+ if( lb->nextInAEL != rb )
+ {
+ if (rb->outIdx >= 0 && rb->prevInAEL->outIdx >= 0 &&
+ SlopesEqual(*rb->prevInAEL, *rb, m_UseFullRange))
+ AddJoin(rb, rb->prevInAEL);
+
+ TEdge* e = lb->nextInAEL;
+ IntPoint pt = IntPoint(lb->xcurr, lb->ycurr);
+ while( e != rb )
+ {
+ if(!e) throw clipperException("InsertLocalMinimaIntoAEL: missing rightbound!");
+ //nb: For calculating winding counts etc, IntersectEdges() assumes
+ //that param1 will be to the right of param2 ABOVE the intersection ...
+ IntersectEdges( rb , e , pt , ipNone); //order important here
+ e = e->nextInAEL;
+ }
+ }
+ PopLocalMinima();
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DeleteFromAEL(TEdge *e)
+{
+ TEdge* AelPrev = e->prevInAEL;
+ TEdge* AelNext = e->nextInAEL;
+ if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted
+ if( AelPrev ) AelPrev->nextInAEL = AelNext;
+ else m_ActiveEdges = AelNext;
+ if( AelNext ) AelNext->prevInAEL = AelPrev;
+ e->nextInAEL = 0;
+ e->prevInAEL = 0;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DeleteFromSEL(TEdge *e)
+{
+ TEdge* SelPrev = e->prevInSEL;
+ TEdge* SelNext = e->nextInSEL;
+ if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted
+ if( SelPrev ) SelPrev->nextInSEL = SelNext;
+ else m_SortedEdges = SelNext;
+ if( SelNext ) SelNext->prevInSEL = SelPrev;
+ e->nextInSEL = 0;
+ e->prevInSEL = 0;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::IntersectEdges(TEdge *e1, TEdge *e2,
+ const IntPoint &pt, const IntersectProtects protects)
+{
+ //e1 will be to the left of e2 BELOW the intersection. Therefore e1 is before
+ //e2 in AEL except when e1 is being inserted at the intersection point ...
+ bool e1stops = !(ipLeft & protects) && !e1->nextInLML &&
+ e1->xtop == pt.X && e1->ytop == pt.Y;
+ bool e2stops = !(ipRight & protects) && !e2->nextInLML &&
+ e2->xtop == pt.X && e2->ytop == pt.Y;
+ bool e1Contributing = ( e1->outIdx >= 0 );
+ bool e2contributing = ( e2->outIdx >= 0 );
+
+ //update winding counts...
+ //assumes that e1 will be to the right of e2 ABOVE the intersection
+ if ( e1->polyType == e2->polyType )
+ {
+ if ( IsEvenOddFillType( *e1) )
+ {
+ int oldE1WindCnt = e1->windCnt;
+ e1->windCnt = e2->windCnt;
+ e2->windCnt = oldE1WindCnt;
+ } else
+ {
+ if (e1->windCnt + e2->windDelta == 0 ) e1->windCnt = -e1->windCnt;
+ else e1->windCnt += e2->windDelta;
+ if ( e2->windCnt - e1->windDelta == 0 ) e2->windCnt = -e2->windCnt;
+ else e2->windCnt -= e1->windDelta;
+ }
+ } else
+ {
+ if (!IsEvenOddFillType(*e2)) e1->windCnt2 += e2->windDelta;
+ else e1->windCnt2 = ( e1->windCnt2 == 0 ) ? 1 : 0;
+ if (!IsEvenOddFillType(*e1)) e2->windCnt2 -= e1->windDelta;
+ else e2->windCnt2 = ( e2->windCnt2 == 0 ) ? 1 : 0;
+ }
+
+ PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2;
+ if (e1->polyType == ptSubject)
+ {
+ e1FillType = m_SubjFillType;
+ e1FillType2 = m_ClipFillType;
+ } else
+ {
+ e1FillType = m_ClipFillType;
+ e1FillType2 = m_SubjFillType;
+ }
+ if (e2->polyType == ptSubject)
+ {
+ e2FillType = m_SubjFillType;
+ e2FillType2 = m_ClipFillType;
+ } else
+ {
+ e2FillType = m_ClipFillType;
+ e2FillType2 = m_SubjFillType;
+ }
+
+ long64 e1Wc, e2Wc;
+ switch (e1FillType)
+ {
+ case pftPositive: e1Wc = e1->windCnt; break;
+ case pftNegative: e1Wc = -e1->windCnt; break;
+ default: e1Wc = Abs(e1->windCnt);
+ }
+ switch(e2FillType)
+ {
+ case pftPositive: e2Wc = e2->windCnt; break;
+ case pftNegative: e2Wc = -e2->windCnt; break;
+ default: e2Wc = Abs(e2->windCnt);
+ }
+
+ if ( e1Contributing && e2contributing )
+ {
+ if ( e1stops || e2stops ||
+ (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
+ (e1->polyType != e2->polyType && m_ClipType != ctXor) )
+ AddLocalMaxPoly(e1, e2, pt);
+ else
+ {
+ AddOutPt(e1, pt);
+ AddOutPt(e2, pt);
+ SwapSides( *e1 , *e2 );
+ SwapPolyIndexes( *e1 , *e2 );
+ }
+ }
+ else if ( e1Contributing )
+ {
+ if (e2Wc == 0 || e2Wc == 1)
+ {
+ AddOutPt(e1, pt);
+ SwapSides(*e1, *e2);
+ SwapPolyIndexes(*e1, *e2);
+ }
+ }
+ else if ( e2contributing )
+ {
+ if (e1Wc == 0 || e1Wc == 1)
+ {
+ AddOutPt(e2, pt);
+ SwapSides(*e1, *e2);
+ SwapPolyIndexes(*e1, *e2);
+ }
+ }
+ else if ( (e1Wc == 0 || e1Wc == 1) &&
+ (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops )
+ {
+ //neither edge is currently contributing ...
+
+ long64 e1Wc2, e2Wc2;
+ switch (e1FillType2)
+ {
+ case pftPositive: e1Wc2 = e1->windCnt2; break;
+ case pftNegative : e1Wc2 = -e1->windCnt2; break;
+ default: e1Wc2 = Abs(e1->windCnt2);
+ }
+ switch (e2FillType2)
+ {
+ case pftPositive: e2Wc2 = e2->windCnt2; break;
+ case pftNegative: e2Wc2 = -e2->windCnt2; break;
+ default: e2Wc2 = Abs(e2->windCnt2);
+ }
+
+ if (e1->polyType != e2->polyType)
+ AddLocalMinPoly(e1, e2, pt);
+ else if (e1Wc == 1 && e2Wc == 1)
+ switch( m_ClipType ) {
+ case ctIntersection:
+ if (e1Wc2 > 0 && e2Wc2 > 0)
+ AddLocalMinPoly(e1, e2, pt);
+ break;
+ case ctUnion:
+ if ( e1Wc2 <= 0 && e2Wc2 <= 0 )
+ AddLocalMinPoly(e1, e2, pt);
+ break;
+ case ctDifference:
+ if (((e1->polyType == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) ||
+ ((e1->polyType == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0)))
+ AddLocalMinPoly(e1, e2, pt);
+ break;
+ case ctXor:
+ AddLocalMinPoly(e1, e2, pt);
+ }
+ else
+ SwapSides( *e1, *e2 );
+ }
+
+ if( (e1stops != e2stops) &&
+ ( (e1stops && (e1->outIdx >= 0)) || (e2stops && (e2->outIdx >= 0)) ) )
+ {
+ SwapSides( *e1, *e2 );
+ SwapPolyIndexes( *e1, *e2 );
+ }
+
+ //finally, delete any non-contributing maxima edges ...
+ if( e1stops ) DeleteFromAEL( e1 );
+ if( e2stops ) DeleteFromAEL( e2 );
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SetHoleState(TEdge *e, OutRec *outrec)
+{
+ bool isHole = false;
+ TEdge *e2 = e->prevInAEL;
+ while (e2)
+ {
+ if (e2->outIdx >= 0)
+ {
+ isHole = !isHole;
+ if (! outrec->FirstLeft)
+ outrec->FirstLeft = m_PolyOuts[e2->outIdx];
+ }
+ e2 = e2->prevInAEL;
+ }
+ if (isHole) outrec->isHole = true;
+}
+//------------------------------------------------------------------------------
+
+OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2)
+{
+ //work out which polygon fragment has the correct hole state ...
+ if (!outRec1->bottomPt)
+ outRec1->bottomPt = GetBottomPt(outRec1->pts);
+ if (!outRec2->bottomPt)
+ outRec2->bottomPt = GetBottomPt(outRec2->pts);
+ OutPt *outPt1 = outRec1->bottomPt;
+ OutPt *outPt2 = outRec2->bottomPt;
+ if (outPt1->pt.Y > outPt2->pt.Y) return outRec1;
+ else if (outPt1->pt.Y < outPt2->pt.Y) return outRec2;
+ else if (outPt1->pt.X < outPt2->pt.X) return outRec1;
+ else if (outPt1->pt.X > outPt2->pt.X) return outRec2;
+ else if (outPt1->next == outPt1) return outRec2;
+ else if (outPt2->next == outPt2) return outRec1;
+ else if (FirstIsBottomPt(outPt1, outPt2)) return outRec1;
+ else return outRec2;
+}
+//------------------------------------------------------------------------------
+
+bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2)
+{
+ do
+ {
+ outRec1 = outRec1->FirstLeft;
+ if (outRec1 == outRec2) return true;
+ } while (outRec1);
+ return false;
+}
+//------------------------------------------------------------------------------
+
+OutRec* Clipper::GetOutRec(int idx)
+{
+ OutRec* outrec = m_PolyOuts[idx];
+ while (outrec != m_PolyOuts[outrec->idx])
+ outrec = m_PolyOuts[outrec->idx];
+ return outrec;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AppendPolygon(TEdge *e1, TEdge *e2)
+{
+ //get the start and ends of both output polygons ...
+ OutRec *outRec1 = m_PolyOuts[e1->outIdx];
+ OutRec *outRec2 = m_PolyOuts[e2->outIdx];
+
+ OutRec *holeStateRec;
+ if (Param1RightOfParam2(outRec1, outRec2))
+ holeStateRec = outRec2;
+ else if (Param1RightOfParam2(outRec2, outRec1))
+ holeStateRec = outRec1;
+ else
+ holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+ OutPt* p1_lft = outRec1->pts;
+ OutPt* p1_rt = p1_lft->prev;
+ OutPt* p2_lft = outRec2->pts;
+ OutPt* p2_rt = p2_lft->prev;
+
+ EdgeSide side;
+ //join e2 poly onto e1 poly and delete pointers to e2 ...
+ if( e1->side == esLeft )
+ {
+ if( e2->side == esLeft )
+ {
+ //z y x a b c
+ ReversePolyPtLinks(p2_lft);
+ p2_lft->next = p1_lft;
+ p1_lft->prev = p2_lft;
+ p1_rt->next = p2_rt;
+ p2_rt->prev = p1_rt;
+ outRec1->pts = p2_rt;
+ } else
+ {
+ //x y z a b c
+ p2_rt->next = p1_lft;
+ p1_lft->prev = p2_rt;
+ p2_lft->prev = p1_rt;
+ p1_rt->next = p2_lft;
+ outRec1->pts = p2_lft;
+ }
+ side = esLeft;
+ } else
+ {
+ if( e2->side == esRight )
+ {
+ //a b c z y x
+ ReversePolyPtLinks(p2_lft);
+ p1_rt->next = p2_rt;
+ p2_rt->prev = p1_rt;
+ p2_lft->next = p1_lft;
+ p1_lft->prev = p2_lft;
+ } else
+ {
+ //a b c x y z
+ p1_rt->next = p2_lft;
+ p2_lft->prev = p1_rt;
+ p1_lft->prev = p2_rt;
+ p2_rt->next = p1_lft;
+ }
+ side = esRight;
+ }
+
+ outRec1->bottomPt = 0;
+ if (holeStateRec == outRec2)
+ {
+ if (outRec2->FirstLeft != outRec1)
+ outRec1->FirstLeft = outRec2->FirstLeft;
+ outRec1->isHole = outRec2->isHole;
+ }
+ outRec2->pts = 0;
+ outRec2->bottomPt = 0;
+
+ outRec2->FirstLeft = outRec1;
+
+ int OKIdx = e1->outIdx;
+ int ObsoleteIdx = e2->outIdx;
+
+ e1->outIdx = -1; //nb: safe because we only get here via AddLocalMaxPoly
+ e2->outIdx = -1;
+
+ TEdge* e = m_ActiveEdges;
+ while( e )
+ {
+ if( e->outIdx == ObsoleteIdx )
+ {
+ e->outIdx = OKIdx;
+ e->side = side;
+ break;
+ }
+ e = e->nextInAEL;
+ }
+
+ outRec2->idx = outRec1->idx;
+}
+//------------------------------------------------------------------------------
+
+OutRec* Clipper::CreateOutRec()
+{
+ OutRec* result = new OutRec;
+ result->isHole = false;
+ result->FirstLeft = 0;
+ result->pts = 0;
+ result->bottomPt = 0;
+ result->polyNode = 0;
+ m_PolyOuts.push_back(result);
+ result->idx = (int)m_PolyOuts.size()-1;
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
+{
+ bool ToFront = (e->side == esLeft);
+ if( e->outIdx < 0 )
+ {
+ OutRec *outRec = CreateOutRec();
+ e->outIdx = outRec->idx;
+ OutPt* newOp = new OutPt;
+ outRec->pts = newOp;
+ newOp->pt = pt;
+ newOp->idx = outRec->idx;
+ newOp->next = newOp;
+ newOp->prev = newOp;
+ SetHoleState(e, outRec);
+ } else
+ {
+ OutRec *outRec = m_PolyOuts[e->outIdx];
+ OutPt* op = outRec->pts;
+ if ((ToFront && PointsEqual(pt, op->pt)) ||
+ (!ToFront && PointsEqual(pt, op->prev->pt))) return;
+
+ OutPt* newOp = new OutPt;
+ newOp->pt = pt;
+ newOp->idx = outRec->idx;
+ newOp->next = op;
+ newOp->prev = op->prev;
+ newOp->prev->next = newOp;
+ op->prev = newOp;
+ if (ToFront) outRec->pts = newOp;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ProcessHorizontals()
+{
+ TEdge* horzEdge = m_SortedEdges;
+ while( horzEdge )
+ {
+ DeleteFromSEL( horzEdge );
+ ProcessHorizontal( horzEdge );
+ horzEdge = m_SortedEdges;
+ }
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::IsTopHorz(const long64 XPos)
+{
+ TEdge* e = m_SortedEdges;
+ while( e )
+ {
+ if( ( XPos >= std::min(e->xcurr, e->xtop) ) &&
+ ( XPos <= std::max(e->xcurr, e->xtop) ) ) return false;
+ e = e->nextInSEL;
+ }
+ return true;
+}
+//------------------------------------------------------------------------------
+
+inline bool IsMinima(TEdge *e)
+{
+ return e && (e->prev->nextInLML != e) && (e->next->nextInLML != e);
+}
+//------------------------------------------------------------------------------
+
+inline bool IsMaxima(TEdge *e, const long64 Y)
+{
+ return e && e->ytop == Y && !e->nextInLML;
+}
+//------------------------------------------------------------------------------
+
+inline bool IsIntermediate(TEdge *e, const long64 Y)
+{
+ return e->ytop == Y && e->nextInLML;
+}
+//------------------------------------------------------------------------------
+
+TEdge *GetMaximaPair(TEdge *e)
+{
+ if( !IsMaxima(e->next, e->ytop) || e->next->xtop != e->xtop )
+ return e->prev; else
+ return e->next;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SwapPositionsInAEL(TEdge *edge1, TEdge *edge2)
+{
+ if( edge1->nextInAEL == edge2 )
+ {
+ TEdge* next = edge2->nextInAEL;
+ if( next ) next->prevInAEL = edge1;
+ TEdge* prev = edge1->prevInAEL;
+ if( prev ) prev->nextInAEL = edge2;
+ edge2->prevInAEL = prev;
+ edge2->nextInAEL = edge1;
+ edge1->prevInAEL = edge2;
+ edge1->nextInAEL = next;
+ }
+ else if( edge2->nextInAEL == edge1 )
+ {
+ TEdge* next = edge1->nextInAEL;
+ if( next ) next->prevInAEL = edge2;
+ TEdge* prev = edge2->prevInAEL;
+ if( prev ) prev->nextInAEL = edge1;
+ edge1->prevInAEL = prev;
+ edge1->nextInAEL = edge2;
+ edge2->prevInAEL = edge1;
+ edge2->nextInAEL = next;
+ }
+ else
+ {
+ TEdge* next = edge1->nextInAEL;
+ TEdge* prev = edge1->prevInAEL;
+ edge1->nextInAEL = edge2->nextInAEL;
+ if( edge1->nextInAEL ) edge1->nextInAEL->prevInAEL = edge1;
+ edge1->prevInAEL = edge2->prevInAEL;
+ if( edge1->prevInAEL ) edge1->prevInAEL->nextInAEL = edge1;
+ edge2->nextInAEL = next;
+ if( edge2->nextInAEL ) edge2->nextInAEL->prevInAEL = edge2;
+ edge2->prevInAEL = prev;
+ if( edge2->prevInAEL ) edge2->prevInAEL->nextInAEL = edge2;
+ }
+
+ if( !edge1->prevInAEL ) m_ActiveEdges = edge1;
+ else if( !edge2->prevInAEL ) m_ActiveEdges = edge2;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::SwapPositionsInSEL(TEdge *edge1, TEdge *edge2)
+{
+ if( !( edge1->nextInSEL ) && !( edge1->prevInSEL ) ) return;
+ if( !( edge2->nextInSEL ) && !( edge2->prevInSEL ) ) return;
+
+ if( edge1->nextInSEL == edge2 )
+ {
+ TEdge* next = edge2->nextInSEL;
+ if( next ) next->prevInSEL = edge1;
+ TEdge* prev = edge1->prevInSEL;
+ if( prev ) prev->nextInSEL = edge2;
+ edge2->prevInSEL = prev;
+ edge2->nextInSEL = edge1;
+ edge1->prevInSEL = edge2;
+ edge1->nextInSEL = next;
+ }
+ else if( edge2->nextInSEL == edge1 )
+ {
+ TEdge* next = edge1->nextInSEL;
+ if( next ) next->prevInSEL = edge2;
+ TEdge* prev = edge2->prevInSEL;
+ if( prev ) prev->nextInSEL = edge1;
+ edge1->prevInSEL = prev;
+ edge1->nextInSEL = edge2;
+ edge2->prevInSEL = edge1;
+ edge2->nextInSEL = next;
+ }
+ else
+ {
+ TEdge* next = edge1->nextInSEL;
+ TEdge* prev = edge1->prevInSEL;
+ edge1->nextInSEL = edge2->nextInSEL;
+ if( edge1->nextInSEL ) edge1->nextInSEL->prevInSEL = edge1;
+ edge1->prevInSEL = edge2->prevInSEL;
+ if( edge1->prevInSEL ) edge1->prevInSEL->nextInSEL = edge1;
+ edge2->nextInSEL = next;
+ if( edge2->nextInSEL ) edge2->nextInSEL->prevInSEL = edge2;
+ edge2->prevInSEL = prev;
+ if( edge2->prevInSEL ) edge2->prevInSEL->nextInSEL = edge2;
+ }
+
+ if( !edge1->prevInSEL ) m_SortedEdges = edge1;
+ else if( !edge2->prevInSEL ) m_SortedEdges = edge2;
+}
+//------------------------------------------------------------------------------
+
+TEdge* GetNextInAEL(TEdge *e, Direction dir)
+{
+ return dir == dLeftToRight ? e->nextInAEL : e->prevInAEL;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ProcessHorizontal(TEdge *horzEdge)
+{
+ Direction dir;
+ long64 horzLeft, horzRight;
+
+ if( horzEdge->xcurr < horzEdge->xtop )
+ {
+ horzLeft = horzEdge->xcurr;
+ horzRight = horzEdge->xtop;
+ dir = dLeftToRight;
+ } else
+ {
+ horzLeft = horzEdge->xtop;
+ horzRight = horzEdge->xcurr;
+ dir = dRightToLeft;
+ }
+
+ TEdge* eMaxPair;
+ if( horzEdge->nextInLML ) eMaxPair = 0;
+ else eMaxPair = GetMaximaPair(horzEdge);
+
+ TEdge* e = GetNextInAEL( horzEdge , dir );
+ while( e )
+ {
+ if ( e->xcurr == horzEdge->xtop && !eMaxPair )
+ {
+ if (SlopesEqual(*e, *horzEdge->nextInLML, m_UseFullRange))
+ {
+ //if output polygons share an edge, they'll need joining later ...
+ if (horzEdge->outIdx >= 0 && e->outIdx >= 0)
+ AddJoin(horzEdge->nextInLML, e, horzEdge->outIdx);
+ break; //we've reached the end of the horizontal line
+ }
+ else if (e->dx < horzEdge->nextInLML->dx)
+ //we really have got to the end of the intermediate horz edge so quit.
+ //nb: More -ve slopes follow more +ve slopes ABOVE the horizontal.
+ break;
+ }
+
+ TEdge* eNext = GetNextInAEL( e, dir );
+
+ if (eMaxPair ||
+ ((dir == dLeftToRight) && (e->xcurr < horzRight)) ||
+ ((dir == dRightToLeft) && (e->xcurr > horzLeft)))
+ {
+ //so far we're still in range of the horizontal edge
+ if( e == eMaxPair )
+ {
+ //horzEdge is evidently a maxima horizontal and we've arrived at its end.
+ if (dir == dLeftToRight)
+ IntersectEdges(horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr), ipNone);
+ else
+ IntersectEdges(e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr), ipNone);
+ if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error");
+ return;
+ }
+ else if( NEAR_EQUAL(e->dx, HORIZONTAL) && !IsMinima(e) && !(e->xcurr > e->xtop) )
+ {
+ //An overlapping horizontal edge. Overlapping horizontal edges are
+ //processed as if layered with the current horizontal edge (horizEdge)
+ //being infinitesimally lower that the next (e). Therfore, we
+ //intersect with e only if e.xcurr is within the bounds of horzEdge ...
+ if( dir == dLeftToRight )
+ IntersectEdges( horzEdge , e, IntPoint(e->xcurr, horzEdge->ycurr),
+ (IsTopHorz( e->xcurr ))? ipLeft : ipBoth );
+ else
+ IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr),
+ (IsTopHorz( e->xcurr ))? ipRight : ipBoth );
+ }
+ else if( dir == dLeftToRight )
+ {
+ IntersectEdges( horzEdge, e, IntPoint(e->xcurr, horzEdge->ycurr),
+ (IsTopHorz( e->xcurr ))? ipLeft : ipBoth );
+ }
+ else
+ {
+ IntersectEdges( e, horzEdge, IntPoint(e->xcurr, horzEdge->ycurr),
+ (IsTopHorz( e->xcurr ))? ipRight : ipBoth );
+ }
+ SwapPositionsInAEL( horzEdge, e );
+ }
+ else if( (dir == dLeftToRight && e->xcurr >= horzRight) ||
+ (dir == dRightToLeft && e->xcurr <= horzLeft) ) break;
+ e = eNext;
+ } //end while
+
+ if( horzEdge->nextInLML )
+ {
+ if( horzEdge->outIdx >= 0 )
+ AddOutPt( horzEdge, IntPoint(horzEdge->xtop, horzEdge->ytop));
+ UpdateEdgeIntoAEL( horzEdge );
+ }
+ else
+ {
+ if ( horzEdge->outIdx >= 0 )
+ IntersectEdges( horzEdge, eMaxPair,
+ IntPoint(horzEdge->xtop, horzEdge->ycurr), ipBoth);
+ if (eMaxPair->outIdx >= 0) throw clipperException("ProcessHorizontal error");
+ DeleteFromAEL(eMaxPair);
+ DeleteFromAEL(horzEdge);
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::UpdateEdgeIntoAEL(TEdge *&e)
+{
+ if( !e->nextInLML ) throw
+ clipperException("UpdateEdgeIntoAEL: invalid call");
+ TEdge* AelPrev = e->prevInAEL;
+ TEdge* AelNext = e->nextInAEL;
+ e->nextInLML->outIdx = e->outIdx;
+ if( AelPrev ) AelPrev->nextInAEL = e->nextInLML;
+ else m_ActiveEdges = e->nextInLML;
+ if( AelNext ) AelNext->prevInAEL = e->nextInLML;
+ e->nextInLML->side = e->side;
+ e->nextInLML->windDelta = e->windDelta;
+ e->nextInLML->windCnt = e->windCnt;
+ e->nextInLML->windCnt2 = e->windCnt2;
+ e = e->nextInLML;
+ e->prevInAEL = AelPrev;
+ e->nextInAEL = AelNext;
+ if( !NEAR_EQUAL(e->dx, HORIZONTAL) ) InsertScanbeam( e->ytop );
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::ProcessIntersections(const long64 botY, const long64 topY)
+{
+ if( !m_ActiveEdges ) return true;
+ try {
+ BuildIntersectList(botY, topY);
+ if (!m_IntersectNodes) return true;
+ if (!m_IntersectNodes->next || FixupIntersectionOrder()) ProcessIntersectList();
+ else return false;
+ }
+ catch(...) {
+ m_SortedEdges = 0;
+ DisposeIntersectNodes();
+ throw clipperException("ProcessIntersections error");
+ }
+ m_SortedEdges = 0;
+ return true;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DisposeIntersectNodes()
+{
+ while ( m_IntersectNodes )
+ {
+ IntersectNode* iNode = m_IntersectNodes->next;
+ delete m_IntersectNodes;
+ m_IntersectNodes = iNode;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildIntersectList(const long64 botY, const long64 topY)
+{
+ if ( !m_ActiveEdges ) return;
+
+ //prepare for sorting ...
+ TEdge* e = m_ActiveEdges;
+ m_SortedEdges = e;
+ while( e )
+ {
+ e->prevInSEL = e->prevInAEL;
+ e->nextInSEL = e->nextInAEL;
+ e->xcurr = TopX( *e, topY );
+ e = e->nextInAEL;
+ }
+
+ //bubblesort ...
+ bool isModified;
+ do
+ {
+ isModified = false;
+ e = m_SortedEdges;
+ while( e->nextInSEL )
+ {
+ TEdge *eNext = e->nextInSEL;
+ IntPoint pt;
+ if(e->xcurr > eNext->xcurr)
+ {
+ if (!IntersectPoint(*e, *eNext, pt, m_UseFullRange) && e->xcurr > eNext->xcurr +1)
+ throw clipperException("Intersection error");
+ if (pt.Y > botY)
+ {
+ pt.Y = botY;
+ pt.X = TopX(*e, pt.Y);
+ }
+ InsertIntersectNode( e, eNext, pt );
+ SwapPositionsInSEL(e, eNext);
+ isModified = true;
+ }
+ else
+ e = eNext;
+ }
+ if( e->prevInSEL ) e->prevInSEL->nextInSEL = 0;
+ else break;
+ }
+ while ( isModified );
+ m_SortedEdges = 0; //important
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt)
+{
+ IntersectNode* newNode = new IntersectNode;
+ newNode->edge1 = e1;
+ newNode->edge2 = e2;
+ newNode->pt = pt;
+ newNode->next = 0;
+ if( !m_IntersectNodes ) m_IntersectNodes = newNode;
+ else if(newNode->pt.Y > m_IntersectNodes->pt.Y )
+ {
+ newNode->next = m_IntersectNodes;
+ m_IntersectNodes = newNode;
+ }
+ else
+ {
+ IntersectNode* iNode = m_IntersectNodes;
+ while(iNode->next && newNode->pt.Y <= iNode->next->pt.Y)
+ iNode = iNode->next;
+ newNode->next = iNode->next;
+ iNode->next = newNode;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ProcessIntersectList()
+{
+ while( m_IntersectNodes )
+ {
+ IntersectNode* iNode = m_IntersectNodes->next;
+ {
+ IntersectEdges( m_IntersectNodes->edge1 ,
+ m_IntersectNodes->edge2 , m_IntersectNodes->pt, ipBoth );
+ SwapPositionsInAEL( m_IntersectNodes->edge1 , m_IntersectNodes->edge2 );
+ }
+ delete m_IntersectNodes;
+ m_IntersectNodes = iNode;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DoMaxima(TEdge *e, long64 topY)
+{
+ TEdge* eMaxPair = GetMaximaPair(e);
+ long64 X = e->xtop;
+ TEdge* eNext = e->nextInAEL;
+ while( eNext != eMaxPair )
+ {
+ if (!eNext) throw clipperException("DoMaxima error");
+ IntersectEdges( e, eNext, IntPoint(X, topY), ipBoth );
+ SwapPositionsInAEL(e, eNext);
+ eNext = e->nextInAEL;
+ }
+ if( e->outIdx < 0 && eMaxPair->outIdx < 0 )
+ {
+ DeleteFromAEL( e );
+ DeleteFromAEL( eMaxPair );
+ }
+ else if( e->outIdx >= 0 && eMaxPair->outIdx >= 0 )
+ {
+ IntersectEdges( e, eMaxPair, IntPoint(X, topY), ipNone );
+ }
+ else throw clipperException("DoMaxima error");
+}
+//------------------------------------------------------------------------------
+
+void Clipper::ProcessEdgesAtTopOfScanbeam(const long64 topY)
+{
+ TEdge* e = m_ActiveEdges;
+ while( e )
+ {
+ //1. process maxima, treating them as if they're 'bent' horizontal edges,
+ // but exclude maxima with horizontal edges. nb: e can't be a horizontal.
+ if( IsMaxima(e, topY) && !NEAR_EQUAL(GetMaximaPair(e)->dx, HORIZONTAL) )
+ {
+ //'e' might be removed from AEL, as may any following edges so ...
+ TEdge* ePrev = e->prevInAEL;
+ DoMaxima(e, topY);
+ if( !ePrev ) e = m_ActiveEdges;
+ else e = ePrev->nextInAEL;
+ }
+ else
+ {
+ bool intermediateVert = IsIntermediate(e, topY);
+ //2. promote horizontal edges, otherwise update xcurr and ycurr ...
+ if (intermediateVert && NEAR_EQUAL(e->nextInLML->dx, HORIZONTAL) )
+ {
+ if (e->outIdx >= 0)
+ {
+ AddOutPt(e, IntPoint(e->xtop, e->ytop));
+
+ for (HorzJoinList::size_type i = 0; i < m_HorizJoins.size(); ++i)
+ {
+ IntPoint pt, pt2;
+ HorzJoinRec* hj = m_HorizJoins[i];
+ if (GetOverlapSegment(IntPoint(hj->edge->xbot, hj->edge->ybot),
+ IntPoint(hj->edge->xtop, hj->edge->ytop),
+ IntPoint(e->nextInLML->xbot, e->nextInLML->ybot),
+ IntPoint(e->nextInLML->xtop, e->nextInLML->ytop), pt, pt2))
+ AddJoin(hj->edge, e->nextInLML, hj->savedIdx, e->outIdx);
+ }
+
+ AddHorzJoin(e->nextInLML, e->outIdx);
+ }
+ UpdateEdgeIntoAEL(e);
+ AddEdgeToSEL(e);
+ } else
+ {
+ e->xcurr = TopX( *e, topY );
+ e->ycurr = topY;
+
+ if (m_ForceSimple && e->prevInAEL &&
+ e->prevInAEL->xcurr == e->xcurr &&
+ e->outIdx >= 0 && e->prevInAEL->outIdx >= 0)
+ {
+ if (intermediateVert)
+ AddOutPt(e->prevInAEL, IntPoint(e->xcurr, topY));
+ else
+ AddOutPt(e, IntPoint(e->xcurr, topY));
+ }
+ }
+ e = e->nextInAEL;
+ }
+ }
+
+ //3. Process horizontals at the top of the scanbeam ...
+ ProcessHorizontals();
+
+ //4. Promote intermediate vertices ...
+ e = m_ActiveEdges;
+ while( e )
+ {
+ if( IsIntermediate( e, topY ) )
+ {
+ if( e->outIdx >= 0 ) AddOutPt(e, IntPoint(e->xtop,e->ytop));
+ UpdateEdgeIntoAEL(e);
+
+ //if output polygons share an edge, they'll need joining later ...
+ TEdge* ePrev = e->prevInAEL;
+ TEdge* eNext = e->nextInAEL;
+ if (ePrev && ePrev->xcurr == e->xbot &&
+ ePrev->ycurr == e->ybot && e->outIdx >= 0 &&
+ ePrev->outIdx >= 0 && ePrev->ycurr > ePrev->ytop &&
+ SlopesEqual(*e, *ePrev, m_UseFullRange))
+ {
+ AddOutPt(ePrev, IntPoint(e->xbot, e->ybot));
+ AddJoin(e, ePrev);
+ }
+ else if (eNext && eNext->xcurr == e->xbot &&
+ eNext->ycurr == e->ybot && e->outIdx >= 0 &&
+ eNext->outIdx >= 0 && eNext->ycurr > eNext->ytop &&
+ SlopesEqual(*e, *eNext, m_UseFullRange))
+ {
+ AddOutPt(eNext, IntPoint(e->xbot, e->ybot));
+ AddJoin(e, eNext);
+ }
+ }
+ e = e->nextInAEL;
+ }
+}
+//------------------------------------------------------------------------------
+
+void Clipper::FixupOutPolygon(OutRec &outrec)
+{
+ //FixupOutPolygon() - removes duplicate points and simplifies consecutive
+ //parallel edges by removing the middle vertex.
+ OutPt *lastOK = 0;
+ outrec.bottomPt = 0;
+ OutPt *pp = outrec.pts;
+
+ for (;;)
+ {
+ if (pp->prev == pp || pp->prev == pp->next )
+ {
+ DisposeOutPts(pp);
+ outrec.pts = 0;
+ return;
+ }
+ //test for duplicate points and for same slope (cross-product) ...
+ if ( PointsEqual(pp->pt, pp->next->pt) ||
+ SlopesEqual(pp->prev->pt, pp->pt, pp->next->pt, m_UseFullRange) )
+ {
+ lastOK = 0;
+ OutPt *tmp = pp;
+ pp->prev->next = pp->next;
+ pp->next->prev = pp->prev;
+ pp = pp->prev;
+ delete tmp;
+ }
+ else if (pp == lastOK) break;
+ else
+ {
+ if (!lastOK) lastOK = pp;
+ pp = pp->next;
+ }
+ }
+ outrec.pts = pp;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildResult(Polygons &polys)
+{
+ polys.reserve(m_PolyOuts.size());
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ if (m_PolyOuts[i]->pts)
+ {
+ Polygon pg;
+ OutPt* p = m_PolyOuts[i]->pts;
+ do
+ {
+ pg.push_back(p->pt);
+ p = p->prev;
+ } while (p != m_PolyOuts[i]->pts);
+ if (pg.size() > 2)
+ polys.push_back(pg);
+ }
+ }
+}
+//------------------------------------------------------------------------------
+
+int PointCount(OutPt *pts)
+{
+ if (!pts) return 0;
+ int result = 0;
+ OutPt* p = pts;
+ do
+ {
+ result++;
+ p = p->next;
+ }
+ while (p != pts);
+ return result;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::BuildResult2(PolyTree& polytree)
+{
+ polytree.Clear();
+ polytree.AllNodes.reserve(m_PolyOuts.size());
+ //add each output polygon/contour to polytree ...
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ int cnt = PointCount(outRec->pts);
+ if (cnt < 3) continue;
+ FixHoleLinkage(*outRec);
+ PolyNode* pn = new PolyNode();
+ //nb: polytree takes ownership of all the PolyNodes
+ polytree.AllNodes.push_back(pn);
+ outRec->polyNode = pn;
+ pn->Parent = 0;
+ pn->Index = 0;
+ pn->Contour.reserve(cnt);
+ OutPt *op = outRec->pts;
+ for (int j = 0; j < cnt; j++)
+ {
+ pn->Contour.push_back(op->pt);
+ op = op->prev;
+ }
+ }
+
+ //fixup PolyNode links etc ...
+ polytree.Childs.reserve(m_PolyOuts.size());
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ if (!outRec->polyNode) continue;
+ if (outRec->FirstLeft)
+ outRec->FirstLeft->polyNode->AddChild(*outRec->polyNode);
+ else
+ polytree.AddChild(*outRec->polyNode);
+ }
+}
+//------------------------------------------------------------------------------
+
+void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2)
+{
+ //just swap the contents (because fIntersectNodes is a single-linked-list)
+ IntersectNode inode = int1; //gets a copy of Int1
+ int1.edge1 = int2.edge1;
+ int1.edge2 = int2.edge2;
+ int1.pt = int2.pt;
+ int2.edge1 = inode.edge1;
+ int2.edge2 = inode.edge2;
+ int2.pt = inode.pt;
+}
+//------------------------------------------------------------------------------
+
+inline bool EdgesAdjacent(const IntersectNode &inode)
+{
+ return (inode.edge1->nextInSEL == inode.edge2) ||
+ (inode.edge1->prevInSEL == inode.edge2);
+}
+//------------------------------------------------------------------------------
+
+bool Clipper::FixupIntersectionOrder()
+{
+ //pre-condition: intersections are sorted bottom-most (then left-most) first.
+ //Now it's crucial that intersections are made only between adjacent edges,
+ //so to ensure this the order of intersections may need adjusting ...
+ IntersectNode *inode = m_IntersectNodes;
+ CopyAELToSEL();
+ while (inode)
+ {
+ if (!EdgesAdjacent(*inode))
+ {
+ IntersectNode *nextNode = inode->next;
+ while (nextNode && !EdgesAdjacent(*nextNode))
+ nextNode = nextNode->next;
+ if (!nextNode)
+ return false;
+ SwapIntersectNodes(*inode, *nextNode);
+ }
+ SwapPositionsInSEL(inode->edge1, inode->edge2);
+ inode = inode->next;
+ }
+ return true;
+}
+//------------------------------------------------------------------------------
+
+inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2)
+{
+ if (e2.xcurr == e1.xcurr)
+ {
+ if (e2.ytop > e1.ytop)
+ return e2.xtop < TopX(e1, e2.ytop);
+ else return e1.xtop > TopX(e2, e1.ytop);
+ }
+ else return e2.xcurr < e1.xcurr;
+}
+//------------------------------------------------------------------------------
+
+void Clipper::InsertEdgeIntoAEL(TEdge *edge)
+{
+ edge->prevInAEL = 0;
+ edge->nextInAEL = 0;
+ if( !m_ActiveEdges )
+ {
+ m_ActiveEdges = edge;
+ }
+ else if( E2InsertsBeforeE1(*m_ActiveEdges, *edge) )
+ {
+ edge->nextInAEL = m_ActiveEdges;
+ m_ActiveEdges->prevInAEL = edge;
+ m_ActiveEdges = edge;
+ } else
+ {
+ TEdge* e = m_ActiveEdges;
+ while( e->nextInAEL && !E2InsertsBeforeE1(*e->nextInAEL , *edge) )
+ e = e->nextInAEL;
+ edge->nextInAEL = e->nextInAEL;
+ if( e->nextInAEL ) e->nextInAEL->prevInAEL = edge;
+ edge->prevInAEL = e;
+ e->nextInAEL = edge;
+ }
+}
+//----------------------------------------------------------------------
+
+bool Clipper::JoinPoints(const JoinRec *j, OutPt *&p1, OutPt *&p2)
+{
+ OutRec *outRec1 = m_PolyOuts[j->poly1Idx];
+ OutRec *outRec2 = m_PolyOuts[j->poly2Idx];
+ if (!outRec1 || !outRec2) return false;
+ OutPt *pp1a = outRec1->pts;
+ OutPt *pp2a = outRec2->pts;
+ IntPoint pt1 = j->pt2a, pt2 = j->pt2b;
+ IntPoint pt3 = j->pt1a, pt4 = j->pt1b;
+ if (!FindSegment(pp1a, m_UseFullRange, pt1, pt2)) return false;
+ if (outRec1 == outRec2)
+ {
+ //we're searching the same polygon for overlapping segments so
+ //segment 2 mustn't be the same as segment 1 ...
+ pp2a = pp1a->next;
+ if (!FindSegment(pp2a, m_UseFullRange, pt3, pt4) || (pp2a == pp1a))
+ return false;
+ }
+ else if (!FindSegment(pp2a, m_UseFullRange, pt3, pt4)) return false;
+
+ if (!GetOverlapSegment(pt1, pt2, pt3, pt4, pt1, pt2)) return false;
+
+ OutPt *p3, *p4, *prev = pp1a->prev;
+ //get p1 & p2 polypts - the overlap start & endpoints on poly1
+ if (PointsEqual(pp1a->pt, pt1)) p1 = pp1a;
+ else if (PointsEqual(prev->pt, pt1)) p1 = prev;
+ else p1 = InsertPolyPtBetween(pp1a, prev, pt1);
+
+ if (PointsEqual(pp1a->pt, pt2)) p2 = pp1a;
+ else if (PointsEqual(prev->pt, pt2)) p2 = prev;
+ else if ((p1 == pp1a) || (p1 == prev))
+ p2 = InsertPolyPtBetween(pp1a, prev, pt2);
+ else if (Pt3IsBetweenPt1AndPt2(pp1a->pt, p1->pt, pt2))
+ p2 = InsertPolyPtBetween(pp1a, p1, pt2); else
+ p2 = InsertPolyPtBetween(p1, prev, pt2);
+
+ //get p3 & p4 polypts - the overlap start & endpoints on poly2
+ prev = pp2a->prev;
+ if (PointsEqual(pp2a->pt, pt1)) p3 = pp2a;
+ else if (PointsEqual(prev->pt, pt1)) p3 = prev;
+ else p3 = InsertPolyPtBetween(pp2a, prev, pt1);
+
+ if (PointsEqual(pp2a->pt, pt2)) p4 = pp2a;
+ else if (PointsEqual(prev->pt, pt2)) p4 = prev;
+ else if ((p3 == pp2a) || (p3 == prev))
+ p4 = InsertPolyPtBetween(pp2a, prev, pt2);
+ else if (Pt3IsBetweenPt1AndPt2(pp2a->pt, p3->pt, pt2))
+ p4 = InsertPolyPtBetween(pp2a, p3, pt2); else
+ p4 = InsertPolyPtBetween(p3, prev, pt2);
+
+ //p1.pt == p3.pt and p2.pt == p4.pt so join p1 to p3 and p2 to p4 ...
+ if (p1->next == p2 && p3->prev == p4)
+ {
+ p1->next = p3;
+ p3->prev = p1;
+ p2->prev = p4;
+ p4->next = p2;
+ return true;
+ }
+ else if (p1->prev == p2 && p3->next == p4)
+ {
+ p1->prev = p3;
+ p3->next = p1;
+ p2->next = p4;
+ p4->prev = p2;
+ return true;
+ }
+ else
+ return false; //an orientation is probably wrong
+}
+//----------------------------------------------------------------------
+
+void Clipper::FixupJoinRecs(JoinRec *j, OutPt *pt, unsigned startIdx)
+{
+ for (JoinList::size_type k = startIdx; k < m_Joins.size(); k++)
+ {
+ JoinRec* j2 = m_Joins[k];
+ if (j2->poly1Idx == j->poly1Idx && PointIsVertex(j2->pt1a, pt))
+ j2->poly1Idx = j->poly2Idx;
+ if (j2->poly2Idx == j->poly1Idx && PointIsVertex(j2->pt2a, pt))
+ j2->poly2Idx = j->poly2Idx;
+ }
+}
+//----------------------------------------------------------------------
+
+bool Poly2ContainsPoly1(OutPt* outPt1, OutPt* outPt2, bool UseFullInt64Range)
+{
+ OutPt* pt = outPt1;
+ //Because the polygons may be touching, we need to find a vertex that
+ //isn't touching the other polygon ...
+ if (PointOnPolygon(pt->pt, outPt2, UseFullInt64Range))
+ {
+ pt = pt->next;
+ while (pt != outPt1 && PointOnPolygon(pt->pt, outPt2, UseFullInt64Range))
+ pt = pt->next;
+ if (pt == outPt1) return true;
+ }
+ return PointInPolygon(pt->pt, outPt2, UseFullInt64Range);
+}
+//----------------------------------------------------------------------
+
+void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec)
+{
+
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ if (outRec->pts && outRec->FirstLeft == OldOutRec)
+ {
+ if (Poly2ContainsPoly1(outRec->pts, NewOutRec->pts, m_UseFullRange))
+ outRec->FirstLeft = NewOutRec;
+ }
+ }
+}
+//----------------------------------------------------------------------
+
+void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec)
+{
+ for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
+ {
+ OutRec* outRec = m_PolyOuts[i];
+ if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec;
+ }
+}
+//----------------------------------------------------------------------
+
+void Clipper::JoinCommonEdges()
+{
+ for (JoinList::size_type i = 0; i < m_Joins.size(); i++)
+ {
+ JoinRec* j = m_Joins[i];
+
+ OutRec *outRec1 = GetOutRec(j->poly1Idx);
+ OutRec *outRec2 = GetOutRec(j->poly2Idx);
+
+ if (!outRec1->pts || !outRec2->pts) continue;
+
+ //get the polygon fragment with the correct hole state (FirstLeft)
+ //before calling JoinPoints() ...
+ OutRec *holeStateRec;
+ if (outRec1 == outRec2) holeStateRec = outRec1;
+ else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2;
+ else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1;
+ else holeStateRec = GetLowermostRec(outRec1, outRec2);
+
+ OutPt *p1, *p2;
+ if (!JoinPoints(j, p1, p2)) continue;
+
+ if (outRec1 == outRec2)
+ {
+ //instead of joining two polygons, we've just created a new one by
+ //splitting one polygon into two.
+ outRec1->pts = p1;
+ outRec1->bottomPt = 0;
+ outRec2 = CreateOutRec();
+ outRec2->pts = p2;
+
+ if (Poly2ContainsPoly1(outRec2->pts, outRec1->pts, m_UseFullRange))
+ {
+ //outRec2 is contained by outRec1 ...
+ outRec2->isHole = !outRec1->isHole;
+ outRec2->FirstLeft = outRec1;
+
+ FixupJoinRecs(j, p2, i+1);
+
+ //fixup FirstLeft pointers that may need reassigning to OutRec1
+ if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
+
+ FixupOutPolygon(*outRec1); //nb: do this BEFORE testing orientation
+ FixupOutPolygon(*outRec2); // but AFTER calling FixupJoinRecs()
+
+
+ if ((outRec2->isHole ^ m_ReverseOutput) == (Area(*outRec2, m_UseFullRange) > 0))
+ ReversePolyPtLinks(outRec2->pts);
+
+ } else if (Poly2ContainsPoly1(outRec1->pts, outRec2->pts, m_UseFullRange))
+ {
+ //outRec1 is contained by outRec2 ...
+ outRec2->isHole = outRec1->isHole;
+ outRec1->isHole = !outRec2->isHole;
+ outRec2->FirstLeft = outRec1->FirstLeft;
+ outRec1->FirstLeft = outRec2;
+
+ FixupJoinRecs(j, p2, i+1);
+
+ //fixup FirstLeft pointers that may need reassigning to OutRec1
+ if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2);
+
+ FixupOutPolygon(*outRec1); //nb: do this BEFORE testing orientation
+ FixupOutPolygon(*outRec2); // but AFTER calling FixupJoinRecs()
+
+ if ((outRec1->isHole ^ m_ReverseOutput) == (Area(*outRec1, m_UseFullRange) > 0))
+ ReversePolyPtLinks(outRec1->pts);
+ }
+ else
+ {
+ //the 2 polygons are completely separate ...
+ outRec2->isHole = outRec1->isHole;
+ outRec2->FirstLeft = outRec1->FirstLeft;
+
+ FixupJoinRecs(j, p2, i+1);
+
+ //fixup FirstLeft pointers that may need reassigning to OutRec2
+ if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2);
+
+ FixupOutPolygon(*outRec1); //nb: do this BEFORE testing orientation
+ FixupOutPolygon(*outRec2); // but AFTER calling FixupJoinRecs()
+ }
+
+ } else
+ {
+ //joined 2 polygons together ...
+
+ //cleanup redundant edges ...
+ FixupOutPolygon(*outRec1);
+
+ outRec2->pts = 0;
+ outRec2->bottomPt = 0;
+ outRec2->idx = outRec1->idx;
+
+ outRec1->isHole = holeStateRec->isHole;
+ if (holeStateRec == outRec2)
+ outRec1->FirstLeft = outRec2->FirstLeft;
+ outRec2->FirstLeft = outRec1;
+
+ //fixup FirstLeft pointers that may need reassigning to OutRec1
+ if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1);
+ }
+ }
+}
+//------------------------------------------------------------------------------
+
+inline void UpdateOutPtIdxs(OutRec& outrec)
+{
+ OutPt* op = outrec.pts;
+ do
+ {
+ op->idx = outrec.idx;
+ op = op->prev;
+ }
+ while(op != outrec.pts);
+}
+//------------------------------------------------------------------------------
+
+void Clipper::DoSimplePolygons()
+{
+ PolyOutList::size_type i = 0;
+ while (i < m_PolyOuts.size())
+ {
+ OutRec* outrec = m_PolyOuts[i++];
+ OutPt* op = outrec->pts;
+ if (!op) continue;
+ do //for each Pt in Polygon until duplicate found do ...
+ {
+ OutPt* op2 = op->next;
+ while (op2 != outrec->pts)
+ {
+ if (PointsEqual(op->pt, op2->pt) && op2->next != op && op2->prev != op)
+ {
+ //split the polygon into two ...
+ OutPt* op3 = op->prev;
+ OutPt* op4 = op2->prev;
+ op->prev = op4;
+ op4->next = op;
+ op2->prev = op3;
+ op3->next = op2;
+
+ outrec->pts = op;
+ OutRec* outrec2 = CreateOutRec();
+ outrec2->pts = op2;
+ UpdateOutPtIdxs(*outrec2);
+ if (Poly2ContainsPoly1(outrec2->pts, outrec->pts, m_UseFullRange))
+ {
+ //OutRec2 is contained by OutRec1 ...
+ outrec2->isHole = !outrec->isHole;
+ outrec2->FirstLeft = outrec;
+ }
+ else
+ if (Poly2ContainsPoly1(outrec->pts, outrec2->pts, m_UseFullRange))
+ {
+ //OutRec1 is contained by OutRec2 ...
+ outrec2->isHole = outrec->isHole;
+ outrec->isHole = !outrec2->isHole;
+ outrec2->FirstLeft = outrec->FirstLeft;
+ outrec->FirstLeft = outrec2;
+ } else
+ {
+ //the 2 polygons are separate ...
+ outrec2->isHole = outrec->isHole;
+ outrec2->FirstLeft = outrec->FirstLeft;
+ }
+ op2 = op; //ie get ready for the next iteration
+ }
+ op2 = op2->next;
+ }
+ op = op->next;
+ }
+ while (op != outrec->pts);
+ }
+}
+//------------------------------------------------------------------------------
+
+void ReversePolygon(Polygon& p)
+{
+ std::reverse(p.begin(), p.end());
+}
+//------------------------------------------------------------------------------
+
+void ReversePolygons(Polygons& p)
+{
+ for (Polygons::size_type i = 0; i < p.size(); ++i)
+ ReversePolygon(p[i]);
+}
+
+//------------------------------------------------------------------------------
+// OffsetPolygon functions ...
+//------------------------------------------------------------------------------
+
+struct DoublePoint
+{
+ double X;
+ double Y;
+ DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
+};
+//------------------------------------------------------------------------------
+
+Polygon BuildArc(const IntPoint &pt,
+ const double a1, const double a2, const double r, double limit)
+{
+ //see notes in clipper.pas regarding steps
+ double arcFrac = std::fabs(a2 - a1) / (2 * pi);
+ int steps = (int)(arcFrac * pi / std::acos(1 - limit / std::fabs(r)));
+ if (steps < 2) steps = 2;
+ else if (steps > (int)(222.0 * arcFrac)) steps = (int)(222.0 * arcFrac);
+
+ double x = std::cos(a1);
+ double y = std::sin(a1);
+ double c = std::cos((a2 - a1) / steps);
+ double s = std::sin((a2 - a1) / steps);
+ Polygon result(steps +1);
+ for (int i = 0; i <= steps; ++i)
+ {
+ result[i].X = pt.X + Round(x * r);
+ result[i].Y = pt.Y + Round(y * r);
+ double x2 = x;
+ x = x * c - s * y; //cross product
+ y = x2 * s + y * c; //dot product
+ }
+ return result;
+}
+//------------------------------------------------------------------------------
+
+DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2)
+{
+ if(pt2.X == pt1.X && pt2.Y == pt1.Y)
+ return DoublePoint(0, 0);
+
+ double dx = (double)(pt2.X - pt1.X);
+ double dy = (double)(pt2.Y - pt1.Y);
+ double f = 1 *1.0/ std::sqrt( dx*dx + dy*dy );
+ dx *= f;
+ dy *= f;
+ return DoublePoint(dy, -dx);
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+class OffsetBuilder
+{
+private:
+ const Polygons& m_p;
+ Polygon* m_curr_poly;
+ std::vector<DoublePoint> normals;
+ double m_delta, m_rmin, m_r;
+ size_t m_i, m_j, m_k;
+ static const int buffLength = 128;
+
+public:
+
+OffsetBuilder(const Polygons& in_polys, Polygons& out_polys,
+ bool isPolygon, double delta, JoinType jointype, EndType endtype, double limit): m_p(in_polys)
+{
+ //precondition: &out_polys != &in_polys
+
+ if (NEAR_ZERO(delta)) {out_polys = in_polys; return;}
+ m_rmin = 0.5;
+ m_delta = delta;
+ if (jointype == jtMiter)
+ {
+ if (limit > 2) m_rmin = 2.0 / (limit * limit);
+ limit = 0.25; //just in case endtype == etRound
+ }
+ else
+ {
+ if (limit <= 0) limit = 0.25;
+ else if (limit > std::fabs(delta)) limit = std::fabs(delta);
+ }
+
+ double deltaSq = delta*delta;
+ out_polys.clear();
+ out_polys.resize(m_p.size());
+ for (m_i = 0; m_i < m_p.size(); m_i++)
+ {
+ size_t len = m_p[m_i].size();
+
+ if (len == 0 || (len < 3 && delta <= 0))
+ continue;
+ else if (len == 1)
+ {
+ out_polys[m_i] = BuildArc(m_p[m_i][0], 0, 2*pi, delta, limit);
+ continue;
+ }
+
+ bool forceClose = PointsEqual(m_p[m_i][0], m_p[m_i][len -1]);
+ if (forceClose) len--;
+
+ //build normals ...
+ normals.clear();
+ normals.resize(len);
+ for (m_j = 0; m_j < len -1; ++m_j)
+ normals[m_j] = GetUnitNormal(m_p[m_i][m_j], m_p[m_i][m_j +1]);
+ if (isPolygon || forceClose)
+ normals[len-1] = GetUnitNormal(m_p[m_i][len-1], m_p[m_i][0]);
+ else //is open polyline
+ normals[len-1] = normals[len-2];
+
+ m_curr_poly = &out_polys[m_i];
+ m_curr_poly->reserve(len);
+
+ if (isPolygon || forceClose)
+ {
+ m_k = len -1;
+ for (m_j = 0; m_j < len; ++m_j)
+ OffsetPoint(jointype, limit);
+
+ if (!isPolygon)
+ {
+ size_t j = out_polys.size();
+ out_polys.resize(j+1);
+ m_curr_poly = &out_polys[j];
+ m_curr_poly->reserve(len);
+ m_delta = -m_delta;
+
+ m_k = len -1;
+ for (m_j = 0; m_j < len; ++m_j)
+ OffsetPoint(jointype, limit);
+ m_delta = -m_delta;
+ ReversePolygon(*m_curr_poly);
+ }
+ }
+ else //is open polyline
+ {
+ //offset the polyline going forward ...
+ m_k = 0;
+ for (m_j = 1; m_j < len -1; ++m_j)
+ OffsetPoint(jointype, limit);
+
+ //handle the end (butt, round or square) ...
+ IntPoint pt1;
+ if (endtype == etButt)
+ {
+ m_j = len - 1;
+ pt1 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta),
+ Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta));
+ AddPoint(pt1);
+ pt1 = IntPoint(Round(m_p[m_i][m_j].X - normals[m_j].X * m_delta),
+ Round(m_p[m_i][m_j].Y - normals[m_j].Y * m_delta));
+ AddPoint(pt1);
+ }
+ else
+ {
+ m_j = len - 1;
+ m_k = len - 2;
+ normals[m_j].X = -normals[m_j].X;
+ normals[m_j].Y = -normals[m_j].Y;
+ if (endtype == etSquare) DoSquare();
+ else DoRound(limit);
+ }
+
+ //re-build Normals ...
+ for (int j = len - 1; j > 0; --j)
+ {
+ normals[j].X = -normals[j - 1].X;
+ normals[j].Y = -normals[j - 1].Y;
+ }
+ normals[0].X = -normals[1].X;
+ normals[0].Y = -normals[1].Y;
+
+ //offset the polyline going backward ...
+ m_k = len -1;
+ for (m_j = m_k - 1; m_j > 0; --m_j)
+ OffsetPoint(jointype, limit);
+
+ //finally handle the start (butt, round or square) ...
+ if (endtype == etButt)
+ {
+ pt1 = IntPoint(Round(m_p[m_i][0].X - normals[0].X * m_delta),
+ Round(m_p[m_i][0].Y - normals[0].Y * m_delta));
+ AddPoint(pt1);
+ pt1 = IntPoint(Round(m_p[m_i][0].X + normals[0].X * m_delta),
+ Round(m_p[m_i][0].Y + normals[0].Y * m_delta));
+ AddPoint(pt1);
+ } else
+ {
+ m_k = 1;
+ if (endtype == etSquare) DoSquare();
+ else DoRound(limit);
+ }
+ }
+ }
+
+ //and clean up untidy corners using Clipper ...
+ Clipper clpr;
+ clpr.AddPolygons(out_polys, ptSubject);
+ if (delta > 0)
+ {
+ if (!clpr.Execute(ctUnion, out_polys, pftPositive, pftPositive))
+ out_polys.clear();
+ }
+ else
+ {
+ IntRect r = clpr.GetBounds();
+ Polygon outer(4);
+ outer[0] = IntPoint(r.left - 10, r.bottom + 10);
+ outer[1] = IntPoint(r.right + 10, r.bottom + 10);
+ outer[2] = IntPoint(r.right + 10, r.top - 10);
+ outer[3] = IntPoint(r.left - 10, r.top - 10);
+
+ clpr.AddPolygon(outer, ptSubject);
+ clpr.ReverseSolution(true);
+ if (clpr.Execute(ctUnion, out_polys, pftNegative, pftNegative))
+ out_polys.erase(out_polys.begin());
+ else
+ out_polys.clear();
+ }
+}
+//------------------------------------------------------------------------------
+
+private:
+
+void OffsetPoint(JoinType jointype, double limit)
+{
+ switch (jointype)
+ {
+ case jtMiter:
+ {
+ m_r = 1 + (normals[m_j].X*normals[m_k].X +
+ normals[m_j].Y*normals[m_k].Y);
+ if (m_r >= m_rmin) DoMiter(); else DoSquare();
+ break;
+ }
+ case jtSquare: DoSquare(); break;
+ case jtRound: DoRound(limit); break;
+ }
+ m_k = m_j;
+}
+//------------------------------------------------------------------------------
+
+void AddPoint(const IntPoint& pt)
+{
+ if (m_curr_poly->size() == m_curr_poly->capacity())
+ m_curr_poly->reserve(m_curr_poly->capacity() + buffLength);
+ m_curr_poly->push_back(pt);
+}
+//------------------------------------------------------------------------------
+
+void DoSquare()
+{
+ IntPoint pt1 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta),
+ Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta));
+ IntPoint pt2 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta),
+ Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta));
+ if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0)
+ {
+ double a1 = std::atan2(normals[m_k].Y, normals[m_k].X);
+ double a2 = std::atan2(-normals[m_j].Y, -normals[m_j].X);
+ a1 = std::fabs(a2 - a1);
+ if (a1 > pi) a1 = pi * 2 - a1;
+ double dx = std::tan((pi - a1) / 4) * std::fabs(m_delta);
+ pt1 = IntPoint((long64)(pt1.X -normals[m_k].Y * dx), (long64)(pt1.Y + normals[m_k].X * dx));
+ AddPoint(pt1);
+ pt2 = IntPoint((long64)(pt2.X + normals[m_j].Y * dx), (long64)(pt2.Y -normals[m_j].X * dx));
+ AddPoint(pt2);
+ }
+ else
+ {
+ AddPoint(pt1);
+ AddPoint(m_p[m_i][m_j]);
+ AddPoint(pt2);
+ }
+}
+//------------------------------------------------------------------------------
+
+void DoMiter()
+{
+ if ((normals[m_k].X * normals[m_j].Y - normals[m_j].X * normals[m_k].Y) * m_delta >= 0)
+ {
+ double q = m_delta / m_r;
+ AddPoint(IntPoint(Round(m_p[m_i][m_j].X + (normals[m_k].X + normals[m_j].X) * q),
+ Round(m_p[m_i][m_j].Y + (normals[m_k].Y + normals[m_j].Y) * q)));
+ }
+ else
+ {
+ IntPoint pt1 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta),
+ Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta));
+ IntPoint pt2 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta),
+ Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta));
+ AddPoint(pt1);
+ AddPoint(m_p[m_i][m_j]);
+ AddPoint(pt2);
+ }
+}
+//------------------------------------------------------------------------------
+
+void DoRound(double limit)
+{
+ IntPoint pt1 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_k].X * m_delta),
+ Round(m_p[m_i][m_j].Y + normals[m_k].Y * m_delta));
+ IntPoint pt2 = IntPoint(Round(m_p[m_i][m_j].X + normals[m_j].X * m_delta),
+ Round(m_p[m_i][m_j].Y + normals[m_j].Y * m_delta));
+ AddPoint(pt1);
+ //round off reflex angles (ie > 180 deg) unless almost flat (ie < ~10deg).
+ if ((normals[m_k].X*normals[m_j].Y - normals[m_j].X*normals[m_k].Y) * m_delta >= 0)
+ {
+ if (normals[m_j].X * normals[m_k].X + normals[m_j].Y * normals[m_k].Y < 0.985)
+ {
+ double a1 = std::atan2(normals[m_k].Y, normals[m_k].X);
+ double a2 = std::atan2(normals[m_j].Y, normals[m_j].X);
+ if (m_delta > 0 && a2 < a1) a2 += pi *2;
+ else if (m_delta < 0 && a2 > a1) a2 -= pi *2;
+ Polygon arc = BuildArc(m_p[m_i][m_j], a1, a2, m_delta, limit);
+ for (Polygon::size_type m = 0; m < arc.size(); m++)
+ AddPoint(arc[m]);
+ }
+ }
+ else
+ AddPoint(m_p[m_i][m_j]);
+ AddPoint(pt2);
+}
+//--------------------------------------------------------------------------
+
+}; //end PolyOffsetBuilder
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+bool UpdateBotPt(const IntPoint &pt, IntPoint &botPt)
+{
+ if (pt.Y > botPt.Y || (pt.Y == botPt.Y && pt.X < botPt.X))
+ {
+ botPt = pt;
+ return true;
+ }
+ else return false;
+}
+//--------------------------------------------------------------------------
+
+void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys,
+ double delta, JoinType jointype, double limit, bool autoFix)
+{
+ if (!autoFix && &in_polys != &out_polys)
+ {
+ OffsetBuilder(in_polys, out_polys, true, delta, jointype, etClosed, limit);
+ return;
+ }
+
+ Polygons inPolys = Polygons(in_polys);
+ out_polys.clear();
+
+ //ChecksInput - fixes polygon orientation if necessary and removes
+ //duplicate vertices. Can be set false when you're sure that polygon
+ //orientation is correct and that there are no duplicate vertices.
+ if (autoFix)
+ {
+ size_t polyCount = inPolys.size(), botPoly = 0;
+ while (botPoly < polyCount && inPolys[botPoly].empty()) botPoly++;
+ if (botPoly == polyCount) return;
+
+ //botPt: used to find the lowermost (in inverted Y-axis) & leftmost point
+ //This point (on m_p[botPoly]) must be on an outer polygon ring and if
+ //its orientation is false (counterclockwise) then assume all polygons
+ //need reversing ...
+ IntPoint botPt = inPolys[botPoly][0];
+ for (size_t i = botPoly; i < polyCount; ++i)
+ {
+ if (inPolys[i].size() < 3) { inPolys[i].clear(); continue; }
+ if (UpdateBotPt(inPolys[i][0], botPt)) botPoly = i;
+ Polygon::iterator it = inPolys[i].begin() +1;
+ while (it != inPolys[i].end())
+ {
+ if (PointsEqual(*it, *(it -1)))
+ it = inPolys[i].erase(it);
+ else
+ {
+ if (UpdateBotPt(*it, botPt)) botPoly = i;
+ ++it;
+ }
+ }
+ }
+ if (!Orientation(inPolys[botPoly]))
+ ReversePolygons(inPolys);
+ }
+ OffsetBuilder(inPolys, out_polys, true, delta, jointype, etClosed, limit);
+}
+//------------------------------------------------------------------------------
+
+void OffsetPolyLines(const Polygons &in_lines, Polygons &out_lines,
+ double delta, JoinType jointype, EndType endtype,
+ double limit, bool autoFix)
+{
+ if (!autoFix && endtype != etClosed && &in_lines != &out_lines)
+ {
+ OffsetBuilder(in_lines, out_lines, false, delta, jointype, endtype, limit);
+ return;
+ }
+
+ Polygons inLines = Polygons(in_lines);
+ if (autoFix)
+ for (size_t i = 0; i < inLines.size(); ++i)
+ {
+ if (inLines[i].size() < 2) { inLines[i].clear(); continue; }
+ Polygon::iterator it = inLines[i].begin() +1;
+ while (it != inLines[i].end())
+ {
+ if (PointsEqual(*it, *(it -1)))
+ it = inLines[i].erase(it);
+ else
+ ++it;
+ }
+ }
+
+ if (endtype == etClosed)
+ {
+ size_t sz = inLines.size();
+ inLines.resize(sz * 2);
+ for (size_t i = 0; i < sz; ++i)
+ {
+ inLines[sz+i] = inLines[i];
+ ReversePolygon(inLines[sz+i]);
+ }
+ OffsetBuilder(inLines, out_lines, true, delta, jointype, endtype, limit);
+ }
+ else
+ OffsetBuilder(inLines, out_lines, false, delta, jointype, endtype, limit);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType)
+{
+ Clipper c;
+ c.ForceSimple(true);
+ c.AddPolygon(in_poly, ptSubject);
+ c.Execute(ctUnion, out_polys, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType)
+{
+ Clipper c;
+ c.ForceSimple(true);
+ c.AddPolygons(in_polys, ptSubject);
+ c.Execute(ctUnion, out_polys, fillType, fillType);
+}
+//------------------------------------------------------------------------------
+
+void SimplifyPolygons(Polygons &polys, PolyFillType fillType)
+{
+ SimplifyPolygons(polys, polys, fillType);
+}
+//------------------------------------------------------------------------------
+
+inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2)
+{
+ double dx = ((double)pt1.X - pt2.X);
+ double dy = ((double)pt1.Y - pt2.Y);
+ return (dx*dx + dy*dy);
+}
+//------------------------------------------------------------------------------
+
+DoublePoint ClosestPointOnLine(const IntPoint& pt, const IntPoint& linePt1, const IntPoint& linePt2)
+{
+ double dx = ((double)linePt2.X - linePt1.X);
+ double dy = ((double)linePt2.Y - linePt1.Y);
+ if (dx == 0 && dy == 0)
+ return DoublePoint((double)linePt1.X, (double)linePt1.Y);
+ double q = ((pt.X-linePt1.X)*dx + (pt.Y-linePt1.Y)*dy) / (dx*dx + dy*dy);
+ return DoublePoint(
+ (1-q)*linePt1.X + q*linePt2.X,
+ (1-q)*linePt1.Y + q*linePt2.Y);
+}
+//------------------------------------------------------------------------------
+
+bool SlopesNearColinear(const IntPoint& pt1,
+ const IntPoint& pt2, const IntPoint& pt3, double distSqrd)
+{
+ if (DistanceSqrd(pt1, pt2) > DistanceSqrd(pt1, pt3)) return false;
+ DoublePoint cpol = ClosestPointOnLine(pt2, pt1, pt3);
+ double dx = pt2.X - cpol.X;
+ double dy = pt2.Y - cpol.Y;
+ return (dx*dx + dy*dy) < distSqrd;
+}
+//------------------------------------------------------------------------------
+
+bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd)
+{
+ double dx = (double)pt1.X - pt2.X;
+ double dy = (double)pt1.Y - pt2.Y;
+ return ((dx * dx) + (dy * dy) <= distSqrd);
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygon(const Polygon& in_poly, Polygon& out_poly, double distance)
+{
+ //distance = proximity in units/pixels below which vertices
+ //will be stripped. Default ~= sqrt(2).
+ int highI = in_poly.size() -1;
+ double distSqrd = distance * distance;
+ while (highI > 0 && PointsAreClose(in_poly[highI], in_poly[0], distSqrd)) highI--;
+ if (highI < 2) { out_poly.clear(); return; }
+
+ if (&in_poly != &out_poly)
+ out_poly.resize(highI + 1);
+
+ IntPoint pt = in_poly[highI];
+ int i = 0, k = 0;
+ for (;;)
+ {
+ while (i < highI && PointsAreClose(pt, in_poly[i+1], distSqrd)) i+=2;
+ int i2 = i;
+ while (i < highI && (PointsAreClose(in_poly[i], in_poly[i+1], distSqrd) ||
+ SlopesNearColinear(pt, in_poly[i], in_poly[i+1], distSqrd))) i++;
+ if (i >= highI) break;
+ else if (i != i2) continue;
+ pt = in_poly[i++];
+ out_poly[k++] = pt;
+ }
+ if (i <= highI) out_poly[k++] = in_poly[i];
+ if (k > 2 && SlopesNearColinear(out_poly[k -2], out_poly[k -1], out_poly[0], distSqrd)) k--;
+ if (k < 3) out_poly.clear();
+ else if (k <= highI) out_poly.resize(k);
+}
+//------------------------------------------------------------------------------
+
+void CleanPolygons(const Polygons& in_polys, Polygons& out_polys, double distance)
+{
+ for (Polygons::size_type i = 0; i < in_polys.size(); ++i)
+ CleanPolygon(in_polys[i], out_polys[i], distance);
+}
+//------------------------------------------------------------------------------
+
+void AddPolyNodeToPolygons(const PolyNode& polynode, Polygons& polygons)
+{
+ if (!polynode.Contour.empty())
+ polygons.push_back(polynode.Contour);
+ for (int i = 0; i < polynode.ChildCount(); ++i)
+ AddPolyNodeToPolygons(*polynode.Childs[i], polygons);
+}
+//------------------------------------------------------------------------------
+
+void PolyTreeToPolygons(const PolyTree& polytree, Polygons& polygons)
+{
+ polygons.resize(0);
+ polygons.reserve(polytree.Total());
+ AddPolyNodeToPolygons(polytree, polygons);
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, IntPoint& p)
+{
+ s << p.X << ' ' << p.Y << "\n";
+ return s;
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, Polygon &p)
+{
+ for (Polygon::size_type i = 0; i < p.size(); i++)
+ s << p[i];
+ s << "\n";
+ return s;
+}
+//------------------------------------------------------------------------------
+
+std::ostream& operator <<(std::ostream &s, Polygons &p)
+{
+ for (Polygons::size_type i = 0; i < p.size(); i++)
+ s << p[i];
+ s << "\n";
+ return s;
+}
+//------------------------------------------------------------------------------
+
+} //ClipperLib namespace
diff --git a/hyp2mat/lib/clipper.hpp b/hyp2mat/lib/clipper.hpp
new file mode 100644
index 0000000..4dcfe12
--- /dev/null
+++ b/hyp2mat/lib/clipper.hpp
@@ -0,0 +1,351 @@
+/*******************************************************************************
+* *
+* Author : Angus Johnson *
+* Version : 5.1.6 *
+* Date : 23 May 2013 *
+* Website : http://www.angusj.com *
+* Copyright : Angus Johnson 2010-2013 *
+* *
+* License: *
+* Use, modification & distribution is subject to Boost Software License Ver 1. *
+* http://www.boost.org/LICENSE_1_0.txt *
+* *
+* Attributions: *
+* The code in this library is an extension of Bala Vatti's clipping algorithm: *
+* "A generic solution to polygon clipping" *
+* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
+* http://portal.acm.org/citation.cfm?id=129906 *
+* *
+* Computer graphics and geometric modeling: implementation and algorithms *
+* By Max K. Agoston *
+* Springer; 1 edition (January 4, 2005) *
+* http://books.google.com/books?q=vatti+clipping+agoston *
+* *
+* See also: *
+* "Polygon Offsetting by Computing Winding Numbers" *
+* Paper no. DETC2005-85513 pp. 565-575 *
+* ASME 2005 International Design Engineering Technical Conferences *
+* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
+* September 24-28, 2005 , Long Beach, California, USA *
+* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
+* *
+*******************************************************************************/
+
+#ifndef clipper_hpp
+#define clipper_hpp
+
+#define CLIPPER_VERSION "5.1.6"
+
+#include <vector>
+#include <stdexcept>
+#include <cstring>
+#include <cstdlib>
+#include <ostream>
+
+namespace ClipperLib {
+
+enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
+enum PolyType { ptSubject, ptClip };
+//By far the most widely used winding rules for polygon filling are
+//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
+//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
+//see http://glprogramming.com/red/chapter11.html
+enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
+
+typedef signed long long long64;
+typedef unsigned long long ulong64;
+
+struct IntPoint {
+public:
+ long64 X;
+ long64 Y;
+ IntPoint(long64 x = 0, long64 y = 0): X(x), Y(y) {};
+ friend std::ostream& operator <<(std::ostream &s, IntPoint &p);
+};
+
+typedef std::vector< IntPoint > Polygon;
+typedef std::vector< Polygon > Polygons;
+
+
+std::ostream& operator <<(std::ostream &s, Polygon &p);
+std::ostream& operator <<(std::ostream &s, Polygons &p);
+
+class PolyNode;
+typedef std::vector< PolyNode* > PolyNodes;
+
+class PolyNode
+{
+public:
+ PolyNode();
+ Polygon Contour;
+ PolyNodes Childs;
+ PolyNode* Parent;
+ PolyNode* GetNext() const;
+ bool IsHole() const;
+ int ChildCount() const;
+private:
+ PolyNode* GetNextSiblingUp() const;
+ unsigned Index; //node index in Parent.Childs
+ void AddChild(PolyNode& child);
+ friend class Clipper; //to access Index
+};
+
+class PolyTree: public PolyNode
+{
+public:
+ ~PolyTree(){Clear();};
+ PolyNode* GetFirst() const;
+ void Clear();
+ int Total() const;
+private:
+ PolyNodes AllNodes;
+ friend class Clipper; //to access AllNodes
+};
+
+enum JoinType { jtSquare, jtRound, jtMiter };
+enum EndType { etClosed, etButt, etSquare, etRound};
+
+bool Orientation(const Polygon &poly);
+double Area(const Polygon &poly);
+
+void OffsetPolygons(const Polygons &in_polys, Polygons &out_polys,
+ double delta, JoinType jointype = jtSquare, double limit = 0, bool autoFix = true);
+
+void OffsetPolyLines(const Polygons &in_lines, Polygons &out_lines,
+ double delta, JoinType jointype = jtSquare, EndType endtype = etSquare, double limit = 0, bool autoFix = true);
+
+void SimplifyPolygon(const Polygon &in_poly, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
+void SimplifyPolygons(const Polygons &in_polys, Polygons &out_polys, PolyFillType fillType = pftEvenOdd);
+void SimplifyPolygons(Polygons &polys, PolyFillType fillType = pftEvenOdd);
+
+void CleanPolygon(const Polygon& in_poly, Polygon& out_poly, double distance = 1.415);
+void CleanPolygons(const Polygons& in_polys, Polygons& out_polys, double distance = 1.415);
+
+void PolyTreeToPolygons(const PolyTree& polytree, Polygons& polygons);
+
+void ReversePolygon(Polygon& p);
+void ReversePolygons(Polygons& p);
+
+//used internally ...
+enum EdgeSide { esLeft = 1, esRight = 2};
+enum IntersectProtects { ipNone = 0, ipLeft = 1, ipRight = 2, ipBoth = 3 };
+//inline IntersectProtects operator|(IntersectProtects a, IntersectProtects b)
+//{return static_cast<IntersectProtects>(static_cast<int>(a) | static_cast<int>(b));}
+
+struct TEdge {
+ long64 xbot;
+ long64 ybot;
+ long64 xcurr;
+ long64 ycurr;
+ long64 xtop;
+ long64 ytop;
+ double dx;
+ long64 deltaX;
+ long64 deltaY;
+ PolyType polyType;
+ EdgeSide side;
+ int windDelta; //1 or -1 depending on winding direction
+ int windCnt;
+ int windCnt2; //winding count of the opposite polytype
+ int outIdx;
+ TEdge *next;
+ TEdge *prev;
+ TEdge *nextInLML;
+ TEdge *nextInAEL;
+ TEdge *prevInAEL;
+ TEdge *nextInSEL;
+ TEdge *prevInSEL;
+};
+
+struct IntersectNode {
+ TEdge *edge1;
+ TEdge *edge2;
+ IntPoint pt;
+ IntersectNode *next;
+};
+
+struct LocalMinima {
+ long64 Y;
+ TEdge *leftBound;
+ TEdge *rightBound;
+ LocalMinima *next;
+};
+
+struct Scanbeam {
+ long64 Y;
+ Scanbeam *next;
+};
+
+struct OutPt; //forward declaration
+
+struct OutRec {
+ int idx;
+ bool isHole;
+ OutRec *FirstLeft; //see comments in clipper.pas
+ PolyNode *polyNode;
+ OutPt *pts;
+ OutPt *bottomPt;
+};
+
+struct OutPt {
+ int idx;
+ IntPoint pt;
+ OutPt *next;
+ OutPt *prev;
+};
+
+struct JoinRec {
+ IntPoint pt1a;
+ IntPoint pt1b;
+ int poly1Idx;
+ IntPoint pt2a;
+ IntPoint pt2b;
+ int poly2Idx;
+};
+
+struct HorzJoinRec {
+ TEdge *edge;
+ int savedIdx;
+};
+
+struct IntRect { long64 left; long64 top; long64 right; long64 bottom; };
+
+typedef std::vector < OutRec* > PolyOutList;
+typedef std::vector < TEdge* > EdgeList;
+typedef std::vector < JoinRec* > JoinList;
+typedef std::vector < HorzJoinRec* > HorzJoinList;
+
+//ClipperBase is the ancestor to the Clipper class. It should not be
+//instantiated directly. This class simply abstracts the conversion of sets of
+//polygon coordinates into edge objects that are stored in a LocalMinima list.
+class ClipperBase
+{
+public:
+ ClipperBase();
+ virtual ~ClipperBase();
+ bool AddPolygon(const Polygon &pg, PolyType polyType);
+ bool AddPolygons( const Polygons &ppg, PolyType polyType);
+ virtual void Clear();
+ IntRect GetBounds();
+protected:
+ void DisposeLocalMinimaList();
+ TEdge* AddBoundsToLML(TEdge *e);
+ void PopLocalMinima();
+ virtual void Reset();
+ void InsertLocalMinima(LocalMinima *newLm);
+ LocalMinima *m_CurrentLM;
+ LocalMinima *m_MinimaList;
+ bool m_UseFullRange;
+ EdgeList m_edges;
+};
+
+class Clipper : public virtual ClipperBase
+{
+public:
+ Clipper();
+ ~Clipper();
+ bool Execute(ClipType clipType,
+ Polygons &solution,
+ PolyFillType subjFillType = pftEvenOdd,
+ PolyFillType clipFillType = pftEvenOdd);
+ bool Execute(ClipType clipType,
+ PolyTree &polytree,
+ PolyFillType subjFillType = pftEvenOdd,
+ PolyFillType clipFillType = pftEvenOdd);
+ void Clear();
+ bool ReverseSolution() {return m_ReverseOutput;};
+ void ReverseSolution(bool value) {m_ReverseOutput = value;};
+ bool ForceSimple() {return m_ForceSimple;};
+ void ForceSimple(bool value) {m_ForceSimple = value;};
+protected:
+ void Reset();
+ virtual bool ExecuteInternal();
+private:
+ PolyOutList m_PolyOuts;
+ JoinList m_Joins;
+ HorzJoinList m_HorizJoins;
+ ClipType m_ClipType;
+ Scanbeam *m_Scanbeam;
+ TEdge *m_ActiveEdges;
+ TEdge *m_SortedEdges;
+ IntersectNode *m_IntersectNodes;
+ bool m_ExecuteLocked;
+ PolyFillType m_ClipFillType;
+ PolyFillType m_SubjFillType;
+ bool m_ReverseOutput;
+ bool m_UsingPolyTree;
+ bool m_ForceSimple;
+ void DisposeScanbeamList();
+ void SetWindingCount(TEdge& edge);
+ bool IsEvenOddFillType(const TEdge& edge) const;
+ bool IsEvenOddAltFillType(const TEdge& edge) const;
+ void InsertScanbeam(const long64 Y);
+ long64 PopScanbeam();
+ void InsertLocalMinimaIntoAEL(const long64 botY);
+ void InsertEdgeIntoAEL(TEdge *edge);
+ void AddEdgeToSEL(TEdge *edge);
+ void CopyAELToSEL();
+ void DeleteFromSEL(TEdge *e);
+ void DeleteFromAEL(TEdge *e);
+ void UpdateEdgeIntoAEL(TEdge *&e);
+ void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
+ bool IsContributing(const TEdge& edge) const;
+ bool IsTopHorz(const long64 XPos);
+ void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
+ void DoMaxima(TEdge *e, long64 topY);
+ void ProcessHorizontals();
+ void ProcessHorizontal(TEdge *horzEdge);
+ void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
+ void AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
+ OutRec* GetOutRec(int idx);
+ void AppendPolygon(TEdge *e1, TEdge *e2);
+ void IntersectEdges(TEdge *e1, TEdge *e2,
+ const IntPoint &pt, const IntersectProtects protects);
+ OutRec* CreateOutRec();
+ void AddOutPt(TEdge *e, const IntPoint &pt);
+ void DisposeAllPolyPts();
+ void DisposeOutRec(PolyOutList::size_type index);
+ bool ProcessIntersections(const long64 botY, const long64 topY);
+ void InsertIntersectNode(TEdge *e1, TEdge *e2, const IntPoint &pt);
+ void BuildIntersectList(const long64 botY, const long64 topY);
+ void ProcessIntersectList();
+ void ProcessEdgesAtTopOfScanbeam(const long64 topY);
+ void BuildResult(Polygons& polys);
+ void BuildResult2(PolyTree& polytree);
+ void SetHoleState(TEdge *e, OutRec *outrec);
+ void DisposeIntersectNodes();
+ bool FixupIntersectionOrder();
+ void FixupOutPolygon(OutRec &outrec);
+ bool IsHole(TEdge *e);
+ void FixHoleLinkage(OutRec &outrec);
+ void AddJoin(TEdge *e1, TEdge *e2, int e1OutIdx = -1, int e2OutIdx = -1);
+ void ClearJoins();
+ void AddHorzJoin(TEdge *e, int idx);
+ void ClearHorzJoins();
+ bool JoinPoints(const JoinRec *j, OutPt *&p1, OutPt *&p2);
+ void FixupJoinRecs(JoinRec *j, OutPt *pt, unsigned startIdx);
+ void JoinCommonEdges();
+ void DoSimplePolygons();
+ void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
+ void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
+};
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+class clipperException : public std::exception
+{
+ public:
+ clipperException(const char* description): m_descr(description) {}
+ virtual ~clipperException() throw() {}
+ virtual const char* what() const throw() {return m_descr.c_str();}
+ private:
+ std::string m_descr;
+};
+//------------------------------------------------------------------------------
+
+} //ClipperLib namespace
+
+#endif //clipper_hpp
+
+
diff --git a/hyp2mat/lib/clipper.txt b/hyp2mat/lib/clipper.txt
new file mode 100644
index 0000000..51acabf
--- /dev/null
+++ b/hyp2mat/lib/clipper.txt
@@ -0,0 +1,29 @@
+The Clipper code library, the "Software" (that includes Delphi, C++ & C#
+source code, accompanying samples and documentation), has been released
+under the following license, terms and conditions:
+
+Boost Software License - Version 1.0 - August 17th, 2003
+http://www.boost.org/LICENSE_1_0.txt
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
diff --git a/hyp2mat/lib/copper.cc b/hyp2mat/lib/copper.cc
new file mode 100644
index 0000000..3b5addd
--- /dev/null
+++ b/hyp2mat/lib/copper.cc
@@ -0,0 +1,382 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hyperlynx.h"
+#include "crop.h"
+#include <iostream>
+#include <algorithm>
+
+using namespace Hyp2Mat;
+
+/*
+ * These routines calculate the copper layers.
+ * The routines operate in one of two modes: raw or normal.
+ *
+ * In raw mode, polygons are simply copied from input to output. No processing is done. Raw mode is mainly useful for debugging purposes.
+ *
+ * In normal mode, all copper on a single layer is calculated as a set of non-overlapping polygons.
+ * Two kinds of copper polygons exist: normal and "PLANE".
+ *
+ * Normal copper polygons are simply added to the copper of the plane.
+ * Copper polygons of type "PLANE" have a clearance from other nets. This clearance is the "plane separation".
+ * Plane separation can be specified for the board, for a layer, for a net or for a polygon.
+ *
+ * Copper polygons of type "PLANE" are calculated as follows:
+ * - all polygons of all other nets on the same layer are computed, and explanded by the plane separation. This produces the mask.
+ * - The mask is subtracted from the "PLANE" type polygon.
+ *
+ * If mask polygon and plane polygon have a different value for plane separation the largest value is used.
+ *
+ * Note there is no plane separation between polygons of the same net.
+ */
+
+/*
+ * copy layer stackup
+ */
+
+void HyperLynx::CopyStackUp(Hyp2Mat::PCB& pcb)
+{
+ /* iterate over all layers */
+ for (HypFile::LayerList::iterator l = hyp_file.stackup.begin(); l != hyp_file.stackup.end(); ++l) {
+
+ /* check if we're interested in this layer. if no layers are specified, copy all layers */
+ bool layer_wanted = layers.empty() || (std::find(layers.begin(), layers.end(), l->layer_name) != layers.end());
+
+ /* copy this layer if needed */
+ if (layer_wanted) CopyLayer(pcb, *l);
+ }
+
+ return;
+}
+
+/*
+ * copy layer
+ */
+
+void HyperLynx::CopyLayer(Hyp2Mat::PCB& pcb, HypFile::Layer& hyp_layer)
+{
+
+ Hyp2Mat::Layer layer;
+ layer.layer_name = hyp_layer.layer_name;
+ layer.material_name = hyp_layer.material_name;
+ switch (hyp_layer.layer_type) {
+ case HypFile::LAYER_SIGNAL : layer.layer_type = Hyp2Mat::LAYER_SIGNAL;
+ break;
+ case HypFile::LAYER_DIELECTRIC : layer.layer_type = Hyp2Mat::LAYER_DIELECTRIC;
+ break;
+ case HypFile::LAYER_PLANE : layer.layer_type = Hyp2Mat::LAYER_PLANE;
+ break;
+ default: layer.layer_type = Hyp2Mat::LAYER_SIGNAL;
+ break;
+ }
+ layer.z0 = hyp_layer.z0;
+ layer.z1 = hyp_layer.z1;
+ layer.thickness = hyp_layer.thickness;
+ layer.epsilon_r = hyp_layer.epsilon_r;
+ layer.loss_tangent = hyp_layer.loss_tangent;
+ layer.bulk_resistivity = hyp_layer.bulk_resistivity;
+ layer.resistivity_temp_coeff = hyp_layer.temperature_coefficient;
+
+ /* if the layer is metallic, copy the metal */
+ if (layer.layer_type != Hyp2Mat::LAYER_DIELECTRIC) {
+
+ /* Use default plane separation if layer plane separation is not set. layer plane separation is -1.0 if not set */
+ layer_plane_separation = hyp_file.board.plane_separation;
+
+ /* board-level override from SetClearance method / --clearance command-line parameter */
+ if (HyperLynx::clearance >= 0) layer_plane_separation = HyperLynx::clearance;
+
+ if (hyp_layer.plane_separation >= 0) layer_plane_separation = hyp_layer.plane_separation;
+
+ /* copy layer copper */
+ Hyp2Mat::FloatPolygons raw_polygons;
+ Hyp2Mat::Polygon layer_copper = CopyCopper(layer, raw_polygons);
+
+ if (raw)
+ layer.metal = raw_polygons;
+ else
+ layer.metal = layer_copper.Result(); /* calculate layer copper */
+ }
+
+ /* add to stackup */
+ pcb.stackup.push_back(layer);
+
+ return;
+}
+
+/*
+ * copy copper
+ */
+
+Hyp2Mat::Polygon HyperLynx::CopyCopper(Hyp2Mat::Layer layer, Hyp2Mat::FloatPolygons& raw_polygons)
+{
+
+ /* vectors of different polygon types. element [i] belongs to net hyp_file.net[i].net_name */
+ std::vector<Hyp2Mat::Polygon> net_pour; /* POLYGON T=POUR and ordinary line and arc segments */
+ std::vector<Hyp2Mat::Polygon> net_plane; /* POLYGON T=PLANE */
+ std::vector<Hyp2Mat::Polygon> net_copper; /* POLYGON T=COPPER */
+ std::vector<Hyp2Mat::Polygon> net_pads; /* pads */
+ std::vector<Hyp2Mat::Polygon> net_antipads; /* anti-pads */
+ std::vector<bool> net_wanted; /* true if net needs to be included in the output */
+
+ HypFile::NetList::size_type net_size = hyp_file.net.size();
+ net_pour.resize(net_size);
+ net_plane.resize(net_size);
+ net_copper.resize(net_size);
+ net_pads.resize(net_size);
+ net_antipads.resize(net_size);
+ net_wanted.resize(net_size);
+
+ /* iterate over all nets twice.
+ The first time we calculate all the copper except "plane" polygons.
+ The second time we calculate plane polygon pours. Plane polygons have a clearance from copper of other nets */
+
+ /* Determine which nets we need to include */
+ for (int i = 0; i < hyp_file.net.size(); ++i) {
+ /* check if we're interested in this net. if no nets are specified, copy all nets */
+ net_wanted[i] = nets.empty() || (std::find(nets.begin(), nets.end(), hyp_file.net[i].net_name) != nets.end());
+ }
+
+ /* calculate all copper. */
+ for (int i = 0; i < hyp_file.net.size(); ++i) {
+
+ /* skip unwanted nets */
+ if (!net_wanted[i]) continue;
+
+ /* copy anti-metal of this net */
+ net_pour[i] = CopyNet(layer, hyp_file.net[i], POLYGON_TYPE_POUR, raw_polygons);
+ net_copper[i] = CopyNet(layer, hyp_file.net[i], POLYGON_TYPE_COPPER, raw_polygons);
+ net_plane[i] = CopyNet(layer, hyp_file.net[i], POLYGON_TYPE_PLANE, raw_polygons);
+ net_pads[i] = CopyNet(layer, hyp_file.net[i], POLYGON_TYPE_PAD, raw_polygons);
+ net_antipads[i] = CopyNet(layer, hyp_file.net[i], POLYGON_TYPE_ANTIPAD, raw_polygons);
+ }
+
+ /*
+ * sum all copper on this layer
+ */
+
+ Hyp2Mat::Polygon layer_copper;
+
+ /* add plane and pour polygons */
+ for (int i = 0; i < hyp_file.net.size(); ++i) {
+
+ /* skip unwanted nets */
+ if (!net_wanted[i]) continue;
+
+ layer_copper.Union(net_pour[i]);
+ layer_copper.Union(net_plane[i]);
+ }
+
+ /* subtract antipads from pour and plane polygons. */
+ for (int i = 0; i < hyp_file.net.size(); ++i) {
+
+ /* skip unwanted nets */
+ if (!net_wanted[i]) continue;
+
+ layer_copper.Difference(net_antipads[i]);
+ }
+
+ /* add copper and pad polygons */
+ for (int i = 0; i < hyp_file.net.size(); ++i) {
+
+ /* skip unwanted nets */
+ if (!net_wanted[i]) continue;
+
+ layer_copper.Union(net_copper[i]);
+ layer_copper.Union(net_pads[i]);
+ }
+
+ /*
+ * Crop to board size
+ */
+
+ if (!board.IsEmpty()) {
+ layer_copper.Intersection(board);
+ layer_copper = CropPolygon(layer_copper, bounds);
+ }
+
+ return layer_copper;
+}
+
+/*
+ * copy net
+ * copy all polygons of a net.
+ * if anti is true, copy anti-pads
+ * if plane is true, copy plane polygons (and subtract other_nets)
+ */
+
+Hyp2Mat::Polygon HyperLynx::CopyNet(Hyp2Mat::Layer layer, HypFile::Net& hyp_net, polygon_type_enum poly_type, Hyp2Mat::FloatPolygons& raw_polygons)
+{
+ Hyp2Mat::Polygon net_copper;
+
+ /* calculate net-specific plane separation */
+ net_plane_separation = layer_plane_separation;
+ if (hyp_net.plane_separation >= 0) net_plane_separation = hyp_net.plane_separation;
+
+ /* iterate over all Hyperlynx polygon id's */
+ for (HypFile::PolygonMap::iterator j = hyp_net.metal.begin(); j != hyp_net.metal.end(); ++j) {
+
+ if (j->second.empty()) continue;
+
+ /* polygon layer */
+ std::string poly_layer_name = j->second.begin()->layer_name;
+
+ /* this polygon is on our layer if the polygon's layer name is the current layer's name.*/
+ if (poly_layer_name != layer.layer_name) continue;
+
+ /* Check polygon is the correct type (pour, plane, copper, pad or antipad) */
+ if (j->second.begin()->polygon_type != poly_type) continue;
+
+ /* Join all Hyperlynx polygons/polyvoids with the same id in a single Hyp2Mat polygon */
+ Hyp2Mat::Polygon poly_copper = CopyPolygon(j->second, raw_polygons);
+ net_copper.Union(poly_copper);
+ }
+
+ return net_copper;
+}
+
+/*
+ * copy polygon
+ * polygon is copied twice; once to the list of "raw" polygons for --raw output;
+ * once added to this layers' copper for normal output.
+ */
+
+Hyp2Mat::Polygon HyperLynx::CopyPolygon(HypFile::PolygonList metal, Hyp2Mat::FloatPolygons& raw_polygons)
+{
+ Hyp2Mat::Polygon poly;
+
+ if (metal.empty()) return poly; /* ought never to happen */
+
+ /* Join all Hyperlynx polygons/polyvoids with the same id in a single Hyp2Mat polygon */
+ for (HypFile::PolygonList::iterator k = metal.begin(); k != metal.end(); ++k) {
+
+ /* copy vertices */
+ Hyp2Mat::FloatPolygon edge;
+ for (HypFile::PointList::iterator v = k->vertex.begin(); v != k->vertex.end(); ++v)
+ edge.push_back(Hyp2Mat::FloatPoint(v->x, v->y));
+
+ /* fix orientation */
+ if (IsClockwise(edge) != k->positive) Reverse(edge);
+
+ /* add to raw polygons */
+ Hyp2Mat::FloatPoly raw_poly;
+ raw_poly.poly = edge;
+ raw_poly.is_hole = !k->positive;
+ raw_poly.nesting_level = 0;
+ raw_polygons.push_back(raw_poly);
+
+ /* add to cooked polygons */
+ if (k->positive) poly.AddEdge(edge);
+ else poly.AddHole(edge);
+ }
+
+ /* take line width of polygon into account */
+ double width = metal.begin()->width;
+ poly.Simplify();
+ poly.Offset(width/2);
+
+ if (metal.front().polygon_type == POLYGON_TYPE_PLANE) {
+ /* calculate polygon-specific plane separation */
+ polygon_plane_separation = net_plane_separation;
+ if (metal.front().plane_separation >= 0) polygon_plane_separation = metal.front().plane_separation;
+
+ /* calculate mask needed to get clearance with other nets */
+ Hyp2Mat::Polygon mask;
+ mask = PlaneSeparation(metal.front().layer_name, metal.front().net_name);
+
+ /* subtract mask from polygon */
+ poly.Difference(mask);
+ }
+
+ return poly;
+}
+
+/*
+ * Creates the mask by which to trim a plane polygon, taking into account plane separation.
+ *
+ * Algorithm: sum all polygons on layer 'layer_name' belonging to nets other than net 'net_name',
+ * expanded by the biggest polygon_plane separation.
+ */
+
+Hyp2Mat::Polygon HyperLynx::PlaneSeparation(std::string layer_name, std::string net_name)
+{
+ Hyp2Mat::Polygon mask;
+
+ if (polygon_plane_separation < 0) return mask; // XXX correct?
+
+ /* iterate over all nets */
+ for (HypFile::NetList::iterator i = hyp_file.net.begin(); i != hyp_file.net.end(); ++i) {
+
+ /* all nets except net 'net_name' */
+ if (i->net_name == net_name) continue;
+
+ /* wanted nets only */
+ bool net_wanted = nets.empty() || (std::find(nets.begin(), nets.end(), net_name) != nets.end());
+ if (!net_wanted) continue;
+
+ /* iterate over all Hyperlynx polygon id's */
+ for (HypFile::PolygonMap::iterator j = i->metal.begin(); j != i->metal.end(); ++j) {
+ Hyp2Mat::Polygon poly;
+
+ if (j->second.empty()) continue; /* ought never to happen */
+
+ /* polygons on layer 'layer_name' only */
+ if (j->second.begin()->layer_name != layer_name) continue;
+
+ /* iterate over all edges */
+ for (HypFile::PolygonList::iterator k = j->second.begin(); k != j->second.end(); ++k) {
+
+ /* iterate over all vertices */
+ Hyp2Mat::FloatPolygon edge;
+ for (HypFile::PointList::iterator v = k->vertex.begin(); v != k->vertex.end(); ++v)
+ edge.push_back(Hyp2Mat::FloatPoint(v->x, v->y));
+
+ /* add edge to polygon */
+ if (k->positive) poly.AddEdge(edge);
+ else poly.AddHole(edge);
+ }
+
+ double width = j->second.begin()->width;
+
+ /* calculate plane separation of the masking polygon */
+ double mask_plane_separation;
+ mask_plane_separation = layer_plane_separation; /* layer */
+ if (i->plane_separation >= 0) mask_plane_separation = i->plane_separation; /* net */
+ if (j->second.begin()->plane_separation >= 0) mask_plane_separation = j->second.begin()->plane_separation; /* polygon */
+
+ /* compare plane separation of polygon and masking polygon; choose biggest plane separation. */
+ double biggest_plane_separation;
+ if (polygon_plane_separation > mask_plane_separation) biggest_plane_separation = polygon_plane_separation;
+ else biggest_plane_separation = mask_plane_separation;
+
+ /* grow masking polygon by plane separation */
+ poly.Offset(width/2 + biggest_plane_separation);
+
+ /* add to mask */
+ mask.Union(poly);
+
+ }
+
+ }
+
+ return mask;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/crop.cc b/hyp2mat/lib/crop.cc
new file mode 100644
index 0000000..ebf05c9
--- /dev/null
+++ b/hyp2mat/lib/crop.cc
@@ -0,0 +1,108 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "crop.h"
+
+using namespace Hyp2Mat;
+
+/*
+ * Adjust bounds to the size of the board
+ */
+
+Hyp2Mat::Bounds Hyp2Mat::AdjustBounds(Hyp2Mat::PCB& pcb, Hyp2Mat::Bounds bounds)
+{
+ Bounds new_bounds = bounds;
+ Bounds size = pcb.GetBounds();
+
+ if (new_bounds.x_min < size.x_min) new_bounds.x_min = size.x_min;
+ if (new_bounds.x_max > size.x_max) new_bounds.x_max = size.x_max;
+ if (new_bounds.y_min < size.y_min) new_bounds.y_min = size.y_min;
+ if (new_bounds.y_max > size.y_max) new_bounds.y_max = size.y_max;
+ if (new_bounds.z_min < size.z_min) new_bounds.z_min = size.z_min;
+ if (new_bounds.z_max > size.z_max) new_bounds.z_max = size.z_max;
+
+ return new_bounds;
+}
+
+/*
+ * Crop polygon
+ */
+
+Hyp2Mat::Polygon Hyp2Mat::CropPolygon (Hyp2Mat::Polygon poly, Hyp2Mat::Bounds bounds)
+{
+ Hyp2Mat::Polygon result;
+ FloatPolygon bounding_poly;
+
+ bounding_poly.push_back(FloatPoint(bounds.x_min, bounds.y_min));
+ bounding_poly.push_back(FloatPoint(bounds.x_max, bounds.y_min));
+ bounding_poly.push_back(FloatPoint(bounds.x_max, bounds.y_max));
+ bounding_poly.push_back(FloatPoint(bounds.x_min, bounds.y_max));
+ result.AddEdge(bounding_poly);
+
+ result.Intersection(poly);
+
+ return result;
+}
+
+/*
+ * Remove all vias which are out of bounds.
+ * XXX no longer needed?
+ */
+
+void Hyp2Mat::CropVias(Hyp2Mat::PCB& pcb, Hyp2Mat::Bounds bounds)
+{
+ Hyp2Mat::Via new_via;
+ Hyp2Mat::ViaList new_vialist;
+
+ for (Hyp2Mat::ViaList::iterator i = pcb.via.begin(); i != pcb.via.end(); ++i) {
+ if ((i->x > bounds.x_max) || (i->x < bounds.x_min) || (i->y > bounds.y_max) || (i->y < bounds.y_min)) continue;
+ if ((i->z0 > bounds.z_max) && (i->z1 > bounds.z_max)) continue;
+ if ((i->z0 < bounds.z_min) && (i->z1 < bounds.z_min)) continue;
+ new_via = *i;
+ if (new_via.z1 > bounds.z_max) new_via.z1 = bounds.z_max;
+ if (new_via.z0 < bounds.z_min) new_via.z0 = bounds.z_min;
+ new_vialist.push_back(new_via);
+ }
+ pcb.via = new_vialist;
+ return;
+}
+
+/*
+ * Remove all layers which are out of bounds.
+ */
+
+void Hyp2Mat::CropLayers(Hyp2Mat::PCB& pcb, Hyp2Mat::Bounds& bounds)
+{
+ Hyp2Mat::Layer new_layer;
+ Hyp2Mat::LayerList new_stackup;
+
+ for (Hyp2Mat::LayerList::iterator l = pcb.stackup.begin(); l != pcb.stackup.end(); ++l) {
+ if ((l->z0 > bounds.z_max) && (l->z1 > bounds.z_max)) continue;
+ if ((l->z0 < bounds.z_min) && (l->z1 < bounds.z_min)) continue;
+ new_layer = *l;
+ if (new_layer.z1 > bounds.z_max) new_layer.z1 = bounds.z_max;
+ if (new_layer.z0 < bounds.z_min) new_layer.z0 = bounds.z_min;
+ new_stackup.push_back(new_layer);
+ }
+ pcb.stackup = new_stackup;
+ return;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/crop.h b/hyp2mat/lib/crop.h
new file mode 100644
index 0000000..dd497bc
--- /dev/null
+++ b/hyp2mat/lib/crop.h
@@ -0,0 +1,36 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CROP_H
+#define CROP_H
+
+#include "hyp2mat.h"
+#include "polygon.h"
+
+namespace Hyp2Mat {
+ Hyp2Mat::Bounds AdjustBounds(Hyp2Mat::PCB& pcb, Bounds bounds);
+ Hyp2Mat::Polygon CropPolygon (Hyp2Mat::Polygon poly, Hyp2Mat::Bounds bounds);
+ void CropVias(Hyp2Mat::PCB& pcb, Hyp2Mat::Bounds bounds);
+ void CropLayers(Hyp2Mat::PCB& pcb, Hyp2Mat::Bounds& bounds);
+ }
+
+#endif
+
+/* not truncated */
diff --git a/hyp2mat/lib/csxcad.cc b/hyp2mat/lib/csxcad.cc
new file mode 100644
index 0000000..a1ec204
--- /dev/null
+++ b/hyp2mat/lib/csxcad.cc
@@ -0,0 +1,376 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <algorithm>
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <cmath>
+
+#include "csxcad.h"
+
+using namespace Hyp2Mat;
+
+CSXCAD::CSXCAD()
+{
+ /* CSXcad priorities */
+
+ prio_dielectric = 100; // FR4 dielectric
+ prio_material = 200; // copper
+ prio_via = 300; // via metal
+ prio_drill = 400; // hole
+
+ return;
+}
+
+void CSXCAD::export_edge(FloatPolygon& edge)
+{
+ std::cout << "pgon = [];" << std::endl;
+ for (FloatPolygon::iterator i = edge.begin(); i != edge.end(); ++i) {
+ std::cout << "pgon(:, end+1) = [" << i->x << ";" << i->y << "];" << std::endl;
+ }
+ return;
+}
+
+/*
+ * quote a string using matlab conventions.
+ */
+
+std::string CSXCAD::string2matlab(std::string str)
+{
+ std::ostringstream ostring;
+
+ // escape non-alpha characters, or WriteOpenEMS (in matlab) may crash on characters such as '%' in strings
+
+ ostring << "'";
+ for (unsigned int i = 0; i < str.size(); i++) {
+ if (str[i] == '\'') ostring << '\'';
+ if (str[i] == '%') ostring << '%';
+ ostring << str[i];
+ }
+ ostring << "'";
+
+ return ostring.str();
+}
+
+/* true if polygon list contains at least one (positive) polygon */
+
+bool CSXCAD::contains_polygon(Hyp2Mat::FloatPolygons& polygons)
+{
+ bool result = false;
+
+ for (FloatPolygons::iterator i = polygons.begin(); i != polygons.end(); ++i) {
+ result = !i->is_hole;
+ if (result) break;
+ };
+
+ return result;
+}
+
+/* true if polygon list contains at least one hole */
+
+bool CSXCAD::contains_hole(Hyp2Mat::FloatPolygons& polygons)
+{
+ bool result = false;
+
+ for (FloatPolygons::iterator i = polygons.begin(); i != polygons.end(); ++i) {
+ result = i->is_hole;
+ if (result) break;
+ };
+
+ return result;
+}
+
+ /*
+ * Export dielectric
+ * If pcb_outline is true, export exact board shape, including holes.
+ * If pcb_outline is false, export bounding box.
+ */
+
+void CSXCAD::export_board(Hyp2Mat::PCB& pcb, bool pcb_outline)
+{
+ /* CSXCAD coordinate grid definition */
+ Bounds bounds = pcb.GetBounds();
+
+ std::cout << "function CSX = pcb(CSX)" << std::endl;
+ std::cout << "% matlab script created by hyp2mat" << std::endl;
+ std::cout << "% create minimal mesh" << std::endl;
+ std::cout << "mesh = {};" << std::endl;
+ std::cout << "mesh.x = [" << bounds.x_min << " " << bounds.x_max << "];" << std::endl;
+ std::cout << "mesh.y = [" << bounds.y_min << " " << bounds.y_max << "];" << std::endl;
+ std::cout << "mesh.z = [" << bounds.z_min << " " << bounds.z_max << "];" << std::endl;
+ std::cout << "% add mesh" << std::endl;
+ std::cout << "CSX = DefineRectGrid(CSX, 1, mesh);" << std::endl;
+
+ /*
+ * Export the board. The board outline is positive;
+ */
+
+ /*
+ * create board material if at least one positive polygon present
+ * We output one polygon for each dielectric layer, as each layer may have
+ * a different epsilon_r.
+ */
+
+ if (contains_polygon(pcb.board)) {
+
+ for (FloatPolygons::iterator i = pcb.board.begin(); i != pcb.board.end(); ++i) {
+ /* only output board material, not holes */
+ if (i->is_hole) continue;
+ for (LayerList::reverse_iterator l = pcb.stackup.rbegin(); l != pcb.stackup.rend(); ++l) {
+ /* only output dielectrics */
+ if (l->layer_type != LAYER_DIELECTRIC) continue;
+
+ /* Output command to create material */
+ std::cout << "CSX = AddHyperLynxDielectric(CSX, 'Dielectric_" << l->layer_name << "', " << l->epsilon_r << ", " << l->loss_tangent << ");" << std::endl;
+
+ /* output CSXCAD polygon */
+ std::cout << "% board outline, layer " << l->layer_name << std::endl;
+ int priority = prio_dielectric + i->nesting_level;
+ if (pcb_outline) {
+ /* Use AddLinPoly */
+ export_edge(i->poly);
+ std::cout << "CSX = AddLinPoly(CSX, 'Dielectric_" << l->layer_name << "', " << priority << ", 2, " << l->z0 << ", pgon, " << l->z1 - l->z0 << ");" << std::endl;
+ }
+ else {
+ /* Use AddBox */
+ std::cout << "CSX = AddBox(CSX, 'Dielectric_" << l->layer_name << "', " << priority << ", [ ";
+ std::cout << bounds.x_min << ", " << bounds.y_min << ", " << l->z0 << "], [ ";
+ std::cout << bounds.x_max << ", " << bounds.y_max << ", " << l->z1 << "] );" << std::endl;
+ }
+ }
+ }
+ };
+
+
+ /*
+ * Export board cutouts
+ */
+
+ /*
+ * create board cutout material if at least one negative polygon present
+ * For each cutout we create a single polygon which goes through all layers.
+ */
+
+ if (pcb_outline && contains_hole(pcb.board)) {
+ std::cout << "% create board cutout material" << std::endl;
+ std::cout << "CSX = AddMaterial( CSX, 'Drill');" << std::endl;
+ std::cout << "CSX = SetMaterialProperty( CSX, 'Drill', 'Epsilon', 1, 'Mue', 1);" << std::endl;
+
+ for (FloatPolygons::iterator i = pcb.board.begin(); i != pcb.board.end(); ++i) {
+ /* output CSXCAD polygon */
+ if (!i->is_hole) continue;
+ std::cout << "% board cutout" << std::endl;
+ export_edge(i->poly);
+ int priority = prio_dielectric + i->nesting_level;
+ std::cout << "CSX = AddLinPoly(CSX, 'Drill', " << priority << ", 2, " << bounds.z_min << ", pgon, " << bounds.z_max - bounds.z_min << ");" << std::endl;
+ }
+ };
+
+ return;
+}
+
+ /*
+ * Export copper.
+ * If lossy_copper is true, take copper conductivity into account.
+ * If lossy_copper is false, assume copper is perfect conductor.
+ */
+
+void CSXCAD::export_layer(Hyp2Mat::PCB& pcb, Hyp2Mat::Layer& layer, bool lossy_copper, bool metal_3d, bool odd)
+{
+ std::string layer_material = layer.layer_name + "_copper";
+ std::string layer_cutout = layer.layer_name + "_cutout";
+
+ // create layer material if at least one positive polygon present
+ if (contains_polygon(layer.metal)) {
+ std::cout << "% create layer " << layer.layer_name << " material" << std::endl;
+
+ double copper_resistivity = 0.0;
+ if (lossy_copper && (layer.bulk_resistivity > 0)) copper_resistivity = layer.bulk_resistivity;
+
+ if (metal_3d)
+ std::cout << "CSX = AddHyperLynxMetal3D(CSX, '" << layer_material << "', " << copper_resistivity << ", " << layer.thickness << ");" << std::endl;
+ else
+ std::cout << "CSX = AddHyperLynxMetal2D(CSX, '" << layer_material << "', " << copper_resistivity << ", " << layer.thickness << ");" << std::endl;
+ };
+
+ // create layer cutout material if at least one hole present
+ if (contains_hole(layer.metal)) {
+ std::cout << "% create layer " << layer.layer_name << " cutout" << std::endl;
+ std::cout << "CSX = AddMaterial( CSX, '" << layer_cutout << "');" << std::endl;
+ // outer and inner copper layers have different epsilonr
+ std::cout << "CSX = SetMaterialProperty( CSX, '" << layer_cutout << "', 'Epsilon', " << layer.epsilon_r << ", 'Mue', 1);" << std::endl;
+ };
+
+ /*
+ * Export the layer.
+ */
+
+ std::string material;
+
+ for (FloatPolygons::iterator i = layer.metal.begin(); i != layer.metal.end(); ++i) {
+ /* output CSXCAD polygon */
+ if (!i->is_hole) {
+ std::cout << "% copper" << std::endl;
+ material = layer_material;
+ }
+ else {
+ std::cout << "% cutout" << std::endl;
+ material = layer_cutout;
+ };
+
+ export_edge(i->poly);
+ int priority = prio_material + i->nesting_level;
+ /* Default is exporting copper as a 2D conducting sheet */
+ if (metal_3d) {
+ // We assume alternating layers of substrate and prepreg,and that metal layers are pushed into the prepreg.
+ // This means that the top metal layer is pointing up, the second metal layer is pointing down, and so on.
+ double thickness = layer.thickness;
+ if (odd) thickness = -thickness;
+ std::cout << "CSX = AddLinPoly(CSX, '" << material << "', " << priority << ", 2, " << layer.z0 << ", pgon, " << thickness << ");" << std::endl;
+ }
+ else
+ std::cout << "CSX = AddPolygon(CSX, '" << material << "', " << priority << ", 2, " << layer.z0 << ", pgon);" << std::endl;
+ }
+
+ return;
+}
+
+/*
+ * Export vias
+ */
+
+void CSXCAD::export_vias(Hyp2Mat::PCB& pcb)
+{
+ if (!pcb.via.empty()) {
+ /* create via material */
+ std::cout << "% via copper" << std::endl;
+ std::cout << "CSX = AddMetal( CSX, 'via' );" << std::endl;
+ for (ViaList::iterator i = pcb.via.begin(); i != pcb.via.end(); ++i) {
+ double z0 = i->z0;
+ double z1 = i->z1;
+ std::cout << "CSX = AddCylinder(CSX, 'via', " << prio_via ;
+ std::cout << ", [ " << i->x << " , " << i->y << " , " << z0;
+ std::cout << " ], [ " << i->x << " , " << i->y << " , " << z1;
+ std::cout << " ], " << i->radius << ");" << std::endl;
+ }
+ }
+ return;
+}
+
+/*
+ * Export devices
+ */
+
+void CSXCAD::export_devices(Hyp2Mat::PCB& pcb)
+{
+ std::cout << "% devices" << std::endl;
+ std::cout << "CSX.HyperLynxDevice = {};" << std::endl;
+ for (DeviceList::iterator i = pcb.device.begin(); i != pcb.device.end(); ++i) {
+ std::cout << "CSX.HyperLynxDevice{end+1} = struct('name', " << string2matlab(i->name) << ", 'ref', " << string2matlab(i->ref);
+
+ /* output device value if available */
+ if (i->value_type == DEVICE_VALUE_FLOAT)
+ std::cout << ", 'value', " << i->value_float;
+ else if (i->value_type == DEVICE_VALUE_STRING)
+ std::cout << ", 'value', " << string2matlab(i->value_string);
+
+ std::cout << ", 'layer_name', " << string2matlab(i->layer_name);
+ std::cout << ");" << std::endl;
+ }
+}
+
+/*
+ * Export port
+ */
+
+void CSXCAD::export_ports(Hyp2Mat::PCB& pcb)
+{
+ std::cout << "% ports" << std::endl;
+ std::cout << "CSX.HyperLynxPort = {};" << std::endl;
+ for (PinList::iterator i = pcb.pin.begin(); i != pcb.pin.end(); ++i) {
+ /* csxcad requires rectangular ports, axis aligned. Use the bounding box. XXX fixme ? Use largest inscribed rectangle instead */
+ double x_max = 0, y_max = 0, x_min = 0, y_min = 0;
+ bool first = true;
+ for (FloatPolygon::iterator j = i->metal.begin(); j != i->metal.end(); ++j) {
+ if ((j->x > x_max) || first) x_max = j->x;
+ if ((j->y > y_max) || first) y_max = j->y;
+ if ((j->x < x_min) || first) x_min = j->x;
+ if ((j->y < y_min) || first) y_min = j->y;
+ first = false;
+ }
+
+ /* determine whether port is on top or bottom layer of pcb */
+ double dbottom = std::abs(i->z0 - pcb.stackup.back().z0);
+ double dtop = std::abs(i->z1 - pcb.stackup.front().z1);
+ bool on_top = dtop <= dbottom;
+
+ std::cout << "CSX.HyperLynxPort{end+1} = struct('ref', " << string2matlab(i->ref);
+ std::cout << ", 'xc', " << i->x << ", 'yc', " << i->y << ", 'z', " << i->z0;
+ std::cout << ", 'x1', " << x_min << ", 'y1', " << y_min ;
+ std::cout << ", 'x2', " << x_max << ", 'y2', " << y_max ;
+ std::cout << ", 'position', " << (on_top ? "'top'" : "'bottom'");
+ std::cout << ", 'layer_name', " << string2matlab(i->layer_name) << ");" << std::endl;
+ }
+ return;
+}
+
+/*
+ * Write pcb to file in CSXCAD format
+ */
+
+void CSXCAD::Write(const std::string& filename, Hyp2Mat::PCB pcb, bool pcb_outline, bool lossy_copper, bool metal_3d)
+{
+
+ /* open file for output */
+
+ if ((filename != "-") && (freopen(filename.c_str(), "w", stdout) == NULL)) {
+ std::cerr << "could not open '" << filename << "' for writing";
+ return;
+ }
+
+ /* Export dielectric. If pcb_outline is true, export exact board shape, including holes.
+ If pcb_outline is false, export bounding box. */
+ export_board(pcb, pcb_outline);
+
+ /* Export copper */
+ std::cout << "% copper" << std::endl;
+ bool odd = false;
+ for (LayerList::iterator l = pcb.stackup.begin(); l != pcb.stackup.end(); ++l) {
+ if (l->layer_type == LAYER_DIELECTRIC) odd = !odd;
+ export_layer(pcb, *l, lossy_copper, metal_3d, odd);
+ }
+
+ /* Export vias */
+ export_vias(pcb);
+
+ export_devices(pcb);
+
+ export_ports(pcb);
+
+ std::cout << "%not truncated" << std::endl;
+
+ fclose(stdout);
+
+ return;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/csxcad.h b/hyp2mat/lib/csxcad.h
new file mode 100644
index 0000000..af7ec5b
--- /dev/null
+++ b/hyp2mat/lib/csxcad.h
@@ -0,0 +1,53 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CSXCAD_H
+#define CSXCAD_H
+
+#include "hyp2mat.h"
+
+class CSXCAD {
+public:
+ CSXCAD();
+ void Write(const std::string& filename, Hyp2Mat::PCB pcb, bool pcb_outline, bool lossy_copper, bool metal_3d); /* save in CSXcad format */
+
+private:
+ int prio_dielectric; // FR4 dielectric
+ int prio_material; // copper
+ int prio_via; // via metal
+ int prio_drill; // hole
+
+ double adjust_z(Hyp2Mat::PCB& pcb, double z);
+
+ void export_edge(Hyp2Mat::FloatPolygon& edge); /* output a polygon edge */
+ void export_layer(Hyp2Mat::PCB& pcb, Hyp2Mat::Layer& layer, bool lossy_copper, bool metal_3d, bool odd); /* output a copper layer */
+ void export_board(Hyp2Mat::PCB& pcb, bool pcb_outline); /* output the dielectric */
+ void export_vias(Hyp2Mat::PCB& pcb);
+ void export_devices(Hyp2Mat::PCB& pcb);
+ void export_ports(Hyp2Mat::PCB& pcb);
+ bool contains_polygon(Hyp2Mat::FloatPolygons& polygons); /* true if polygon list contains at least one (positive) polygon */
+ bool contains_hole(Hyp2Mat::FloatPolygons& polygons); /* true if polygon list contains at least one hole */
+ std::string string2matlab(std::string str); /* quote a string using matlab conventions */
+
+ };
+
+#endif
+
+/* not truncated */
diff --git a/hyp2mat/lib/draw.cc b/hyp2mat/lib/draw.cc
new file mode 100644
index 0000000..0de9634
--- /dev/null
+++ b/hyp2mat/lib/draw.cc
@@ -0,0 +1,296 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Uses the Clipper library from Angus Johnson to do polygon arithmetic
+ */
+
+#include <iostream>
+#include <cstdio>
+#include <cmath>
+#include "hypfile.h"
+
+using namespace std;
+using namespace HypFile;
+
+/*
+ * Polygon constructor
+ */
+
+Polygon::Polygon()
+{
+ vertex.clear();
+ polygon_type = POLYGON_TYPE_COPPER;
+ id = -1;
+ positive = true; /* is a polygon, not a hole */
+ width = 0.0;
+ plane_separation = -1.0; /* negative if not set */
+ left_plane_separation = -1.0; /* negative if not set */
+ net_name.clear();
+ layer_name.clear();
+}
+
+/*
+ * Draw an arc from (x1, y1) to (x2, y2) with center (xc, yc) and radius r using a polygonal approximation.
+ * Arc may be clockwise or counterclockwise.
+ */
+
+Polygon HypFile::Hyp::arc2poly(double x1, double y1, double x2, double y2, double xc, double yc, double r, bool clockwise)
+{
+
+ Polygon arc_segment;
+ arc_segment.width = 0;
+ arc_segment.positive = true;
+ arc_segment.layer_name = "";
+
+ int poly_points = 0;
+
+ double alpha = atan2(y1-yc, x1-xc);
+ double beta = atan2(y2-yc, x2-xc);
+
+
+ if (clockwise) {
+ // draw arc clockwise from (x1,y1) to (x2,y2)
+ if (alpha < beta)
+ alpha = alpha + 2 * pi;
+ }
+ else {
+ // draw arc counterclockwise from (x1,y1) to (x2,y2)
+ if (beta < alpha)
+ beta = beta + 2 * pi;
+ // draw a circle if starting and end points are the same
+ if ((x1 == x2) && (y1 == y2))
+ beta = alpha + 2 * pi;
+ }
+
+ // Calculate number of segments needed for a full circle.
+ int segments = min_circle_segments;
+
+ if (arc_precision > 0) {
+ // Increase number of segments until difference between circular arc
+ // and polygonal approximation is less than max_arc_error
+
+ double arc_error;
+ do {
+ arc_error = r * (1 - cos(pi/segments));
+ if (arc_error > arc_precision) segments += 4;
+ }
+ while (arc_error > arc_precision);
+
+ }
+ else if (arc_precision < 0)
+ error("error: negative arc precision");
+
+ // A full circle is drawn using 'segments' segments; a 90 degree arc using segments/4.
+ poly_points = round(segments*abs(beta-alpha)/(2*pi));
+
+ // Sanity checks
+ if (poly_points < 1) poly_points = 1;
+
+ // first point
+ arc_segment.vertex.push_back(Point(x1, y1));
+
+ // intermediate points
+ for (int i = 1; i < poly_points ; i++) {
+ double angle = alpha + (beta - alpha) * i / poly_points;
+ double x = xc + r * cos(angle);
+ double y = yc + r * sin(angle);
+ arc_segment.vertex.push_back(Point(x, y));
+ }
+
+ // last point
+ arc_segment.vertex.push_back(Point(x2, y2));
+
+ return arc_segment;
+}
+
+/*
+ * Draws straight metal trace segment as a polygon.
+ */
+
+Polygon HypFile::Hyp::segment2poly(double x1, double y1, double x2, double y2, double width)
+{
+/*
+ * Note a hyperlynx line segment is drawn, ending in two half-circles:
+ * ------------------------------- ^
+ * / \ |
+ * | | width
+ * \ / |
+ * ------------------------------- V
+ *
+ * <----------- length ---------->
+ */
+
+ Polygon line_segment;
+ line_segment.width = 0;
+ line_segment.positive = true;
+ line_segment.layer_name = "";
+
+ /* Sanity check */
+ double dx;
+ double dy;
+ if (( x1 == x2) && (y2 == y1)) {
+ dx = width/2;
+ dy = 0;
+ }
+ else {
+ double length = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
+ dx = width / 2 * (y2 - y1) / length;
+ dy = width / 2 * (x2 - x1) / length;
+ }
+
+ Polygon arc2 = arc2poly(x2 + dx, y2 - dy, x2 - dx, y2 + dy, x2, y2, width/2, false);
+ Polygon arc1 = arc2poly(x1 - dx, y1 + dy, x1 + dx, y1 - dy, x1, y1, width/2, false);
+
+ line_segment.vertex = arc2.vertex;
+ line_segment.vertex.insert(line_segment.vertex.end(), arc1.vertex.begin(), arc1.vertex.end());
+
+ return line_segment;
+}
+
+/*
+ * Convert a pad to a polygon
+ */
+
+Polygon HypFile::Hyp::pad2poly(double pad_x, double pad_y, Pad pad)
+{
+ Polygon pad_poly;
+ pad_poly.width = 0;
+ pad_poly.layer_name = pad.layer_name;
+ pad_poly.positive = true;
+ if (pad.pad_type == PAD_TYPE_ANTIPAD) pad_poly.polygon_type = POLYGON_TYPE_ANTIPAD;
+ else pad_poly.polygon_type = POLYGON_TYPE_PAD;
+
+ double sx = pad.pad_sx;
+ double sy = pad.pad_sy;
+ double angle = pad.pad_angle;
+
+ if (fmod(angle, 180) == 0) angle = 0;
+ else if (fmod(angle, 90) == 0) {
+ double t;
+ t = sx;
+ sx = sy;
+ sy = t;
+ angle = 0;
+ }
+
+ /*
+ * Circular and oval pads
+ */
+
+ if (pad.pad_shape == PAD_SHAPE_OVAL) {
+ /* calculate number of segments needed */
+ int segments = min_circle_segments;
+ if (arc_precision > 0) {
+ /* Increase number of segments until difference between oval
+ and polygonal approximation is less than max_arc_error */
+ double arc_error_x;
+ double arc_error_y;
+ do {
+ arc_error_x = sx / 2 * (1 - cos(pi/segments));
+ arc_error_y = sy / 2 * (1 - cos(pi/segments));
+ if ((arc_error_x > arc_precision) || (arc_error_y > arc_precision)) segments += 4;
+ }
+ while ((arc_error_x > arc_precision) || (arc_error_y > arc_precision));
+ }
+ /* calculate points */
+ for (int i = 0; i < segments; i++) {
+ double alpha = 2 * pi * i / segments;
+ double x = sx / 2 / cos(pi / segments) * cos(alpha);
+ double y = sy / 2 / cos(pi / segments) * sin(alpha);
+ pad_poly.vertex.push_back(Point(x, y));
+ }
+ }
+
+ /*
+ * Square and rectangular pads
+ */
+
+ else if (pad.pad_shape == PAD_SHAPE_RECTANGULAR) {
+ pad_poly.vertex.push_back(Point( sx/2, sy/2));
+ pad_poly.vertex.push_back(Point( sx/2, -sy/2));
+ pad_poly.vertex.push_back(Point(-sx/2, -sy/2));
+ pad_poly.vertex.push_back(Point(-sx/2, sy/2));
+ }
+
+ /*
+ * Oblong pads
+ */
+
+ else if (pad.pad_shape == PAD_SHAPE_OBLONG) {
+ if (sx > sy) {
+ /* horizontal oblong pad */
+ double x1 = (sx - sy)/2;
+ double y1 = -sy/2;
+ double x2 = x1;
+ double y2 = -y1;
+ double xc = (sx - sy)/2;
+ double yc = 0;
+ double r = sy/2;
+ Polygon arc_right = arc2poly(x1, y1, x2, y2, xc, yc, r, false);
+ Polygon arc_left = arc2poly(-x1, -y1, -x2, -y2, -xc, -yc, r, false);
+ pad_poly.vertex = arc_right.vertex;
+ pad_poly.vertex.insert(pad_poly.vertex.end(), arc_left.vertex.begin(), arc_left.vertex.end());
+ }
+ else {
+ /* vertical oblong pad */
+ double x1 = sx/2;
+ double y1 = (sy - sx)/2;
+ double x2 = -x1;
+ double y2 = y1;
+ double xc = 0;
+ double yc = (sy-sx)/2;
+ double r = sx/2;
+ Polygon arc_top = arc2poly(x1, y1, x2, y2, xc, yc, r, false);
+ Polygon arc_bottom = arc2poly(-x1, -y1, -x2, -y2, -xc, -yc, r, false);
+ pad_poly.vertex = arc_top.vertex;
+ pad_poly.vertex.insert(pad_poly.vertex.end(), arc_bottom.vertex.begin(), arc_bottom.vertex.end());
+ }
+ }
+
+ /*
+ * Unknown pad shape
+ */
+
+ else error("unknown pad shape");
+
+ /* Rotate polygon over pad rotation angle */
+ if (angle != 0) {
+ double cosa = cos(angle * pi / 180);
+ double sina = sin(angle * pi / 180);
+ for (PointList::iterator i = pad_poly.vertex.begin(); i != pad_poly.vertex.end(); ++i) {
+ double x_rot = cosa * i->x - sina * i->y ;
+ double y_rot = sina * i->x + cosa * i->y ;
+ i->x = x_rot;
+ i->y = y_rot;
+ }
+ }
+
+ /* Translate polygon to pad center */
+ for (PointList::iterator i = pad_poly.vertex.begin(); i != pad_poly.vertex.end(); ++i) {
+ i->x += pad_x;
+ i->y += pad_y;
+ }
+
+ /* return pad converted to polygon */
+ return pad_poly;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/exec_board.cc b/hyp2mat/lib/exec_board.cc
new file mode 100644
index 0000000..103d586
--- /dev/null
+++ b/hyp2mat/lib/exec_board.cc
@@ -0,0 +1,251 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <cstdio>
+#include <cmath>
+#include "hypfile.h"
+
+using namespace std;
+using namespace HypFile;
+
+/*
+ * Hyperlynx 'BOARD_FILE' section.
+ * Hyperlynx file header.
+ */
+
+bool HypFile::Hyp::exec_board_file(parse_param& h)
+{
+ if (trace_hyp) cerr << "board_file" << endl;
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'VERSION' record.
+ * Specifies version number.
+ * Required record; must be first record of the file.
+ */
+
+bool HypFile::Hyp::exec_version(parse_param& h)
+{
+ if (trace_hyp) cerr << "version: vers = " << h.vers << endl;
+
+ if (h.vers < 2.0) error("warning: version 1.x deprecated");
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'DATA_MODE' record.
+ * Specifies whether data is sufficient for power simulation.
+ */
+
+bool HypFile::Hyp::exec_data_mode(parse_param& h)
+{
+ if (trace_hyp) cerr << "data_mode: detailed = " << h.detailed << endl;
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'UNITS' record.
+ * Specifies measurement system (english/metric) for the rest of the file.
+ */
+
+bool HypFile::Hyp::exec_units(parse_param& h)
+{
+ if (trace_hyp) cerr << "units: unit_system_english = " << h.unit_system_english << " metal_thickness_weight = " << h.metal_thickness_weight << endl;
+ // convert everything to meter
+
+ if (h.unit_system_english) {
+ unit = inches; // lengths in inches. 1 in = 2.54 cm = 0.0254 m
+ if (h.metal_thickness_weight)
+ metal_thickness_unit = copper_imperial_weight * unit; // metal thickness in ounces/ft2. 1 oz/ft2 copper = 1.341 mil
+ else
+ metal_thickness_unit = unit; // metal thickness in inches
+ }
+ else {
+ unit = 0.01; // lengths in centimeters. 1 cm = 0.01 m
+ if (h.metal_thickness_weight)
+ metal_thickness_unit = copper_metric_weight * unit; // metal thickness in grams/cm2. 1 gr/cm2 copper = 0.1116 cm
+ else
+ metal_thickness_unit = unit; // metal thickness in centimeters
+ }
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'PLANE_SEP' record.
+ * Defines default trace to plane separation
+ */
+
+bool HypFile::Hyp::exec_plane_sep(parse_param& h)
+{
+ if (trace_hyp) cerr << "plane_sep: default_plane_separation = " << h.default_plane_separation << endl;
+
+ board.plane_separation = h.default_plane_separation * unit;
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'PERIMETER_SEGMENT' subrecord of 'BOARD' record.
+ * Draws linear board outline segment.
+ */
+
+bool HypFile::Hyp::exec_perimeter_segment(parse_param& h)
+{
+ if (trace_hyp) cerr << "perimeter_segment: x1 = " << h.x1 << " y1 = " << h.y1 << " x2 = " << h.x2 << " y2 = " << h.y2 << endl;
+
+ Polygon poly;
+
+ h.x1 *= unit;
+ h.y1 *= unit;
+ h.x2 *= unit;
+ h.y2 *= unit;
+
+ poly.width = 0;
+ poly.positive = true;
+ poly.vertex.push_back(Point(h.x1, h.y1));
+ poly.vertex.push_back(Point(h.x2, h.y2));
+
+ // Add segment to board outline
+ add_perimeter_polygon(poly);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'PERIMETER_ARC' subrecord of 'BOARD' record.
+ * Draws arc segment of board outline.
+ */
+
+bool HypFile::Hyp::exec_perimeter_arc(parse_param& h)
+{
+ if (trace_hyp) cerr << "perimeter_arc: x1 = " << h.x1 << " y1 = " << h.y1 << " x2 = " << h.x2 << " y2 = " << h.y2 << " xc = " << h.xc << " yc = " << h.yc << " r = " << h.r << endl;
+
+ Polygon poly;
+
+ h.x1 *= unit;
+ h.y1 *= unit;
+ h.x2 *= unit;
+ h.y2 *= unit;
+ h.xc *= unit;
+ h.yc *= unit;
+ h.r *= unit;
+
+ poly = arc2poly(h.x1, h.y1, h.x2, h.y2, h.xc, h.yc, h.r, false); // 'PERIMETER_ARC' draws arc counterclockwise
+
+ // Add arc to board outline
+ add_perimeter_polygon(poly);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'A' attribute subrecord of 'BOARD' record.
+ * Defines board attributes as name/value pairs.
+ */
+
+bool HypFile::Hyp::exec_board_attribute(parse_param& h)
+{
+ if (trace_hyp) cerr << "board_attribute: name = " << h.name << " value = " << h.value << endl;
+
+ Attribute a;
+ a.name = h.name;
+ a.value = h.value;
+
+ board.attribute.push_back(a);
+
+ return false;
+}
+
+/*
+ * Add a polygon to the board outline
+ */
+
+void HypFile::Hyp::add_perimeter_polygon(Polygon new_segment)
+{
+ // Note first polygon of board perimeter is the board outline, and positive.
+ // Subsequent polygons are board cutouts, and negative.
+
+ // safety check
+ if (new_segment.vertex.empty())
+ return;
+
+ if (new_segment.vertex.size() == 1) {
+ error("runt perimeter segment");
+ return;
+ }
+
+ /* add new segment to list of PERIMETER_SEGMENT and PERIMETER_ARC segments */
+ board.segments.push_back(new_segment);
+
+ /* re-calculate board edge
+ * loop over the list of segments, trying to re-construct the
+ * board edge piece by piece. */
+
+ board.edge.clear();
+ PolygonList segs = board.segments;
+ Polygon current_edge;
+
+ bool found = false;
+ do {
+ found = false;
+ /* find segment to add to existing edge */
+ for (PolygonList::iterator i = segs.begin(); i != segs.end(); ++i) {
+
+ if (current_edge.vertex.empty() || (i->vertex.front() == current_edge.vertex.back())) {
+ /* first point of segment is last point of current edge: add segment to edge */
+ found = true;
+ current_edge.vertex.insert(current_edge.vertex.end(), i->vertex.begin(), i->vertex.end());
+ }
+ else if (i->vertex.back() == current_edge.vertex.back()) {
+ /* last point of segment is last point of current edge: add segment to edge back to front */
+ found = true;
+ current_edge.vertex.insert(current_edge.vertex.end(), i->vertex.rbegin(), i->vertex.rend());
+ };
+
+ if (found) {
+ /* erase added segment from list of unused segments */
+ segs.erase(i);
+ /* check if current edge is closed */
+ if (current_edge.vertex.front() == current_edge.vertex.back()) {
+ /* store old edge, begin new one */
+ current_edge.positive = false;
+ board.edge.push_back(current_edge);
+ current_edge.vertex.clear();
+ }
+ break;
+ }
+
+ }
+ } while (found);
+
+ /* only first polygon of board perimeter is positive, rest are holes. */
+ if (!board.edge.empty()) board.edge.front().positive = true;
+
+ return;
+
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/exec_devices.cc b/hyp2mat/lib/exec_devices.cc
new file mode 100644
index 0000000..59b7f00
--- /dev/null
+++ b/hyp2mat/lib/exec_devices.cc
@@ -0,0 +1,89 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include "hypfile.h"
+
+using namespace std;
+using namespace HypFile;
+
+/*
+ * Hyperlynx 'DEVICE' record.
+ * Specifies pcb components: resistors, capacitors, ICs, ...
+ * Required record.
+ */
+
+bool HypFile::Hyp::exec_devices(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "device: ";
+ cerr << "device_type = " << h.device_type;
+ cerr << " ref = " << h.ref ;
+ if (h.name_set) cerr << " name = " << h.name;
+ if (h.value_float_set) cerr << " value_float = " << h.value_float;
+ if (h.value_string_set) cerr << " value_string = " << h.value_string;
+ if (h.layer_name_set) cerr << " layer_name = " << h.layer_name;
+ if (h.package_set) cerr << " package = " << h.package;
+ cerr << endl;
+ }
+
+ Device d;
+
+ d.device_type = h.device_type;
+
+ d.ref = h.ref;
+
+ if (h.name_set)
+ d.name = h.name;
+ else
+ d.name = "";
+
+ if (h.value_float_set)
+ d.value_float = h.value_float;
+ else
+ d.value_float = 0;
+
+ d.value_float_set = h.value_float_set;
+
+ if (h.value_string_set)
+ d.value_string = h.value_string;
+ else
+ d.value_string = "";
+
+ d.value_string_set = h.value_string_set;
+
+ if (h.layer_name_set)
+ d.layer_name = h.layer_name;
+ else {
+ error ("device layer not set");
+ return true;
+ }
+
+ if (h.package_set)
+ d.package = h.package;
+ else
+ d.package = "";
+
+ device.push_back(d);
+
+ return false;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/exec_end.cc b/hyp2mat/lib/exec_end.cc
new file mode 100644
index 0000000..a87ca25
--- /dev/null
+++ b/hyp2mat/lib/exec_end.cc
@@ -0,0 +1,53 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include "hypfile.h"
+
+using namespace std;
+using namespace HypFile;
+
+/*
+ * Hyperlynx 'END' record.
+ * Marks end of board description.
+ */
+
+bool HypFile::Hyp::exec_end(parse_param& h)
+{
+ if (trace_hyp) cerr << "end" << endl;
+
+ /* Optionally flood layers with copper */
+ flood_layers_with_copper();
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'KEY' record.
+ */
+
+bool HypFile::Hyp::exec_key(parse_param& h)
+{
+ if (trace_hyp) cerr << "key: key = " << h.key << endl;
+
+ return false;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/exec_net.cc b/hyp2mat/lib/exec_net.cc
new file mode 100644
index 0000000..50000b3
--- /dev/null
+++ b/hyp2mat/lib/exec_net.cc
@@ -0,0 +1,502 @@
+
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <cmath>
+#include "hypfile.h"
+
+using namespace std;
+using namespace HypFile;
+
+/*
+ * Hyperlynx 'NET' record.
+ * Specifies a net.
+ */
+
+bool HypFile::Hyp::exec_net(parse_param& h)
+{
+ if (trace_hyp) cerr << "net: net_name = " << h.net_name << endl;
+
+ Net n;
+ n.net_name = h.net_name;
+ n.plane_separation = -1.0;
+ net.push_back(n);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'PS' subrecord of 'NET' record.
+ * Specifies plane separation of a net.
+ */
+
+bool HypFile::Hyp::exec_net_plane_separation(parse_param& h)
+{
+ if (trace_hyp) cerr << "net_plane_separation: plane_separation = " << h.plane_separation << endl;
+ h.plane_separation *= unit;
+
+ net.back().plane_separation = h.plane_separation;
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'SEG' subrecord of 'NET' record.
+ * Draws straight metal trace segment.
+ */
+
+bool HypFile::Hyp::exec_seg(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "seg: x1 = " << h.x1 << " y1 = " << h.y1 << " x2 = " << h.x2 << " y2 = " << h.y2;
+ cerr << " width = " << h.width << " layer_name = " << h.layer_name;
+ if (h.plane_separation_set) cerr << " plane_separation = " << h.plane_separation;
+ if (h.left_plane_separation_set) cerr << " left_plane_separation = " << h.left_plane_separation;
+ cerr << endl;
+ }
+
+ Polygon p;
+
+ h.x1 *= unit;
+ h.y1 *= unit;
+ h.x2 *= unit;
+ h.y2 *= unit;
+ h.width *= unit;
+ h.plane_separation *= unit;
+ if (!h.plane_separation_set) h.plane_separation = -1.0;
+ h.left_plane_separation *= unit;
+ if (!h.left_plane_separation_set) h.left_plane_separation = -1.0;
+
+ p = segment2poly(h.x1 , h.y1, h.x2, h.y2, h.width);
+ p.layer_name = h.layer_name;
+ p.width = 0;
+ p.positive = true;
+ p.plane_separation = h.plane_separation; /* distance to other copper; -1 if not set */
+ p.left_plane_separation = h.left_plane_separation;
+
+ add_polygon(p);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'ARC' subrecord of 'NET' record.
+ * Draws arc metal trace segment.
+ */
+
+bool HypFile::Hyp::exec_arc(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "arc: x1 = " << h.x1 << " y1 = " << h.y1 << " x2 = " << h.x2 << " y2 = " << h.y2;
+ cerr << " xc = " << h.xc << " yc = " << h.yc << " r = " << h.r;
+ cerr << " width = " << h.width << " layer_name = " << h.layer_name;
+ if (h.plane_separation_set) cerr << " plane_separation = " << h.plane_separation;
+ if (h.left_plane_separation_set) cerr << " left_plane_separation = " << h.left_plane_separation;
+ cerr << endl;
+ }
+
+ h.x1 *= unit;
+ h.y1 *= unit;
+ h.x2 *= unit;
+ h.y2 *= unit;
+ h.xc *= unit;
+ h.yc *= unit;
+ h.r *= unit;
+ h.width *= unit;
+ h.plane_separation *= unit;
+ if (!h.plane_separation_set) h.plane_separation = -1.0;
+ h.left_plane_separation *= unit;
+ if (!h.left_plane_separation_set) h.left_plane_separation = -1.0;
+
+ /* 'ARC' draws arc clockwise */
+ Polygon arc = arc2poly(h.x1, h.y1, h.x2, h.y2, h.xc, h.yc, h.r, true);
+
+ /* draw arc segments */
+ for (PointList::iterator i = arc.vertex.begin(); (i != arc.vertex.end()) && (i != --arc.vertex.end()); ++i) {
+ Polygon line_segment;
+ PointList::iterator j = i + 1;
+ line_segment = segment2poly(i->x, i->y, j->x, j->y, h.width);
+ line_segment.positive = true;
+ line_segment.layer_name = h.layer_name;
+ line_segment.width = 0;
+ line_segment.plane_separation = h.plane_separation;
+ line_segment.left_plane_separation = h.left_plane_separation;
+ add_polygon(line_segment);
+ }
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'VIA' subrecord of 'NET' record.
+ * Draws via as defined in padstack.
+ */
+
+bool HypFile::Hyp::exec_via(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "via: x = " << h.x << " y = " << h.y;
+ if (h.layer1_name_set) cerr << " layer1_name = " << h.layer1_name;
+ if (h.layer2_name_set) cerr << " layer2_name = " << h.layer2_name;
+ cerr << " padstack_name = " << h.padstack_name;
+ cerr << endl;
+ }
+
+ h.x *= unit;
+ h.y *= unit;
+
+ /* lookup padstack */
+ for (PadstackList::iterator i = padstack.begin(); i != padstack.end(); ++i)
+ if (i->padstack_name == h.padstack_name) {
+
+ /* set via begin layer */
+ if (!h.layer1_name_set)
+ h.layer1_name = i->pads.front().layer_name;
+
+ /* set via end layer */
+ if (!h.layer2_name_set)
+ h.layer2_name = i->pads.back().layer_name;
+
+ /* add to list of via holes */
+ add_via(h.x, h.y, h.layer2_name, h.layer1_name, i->drill_size/2);
+
+ /* loop over padstack and generate pads */
+ for (PadList::iterator j = i->pads.begin(); j != i->pads.end(); ++j)
+ add_pad(h.x, h.y, *j);
+
+ return false;
+ }
+
+ /* padstack not found */
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'VIA' subrecord of 'NET' record.
+ * Draws deprecated v1.x via.
+ */
+
+bool HypFile::Hyp::exec_via_v1(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "via: x = " << h.x << " y = " << h.y;
+ cerr << " drill_size = " << h.drill_size;
+ if (h.layer1_name_set) cerr << " layer1_name = " << h.layer1_name;
+ if (h.layer2_name_set) cerr << " layer2_name = " << h.layer2_name;
+ cerr << " pad1_shape = " << h.pad1_shape << " pad1_sx = " << h.pad1_sx << " pad1_sy = " << h.pad1_sy << " pad1_angle = " << h.pad1_angle;
+ cerr << " pad2_shape = " << h.pad2_shape << " pad2_sx = " << h.pad2_sx << " pad2_sy = " << h.pad2_sy << " pad2_angle = " << h.pad2_angle;
+ cerr << endl;
+ }
+
+ h.drill_size *= unit;
+ h.x *= unit;
+ h.y *= unit;
+ h.pad1_sx *= unit;
+ h.pad1_sy *= unit;
+ h.pad2_sx *= unit;
+ h.pad2_sy *= unit;
+
+ /* add to list of via holes */
+ add_via(h.x, h.y, stackup.back().layer_name /* bottom */, stackup.front().layer_name /* top */ , h.drill_size/2);
+
+ /* add top pad */
+ Pad pad;
+ pad.layer_name = stackup.front().layer_name; /* Top layer */
+ if (h.pad1_shape == "OVAL") pad.pad_shape = PAD_SHAPE_OVAL;
+ else if (h.pad1_shape == "RECT") pad.pad_shape = PAD_SHAPE_RECTANGULAR;
+ else if (h.pad1_shape == "OBLONG") pad.pad_shape = PAD_SHAPE_OBLONG;
+ else pad.pad_shape = PAD_SHAPE_OVAL;
+ pad.pad_type = PAD_TYPE_METAL;
+ pad.pad_sx = h.pad1_sx;
+ pad.pad_sy = h.pad1_sy;
+ pad.pad_angle = h.pad1_angle;
+ pad.thermal_clear_shape = PAD_SHAPE_OVAL;
+ pad.thermal_clear_sx = 0;
+ pad.thermal_clear_sy= 0;
+ pad.thermal_clear_angle= 0;
+ add_pad(h.x, h.y, pad);
+
+ /* add bottom pad */
+ pad.layer_name = stackup.back().layer_name; /* Top layer */
+ if (h.pad2_shape == "OVAL") pad.pad_shape = PAD_SHAPE_OVAL;
+ else if (h.pad2_shape == "RECT") pad.pad_shape = PAD_SHAPE_RECTANGULAR;
+ else if (h.pad2_shape == "OBLONG") pad.pad_shape = PAD_SHAPE_OBLONG;
+ else pad.pad_shape = PAD_SHAPE_OVAL;
+ pad.pad_type = PAD_TYPE_METAL;
+ pad.pad_sx = h.pad2_sx;
+ pad.pad_sy = h.pad2_sy;
+ pad.pad_angle = h.pad2_angle;
+ add_pad(h.x, h.y, pad);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'PIN' subrecord of 'NET' record.
+ * Draws PIN as defined in padstack.
+ */
+
+bool HypFile::Hyp::exec_pin(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "pin: x = " << h.x << " y = " << h.y;
+ cerr << " pin_reference = " << h.pin_reference;
+ cerr << " padstack_name = " << h.padstack_name;
+ if (h.pin_function_set) cerr << " pin_function = " << h.pin_function;
+ cerr << endl;
+ }
+
+ h.x *= unit;
+ h.y *= unit;
+
+ /* add to list of pins */
+ Pin p;
+ p.x = h.x;
+ p.y = h.y;
+ p.pin_reference = h.pin_reference;
+ p.padstack_name = h.padstack_name;
+ p.pin_function = PIN_SIM_BOTH;
+ if (h.pin_function_set) p.pin_function = h.pin_function;
+ net.back().pin.push_back(p);
+
+ /* lookup padstack */
+ if (h.padstack_name_set)
+ for (PadstackList::iterator i = padstack.begin(); i != padstack.end(); ++i)
+ if (i->padstack_name == h.padstack_name) {
+ /* add to list of via holes */
+ add_via(h.x, h.y, stackup.back().layer_name /* bottom */, stackup.front().layer_name /* top */, i->drill_size/2);
+
+ /* loop over padstack and generate pads */
+ for (PadList::iterator j = i->pads.begin(); j != i->pads.end(); ++j)
+ add_pad(h.x, h.y, *j);
+ return false;
+ }
+
+ /* padstack not found or not set */
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'PAD' subrecord of 'NET' record.
+ * Draws deprecated v1.x pad.
+ */
+
+bool HypFile::Hyp::exec_pad(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "pad: x = " << h.x << " y = " << h.y;
+ if (h.layer_name_set) cerr << " layer_name = " << h.layer_name;
+ cerr << " pad1_shape = " << h.pad1_shape << " pad1_sx = " << h.pad1_sx << " pad1_sy = " << h.pad1_sy << " pad1_angle = " << h.pad1_angle;
+ cerr << endl;
+ }
+
+ h.x *= unit;
+ h.y *= unit;
+ h.pad1_sx *= unit;
+ h.pad1_sy *= unit;
+
+ /* add pad */
+ Pad pad;
+ pad.layer_name = h.layer_name;
+ if (h.pad1_shape == "OVAL") pad.pad_shape = PAD_SHAPE_OVAL;
+ else if (h.pad1_shape == "RECT") pad.pad_shape = PAD_SHAPE_RECTANGULAR;
+ else if (h.pad1_shape == "OBLONG") pad.pad_shape = PAD_SHAPE_OBLONG;
+ else pad.pad_shape = PAD_SHAPE_OVAL;
+ pad.pad_type = PAD_TYPE_METAL;
+ pad.pad_sx = h.pad1_sx;
+ pad.pad_sy = h.pad1_sy;
+ pad.pad_angle = h.pad1_angle;
+ pad.thermal_clear_shape = PAD_SHAPE_OVAL;
+ pad.thermal_clear_sx = 0;
+ pad.thermal_clear_sy= 0;
+ pad.thermal_clear_angle= 0;
+ add_pad(h.x, h.y, pad);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'USEG' subrecord of 'NET' record.
+ * Designates unrouted metal trace segment
+ */
+
+bool HypFile::Hyp::exec_useg(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "useg: x1 = " << h.x1 << " y1 = " << h.y1 << " layer1_name = " << h.layer1_name;
+ cerr << " x2 = " << h.x2 << " y2 = " << h.y2 << " layer2_name = " << h.layer2_name;
+ if (h.zlayer_name_set) cerr << " zlayer_name = " << h.zlayer_name << " width = " << h.width << " length = " << h.length;
+ if (h.impedance_set) cerr << " impedance = " << h.impedance << " delay = " << h.delay;
+ if (h.resistance_set) cerr << " resistance = " << h.resistance;
+ cerr << endl;
+ }
+
+ h.x1 *= unit;
+ h.y1 *= unit;
+ h.x2 *= unit;
+ h.y2 *= unit;
+ h.width *= unit;
+ h.length *= unit;
+ if (!h.resistance_set) h.resistance = 0;
+
+ UnroutedSegment u;
+ u.x1 = h.x1;
+ u.y1 = h.y1;
+ u.layer1_name = h.layer1_name;
+ u.x2 = h.x2;
+ u.y2 = h.y2;
+ u.layer2_name = h.layer2_name;
+ u.zlayer_name = h.zlayer_name;
+ u.zlayer_name_set = h.zlayer_name_set;
+ u.width = h.width;
+ u.impedance = h.impedance;
+ u.impedance_set = h.impedance_set;
+ u.delay = h.delay;
+ u.resistance = h.resistance;
+ net.back().unrouted_segment.push_back(u);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'A' attribute subrecord of 'NET' record.
+ * Defines net attributes as name/value pairs.
+ */
+
+bool HypFile::Hyp::exec_net_attribute(parse_param& h)
+{
+ if (trace_hyp) cerr << "net_attribute: name = " << h.name << " value = " << h.value << endl;
+
+ Attribute a;
+ a.name = h.name;
+ a.value = h.value;
+
+ net.back().attribute.push_back(a);
+
+ return false;
+}
+
+/*
+ * Add a polygon to the current net
+ */
+
+void HypFile::Hyp::add_polygon(Polygon poly)
+{
+ if (poly.vertex.empty()) return;
+ add_polygon_to_net(net.back(), poly);
+}
+
+/*
+ * Add polygon poly to net pnet
+ */
+
+void HypFile::Hyp::add_polygon_to_net(Net& pnet, Polygon poly) {
+
+ /* set net name of polygon to net */
+ poly.net_name = pnet.net_name;
+
+ /* The polygon id is a positive number if the polygon has been assigned a polygon id.
+ * If the polygon has not been assigned a polygon id, the id is -1.
+ * An polygon which has not been assigned a polygon id has a single, outer edge, no holes.
+ * (the polygon does not have an id a POLYVOID could refer to).
+ */
+
+ if (poly.id < 0) {
+ /*
+ * If a polygon has id -1, assign the polygon an arbitrary negative id, different from -1.
+ * Make sure all polygons of the same net do not clash/have different ids.
+ */
+ int new_id = 0;
+ if (!pnet.metal.empty())
+ new_id = pnet.metal.begin()->first - 1; /* new id is one less than the smallest existing id */
+ if (new_id >= -1) new_id = -2; /* does not clash with an assigned id (positive) or the default (-1) */
+ poly.id = new_id;
+ }
+
+ /* check if polygons with this id already exist */
+ PolygonMap::iterator i = pnet.metal.find(poly.id);
+ if (i == pnet.metal.end()) {
+ PolygonList empty_list;
+ pnet.metal[poly.id] = empty_list;
+ }
+
+ /* add polygon to net */
+ pnet.metal[poly.id].push_back(poly);
+
+ return;
+}
+
+/*
+ * Add a pad at coordinates (x, y)
+ */
+
+void HypFile::Hyp::add_pad(double pad_x, double pad_y, Pad pad)
+{
+ /* convert to polygon */
+ Polygon pad_poly = pad2poly(pad_x, pad_y, pad);
+
+ /* add pad to copper */
+ add_polygon(pad_poly);
+
+ return;
+}
+
+/*
+ * Add via to list of via holes
+ */
+
+void HypFile::Hyp::add_via(double x, double y, std::string layer0_name, std::string layer1_name, double radius)
+{
+ if (radius != 0.0) {
+
+ /* lookup drill vertical begin and end */
+ double z0 = stackup.back().z0; /* defaults to bottom */
+ double z1 = stackup.front().z1; /* defaults to top */
+
+ for (LayerList::iterator l = stackup.begin(); l != stackup.end(); ++l)
+ if (layer0_name == l->layer_name) {
+ z0 = l->z0;
+ z1 = l->z1;
+ }
+
+ for (LayerList::iterator l = stackup.begin(); l != stackup.end(); ++l)
+ if (layer1_name == l->layer_name) {
+ if (l->z1 > z1) z1 = l->z1;
+ if (l->z0 < z0) z0 = l->z0;
+ }
+
+ Via v;
+ v.x = x;
+ v.y = y;
+ v.layer0_name = layer0_name;
+ v.layer1_name = layer1_name;
+ v.z0 = z0;
+ v.z1 = z1;
+ v.radius = radius;
+ net.back().via.push_back(v);
+ }
+
+ return;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/exec_netclass.cc b/hyp2mat/lib/exec_netclass.cc
new file mode 100644
index 0000000..9e592c0
--- /dev/null
+++ b/hyp2mat/lib/exec_netclass.cc
@@ -0,0 +1,76 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include "hypfile.h"
+
+using namespace std;
+using namespace HypFile;
+
+/*
+ * Hyperlynx 'NET_CLASS' record.
+ * Defines a class of nets.
+ */
+
+bool HypFile::Hyp::exec_net_class(parse_param& h)
+{
+ if (trace_hyp) cerr << "net_class: net_class_name = " << h.net_class_name << endl;
+
+ NetClass nc;
+ nc.net_class_name = h.net_class_name;
+ net_class.push_back(nc);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'A' attribute subrecord of 'NET_CLASS' record.
+ * Defines net class attributes as name/value pairs.
+ */
+
+bool HypFile::Hyp::exec_net_class_attribute(parse_param& h)
+{
+ if (trace_hyp) cerr << "netclass_attribute: name = " << h.name << " value = " << h.value << endl;
+
+ Attribute a;
+ a.name = h.name;
+ a.value = h.value;
+
+ net_class.back().attribute.push_back(a);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'N' subrecord of 'NET_CLASS' record.
+ * Defines membership of a net class.
+ */
+
+bool HypFile::Hyp::exec_net_class_element(parse_param& h)
+{
+ if (trace_hyp) cerr << "net_class_element: net_name = " << h.net_name << endl;
+
+ if (!net_class.empty())
+ net_class.back().net_class_element.push_back(h.net_name);
+
+ return false;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/exec_padstack.cc b/hyp2mat/lib/exec_padstack.cc
new file mode 100644
index 0000000..0c2435e
--- /dev/null
+++ b/hyp2mat/lib/exec_padstack.cc
@@ -0,0 +1,269 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include "hypfile.h"
+
+using namespace std;
+using namespace HypFile;
+
+/*
+ * Hyperlynx 'PADSTACK' record.
+ * Defines a padstack.
+ */
+
+/*
+ * Hyperlynx padstack-element subrecord of 'PADSTACK' record.
+ * Defines an individual pad in a padstack.
+ */
+
+bool HypFile::Hyp::exec_padstack_element(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "padstack_element:";
+ if (h.padstack_name_set) cerr << " padstack_name = " << h.padstack_name;
+ if (h.drill_size_set) cerr << " drill_size = " << h.drill_size;
+ cerr << " layer_name = " << h.layer_name;
+ cerr << " pad_shape = " << h.pad_shape;
+ cerr << " pad_sx = " << h.pad_sx;
+ cerr << " pad_sy = " << h.pad_sy;
+ cerr << " pad_angle = " << h.pad_angle;
+ if (h.pad_type_set & (h.pad_type == PAD_TYPE_THERMAL_RELIEF)) {
+ cerr << " thermal_clear_shape = " << h.thermal_clear_shape;
+ cerr << " thermal_clear_sx = " << h.thermal_clear_sx;
+ cerr << " thermal_clear_sy = " << h.thermal_clear_sy;
+ cerr << " thermal_clear_angle = " << h.thermal_clear_angle;
+ }
+ if (h.pad_type_set) cerr << " pad_type = " << h.pad_type;
+ cerr << endl;
+ }
+
+ h.drill_size *= unit;
+ h.pad_sx *= unit;
+ h.pad_sy *= unit;
+ h.thermal_clear_sx *= unit;
+ h.thermal_clear_sy *= unit;
+
+ /* new padstack */
+ if (h.padstack_name_set) {
+ Padstack pdstack;
+ pdstack.padstack_name = h.padstack_name;
+ if (h.drill_size_set)
+ pdstack.drill_size = h.drill_size;
+ else
+ pdstack.drill_size = 0;
+
+ /* add new padstack to list of padstacks */
+ padstack.push_back(pdstack);
+ }
+
+ /* new pad */
+ Pad p;
+
+ p.layer_name = h.layer_name;
+
+ /* pad type */
+ if (h.pad_type_set) p.pad_type = h.pad_type;
+ else if (p.layer_name == "ADEF") p.pad_type = PAD_TYPE_ANTIPAD;
+ else p.pad_type = PAD_TYPE_METAL;
+
+ if (h.pad_shape == 0)
+ p.pad_shape = PAD_SHAPE_OVAL;
+ else if (h.pad_shape == 1)
+ p.pad_shape = PAD_SHAPE_RECTANGULAR;
+ else if (h.pad_shape == 2)
+ p.pad_shape = PAD_SHAPE_OBLONG;
+ else if (h.pad_shape == -1)
+ p.pad_shape = PAD_SHAPE_OVAL; /* workaround for Altium bug */
+ else {
+ error("unknown pad shape");
+ return true;
+ }
+
+ p.pad_sx = h.pad_sx;
+ p.pad_sy = h.pad_sy;
+ p.pad_angle = h.pad_angle;
+
+ if (p.pad_type == PAD_TYPE_THERMAL_RELIEF) {
+ if (h.thermal_clear_shape == 0)
+ p.thermal_clear_shape = PAD_SHAPE_OVAL;
+ else if (h.thermal_clear_shape == 1)
+ p.thermal_clear_shape = PAD_SHAPE_RECTANGULAR;
+ else if (h.thermal_clear_shape == 2)
+ p.thermal_clear_shape = PAD_SHAPE_OBLONG;
+ else {
+ error("unknown thermal clear shape");
+ return true;
+ }
+
+ p.thermal_clear_sx = h.thermal_clear_sx;
+ p.thermal_clear_sy = h.thermal_clear_sy;
+ p.thermal_clear_angle = h.thermal_clear_angle;
+ }
+ else {
+ p.thermal_clear_shape = PAD_SHAPE_OVAL;
+ p.thermal_clear_sx = 0;
+ p.thermal_clear_sy = 0;
+ p.thermal_clear_angle = 0;
+ }
+
+ /* add new pad to list of pads of current padstack */
+ padstack.back().pads.push_back(p);
+
+ return false;
+}
+
+/*
+ * Note:
+ * MDEF and ADEF layer names have special meaning.
+ * MDEF specified the default pad for signal or plane layers.
+ * If a signal or plane layer has no metal pad, and an MDEF default pad is specified, the MDEF pad is applied to the layer.
+ * ADEF specifies the default anti-pad (non-conducting hole in the copper) for plane layers.
+ * If a plane layer has no anti-pad, and an ADEF default pad is specified, the ADEF pad is applied to the layer.
+ */
+
+bool HypFile::Hyp::exec_padstack_end(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "padstack_end:";
+ cerr << endl;
+ };
+
+ PadList new_pads;
+
+ /*
+ * Loop through stackup.
+ * Calculate new padstack, where all MDEF and ADEF pads have been expanded into one or more metal layers.
+ */
+
+ for (LayerList::iterator l = stackup.begin(); l != stackup.end(); ++l) {
+ /* Loop through pad list */
+ bool layer_has_pad = false;
+ bool layer_has_antipad = false;
+ for (PadList::iterator p = padstack.back().pads.begin(); p != padstack.back().pads.end(); ++p) {
+ if (l->layer_name == p->layer_name) {
+ new_pads.push_back(*p);
+ layer_has_pad = layer_has_pad || (p->pad_type == PAD_TYPE_METAL);
+ layer_has_antipad = layer_has_antipad || (p->pad_type == PAD_TYPE_ANTIPAD);
+ }
+ }
+
+ /* If a plane layer has no antipad, add ADEF pad if specified */
+ if (!layer_has_antipad && (l->layer_type == LAYER_PLANE))
+ for (PadList::iterator p = padstack.back().pads.begin(); p != padstack.back().pads.end(); ++p)
+ if (p->layer_name == "ADEF") {
+ Pad new_pad = *p;
+ new_pad.layer_name = l->layer_name; /* change ADEF into current layer name */
+ new_pads.push_back(new_pad);
+ }
+
+ /* If a signal or plane layer has no pad, add MDEF pad if specified */
+ if (!layer_has_pad && ((l->layer_type == LAYER_SIGNAL) || (l->layer_type == LAYER_PLANE)) )
+ for (PadList::iterator p = padstack.back().pads.begin(); p != padstack.back().pads.end(); ++p)
+ if (p->layer_name == "MDEF") {
+ Pad new_pad = *p;
+ new_pad.layer_name = l->layer_name; /* change MDEF into current layer name */
+ new_pads.push_back(new_pad);
+ layer_has_pad = true;
+ }
+
+ /*
+ * Determine begin and end layers of via
+ */
+
+ /* via is from top to bottom if at least one pad is metal MDEF */
+ bool is_through_hole_via = false;
+ for (PadList::iterator p = padstack.back().pads.begin(); p != padstack.back().pads.end(); ++p)
+ if ((p->layer_name == "MDEF") && (p->pad_type == PAD_TYPE_METAL)) is_through_hole_via = true;
+
+ /* determine via top and bottom layers */
+ std::string pad_top_layer;
+ std::string pad_bottom_layer;
+
+ if (is_through_hole_via) {
+ pad_top_layer = stackup.front().layer_name; /* via begins at top layer */
+ pad_bottom_layer = stackup.back().layer_name; /* via ends at bottom layer */
+ }
+ else {
+ /* via begins at highest layer with metal pad, and ends at lowest layer with a metal pad */
+ bool layer_found = false;
+ for (LayerList::iterator l = stackup.begin(); l != stackup.end(); ++l)
+ for (PadList::iterator p = padstack.back().pads.begin(); p != padstack.back().pads.end(); ++p)
+ if ((l->layer_name == p->layer_name) && (p->pad_type == PAD_TYPE_METAL)) {
+ if (!layer_found) {
+ pad_top_layer = l->layer_name;
+ layer_found = true;
+ }
+ pad_bottom_layer = l->layer_name;
+ };
+
+ if (!layer_found) std::cerr << "warning: padstack without metal:" << h.padstack_name << std::endl;
+
+ }
+
+ /*
+ * Fill in missing via pads
+ */
+
+ // XXX Fixme
+ // XXX Check if loops need to begin at pad_top_layer / end at pad_bottom_layer
+ // XXX Check if pad_top_layer / pad_bottom_layer need visibility
+ // XXX Check if need mechanism to remove pads?
+
+ /* If a signal or plane layer has no pad, and no MDEF pad, copy an existing pad */
+ bool is_through_hole_pad = is_through_hole_via && (padstack.back().drill_size > 0);
+ if (!layer_has_pad && is_through_hole_pad && ((l->layer_type == LAYER_SIGNAL) || (l->layer_type == LAYER_PLANE)) ) {
+ Pad new_pad;
+ bool pad_found = false;
+ for (PadList::iterator p = padstack.back().pads.begin(); p != padstack.back().pads.end(); ++p) {
+ if (p->pad_type != PAD_TYPE_METAL) continue;
+ if (!pad_found) {
+ /* if all padstack pads are equal, create a pad on this layer which is like the others */
+ new_pad = *p;
+ new_pad.layer_name = l->layer_name;
+ pad_found = true;
+ }
+ else if ((p->pad_shape != new_pad.pad_shape) || (p->pad_sx != new_pad.pad_sx) || (p->pad_sy != new_pad.pad_sy) || (p->pad_angle != new_pad.pad_angle)) {
+ /* if padstack pads are different, create a circular pad with a diameter equal to the smallest pad dimension */
+ double radius = new_pad.pad_sx;
+ if (radius > new_pad.pad_sy) radius = new_pad.pad_sy;
+ if (radius > p->pad_sx) radius = p->pad_sx;
+ if (radius > p->pad_sy) radius = p->pad_sy;
+ new_pad.pad_shape = PAD_SHAPE_OVAL;
+ new_pad.pad_sx = radius;
+ new_pad.pad_sy = radius;
+ new_pad.pad_angle = 0;
+ }
+ }
+ /* add newly created pad to padstack */
+ if (pad_found) new_pads.push_back(new_pad);
+ }
+
+ }
+
+ /* put new padstack in place. */
+
+ padstack.back().pads = new_pads;
+
+ return false;
+
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/exec_polygon.cc b/hyp2mat/lib/exec_polygon.cc
new file mode 100644
index 0000000..88e8be2
--- /dev/null
+++ b/hyp2mat/lib/exec_polygon.cc
@@ -0,0 +1,275 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include "hypfile.h"
+
+using namespace std;
+using namespace HypFile;
+
+/*
+ * Hyperlynx 'POLYGON' subrecord of 'NET' record.
+ * Draws polygonal metal area.
+ */
+
+bool HypFile::Hyp::exec_polygon_begin(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "polygon begin:";
+ if (h.layer_name_set) cerr << " layer_name = " << h.layer_name;
+ if (h.width_set) cerr << " width = " << h.width;
+ if (h.polygon_type_set) {
+ cerr << " polygon_type = " << h.polygon_type << " ";
+ switch (h.polygon_type) {
+ case POLYGON_TYPE_PLANE: cerr << "POLYGON_TYPE_PLANE"; break;
+ case POLYGON_TYPE_POUR: cerr << "POLYGON_TYPE_POUR"; break;
+ case POLYGON_TYPE_COPPER: cerr << "POLYGON_TYPE_COPPER"; break;
+ default: cerr << "Error"; break;
+ }
+ }
+ if (h.id_set) cerr << " id = " << h.id;
+ cerr << " x = " << h.x << " y = " << h.y << endl;
+ }
+
+ if (!h.layer_name_set) {
+ error("expected polygon layer L = ");
+ return true;
+ }
+
+ if (!h.width_set) h.width = 0;
+
+ if (!h.polygon_type_set) h.polygon_type = POLYGON_TYPE_PLANE;
+
+ if (!h.id_set) {
+ error("expected polygon id ID = ");
+ return true;
+ }
+
+ h.width *= unit;
+ h.x *= unit;
+ h.y *= unit;
+
+ current_polygon.id = h.id;
+ current_polygon.polygon_type = h.polygon_type;
+ current_polygon.width = h.width;
+ current_polygon.positive = true;
+ current_polygon.layer_name = h.layer_name;
+ current_polygon.vertex.clear();
+ current_polygon.vertex.push_back(Point(h.x, h.y));
+
+ return false;
+}
+
+bool HypFile::Hyp::exec_polygon_end(parse_param& h)
+{
+ if (trace_hyp) cerr << "polygon end" << endl;
+
+ add_polygon(current_polygon);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'POLYVOID' subrecord of 'NET' record.
+ * Creates polygonal hole in metal area.
+ */
+
+bool HypFile::Hyp::exec_polyvoid_begin(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "polyvoid begin:";
+ if (h.id_set) cerr << " id = " << h.id;
+ cerr << " x = " << h.x << " y = " << h.y << endl;
+ }
+
+ if (!h.id_set) {
+ error("expected polygon id ID = ");
+ return true;
+ }
+
+ h.x *= unit;
+ h.y *= unit;
+
+ current_polygon.id = h.id;
+ current_polygon.positive = false;
+ current_polygon.vertex.clear();
+ current_polygon.vertex.push_back(Point(h.x, h.y));
+
+ /* inherit layer_name and width from parent */
+ for (PolygonList::iterator i = net.back().metal[h.id].begin(); i != net.back().metal[h.id].end(); ++i) {
+ if (i->positive) {
+ current_polygon.polygon_type = i->polygon_type;
+ current_polygon.width = i->width;
+ current_polygon.layer_name = i->layer_name;
+ return false;
+ }
+ }
+
+ /* no positive polygon/polyline found with same id */
+ error("polyvoid does not have parent polygon or polyline with same id");
+ return true;
+}
+
+bool HypFile::Hyp::exec_polyvoid_end(parse_param& h)
+{
+ if (trace_hyp) cerr << "polyvoid end" << endl;
+
+ add_polygon(current_polygon);
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'POLYLINE' subrecord of 'NET' record.
+ * Draws metal trace.
+ */
+
+bool HypFile::Hyp::exec_polyline_begin(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "polyline begin:";
+ if (h.layer_name_set) cerr << " layer_name = " << h.layer_name;
+ if (h.width_set) cerr << " width = " << h.width;
+ if (h.polygon_type_set) {
+ cerr << " polygon_type = " << h.polygon_type << " ";
+ switch (h.polygon_type) {
+ case POLYGON_TYPE_PLANE: cerr << "POLYGON_TYPE_PLANE"; break;
+ case POLYGON_TYPE_POUR: cerr << "POLYGON_TYPE_POUR"; break;
+ case POLYGON_TYPE_COPPER: cerr << "POLYGON_TYPE_COPPER"; break;
+ default: cerr << "Error"; break;
+ }
+ }
+ if (h.id_set) cerr << " id = " << h.id;
+ cerr << " x = " << h.x << " y = " << h.y << endl;
+ }
+
+ if (!h.layer_name_set) {
+ error("expected polygon layer L = ");
+ return true;
+ }
+
+ if (!h.width_set) {
+ error("expected polygon width W = ");
+ return true;
+ }
+
+ if (!h.polygon_type_set) h.polygon_type = POLYGON_TYPE_PLANE;
+
+ if (!h.id_set) {
+ error("expected polygon id ID = ");
+ return true;
+ }
+
+ h.width *= unit;
+ h.x *= unit;
+ h.y *= unit;
+
+ current_polygon.id = h.id;
+ current_polygon.polygon_type = h.polygon_type;
+ current_polygon.width = h.width;
+ current_polygon.positive = true;
+ current_polygon.layer_name = h.layer_name;
+ current_polygon.vertex.clear();
+ current_polygon.vertex.push_back(Point(h.x, h.y));
+
+ return false;
+}
+
+bool HypFile::Hyp::exec_polyline_end(parse_param& h)
+{
+ if (trace_hyp) cerr << "polyline end" << endl;
+
+ // Draw polyline as sequence of line segments
+ for (PointList::iterator i = current_polygon.vertex.begin(); (i != current_polygon.vertex.end()) && (i != --current_polygon.vertex.end()); ++i) {
+ Polygon line_segment;
+ PointList::iterator j = i + 1;
+ line_segment = segment2poly(i->x, i->y, j->x, j->y, current_polygon.width);
+ line_segment.id = current_polygon.id;
+ line_segment.polygon_type = current_polygon.polygon_type;
+ line_segment.positive = true;
+ line_segment.layer_name = current_polygon.layer_name;
+ line_segment.width = 0;
+ add_polygon(line_segment);
+ }
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'LINE' subrecord of 'NET' record.
+ * Draws straight metal trace.
+ */
+
+bool HypFile::Hyp::exec_line(parse_param& h)
+{
+ if (trace_hyp) cerr << "line: x = " << h.x << " y = " << h.y << endl;
+
+ h.x *= unit;
+ h.y *= unit;
+
+ /* add point to current polygon */
+ current_polygon.vertex.push_back(Point(h.x, h.y));
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'CURVE' subrecord of 'NET' record.
+ * Draws curved metal trace.
+ */
+
+bool HypFile::Hyp::exec_curve(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "curve: x1 = " << h.x1 << " y1 = " << h.y1 << " x2 = " << h.x2 << " y2 = " << h.y2;
+ cerr << " xc = " << h.xc << " yc = " << h.yc << " r = " << h.r << endl;
+ }
+
+ h.x1 *= unit;
+ h.y1 *= unit;
+ h.x2 *= unit;
+ h.y2 *= unit;
+ h.xc *= unit;
+ h.yc *= unit;
+ h.r *= unit;
+ h.width *= unit;
+
+ /* calculate arc */
+ /* 'CURVE' draws arc counterclockwise */
+ Polygon arc = arc2poly(h.x1, h.y1, h.x2, h.y2, h.xc, h.yc, h.r, false);
+
+ /* calculate distance between polygon end vertex and arc begin and arc end */
+ double endvertex_x = current_polygon.vertex.back().x;
+ double endvertex_y = current_polygon.vertex.back().y;
+ double dist1 = (endvertex_x - h.x1) * (endvertex_x - h.x1) + (endvertex_y - h.y1) * (endvertex_y - h.y1);
+ double dist2 = (endvertex_x - h.x2) * (endvertex_x - h.x2) + (endvertex_y - h.y2) * (endvertex_y - h.y2);
+
+ /* add arc to current polygon. begin with point closest to end vertex. */
+ if (dist1 <= dist2)
+ /* add arc from (x1, y1) to (x2, y2) */
+ current_polygon.vertex.insert(current_polygon.vertex.end(), arc.vertex.begin(), arc.vertex.end());
+ else
+ /* add arc from (x2, y2) to (x1, y1) */
+ current_polygon.vertex.insert(current_polygon.vertex.end(), arc.vertex.rbegin(), arc.vertex.rend());
+
+ return false;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/exec_stackup.cc b/hyp2mat/lib/exec_stackup.cc
new file mode 100644
index 0000000..5680cd5
--- /dev/null
+++ b/hyp2mat/lib/exec_stackup.cc
@@ -0,0 +1,446 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iterator>
+#include <iostream>
+#include <algorithm>
+#include "hypfile.h"
+
+using namespace std;
+using namespace HypFile;
+
+/*
+ * Hyperlynx 'STACKUP' record.
+ * Defines pcb layers.
+ */
+
+/*
+ * Hyperlynx 'OPTIONS' subrecord of STACKUP record.
+ * Defines dielectric constant and loss tangent of metal layers.
+ */
+
+bool HypFile::Hyp::exec_options(parse_param& h)
+{
+ if (trace_hyp) cerr << "options: use_die_for_metal = " << h.use_die_for_metal << endl;
+ use_die_for_metal = h.use_die_for_metal;
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'SIGNAL' subrecord of 'STACKUP' record.
+ * Defines a pcb signal layer.
+ */
+
+bool HypFile::Hyp::exec_signal(parse_param& h)
+{
+ if (trace_hyp)
+ cerr << "signal:";
+ trace_layer(h);
+
+ add_metal_layer(h);
+
+ /*
+ *The difference between signal and plane layers is
+ * - signal layers are drawn positive, plane layers are drawn negative
+ * - signal layers need a name
+ */
+
+ stackup.back().layer_type = LAYER_SIGNAL;
+
+ if (!h.layer_name_set)
+ error("missing signal layer name" );
+
+ calc_layer_position();
+ calc_layer_epsilon();
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'DIELECTRIC' subrecord of 'STACKUP' record.
+ * Defines a pcb dielectric layer.
+ */
+
+bool HypFile::Hyp::exec_dielectric(parse_param& h)
+{
+ if (trace_hyp)
+ cerr << "dielectric:";
+ trace_layer(h);
+
+ add_dielectric_layer(h);
+
+ calc_layer_position();
+ calc_layer_epsilon();
+
+ return false;
+}
+
+/*
+ * Hyperlynx 'PLANE' subrecord of 'STACKUP' record.
+ * Defines a pcb power or signal plane.
+ */
+
+bool HypFile::Hyp::exec_plane(parse_param& h)
+{
+ if (trace_hyp)
+ cerr << "plane:";
+ trace_layer(h);
+
+ add_metal_layer(h);
+
+ stackup.back().layer_type = LAYER_PLANE;
+
+ calc_layer_position();
+ calc_layer_epsilon();
+
+ return false;
+}
+
+/*
+ * Debug output for layers
+ */
+
+bool HypFile::Hyp::trace_layer(parse_param& h)
+{
+ if (trace_hyp) {
+ if (h.thickness_set) cerr << " thickness = " << h.thickness;
+ if (h.plating_thickness_set) cerr << " plating_thickness = " << h.plating_thickness;
+ if (h.bulk_resistivity_set) cerr << " bulk_resistivity = " << h.bulk_resistivity;
+ if (h.temperature_coefficient_set) cerr << " temperature_coefficient = " << h.temperature_coefficient;
+ if (h.epsilon_r_set) cerr << " epsilon_r = " << h.epsilon_r;
+ if (h.loss_tangent_set) cerr << " loss_tangent = " << h.loss_tangent;
+ if (h.conformal_set) cerr << " conformal = " << h.conformal;
+ if (h.prepreg_set) cerr << " prepreg = " << h.prepreg;
+ if (h.layer_name_set) cerr << " layer_name = " << h.layer_name;
+ if (h.material_name_set) cerr << " material_name = " << h.material_name;
+ if (h.plane_separation_set) cerr << " plane_separation = " << h.plane_separation;
+ cerr << endl;
+ }
+
+ return true;
+}
+
+/*
+ * Add a signal or plane layer.
+ */
+
+bool HypFile::Hyp::add_metal_layer(parse_param& h)
+{
+ Layer l;
+
+ l.layer_type = LAYER_SIGNAL;
+
+ if (h.thickness_set)
+ l.thickness = h.thickness * metal_thickness_unit;
+ else {
+ error ("thickness missing");
+ return true;
+ }
+
+ if (h.plating_thickness_set)
+ l.plating_thickness = h.plating_thickness * metal_thickness_unit;
+ else
+ l.plating_thickness = 0;
+
+ if (h.bulk_resistivity_set)
+ l.bulk_resistivity = h.bulk_resistivity;
+ else
+ l.bulk_resistivity = copper_bulk_resistivity;
+
+ if (h.temperature_coefficient_set)
+ l.temperature_coefficient = h.temperature_coefficient;
+ else
+ l.temperature_coefficient = copper_temperature_coefficient;
+
+ if (h.conformal_set) {
+ error("only dielectric layers conformal");
+ return true;
+ }
+ else
+ l.conformal = false;
+
+ if (h.epsilon_r_set)
+ l.epsilon_r = h.epsilon_r;
+ else
+ l.epsilon_r = fr4_epsilon_r;
+
+ if (h.loss_tangent_set)
+ l.loss_tangent = h.loss_tangent;
+ else
+ l.loss_tangent = fr4_loss_tangent;
+
+ // use use_die_for_metal ? XXX
+
+ if (h.prepreg_set) {
+ error("only dielectric layers prepreg");
+ return true;
+ }
+ else
+ l.prepreg = false;
+
+ if (h.layer_name_set)
+ l.layer_name = h.layer_name;
+ else {
+ error("missing signal layer name" );
+ l.layer_name = ""; // Continue anyhow
+ }
+
+ if (h.material_name_set)
+ l.material_name = h.material_name;
+ else
+ l.material_name = "";
+
+ if (h.plane_separation_set)
+ l.plane_separation = h.plane_separation * unit;
+ else
+ l.plane_separation = board.plane_separation;
+
+ // Add layer to stackup
+ stackup.push_back(l);
+
+ return false;
+}
+
+/*
+ * add a substrate layer
+ */
+
+bool HypFile::Hyp::add_dielectric_layer(parse_param& h)
+{
+
+ Layer l;
+
+ l.layer_type = LAYER_DIELECTRIC;
+
+ if (h.thickness_set)
+ l.thickness = h.thickness * unit;
+ else {
+ error ("thickness missing");
+ return true;
+ }
+
+ if (h.plating_thickness_set) {
+ error ("dielectric has no plating thickness");
+ return true;
+ }
+ else
+ l.plating_thickness = 0;
+
+ if (h.bulk_resistivity_set) {
+ error ("dielectric has no bulk resistivity");
+ return true;
+ }
+ else
+ l.bulk_resistivity = 0;
+
+ if (h.temperature_coefficient_set) {
+ error ("dielectric has no bulk resistivity temperature coefficient");
+ return true;
+ }
+ else
+ l.temperature_coefficient = 0;
+
+ if (h.conformal_set)
+ l.conformal = h.conformal;
+ else
+ l.conformal = false;
+
+ if (h.epsilon_r_set)
+ l.epsilon_r = h.epsilon_r;
+ else if (l.conformal)
+ l.epsilon_r = conformal_epsilon_r;
+ else
+ l.epsilon_r = fr4_epsilon_r;
+
+ if (h.loss_tangent_set)
+ l.loss_tangent = h.loss_tangent;
+ else
+ l.loss_tangent = fr4_loss_tangent;
+
+ if (h.prepreg_set)
+ l.prepreg = h.prepreg;
+ else
+ l.prepreg = true;
+
+ if (h.layer_name_set)
+ l.layer_name = h.layer_name;
+ else
+ l.layer_name = "";
+
+ if (h.material_name_set)
+ l.material_name = h.material_name;
+ else
+ l.material_name = "";
+
+ if (h.plane_separation_set) {
+ error ("dielectric has no plane separation");
+ return true;
+ }
+ else
+ l.plane_separation = 0;
+
+ // Add layer to stackup
+ stackup.push_back(l);
+
+ return false;
+}
+
+ /*
+ * Recalculate vertical position of all layers.
+ * Layers are ordered top first, bottom last.
+ * Called when a new layer is added.
+ */
+
+bool HypFile::Hyp::calc_layer_position()
+{
+ // calculate vertical position, from bottom to top
+ double z_top = 0; // current vertical position
+ for (LayerList::reverse_iterator it = stackup.rbegin(); it != stackup.rend(); ++it) {
+ if (it->layer_type == LAYER_DIELECTRIC) {
+ /* substrate and prepreg */
+ it->z0 = z_top;
+ it->z1 = z_top + it->thickness;
+ z_top = it->z1;
+ }
+ else {
+ /* metal layers */
+ it->z0 = z_top;
+ it->z1 = z_top;
+ }
+ }
+
+ return true;
+}
+
+bool HypFile::Hyp::calc_layer_epsilon()
+{
+
+ /* no calculation needed if dielectric constant have been specified */
+ if (use_die_for_metal)
+ return true;
+
+ /* calculate dielectric constants */
+ for (LayerList::iterator i = stackup.begin(); i != stackup.end(); ++i) {
+
+ /* skip dielectric layers */
+ if (i->layer_type == LAYER_DIELECTRIC)
+ continue;
+
+ /* find first dielectric layer above current metal layer */
+ bool upper_layer_found = false;
+ LayerList::iterator upper_layer;
+ for (LayerList::iterator j = stackup.begin(); j != i; ++j)
+ if (j->layer_type == LAYER_DIELECTRIC) {
+ upper_layer_found = true;
+ upper_layer = j;
+ };
+
+ /* find first dielectric layer below current metal layer */
+ bool lower_layer_found = false;
+ LayerList::iterator lower_layer;
+ for (LayerList::iterator j = i; j != stackup.end(); ++j)
+ if (j->layer_type == LAYER_DIELECTRIC) {
+ lower_layer_found = true;
+ lower_layer = j;
+ break;
+ };
+
+ if (!upper_layer_found || !lower_layer_found) {
+ /* assume outer copper layers are in air */
+ i->epsilon_r = 1.0;
+ i->loss_tangent = 0.0;
+ }
+ else if (upper_layer->prepreg && !lower_layer->prepreg) {
+ /* upper layer is prepreg. use values of upper layer */
+ i->epsilon_r = upper_layer->epsilon_r;
+ i->loss_tangent = upper_layer->loss_tangent;
+ }
+ else if (!upper_layer->prepreg && lower_layer->prepreg) {
+ /* lower layer is prepreg. use values of lower layer */
+ i->epsilon_r = lower_layer->epsilon_r;
+ i->loss_tangent = lower_layer->loss_tangent;
+ }
+ else {
+ /* use average of upper and lower layer */
+ i->epsilon_r = (upper_layer->epsilon_r + lower_layer->epsilon_r)/2;
+ i->loss_tangent = (upper_layer->loss_tangent + lower_layer->loss_tangent) /2;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Flood specified layers with copper
+ */
+
+void HypFile::Hyp::flood_layers_with_copper()
+{
+
+ /* check if we have to flood all plane layers */
+ bool flood_plane_layers = std::find(flood_layers.begin(), flood_layers.end(), "plane_layers") != flood_layers.end(); /* look for special value "plane_layers" */
+
+ for (LayerList::iterator l = stackup.begin(); l != stackup.end(); ++l) {
+
+ /* metallic layers only */
+ if (l->layer_type == LAYER_DIELECTRIC) continue;
+
+ /* check if we have to flood this layer */
+ bool flood_this_layer = std::find(flood_layers.begin(), flood_layers.end(), l->layer_name) != flood_layers.end();
+
+ /* flood with copper if we have to flood this layer, or if this is a plane layer and we have to flood all plane layers */
+ if (!(flood_this_layer || (flood_plane_layers && (l->layer_type == LAYER_PLANE)))) continue;
+
+ /* create default copper on plane layer */
+ Polygon default_copper;
+ default_copper.vertex = board.edge.front().vertex; /* polygon is board outline */
+ default_copper.polygon_type = POLYGON_TYPE_PLANE;
+ default_copper.net_name = l->layer_name; /* flood layer VCC with copper belonging to net VCC, layer GND with copper belonging to net GND */
+ default_copper.id = -1;
+ default_copper.positive = true;
+ default_copper.width = 0.0;
+ default_copper.plane_separation = -1.0;
+ default_copper.left_plane_separation = -1.0;
+ default_copper.layer_name = l->layer_name;
+
+ /* Look up net with same name as layer */
+ NetList::iterator n;
+ for (n = net.begin(); n != net.end(); ++n)
+ if (n->net_name == l->layer_name) {
+ add_polygon_to_net(*n, default_copper);
+ break;
+ }
+
+ /* Net with same name as layer not found. Create net */
+ if (n == net.end()) {
+ Net plane_net;
+ plane_net.net_name = l->layer_name;
+ plane_net.plane_separation = -1.0;
+ net.push_back(plane_net);
+ add_polygon_to_net(net.back(), default_copper);
+ }
+
+ }
+
+ return;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/exec_supplies.cc b/hyp2mat/lib/exec_supplies.cc
new file mode 100644
index 0000000..d010516
--- /dev/null
+++ b/hyp2mat/lib/exec_supplies.cc
@@ -0,0 +1,57 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include "hypfile.h"
+
+using namespace std;
+using namespace HypFile;
+
+/*
+ * Hyperlynx 'S' subrecord of 'SUPPLIES' record.
+ * Specifies a power supply net.
+ */
+
+bool HypFile::Hyp::exec_supplies(parse_param& h)
+{
+ if (trace_hyp) {
+ cerr << "supplies: name = " << h.name;
+ cerr << " value_float = " << h.value_float;
+ cerr << " voltage_specified = " << h.voltage_specified;
+ cerr << " conversion = " << h.conversion;
+ cerr << endl;
+ }
+
+ Supply s;
+
+ s.name = h.name;
+
+ s.value_float = h.value_float;
+
+ s.voltage_specified = h.voltage_specified;
+
+ s.conversion = h.conversion;
+
+ supply.push_back(s);
+
+ return false;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/hyp2mat.h b/hyp2mat/lib/hyp2mat.h
new file mode 100644
index 0000000..94a095e
--- /dev/null
+++ b/hyp2mat/lib/hyp2mat.h
@@ -0,0 +1,224 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ Class PCB defines a generic pcb board as a stack of dielectric and copper.
+ The class is intended as the greatest common denominator of formats.
+ The board outline is defined as a polygon, possibly with polygonal holes.
+ For each layer, the copper is defined as a list of polygons, each polygon possibly with polygonal holes.
+ Vias provide connections between the different copper layers.
+ All dimensions are in meter.
+
+ The following methods are defined:
+ ReadHyperLynx reads a hyperlynx file.
+ WritePDF prints a PCB to a pdf file.
+ WriteCSXCAD converts a PCB to an OpenEMS matlab script.
+
+ */
+
+
+#ifndef PCB_H
+#define PCB_H
+
+#include <string>
+#include <vector>
+
+namespace Hyp2Mat {
+
+ std::string version();
+
+ /* polygon types */
+
+ struct FloatPoint {
+ double x;
+ double y;
+ FloatPoint(double x_val = 0, double y_val = 0): x(x_val), y(y_val) {};
+ bool operator==(const FloatPoint &right) const { return ((x == right.x) && ( y == right.y)); };
+ bool operator!=(const FloatPoint &right) const { return ((x != right.x) || ( y != right.y)); };
+ };
+
+ typedef std::vector<FloatPoint> FloatPolygon;
+
+ struct FloatPoly {
+ FloatPolygon poly;
+ bool is_hole;
+ int nesting_level; /* hole within polygon within hole within ... */
+ };
+
+ typedef std::vector<FloatPoly> FloatPolygons;
+
+ /*
+ * a pin has a reference and an outline
+ */
+
+ class Pin {
+ public:
+ Pin();
+ std::string ref; /* reference, e.g. U1.1 */
+ double x; /* pad center coordinates */
+ double y;
+ double z0; /* bottom of copper layer */
+ double z1; /* top of copper layer */
+ std::string layer_name;
+ FloatPolygon metal; /* pad outline */
+ };
+
+ typedef std::vector<Pin> PinList;
+
+ /*
+ * Layers
+ */
+
+ enum layer_enum { LAYER_SIGNAL, LAYER_DIELECTRIC, LAYER_PLANE };
+
+ class Layer {
+ public:
+ std::string layer_name; /* informational only */
+ std::string material_name; /* informational only */
+ layer_enum layer_type;
+ FloatPolygons metal;
+ double thickness; /* layer thickness */
+ double z0; /* vertical position of bottom of layer */
+ double z1; /* vertical position of top of layer */
+ /* dielectric */
+ double epsilon_r;
+ double loss_tangent;
+ /* copper on signal and plane layers */
+ double bulk_resistivity;
+ double resistivity_temp_coeff; /* temperature coefficient of resistivity */
+ };
+
+ typedef std::vector<Layer> LayerList;
+
+ /*
+ * a via is a cylindrical, conductive hole.
+ */
+
+ class Via {
+ public:
+ double x;
+ double y;
+ double z0; /* bottom */
+ double z1; /* top */
+ double radius;
+ };
+
+ typedef std::vector<Via> ViaList;
+
+ /*
+ * Devices
+ */
+
+ enum device_value_enum { DEVICE_VALUE_FLOAT, DEVICE_VALUE_STRING, DEVICE_VALUE_NONE };
+
+ class Device {
+ public:
+ Device();
+ std::string name;
+ std::string ref; /* reference, e.g. R1, C2, L3, IC4 */
+ device_value_enum value_type; /* whether to use value_float or value_string */
+ double value_float; /* in ohms, farad, henry */
+ std::string value_string;
+ std::string layer_name;
+ };
+
+ typedef std::vector<Device> DeviceList;
+
+ /* board dimensions */
+ class Bounds {
+ public:
+ Bounds();
+ double x_min;
+ double x_max;
+ double y_min;
+ double y_max;
+ double z_min;
+ double z_max;
+ };
+
+ /*
+ * class PCB represents a pcb board.
+ */
+
+ class PCB {
+ public:
+ PCB();
+ FloatPolygons board; /* board outline */
+ LayerList stackup;
+ ViaList via;
+ double via_plating_thickness;
+ DeviceList device;
+ PinList pin;
+
+ /*
+ * ReadHyperLynx loads the Hyperlynx file "filename".
+ * "layers" is the names of the layers to import. Default is importing all layers.
+ * "nets" is the names of the nets to import. Default is importing all nets.
+ */
+
+ void ReadHyperLynx(std::string filename, std::vector<std::string> layers = std::vector<std::string>(), std::vector<std::string> nets = std::vector<std::string>());
+ void SetEpsilonR(double epsilon_r); /* set dielectric epsilon r. Overrides value in Hyperlynx file. */
+ void SetBulkResistance(double bulk_resistivity); /* set dielectric bulk resistance. Overrides value in Hyperlynx file. */
+ void SetLossTangent(double loss_tangent); /* set dielectric loss tangent. Overrides value in Hyperlynx file. */
+ void SetGrid(double new_grid); /* set resolution of x and y coordinates */
+ void SetArcPrecision(double new_arc_precision); /* set maximum difference between perfect circle arc and polygonal approximation */
+ void SetClearance(double new_clearance); /* set trace-to-plane clearance */
+ Bounds GetBounds(); /* gets board extension in x,y and z */
+ void SetBounds(Bounds new_bounds); /* crops board in x,y and z */
+
+ void PrintSummary(); /* print layer summary */
+
+ /*
+ * WritePDF exports to the pdf file "filename".
+ * "hue", "saturation" and "brightness" are color options with a value between 0.0 and 1.0.
+ */
+
+ void WritePDF(std::string filename, double hue = -1, double saturation = -1, double brightness = -1);
+
+ /*
+ * WriteCSXCAD exports csxcad matlab code to file "filename".
+ * If "pcb_outline" is true, the detailed pcb shape is exported, including pcb cutouts.
+ * If "pcb_outline" is false, a simple rectangular bounding box is output."
+ */
+
+ void WriteCSXCAD(std::string filename, bool pcb_outline = false, bool lossy_copper = false, bool metal_3d = false);
+
+ unsigned int debug; /* setting debug to 0 switches debugging off */
+ std::vector<std::string> flood_layers; /* names of layers to be flooded with copper */
+ bool raw; /* set raw processing */
+ private:
+ void _UserOverrides();
+ void _CheckBoardOutline();
+ Bounds _bounds;
+ double _arc_precision;
+ double _clearance;
+ double _epsilon_r;
+ bool _epsilon_r_override;
+ double _bulk_resistivity;
+ bool _bulk_resistivity_override;
+ double _loss_tangent;
+ bool _loss_tangent_override;
+ };
+
+}
+
+#endif
+
+/* not truncated */
diff --git a/hyp2mat/lib/hyp2mat.pc.in b/hyp2mat/lib/hyp2mat.pc.in
new file mode 100644
index 0000000..8635861
--- /dev/null
+++ b/hyp2mat/lib/hyp2mat.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: hyp2mat
+Description: Convert Hyperlynx PCB files to matlab scripts
+Requires:
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lhyp2mat
+Libs.private: @LIBS@
+Cflags: -I${includedir}
diff --git a/hyp2mat/lib/hyperlynx.cc b/hyp2mat/lib/hyperlynx.cc
new file mode 100644
index 0000000..adaa043
--- /dev/null
+++ b/hyp2mat/lib/hyperlynx.cc
@@ -0,0 +1,348 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * HypFile is concerned with parsing the hyperlynx file, and contains many variables and methods
+ * which are only of interest during parsing.
+ *
+ * Hyp2Mat is interested in presenting an uncluttered interface.
+ *
+ * This is where data is passed on from HypFile to Hyp2Mat, from the obscure to the obvious.
+ */
+
+#include <iostream>
+#include <algorithm>
+
+#include "config.h"
+#include "hyperlynx.h"
+#include "polygon.h"
+#include "crop.h"
+
+HyperLynx::HyperLynx ()
+{
+ raw = false;
+ arc_precision = 0;
+ clearance = 0.0002;
+}
+
+
+/*
+ * Read a pcb in hyperlynx format
+ */
+
+void HyperLynx::Read (std::string filename, Hyp2Mat::PCB& pcb)
+{
+ /*
+ * load hyperlynx file in hyp_file
+ */
+
+ bool success = LoadHypFile(pcb, filename, arc_precision);
+ if (!success) throw ("load hyp file error");
+
+ /*
+ * Copy hyperlynx file data from hyp_file to pcb
+ */
+ saved_bounds = bounds;
+ CopyBoard(pcb); /* copy board outline. crop board in x, y axis */
+
+ CopyStackUp(pcb); /* copy layer stackup */
+ bounds = AdjustBounds(pcb, saved_bounds);
+ CopyVias(pcb); /* copy vias */
+ CopyViaPlating(pcb); /* copy via plating thickness */
+ CopyDevices(pcb); /* copy device info */
+ CopyPins(pcb); /* copy pin references */
+
+ bounds = AdjustBounds(pcb, saved_bounds);
+ CropLayers(pcb, bounds); /* crop board in z axis */
+
+ if (bounds.z_min == bounds.z_max) std::cerr << "warning: zero height board" << std::endl;
+
+ /*
+ * Check whether to print all net names
+ */
+
+ /* if one of the nets is "?", print list of all nets */
+ if (std::find(nets.begin(), nets.end(), "?") != nets.end())
+ PrintNets();
+
+ return;
+}
+
+/*
+ * Load Hyperlynx file in hyp_file
+ */
+
+bool HyperLynx::LoadHypFile(Hyp2Mat::PCB& pcb, std::string filename, double arc_precision)
+{
+ hyp_file.trace_scanner = (pcb.debug == 4);
+ hyp_file.trace_parser = (pcb.debug == 3);
+ hyp_file.trace_hyp = (pcb.debug == 2);
+
+ if (pcb.debug != 0) {
+ std::cerr << "hyp2mat " << VERSION << " using ClipperLib " << CLIPPER_VERSION << std::endl;
+ }
+
+ if (arc_precision < 0) {
+ std::cerr << "arc precision negative" << std::endl;
+ arc_precision = 0;
+ }
+ hyp_file.arc_precision = arc_precision;
+
+ hyp_file.flood_layers = flood_layers;
+
+ bool success = hyp_file.load(filename);
+
+ if (pcb.debug == 1) {
+ std::cerr << "writing hyp_file dump to hyp_debug.txt" << std::endl;
+ hyp_file.save ("hyp_debug.txt");
+ }
+
+ return success;
+}
+
+/*
+ * copy board outline into polygon "board" and crop according to "bounds".
+ */
+
+void HyperLynx::CopyBoard(Hyp2Mat::PCB& pcb)
+{
+ bool outer_edge = true;
+
+ for (HypFile::PolygonList::iterator i = hyp_file.board.edge.begin(); i != hyp_file.board.edge.end(); ++i) {
+ Hyp2Mat::Polygon poly;
+ Hyp2Mat::FloatPolygon edge;
+ for (HypFile::PointList::iterator j = i->vertex.begin(); j != i->vertex.end(); ++j)
+ edge.push_back(Hyp2Mat::FloatPoint(j->x, j->y));
+ poly.AddEdge(edge);
+ if (outer_edge)
+ board.Union(poly);
+ else
+ board.Difference(poly);
+ outer_edge = false;
+ }
+
+ /*
+ * Cropping - Remove board outside bounds
+ */
+
+ pcb.board = board.Result(); /* store intermediate result */
+ bounds = AdjustBounds(pcb, bounds); /* tighten bounds around board */
+ board = CropPolygon(board, bounds); /* crop board. store board as integer polygon */
+ pcb.board = board.Result(); /* board as floating point polygon */
+
+ return;
+}
+
+/*
+ * copy vias
+ */
+
+void HyperLynx::CopyVias(Hyp2Mat::PCB& pcb)
+{
+ for (HypFile::NetList::iterator i = hyp_file.net.begin(); i != hyp_file.net.end(); ++i) {
+
+ /* check if we're interested in this net. if no nets are specified, copy all nets */
+ bool net_wanted = nets.empty() || (std::find(nets.begin(), nets.end(), i->net_name) != nets.end());
+ if (!net_wanted) continue;
+
+ /* copy vias of current net */
+ for (HypFile::ViaList::iterator j = i->via.begin(); j != i->via.end(); ++j) {
+ Hyp2Mat::Via v;
+ v.x = j->x;
+ v.y = j->y;
+ v.z0 = j->z0;
+ v.z1 = j->z1;
+ v.radius = j->radius;
+
+ /* cropping */
+ if (!board.IsEmpty() && ((v.x > bounds.x_max) || (v.x < bounds.x_min) || (v.y > bounds.y_max) || (v.y < bounds.y_min))) continue; // Only crop in x and y if board outline is defined
+ if ((v.z0 > bounds.z_max) && (v.z1 > bounds.z_max)) continue;
+ if ((v.z0 < bounds.z_min) && (v.z1 < bounds.z_min)) continue;
+ if (v.z1 > bounds.z_max) v.z1 = bounds.z_max;
+ if (v.z0 < bounds.z_min) v.z0 = bounds.z_min;
+
+ /* add to list of vias */
+ pcb.via.push_back(v);
+ }
+ }
+
+ return;
+}
+
+/*
+ * copy via plating
+ */
+
+void HyperLynx::CopyViaPlating(Hyp2Mat::PCB& pcb)
+{
+ /* assume via plating thickness is identical to outer plating thickness */
+ for (HypFile::LayerList::iterator l = hyp_file.stackup.begin(); l != hyp_file.stackup.end(); ++l)
+ if (l->layer_type != HypFile::LAYER_DIELECTRIC) {
+ /* first metal layer */
+ pcb.via_plating_thickness = l->plating_thickness;
+ break;
+ }
+
+ return;
+}
+
+/*
+ * copy devices
+ */
+
+void HyperLynx::CopyDevices(Hyp2Mat::PCB& pcb)
+{
+ for (HypFile::DeviceList::iterator i = hyp_file.device.begin(); i != hyp_file.device.end(); ++i) {
+ /* set up device */
+ Hyp2Mat::Device dev;
+ dev.ref = i->ref;
+ dev.name = i->name;
+ dev.layer_name = i->layer_name;
+
+ if (i->value_float_set) {
+ dev.value_float = i->value_float;
+ dev.value_type = Hyp2Mat::DEVICE_VALUE_FLOAT;
+ }
+ else if (i->value_string_set) {
+ dev.value_string = i->value_string;
+ dev.value_type = Hyp2Mat::DEVICE_VALUE_STRING;
+ }
+ else dev.value_type = Hyp2Mat::DEVICE_VALUE_NONE;
+
+ /* add device to board */
+ pcb.device.push_back(dev);
+ }
+ return;
+}
+
+/*
+ * copy pins
+ */
+
+void HyperLynx::CopyPins(Hyp2Mat::PCB& pcb)
+{
+ /* iterate over all pins */
+ for (HypFile::NetList::iterator i = hyp_file.net.begin(); i != hyp_file.net.end(); ++i) {
+
+ /* check if we're interested in this net. if no nets are specified, copy all nets */
+ bool net_wanted = nets.empty() || (std::find(nets.begin(), nets.end(), i->net_name) != nets.end());
+ if (!net_wanted) continue;
+
+ for (HypFile::PinList::iterator j = i->pin.begin(); j != i->pin.end(); ++j)
+ /* find pin padstack */
+ for (HypFile::PadstackList::iterator p = hyp_file.padstack.begin(); p != hyp_file.padstack.end(); ++p)
+ if (p->padstack_name == j->padstack_name)
+ /* copy this pin */
+ CopyPin(pcb, *j, *p);
+ }
+
+ return;
+}
+
+/*
+ * copy a single pin
+ */
+
+void HyperLynx::CopyPin(Hyp2Mat::PCB& pcb, HypFile::Pin& pin, HypFile::Padstack& padstack)
+{
+ Hyp2Mat::Pin new_pin;
+ new_pin.x = Hyp2Mat::Polygon::AlignToGrid(pin.x);
+ new_pin.y = Hyp2Mat::Polygon::AlignToGrid(pin.y);
+ new_pin.ref = pin.pin_reference;
+
+ HypFile::Pad pad;
+
+ std::string layer_name;
+
+ /* now find pad */
+
+ /* how many layers does the padstack have? */
+ if (padstack.pads.size() == 1) {
+ /* only a single layer: that's easy */
+ pad = padstack.pads.front();
+ }
+ else {
+ /* determine pin device name */
+ size_t found = pin.pin_reference.find('.');
+ if ((found == std::string::npos) || (found == 0)) return; /* device not found */
+ std::string device_name = pin.pin_reference.substr(0, found);
+
+ /* find device pin belongs to */
+ bool device_found = false;
+ for (Hyp2Mat::DeviceList::iterator i = pcb.device.begin(); i != pcb.device.end(); ++i)
+ if (device_name == i->ref) {
+ device_found = true;
+ layer_name = i->layer_name; /* device layer */
+ }
+ if (!device_found) return;
+
+ /* we now know the device layer. Find this layer in the pin padstack. */
+ bool pad_found = false;
+ for (HypFile::PadList::iterator p = padstack.pads.begin(); p != padstack.pads.end(); ++p)
+ if (p->layer_name == layer_name) {
+ pad_found = true;
+ pad = *p;
+ }
+ if (!pad_found) return;
+ }
+
+ /* find layer vertical position */
+ for (Hyp2Mat::LayerList::iterator l = pcb.stackup.begin(); l != pcb.stackup.end(); ++l)
+ if (pad.layer_name == l->layer_name) {
+ new_pin.layer_name = l->layer_name;
+ new_pin.z0 = l->z0;
+ new_pin.z1 = l->z1;
+ }
+
+ /* convert pad to polygon, and copy pad shape */
+ HypFile::Polygon pad_poly = hyp_file.pad2poly(new_pin.x, new_pin.y, pad);
+ new_pin.metal.clear();
+ for (HypFile::PointList::iterator j = pad_poly.vertex.begin(); j != pad_poly.vertex.end(); ++j)
+ new_pin.metal.push_back(Hyp2Mat::FloatPoint(Hyp2Mat::Polygon::AlignToGrid(j->x), Hyp2Mat::Polygon::AlignToGrid(j->y)));
+
+ /* add pin to pcb */
+ pcb.pin.push_back(new_pin);
+
+ return;
+}
+
+/*
+ * Print all available nets
+ */
+
+void HyperLynx::PrintNets()
+{
+ std::vector<std::string> net_names;
+
+ for (HypFile::NetList::iterator i = hyp_file.net.begin(); i != hyp_file.net.end(); ++i)
+ net_names.push_back(i->net_name);
+
+ sort(net_names.begin(), net_names.end());
+ unique(net_names.begin(), net_names.end());
+
+ std::cout << "nets:";
+ for (std::vector<std::string>::iterator i = net_names.begin(); i != net_names.end(); ++i)
+ std::cout << " " << *i;
+ std::cout << std::endl;
+
+ return;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/hyperlynx.h b/hyp2mat/lib/hyperlynx.h
new file mode 100644
index 0000000..bfe3033
--- /dev/null
+++ b/hyp2mat/lib/hyperlynx.h
@@ -0,0 +1,79 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * loads a Hyperlynx file in a Hyp2Mat class
+ */
+
+#ifndef HYPERLYNX_H
+#define HYPERLYNX_H
+
+#include "hyp2mat.h"
+#include "hypfile.h"
+#include "polygon.h"
+
+class HyperLynx {
+public:
+ HyperLynx();
+ void Read (std::string filename, Hyp2Mat::PCB& pcb);
+
+ /* parameters */
+ std::vector<std::string> layers;
+ std::vector<std::string> nets;
+ bool raw;
+ double arc_precision;
+ double clearance;
+ std::vector<std::string> flood_layers;
+ Hyp2Mat::Bounds bounds;
+
+private:
+ HypFile::Hyp hyp_file;
+ Hyp2Mat::Polygon board; /* polygon of board outline */
+ Hyp2Mat::Bounds saved_bounds; /* copy of bounds */
+
+ double layer_plane_separation; /* plane separation for this layer */
+ double net_plane_separation; /* plane separation for this net on this layer */
+ double polygon_plane_separation; /* plane separation for this polygon on this net on this layer */
+
+ bool LoadHypFile(Hyp2Mat::PCB& pcb, std::string filename, double arc_precision);
+ void CopyBoard(Hyp2Mat::PCB& pcb); /* copy board outline */
+
+ void CopyStackUp(Hyp2Mat::PCB& pcb); /* copy all layers, both metal and dielectric */
+ void CopyLayer(Hyp2Mat::PCB& pcb, HypFile::Layer& hyp_layer); /* copy a single layer */
+ Hyp2Mat::Polygon CopyCopper(Hyp2Mat::Layer layer, Hyp2Mat::FloatPolygons& raw_polygons); /* copy all the copper of a layer */
+ Hyp2Mat::Polygon CopyNet(Hyp2Mat::Layer layer, HypFile::Net& hyp_net, polygon_type_enum poly_type, Hyp2Mat::FloatPolygons& raw_polygons); /* copy a single net on a layer */
+
+ Hyp2Mat::Polygon CopyPolygon(HypFile::PolygonList metal, Hyp2Mat::FloatPolygons& raw_polygons); /* copy a single polygon of a net on a layer */
+ Hyp2Mat::Polygon PlaneSeparation(std::string layer_name, std::string net_name);
+
+ void CopyVias(Hyp2Mat::PCB& pcb);
+ void CopyViaPlating(Hyp2Mat::PCB& pcb);
+
+ void CopyDevices(Hyp2Mat::PCB& pcb);
+
+ void CopyPins(Hyp2Mat::PCB& pcb); /* copy all pins to pcb */
+ void CopyPin(Hyp2Mat::PCB& pcb, HypFile::Pin& pin, HypFile::Padstack& padstack); /* copy a single pin to pcb */
+
+ void PrintNets();
+ };
+
+#endif
+
+/* not truncated */
diff --git a/hyp2mat/lib/hypfile.cc b/hyp2mat/lib/hypfile.cc
new file mode 100644
index 0000000..3535719
--- /dev/null
+++ b/hyp2mat/lib/hypfile.cc
@@ -0,0 +1,378 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <cstdio>
+#include <cmath>
+#include "hypfile.h"
+#include "parser.h"
+
+using namespace std;
+using namespace HypFile;
+
+HypFile::Hyp::Hyp()
+ : trace_scanner(false), trace_parser(false), trace_hyp(false)
+{
+ unit = 1;
+ metal_thickness_unit = 1;
+ use_die_for_metal = false;
+
+ min_circle_segments = 8; // 8 seems minimal, 16 seems more than sufficient.
+ arc_precision = 0;
+ pi = M_PI;
+ inches = 0.0254; /* inches to m */
+ copper_imperial_weight = 1.341; /* metal thickness in ounces/ft2. 1 oz/ft2 copper = 1.341 mil */
+ copper_metric_weight = 0.1116; /* metal thickness in grams/cm2. 1 gr/cm2 copper = 0.1116 cm */
+ copper_bulk_resistivity = 1.724e-8;
+ copper_temperature_coefficient = 0.00393;
+ fr4_epsilon_r = 4.3;
+ fr4_loss_tangent = 0.020;
+ conformal_epsilon_r = 3.3; /* dielectric constant of conformal layer */
+ board.plane_separation = -1.0; /* distance between PLANE polygon and copper of different nets; -1 if not set */
+
+}
+
+/*
+ * parse a Hyperlynx file
+ */
+
+bool HypFile::Hyp::load(const std::string &filename)
+{
+ if (trace_hyp) cerr << "parsing " << filename << endl;
+
+ /* open input file */
+ if (filename != "-") {
+ yyin = fopen (filename.c_str(),"r");
+ if (!yyin) {
+ cerr << "Can't open input file " << filename << endl;
+ return false;
+ }
+ }
+
+ /* parse input file */
+ yy_flex_debug = trace_scanner;
+ yydebug = trace_parser;
+ int retval = yyparse(this);
+
+ /* close input file */
+ fclose(yyin);
+
+ bool success = retval == 0;
+ return success;
+}
+
+/*
+ * Error output
+ */
+
+void HypFile::Hyp::error(const std::string& msg)
+{
+ cerr << msg << endl;
+}
+
+/*
+ * Dumps internal structures for debugging.
+ */
+
+bool HypFile::Hyp::save(const std::string& filename)
+{
+ /*
+ * Open file for output
+ */
+
+ if ((filename != "-") && (freopen(filename.c_str(), "w", stdout) == NULL)) {
+ std::ostringstream err_msg;
+ err_msg << "could not open '" << filename << "' for writing";
+ error(err_msg.str());
+ return true;
+ }
+
+ /*
+ * Variables
+ */
+
+ cout << "unit = " << unit << endl;
+ cout << "metal_thickness_unit = " << metal_thickness_unit << endl;
+ cout << "use_die_for_metal = " << use_die_for_metal << endl;
+ cout << "min_circle_segments = " << min_circle_segments << endl;
+ cout << "arc_precision = " << arc_precision << endl;
+ cout << "pi = " << pi << endl;
+ cout << "inches = " << inches << endl;
+ cout << "copper_imperial_weight = " << copper_imperial_weight << endl;
+ cout << "copper_metric_weight = " << copper_metric_weight << endl;
+ cout << "copper_bulk_resistivity = " << copper_bulk_resistivity << endl;
+ cout << "copper_temperature_coefficient = " << copper_temperature_coefficient << endl;
+ cout << "fr4_epsilon_r = " << fr4_epsilon_r << endl;
+ cout << "fr4_loss_tangent = " << fr4_loss_tangent << endl;
+ cout << "conformal_epsilon_r = " << conformal_epsilon_r << endl;
+
+ /*
+ * Export board
+ */
+
+ cout << "board" << endl;
+ for (PolygonList::iterator i = board.edge.begin(); i != board.edge.end(); ++i) {
+ cout << " polygon" << endl;
+ cout << " positive " << i->positive << endl;
+ for (PointList::iterator j = i->vertex.begin(); j != i->vertex.end(); ++j) {
+ cout << " x = " << j->x << endl;
+ cout << " y = " << j->y << endl;
+ }
+ }
+
+ cout << " plane_separation = " << board.plane_separation << endl;
+ cout << " board_attribute" << endl;
+ for (AttributeList::iterator j = board.attribute.begin(); j != board.attribute.end(); ++j) {
+ cout << " name = '" << j->name << "'" << endl;
+ cout << " value = '" << j->value << "'" << endl;
+ }
+
+ /*
+ * Export devices
+ */
+
+ cout << "device" << endl;
+ for (DeviceList::iterator dev_it = device.begin(); dev_it != device.end(); ++dev_it) {
+ cout << " device" << endl;
+ cout << " device_type = '" << dev_it->device_type << "'" << endl;
+ cout << " ref = '" << dev_it->ref << "'" << endl;
+ cout << " name = '" << dev_it->name << "'" << endl;
+ cout << " value_float = " << dev_it->value_float << endl;
+ cout << " value_float_set = " << dev_it->value_float_set << endl;
+ cout << " value_string = '" << dev_it->value_string << "'" << endl;
+ cout << " value_string_set = " << dev_it->value_string_set << endl;
+ cout << " layer_name = '" << dev_it->layer_name << "'" << endl;
+ cout << " package = '" << dev_it->package << "'" << endl;
+ }
+
+ /*
+ * Export supplies
+ */
+
+ cout << "supplies" << endl;
+ for (SupplyList::iterator supp_it = supply.begin(); supp_it != supply.end(); ++supp_it) {
+ cout << " supply" << endl;
+ cout << " name = '" << supp_it->name << "'" << endl;
+ cout << " value_float = " << supp_it->value_float << endl;
+ cout << " voltage_specified = " << supp_it->voltage_specified << endl;
+ cout << " conversion = " << supp_it->conversion << endl;
+ }
+
+ /*
+ * Export stackup
+ */
+
+ cout << "layers" << endl;
+ for (LayerList::iterator it = stackup.begin(); it != stackup.end(); ++it) {
+ cout << " layer" << endl;
+ cout << " layer_name = '" << it->layer_name << "'" << endl;
+ cout << " layer_type = " << it->layer_type << " ";
+ switch (it->layer_type) {
+ case LAYER_SIGNAL: cout << "LAYER_SIGNAL"; break;
+ case LAYER_DIELECTRIC: cout << "LAYER_DIELECTRIC"; break;
+ case LAYER_PLANE: cout << "LAYER_PLANE"; break;
+ default: cout << "Error"; break;
+ }
+ cout << endl;
+
+ cout << " bulk_resistivity = " << it->bulk_resistivity << endl;
+ cout << " conformal = " << it->conformal << endl;
+ cout << " epsilon_r = " << it->epsilon_r << endl;
+ cout << " layer_name = " << it->layer_name << endl;
+ cout << " loss_tangent = " << it->loss_tangent << endl;
+ cout << " material_name = " << it->material_name << endl;
+ cout << " plane_separation = " << it->plane_separation << endl;
+ cout << " plating_thickness = " << it->plating_thickness << endl;
+ cout << " prepreg = " << it->prepreg << endl;
+ cout << " temperature_coefficient = " << it->temperature_coefficient << endl;
+ cout << " thickness = " << it->thickness << endl;
+ cout << " z0 = " << it->z0 << endl;
+ cout << " z1 = " << it->z1 << endl;
+ }
+
+ /*
+ * Padstack
+ */
+
+ cout << "padstacks" << endl;
+ for (PadstackList::iterator it = padstack.begin(); it != padstack.end(); ++it) {
+ cout << " padstack" << endl;
+ cout << " padstack_name = '" << it->padstack_name << "'" << endl;
+ cout << " drill_size = " << it->drill_size << endl;
+ cout << " pads" << endl;
+ for (PadList::iterator p = it->pads.begin(); p != it->pads.end(); ++p) {
+ cout << " pad" << endl;
+ cout << " layer_name = '" << p->layer_name << "'" << endl;
+ cout << " pad_type = " << p->pad_type << " ";
+ switch (p->pad_type) {
+ case PAD_TYPE_METAL: cout << "PAD_TYPE_METAL"; break;
+ case PAD_TYPE_ANTIPAD: cout << "PAD_TYPE_ANTIPAD"; break;
+ case PAD_TYPE_THERMAL_RELIEF: cout << "PAD_TYPE_THERMAL_RELIEF"; break;
+ default: cout << "Error"; break;
+ }
+ cout << endl;
+ cout << " pad_shape = " << p->pad_shape << " ";
+ switch (p->pad_shape) {
+ case PAD_SHAPE_OVAL: cout << "PAD_SHAPE_OVAL"; break;
+ case PAD_SHAPE_RECTANGULAR: cout << "PAD_SHAPE_RECTANGULAR"; break;
+ case PAD_SHAPE_OBLONG: cout << "PAD_SHAPE_OBLONG"; break;
+ default: cout << "Error"; break;
+ }
+ cout << endl;
+ cout << " pad_sx = " << p->pad_sx << endl;
+ cout << " pad_sy = " << p->pad_sy << endl;
+ cout << " pad_angle = " << p->pad_angle << endl;
+ cout << " thermal_clear_shape = " << p->thermal_clear_shape << " ";
+ switch (p->thermal_clear_shape) {
+ case PAD_SHAPE_OVAL: cout << "PAD_SHAPE_OVAL"; break;
+ case PAD_SHAPE_RECTANGULAR: cout << "PAD_SHAPE_RECTANGULAR"; break;
+ case PAD_SHAPE_OBLONG: cout << "PAD_SHAPE_OBLONG"; break;
+ default: cout << "Error"; break;
+ }
+ cout << endl;
+ cout << " thermal_clear_sx = " << p->thermal_clear_sx << endl;
+ cout << " thermal_clear_sy = " << p->thermal_clear_sy << endl;
+ cout << " thermal_clear_angle = " << p->thermal_clear_angle << endl;
+ }
+ }
+
+ /*
+ * Net
+ */
+
+ cout << "net" << endl;
+ for (NetList::iterator i = net.begin(); i != net.end(); ++i) {
+ cout << " net" << endl;
+ cout << " net_name = '" << i->net_name << "'" << endl;
+
+ /*
+ * Net Polygons
+ */
+ for (PolygonMap::iterator j = i->metal.begin(); j != i->metal.end(); ++j) {
+ cout << " polygon id " << j->first << endl;
+ for (PolygonList::iterator k = j->second.begin(); k != j->second.end(); ++k) {
+ cout << " polygon" << endl;
+ cout << " positive " << k->positive << endl;
+ cout << " width " << k->width << endl;
+ cout << " layer_name " << k->layer_name << endl;
+ for (PointList::iterator l = k->vertex.begin(); l != k->vertex.end(); ++l) {
+ cout << " x = " << l->x << endl;
+ cout << " y = " << l->y << endl;
+ }
+ }
+ }
+
+ /*
+ * Net Vias
+ */
+
+ cout << " vias" << endl;
+ for (ViaList::iterator j = i->via.begin(); j != i->via.end(); ++j) {
+ cout << " via" << endl;
+ cout << " x = " << j->x << endl;
+ cout << " y = " << j->y << endl;
+ cout << " layer0_name = " << j->layer0_name << endl;
+ cout << " layer1_name = " << j->layer1_name << endl;
+ cout << " radius = " << j->radius << endl;
+ }
+
+ /*
+ * Net Pins
+ */
+
+ cout << " pins" << endl;
+ for (PinList::iterator j = i->pin.begin(); j != i->pin.end(); ++j) {
+ cout << " pin" << endl;
+ cout << " x = " << j->x << endl;
+ cout << " y = " << j->y << endl;
+ cout << " pin_reference = " << j->pin_reference << endl;
+ cout << " padstack_name = " << j->padstack_name << endl;
+ cout << " pin_function = " << j->pin_function << " ";
+ switch (j->pin_function) {
+ case PIN_SIM_IN: cout << "PIN_SIM_IN"; break;
+ case PIN_SIM_OUT: cout << "PIN_SIM_OUT"; break;
+ case PIN_SIM_BOTH: cout << "PIN_SIM_BOTH"; break;
+ default: cout << "Error"; break;
+ }
+ cout << endl;
+ }
+
+ /*
+ * Net Unrouted segments
+ */
+
+ cout << " unrouted_segments" << endl;
+ for (UnroutedSegmentList::iterator j = i->unrouted_segment.begin(); j != i->unrouted_segment.end(); ++j) {
+ cout << " unrouted_segment" << endl;
+ cout << " x1 = " << j->x1 << endl;
+ cout << " y1 = " << j->y1 << endl;
+ cout << " layer1_name = " << j->layer1_name << endl;
+ cout << " x2 = " << j->x2 << endl;
+ cout << " y2 = " << j->y2 << endl;
+ cout << " layer2_name = " << j->layer2_name << endl;
+ cout << " zlayer_name_set = " << j->zlayer_name_set << endl;
+ cout << " zlayer_name = " << j->zlayer_name << endl;
+ cout << " width = " << j->width << endl;
+ cout << " impedance_set = " << j->impedance_set << endl;
+ cout << " impedance = " << j->impedance << endl;
+ cout << " delay = " << j->delay << endl;
+ cout << " resistance = " << j->resistance << endl;
+ }
+
+ /*
+ * Net Attributes
+ */
+
+ if (!i->attribute.empty()) cout << " attribute" << endl;
+ for (AttributeList::iterator j = i->attribute.begin(); j != i->attribute.end(); ++j) {
+ cout << " name = '" << j->name << "'" << endl;
+ cout << " value = '" << j->value << "'" << endl;
+ }
+ }
+
+ /*
+ * Net Class
+ */
+
+ cout << "net_class" << endl;
+ for (NetClassList::iterator i = net_class.begin(); i != net_class.end(); ++i) {
+ cout << " net_class" << endl;
+ cout << " net_class_name = '" << i->net_class_name << "'" << endl;
+ cout << " net_class_element" << endl;
+ for (StringList::iterator j = i->net_class_element.begin(); j != i->net_class_element.end(); ++j) {
+ cout << " net_name = '" << *j << "'" << endl;
+ }
+ if (!i->attribute.empty()) cout << " attribute" << endl;
+ for (AttributeList::iterator j = i->attribute.begin(); j != i->attribute.end(); ++j) {
+ cout << " name = '" << j->name << "'" << endl;
+ cout << " value = '" << j->value << "'" << endl;
+ }
+ }
+
+ cout << "end" << endl;
+
+ fclose(stdout);
+
+ return true;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/hypfile.h b/hyp2mat/lib/hypfile.h
new file mode 100644
index 0000000..2670672
--- /dev/null
+++ b/hyp2mat/lib/hypfile.h
@@ -0,0 +1,333 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Loads a hyperlynx file from disk.
+ */
+
+#ifndef HYPFILE_H
+#define HYPFILE_H
+
+#include <string>
+#include <vector>
+#include <map>
+#include "parse_param.h"
+
+namespace HypFile {
+
+ class Point {
+ public:
+ double x;
+ double y;
+ Point(double x_val = 0, double y_val = 0): x(x_val), y(y_val) {};
+ bool operator==(const Point &right) const { return ((x == right.x) && ( y == right.y)); };
+ };
+
+ typedef std::vector<Point> PointList;
+
+ class Polygon {
+ public:
+ Polygon();
+ PointList vertex;
+ polygon_type_enum polygon_type;
+ std::string net_name;
+ int id;
+ bool positive; /* false if hole */
+ double width; /* edge width of polygon */
+ /* if polygon_type == POLYGON_TYPE_PLANE keep plane_separation distance from other nets */
+ double plane_separation; /* -1.0 if not set */
+ double left_plane_separation; /* -1.0 if not set */
+ std::string layer_name;
+ };
+
+ typedef std::vector<Polygon> PolygonList;
+
+ struct Via {
+ double x;
+ double y;
+ std::string layer0_name; /* bottom */
+ std::string layer1_name; /* top */
+ double z0;
+ double z1;
+ double radius;
+ };
+
+ typedef std::vector<Via> ViaList;
+
+ struct Pin {
+ double x;
+ double y;
+ std::string pin_reference;
+ std::string padstack_name;
+ pin_function_enum pin_function;
+ };
+
+ typedef std::vector<Pin> PinList;
+
+ struct UnroutedSegment {
+ double x1;
+ double y1;
+ std::string layer1_name;
+ double x2;
+ double y2;
+ std::string layer2_name;
+ bool zlayer_name_set;
+ std::string zlayer_name;
+ double width;
+ bool impedance_set;
+ double impedance;
+ double delay;
+ double resistance;
+ };
+
+ typedef std::vector<UnroutedSegment> UnroutedSegmentList;
+
+ enum layer_enum { LAYER_SIGNAL, LAYER_DIELECTRIC, LAYER_PLANE };
+
+ struct Layer {
+ layer_enum layer_type;
+ double bulk_resistivity;
+ bool conformal;
+ double epsilon_r;
+ std::string layer_name;
+ double loss_tangent;
+ std::string material_name;
+ double plane_separation; /* trace to plane separation */
+ double plating_thickness;
+ bool prepreg;
+ double temperature_coefficient; /* temperature coefficient of resistivity */
+ double thickness; /* layer thickness */
+ double z0; /* vertical position of bottom of layer */
+ double z1; /* vertical position of top of layer */
+ };
+
+ typedef std::vector<Layer> LayerList;
+
+ struct Device {
+ std::string device_type;
+ std::string ref;
+ std::string name;
+ double value_float;
+ bool value_float_set;
+ std::string value_string;
+ bool value_string_set;
+ std::string layer_name;
+ std::string package;
+ };
+
+ typedef std::vector<Device> DeviceList;
+
+ struct Supply {
+ std::string name;
+ double value_float;
+ bool voltage_specified;
+ bool conversion;
+ };
+
+ typedef std::vector<Supply> SupplyList;
+
+ enum pad_shape_enum { PAD_SHAPE_OVAL, PAD_SHAPE_RECTANGULAR, PAD_SHAPE_OBLONG };
+
+ struct Pad {
+ std::string layer_name;
+ pad_type_enum pad_type;
+ pad_shape_enum pad_shape;
+ double pad_sx;
+ double pad_sy;
+ double pad_angle;
+ pad_shape_enum thermal_clear_shape;
+ double thermal_clear_sx;
+ double thermal_clear_sy;
+ double thermal_clear_angle;
+ };
+
+ typedef std::vector<Pad> PadList;
+
+ struct Padstack {
+ std::string padstack_name;
+ double drill_size;
+ PadList pads;
+ };
+
+ typedef std::vector<Padstack> PadstackList;
+
+ struct Attribute {
+ std::string name;
+ std::string value;
+ };
+
+ typedef std::vector<Attribute> AttributeList;
+
+ typedef std::vector<std::string> StringList;
+
+ struct NetClass {
+ std::string net_class_name;
+ StringList net_class_element;
+ AttributeList attribute;
+ };
+
+ /* PolygonByID orders polygons according to polygon id. PolygonList is a list of polygons with the same polygon id */
+
+ typedef std::map<int, PolygonList> PolygonMap;
+
+ struct Net {
+ std::string net_name;
+ PolygonMap metal;
+ double plane_separation;
+ ViaList via;
+ UnroutedSegmentList unrouted_segment;
+ PinList pin;
+ AttributeList attribute;
+ };
+
+ typedef std::vector<Net> NetList;
+
+ struct Board {
+ PolygonList edge;
+ PolygonList segments;
+ AttributeList attribute;
+ double plane_separation;
+ };
+
+ typedef std::vector<NetClass> NetClassList;
+
+ class Hyp
+ {
+ public:
+ Hyp();
+ bool trace_scanner; // flex scanner debugging
+ bool trace_parser; // bison parser debugging
+ bool trace_hyp; // hyperlynx debugging
+ bool load (const std::string& filename); // load Hyperlynx file
+ bool save (const std::string& filename); // write to file
+
+ void error(const std::string& m);
+
+ Board board;
+ LayerList stackup;
+ DeviceList device;
+ SupplyList supply;
+ PadstackList padstack;
+ NetClassList net_class;
+ NetList net;
+
+ /* Physical constants */
+ double pi;
+ double inches; /* inches to m */
+ double copper_imperial_weight; /* metal thickness in ounces/ft2 */
+ double copper_metric_weight; /* metal thickness in grams/cm2 */
+ double copper_bulk_resistivity;
+ double copper_temperature_coefficient; /* temperature coefficient of bulk resistivity */
+ double fr4_epsilon_r; /* dielectric constant of substrate */
+ double fr4_loss_tangent; /* loss tangent of substrate */
+ double conformal_epsilon_r; /* dielectric constant of conformal coating */
+
+ int min_circle_segments; /* minimum number of polygon segments to draw a full circle; needs to be a multiple of 4 */
+ double arc_precision; /* maximum difference between perfect circle arc and polygonal approximation */
+ StringList flood_layers; /* layers to be flooded with copper */
+
+ /* Hyperlynx UNIT and OPTIONS */
+ double unit; /* conversion factor: pcb length units to meters */
+ double metal_thickness_unit; /* conversion factor: metal thickness to meters */
+ bool use_die_for_metal; /* use dielectric constant and loss tangent of dielectric for metal layers */
+
+ Polygon current_polygon; /* polygon currently being parsed */
+
+ /* exec_* routines are called by parser to interpret hyperlynx file */
+ bool exec_board_file(parse_param& h);
+ bool exec_version(parse_param& h);
+ bool exec_data_mode(parse_param& h);
+ bool exec_units(parse_param& h);
+ bool exec_plane_sep(parse_param& h);
+ bool exec_perimeter_segment(parse_param& h);
+ bool exec_perimeter_arc(parse_param& h);
+ bool exec_board_attribute(parse_param& h);
+
+ bool exec_options(parse_param& h);
+ bool exec_signal(parse_param& h);
+ bool exec_dielectric(parse_param& h);
+ bool exec_plane(parse_param& h);
+ bool trace_layer(parse_param& h);
+ bool add_metal_layer(parse_param& h);
+ bool add_dielectric_layer(parse_param& h);
+ bool calc_layer_position();
+ bool calc_layer_epsilon();
+
+ bool exec_devices(parse_param& h);
+
+ bool exec_supplies(parse_param& h);
+
+ bool exec_padstack_element(parse_param& h);
+ bool exec_padstack_end(parse_param& h);
+
+ bool exec_net(parse_param& h);
+ bool exec_net_plane_separation(parse_param& h);
+ bool exec_net_attribute(parse_param& h);
+ bool exec_seg(parse_param& h);
+ bool exec_arc(parse_param& h);
+ bool exec_via(parse_param& h);
+ bool exec_via_v1(parse_param& h); /* Old style via format */
+ bool exec_pin(parse_param& h);
+ bool exec_pad(parse_param& h);
+ bool exec_useg(parse_param& h);
+
+ bool exec_polygon_begin(parse_param& h);
+ bool exec_polygon_end(parse_param& h);
+ bool exec_polyvoid_begin(parse_param& h);
+ bool exec_polyvoid_end(parse_param& h);
+ bool exec_polyline_begin(parse_param& h);
+ bool exec_polyline_end(parse_param& h);
+ bool exec_line(parse_param& h);
+ bool exec_curve(parse_param& h);
+
+ bool exec_net_class(parse_param& h);
+ bool exec_net_class_element(parse_param& h);
+ bool exec_net_class_attribute(parse_param& h);
+
+ bool exec_end(parse_param& h);
+ bool exec_key(parse_param& h);
+
+ /* convert a circular arc to a polygon */
+ Polygon arc2poly(double x1, double y1, double x2, double y2, double xc, double yc, double r, bool clockwise);
+ /* convert a line segment to a polygon */
+ Polygon segment2poly(double x1, double y1, double x2, double y2, double width);
+ /* convert a pad to a polygon */
+ Polygon pad2poly(double pad_x, double pad_y, Pad pad);
+ /* Add a polygon to the board outline */
+ void add_perimeter_polygon(Polygon poly);
+ /* Add a polygon to the current net */
+ void add_polygon(Polygon poly);
+ /* Add a polygon to a current net */
+ void add_polygon_to_net(Net& pnet, Polygon poly);
+ /* Add a pad at coordinates x, y */
+ void add_pad(double x, double y, Pad pad);
+ /* Add a via at coordinates x, y */
+ void add_via(double x, double y, std::string layer0_name, std::string layer1_name, double radius);
+ /* Flood specified layers with copper */
+ void flood_layers_with_copper();
+
+ };
+
+}
+
+
+#endif
+
+/* not truncated */
diff --git a/hyp2mat/lib/misc.cc b/hyp2mat/lib/misc.cc
new file mode 100644
index 0000000..d8488ae
--- /dev/null
+++ b/hyp2mat/lib/misc.cc
@@ -0,0 +1,65 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <limits>
+
+#include "config.h"
+#include "hyp2mat.h"
+
+using namespace Hyp2Mat;
+
+std::string Hyp2Mat::version()
+{
+ return VERSION;
+}
+
+Device::Device()
+{
+ name.clear();
+ ref.clear();
+ value_type = DEVICE_VALUE_NONE;
+ value_float = -1;
+ value_string.clear();
+}
+
+Pin::Pin()
+{
+ ref.clear();
+ x = 0;
+ y = 0;
+ z0 = 0;
+ z1 = 0;
+ layer_name.clear();
+ metal.clear();
+}
+
+Bounds::Bounds()
+{
+ /* bounds default to infinity */
+ double dbl_max = std::numeric_limits<double>::max();
+ x_min = - dbl_max;
+ x_max = dbl_max;
+ y_min = - dbl_max;
+ y_max = dbl_max;
+ z_min = - dbl_max;
+ z_max = dbl_max;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/palette.cc b/hyp2mat/lib/palette.cc
new file mode 100644
index 0000000..b0405a8
--- /dev/null
+++ b/hyp2mat/lib/palette.cc
@@ -0,0 +1,120 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Generate a palette of colors, given hue, saturation and value (brightness).
+ * The number of colors needed may be specified, but does not need to be known in advance.
+ * After Martin Ankerl * "How to Generate Random Colors Programmatically"
+ */
+
+#include <cmath>
+#include "palette.h"
+
+Palette::Palette()
+{
+ /* default values */
+ palette_hue = 0.69; /* blue */
+ palette_saturation = 0.99;
+ palette_value = 0.99;
+ step = (sqrt(5) - 1)/2; /* golden ratio conjugate */
+}
+
+void Palette::SetPalette(double hue, double saturation, double value)
+{
+ /* set user values */
+ if ((0 <= hue) && (hue <= 1)) palette_hue = hue;
+ if ((0 <= saturation) && (saturation <= 1)) palette_saturation = saturation;
+ if ((0 <= value) && (value <= 1)) palette_value = value;
+ return;
+}
+
+void Palette::SetNumberOfColors(unsigned int number_of_colors)
+{
+ if (number_of_colors > 0) step = 1 / (double) number_of_colors;
+ else step = (sqrt(5) - 1)/2; /* golden ratio conjugate */;
+ return;
+}
+
+PaletteColor Palette::Color(int color)
+{
+
+ /* hue, saturation and value (brightness) are double, in range 0..1. (HSV color model) */
+
+ /* calculate hue of color */
+ double integer_part;
+ double h = modf(palette_hue + color * step, &integer_part);
+
+ /* convert hsv color model to rgb */
+ double hi;
+ double f = modf(h * 6, &hi);
+ double v = palette_value;
+ double p = v * (1 - palette_saturation);
+ double q = v * (1 - f * palette_saturation);
+ double t = v * (1 - (1 - f) * palette_saturation);
+
+ double red, green, blue;
+ switch(static_cast<int>(hi + 0.5)) {
+ case 0:
+ red = v;
+ green = t;
+ blue = p;
+ break;
+ case 1:
+ red = q;
+ green = v;
+ blue = p;
+ break;
+ case 2:
+ red = p;
+ green = v;
+ blue = t;
+ break;
+ case 3:
+ red = p;
+ green = q;
+ blue = v;
+ break;
+ case 4:
+ red = t;
+ green = p;
+ blue = v;
+ break;
+ case 5:
+ red = v;
+ green = p;
+ blue = q;
+ break;
+ default:
+ red = 0;
+ green = 0;
+ blue = 0;
+ break;
+ }
+
+ PaletteColor clr;
+ clr.red = red;
+ clr.green = green;
+ clr.blue = blue;
+
+ return clr;
+
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/palette.h b/hyp2mat/lib/palette.h
new file mode 100644
index 0000000..146e358
--- /dev/null
+++ b/hyp2mat/lib/palette.h
@@ -0,0 +1,55 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Generate a palette of colors, given hue, saturation and value (brightness).
+ * The number of colors needed may be specified, but does not need to be known in advance.
+ * After Martin Ankerl "How to Generate Random Colors Programmatically"
+ */
+
+#ifndef PALETTE_H
+#define PALETTE_H
+
+struct PaletteColor {
+ double red;
+ double green;
+ double blue;
+ };
+
+class Palette {
+public:
+ Palette();
+ /* hue, saturation and value (brightness) are double, in range 0..1. (HSV color model) */
+ void SetPalette(double hue, double saturation, double value); /* HSV model */
+ /* Set the number of colors in the palette. If the number of colors is not known in advance, choose 0 */
+ void SetNumberOfColors(unsigned int number_of_colors);
+ /* Return palette color */
+ PaletteColor Color(int color);
+
+private:
+ double palette_hue;
+ double palette_saturation;
+ double palette_value;
+ double step;
+ };
+
+#endif
+
+/* not truncated */
diff --git a/hyp2mat/lib/parse.yy b/hyp2mat/lib/parse.yy
new file mode 100644
index 0000000..66aeb99
--- /dev/null
+++ b/hyp2mat/lib/parse.yy
@@ -0,0 +1,826 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+%code requires {
+#include "hypfile.h"
+}
+
+%parse-param {class HypFile::Hyp *hyp }
+%error-verbose
+%debug
+%defines
+
+%union {
+ int boolval;
+ int intval;
+ double floatval;
+ char* strval;
+}
+
+%{
+#include <cstdlib>
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include "parser.h"
+
+void yyerror(HypFile::Hyp *, const char *);
+
+/* YYPRINT and yyprint print values of the tokens when debugging is switched on */
+void yyprint(FILE *, int, YYSTYPE);
+#define YYPRINT(file, type, value) yyprint (file, type, value)
+
+/* clear parse_param struct at beginning of new record */
+void new_record();
+
+/* struct to pass to calling class */
+parse_param h;
+
+%}
+
+/*
+ * Hyperlynx keywords
+ */
+
+ /* Punctuation: { } ( ) = , */
+
+ /* Sections */
+
+%token BOARD_FILE VERSION DATA_MODE UNITS PLANE_SEP
+%token BOARD STACKUP DEVICES SUPPLIES
+%token PAD PADSTACK NET NET_CLASS END KEY
+
+ /* Keywords */
+
+%token A ARC COPPER CURVE DETAILED DIELECTRIC ENGLISH LENGTH
+%token LINE METRIC N OPTIONS PERIMETER_ARC PERIMETER_SEGMENT PIN
+%token PLANE POLYGON POLYLINE POLYVOID POUR S SEG SIGNAL
+%token SIMPLIFIED SIM_BOTH SIM_IN SIM_OUT USEG VIA WEIGHT
+
+ /* Assignments */
+
+%token A1 A2 BR C C_QM CO_QM D ER F ID
+%token L L1 L2 LPS LT M NAME
+%token P PKG PR_QM PS R REF SX SY S1 S1X S1Y S2 S2X S2Y T TC
+%token USE_DIE_FOR_METAL V V_QM VAL W X X1 X2
+%token XC Y Y1 Y2 YC Z ZL ZLEN ZW
+
+ /* Booleans */
+
+%token YES NO
+
+%token <boolval> BOOL
+%token <intval> POSINT
+%token <floatval> FLOAT
+%token <strval> STRING
+
+%start hyp_file
+
+%%
+
+/*
+ * Note:
+ * Use left recursion when parsing board perimeter and nets.
+ * When using left recursion cpu time is linear with board size.
+ * When using right recursion we run out of memory on large boards.
+ * (Typical error message: line xxx: memory exhausted at 'yyy' )
+ */
+
+ /*
+ hyperlynx file sections:
+
+ board_file
+ version
+ data_mode*
+ units
+ plane_sep*
+ board*
+ stackup*
+ devices
+ supplies*
+ padstack*
+ net
+ net_class*
+ end
+
+ * = optional section
+
+ */
+
+hyp_file
+ : hyp_file hyp_section
+ | hyp_section ;
+
+hyp_section
+ : board_file
+ | version
+ | data_mode
+ | units
+ | plane_sep
+ | board
+ | stackup
+ | devices
+ | supplies
+ | padstack
+ | net
+ | netclass
+ | end
+ | key
+ | '{' error '}' ;
+
+ /* board_file */
+
+board_file
+ : '{' BOARD_FILE { if (hyp->exec_board_file(h)) YYERROR; } '}' ;
+
+ /* version */
+
+version
+ : '{' VERSION '=' FLOAT { h.vers = yylval.floatval; } '}' { if (hyp->exec_version(h)) YYERROR; } ;
+
+ /* data_mode */
+
+data_mode
+ : '{' DATA_MODE '=' mode '}' { if (hyp->exec_data_mode(h)) YYERROR; };
+
+mode
+ : SIMPLIFIED { h.detailed = false; }
+ | DETAILED { h.detailed = true; } ;
+
+ /* units */
+
+units
+ : '{' UNITS '=' unit_system metal_thickness_unit '}' { if (hyp->exec_units(h)) YYERROR; } ;
+
+unit_system
+ : ENGLISH { h.unit_system_english = true; }
+ | METRIC { h.unit_system_english = false; };
+
+metal_thickness_unit
+ : WEIGHT { h.metal_thickness_weight = true; }
+ | LENGTH { h.metal_thickness_weight = false; } ;
+
+ /* plane_sep */
+plane_sep
+ : '{' PLANE_SEP '=' FLOAT { h.default_plane_separation = yylval.floatval; } '}' { if (hyp->exec_plane_sep(h)) YYERROR; } ;
+
+ /* board */
+
+board
+ : '{' BOARD board_paramlist '}'
+ | '{' BOARD '}' ;
+
+board_paramlist
+ : board_paramlist board_param
+ | board_param ;
+
+board_param
+ : perimeter_segment
+ | perimeter_arc
+ | board_attribute
+ | '(' error ')' ;
+
+perimeter_segment
+ : '(' PERIMETER_SEGMENT coord_line ')' { if (hyp->exec_perimeter_segment(h)) YYERROR; } ;
+
+perimeter_arc
+ : '(' PERIMETER_ARC coord_arc ')' { if (hyp->exec_perimeter_arc(h)) YYERROR; } ;
+
+board_attribute
+ : '(' A N '=' STRING { h.name = yylval.strval; } V '=' STRING { h.value = yylval.strval; } ')' { if (hyp->exec_board_attribute(h)) YYERROR; } ;
+
+ /* stackup */
+
+stackup
+ : '{' STACKUP stackup_paramlist '}' ;
+
+stackup_paramlist
+ : stackup_paramlist stackup_param
+ | stackup_param ;
+
+stackup_param
+ : options
+ | signal
+ | dielectric
+ | plane
+ | '(' error ')' ;
+
+options
+ : '(' OPTIONS options_params { if (hyp->exec_options(h)) YYERROR; } ;
+
+options_params
+ : USE_DIE_FOR_METAL '=' BOOL { h.use_die_for_metal = yylval.boolval; } ')'
+ | ')'
+ ;
+
+signal
+ : '(' SIGNAL { new_record(); } signal_paramlist ')' { if (hyp->exec_signal(h)) YYERROR; } ;
+
+signal_paramlist
+ : signal_paramlist signal_param
+ | signal_param ;
+
+signal_param
+ : thickness
+ | plating_thickness
+ | C '=' FLOAT { h.bulk_resistivity = yylval.floatval; h.bulk_resistivity_set = true; }
+ | bulk_resistivity
+ | temperature_coefficient
+ | epsilon_r
+ | loss_tangent
+ | layer_name
+ | material_name
+ | plane_separation ;
+
+dielectric
+ : '(' DIELECTRIC { new_record(); } dielectric_paramlist ')' { if (hyp->exec_dielectric(h)) YYERROR; } ;
+
+dielectric_paramlist
+ : dielectric_paramlist dielectric_param
+ | dielectric_param ;
+
+dielectric_param
+ : thickness
+ | C '=' FLOAT { h.epsilon_r = yylval.floatval; h.epsilon_r_set = true; }
+ | epsilon_r
+ | loss_tangent
+ | conformal
+ | prepreg
+ | layer_name
+ | material_name
+ ;
+
+plane
+ : '(' PLANE { new_record(); } plane_paramlist ')' { if (hyp->exec_plane(h)) YYERROR; } ;
+
+plane_paramlist
+ : plane_paramlist plane_param
+ | plane_param ;
+
+plane_param
+ : thickness
+ | C '=' FLOAT { h.bulk_resistivity = yylval.floatval; h.bulk_resistivity_set = true; }
+ | bulk_resistivity
+ | temperature_coefficient
+ | epsilon_r
+ | loss_tangent
+ | layer_name
+ | material_name
+ | plane_separation ;
+
+thickness
+ : T '=' FLOAT { h.thickness = yylval.floatval; h.thickness_set = true; }
+
+plating_thickness
+ : P '=' FLOAT { h.plating_thickness = yylval.floatval; h.plating_thickness_set = true; }
+
+bulk_resistivity
+ : BR '=' FLOAT { h.bulk_resistivity = yylval.floatval; h.bulk_resistivity_set = true; }
+
+temperature_coefficient
+ : TC '=' FLOAT { h.temperature_coefficient = yylval.floatval; h.temperature_coefficient_set = true; }
+
+epsilon_r
+ : ER '=' FLOAT { h.epsilon_r = yylval.floatval; h.epsilon_r_set = true; }
+
+loss_tangent
+ : LT '=' FLOAT { h.loss_tangent = yylval.floatval; h.loss_tangent_set = true; }
+
+layer_name
+ : L '=' STRING { h.layer_name = yylval.strval; h.layer_name_set = true; }
+
+material_name
+ : M '=' STRING { h.material_name = yylval.strval; h.material_name_set = true; }
+
+plane_separation
+ : PS '=' FLOAT { h.plane_separation = yylval.floatval; h.plane_separation_set = true; }
+
+conformal
+ : CO_QM '=' BOOL { h.conformal = yylval.boolval; h.conformal_set = true; }
+
+prepreg
+ : PR_QM '=' BOOL { h.prepreg = yylval.boolval; h.prepreg_set = true; }
+
+ /* devices */
+
+devices
+ : '{' DEVICES device_list '}'
+ | '{' DEVICES '}' ;
+
+device_list
+ : device_list device
+ | device ;
+
+device
+ : '(' { new_record(); } STRING { h.device_type = yylval.strval; } REF '=' STRING { h.ref = yylval.strval; } device_paramlist ')' { if (hyp->exec_devices(h)) YYERROR; }
+ | '(' error ')' ;
+
+device_paramlist
+ : name device_value
+ | device_value
+ ;
+
+device_value
+ : value device_layer
+ | device_layer
+ ;
+
+device_layer
+ : layer_name package
+ | layer_name
+ ;
+
+name
+ : NAME '=' STRING { h.name = yylval.strval; h.name_set = true; } ;
+
+value
+ : value_float
+ | value_string
+ ;
+
+value_float
+ : VAL '=' FLOAT { h.value_float = yylval.floatval; h.value_float_set = true; } ;
+
+value_string
+ : VAL '=' STRING { h.value_string = yylval.strval; h.value_string_set = true; } ;
+
+package
+ : PKG '=' STRING { h.package = yylval.strval; h.package_set = true; } ;
+
+ /* supplies */
+
+supplies
+ : '{' SUPPLIES supply_list '}' ;
+
+supply_list
+ : supply_list supply
+ | supply ;
+
+supply
+ : '(' S name value_float voltage_spec conversion ')' { if (hyp->exec_supplies(h)) YYERROR; }
+ | '(' error ')' ;
+
+voltage_spec
+ : V_QM '=' BOOL { h.voltage_specified = yylval.boolval; } ;
+
+conversion
+ : C_QM '=' BOOL { h.conversion = yylval.boolval; }
+
+ /* padstack */
+
+padstack
+ : '{' PADSTACK { new_record(); } '=' STRING { h.padstack_name = yylval.strval; h.padstack_name_set = true; } drill_size '}' { if (hyp->exec_padstack_end(h)) YYERROR; } ;
+
+drill_size
+ : ',' FLOAT { h.drill_size = yylval.floatval; h.drill_size_set = true; } padstack_list
+ | ',' padstack_list ;
+ | padstack_list ;
+
+padstack_list
+ : padstack_list padstack_def
+ | padstack_def ;
+
+padstack_def
+ : '(' STRING { h.layer_name = yylval.strval; h.layer_name_set = true; } ',' pad_shape pad_coord pad_type { if (hyp->exec_padstack_element(h)) YYERROR; new_record(); }
+ | '(' error ')' ;
+
+pad_shape
+ : FLOAT { h.pad_shape = yylval.floatval; } ','
+ | ',' { h.pad_shape = -1; } /* Workaround: Altium sometimes prints an empty pad shape */
+ ;
+
+pad_coord
+ : FLOAT { h.pad_sx = yylval.floatval; } ',' FLOAT { h.pad_sy = yylval.floatval; } ',' FLOAT { h.pad_angle = yylval.floatval; }
+
+pad_type
+ : ')'
+ | ',' M ')' { h.pad_type = PAD_TYPE_METAL; h.pad_type_set = true; }
+ | ',' A ')' { h.pad_type = PAD_TYPE_ANTIPAD; h.pad_type_set = true; }
+ | ',' FLOAT { h.thermal_clear_shape = yylval.floatval; }
+ ',' FLOAT { h.thermal_clear_sx = yylval.floatval; }
+ ',' FLOAT { h.thermal_clear_sy = yylval.floatval; }
+ ',' FLOAT { h.thermal_clear_angle = yylval.floatval; }
+ ',' T ')' { h.pad_type = PAD_TYPE_THERMAL_RELIEF; h.pad_type_set = true; }
+ ;
+
+ /* net */
+
+net
+ : '{' NET '=' STRING { h.net_name = yylval.strval; if (hyp->exec_net(h)) YYERROR; } net_def '}' ;
+
+net_def
+ : plane_separation { if (hyp->exec_net_plane_separation(h)) YYERROR; } net_subrecord_list
+ | net_subrecord_list ;
+
+net_subrecord_list
+ : net_subrecord_list net_subrecord
+ | net_subrecord ;
+
+net_subrecord
+ : seg
+ | arc
+ | via
+ | pin
+ | pad
+ | useg
+ | polygon
+ | polyvoid
+ | polyline
+ | net_attribute
+ | '(' error ')'
+ | '{' error '}'
+ ;
+
+seg
+ : '(' SEG { new_record(); } coord_line width layer_name ps_lps_param { if (hyp->exec_seg(h)) YYERROR; } ;
+
+arc
+ : '(' ARC { new_record(); } coord_arc width layer_name ps_lps_param { if (hyp->exec_arc(h)) YYERROR; } ;
+
+ps_lps_param
+ : plane_separation lps_param
+ | lps_param
+ ;
+
+lps_param
+ : left_plane_separation ')'
+ | ')'
+ ;
+
+width
+ : W '=' FLOAT { h.width = yylval.floatval; h.width_set = true; } ;
+
+left_plane_separation
+ : LPS '=' FLOAT { h.left_plane_separation = yylval.floatval; h.left_plane_separation_set = true; } ;
+
+via
+ : '(' VIA { new_record(); } coord_point via_new_or_old_style
+ ;
+
+via_new_or_old_style
+ : via_new_style
+ | via_old_style
+ ;
+
+via_new_style
+ : via_new_style_l1_param { if (hyp->exec_via(h)) YYERROR; } ;
+
+via_new_style_l1_param
+ : layer1_name via_new_style_l2_param
+ | via_new_style_l2_param
+ ;
+
+via_new_style_l2_param
+ : layer2_name via_new_style_padstack_param
+ | via_new_style_padstack_param
+ ;
+
+via_new_style_padstack_param
+ : padstack_name ')'
+ ;
+
+padstack_name
+ : P '=' STRING { h.padstack_name = yylval.strval; h.padstack_name_set = true; } ;
+
+layer1_name
+ : L1 '=' STRING { h.layer1_name = yylval.strval; h.layer1_name_set = true; } ;
+
+layer2_name
+ : L2 '=' STRING { h.layer2_name = yylval.strval; h.layer2_name_set = true; } ;
+
+via_old_style
+ : D '=' FLOAT { h.drill_size = yylval.floatval; } /* deprecated hyperlynx v1.x VIA format */
+ layer1_name
+ layer2_name
+ S1 '=' STRING { h.pad1_shape = yylval.strval; }
+ S1X '=' FLOAT { h.pad1_sx = yylval.floatval; }
+ S1Y '=' FLOAT { h.pad1_sy = yylval.floatval; }
+ A1 '=' FLOAT { h.pad1_angle = yylval.floatval; }
+ S2 '=' STRING { h.pad2_shape = yylval.strval; }
+ S2X '=' FLOAT { h.pad2_sx = yylval.floatval; }
+ S2Y '=' FLOAT { h.pad2_sy = yylval.floatval; }
+ A2 '=' FLOAT { h.pad2_angle = yylval.floatval; }
+ ')' { if (hyp->exec_via_v1(h)) YYERROR; } ;
+ ;
+
+pin
+ : '(' PIN { new_record(); } coord_point pin_reference pin_param { if (hyp->exec_pin(h)) YYERROR; } ;
+
+pin_param
+ : padstack_name pin_function_param
+ | pin_function_param
+ ;
+
+pin_function_param
+ : pin_function ')'
+ | ')'
+ ;
+
+pin_reference
+ : R '=' STRING { h.pin_reference = yylval.strval; h.pin_reference_set = true; } ;
+
+pin_function
+ : F '=' SIM_OUT { h.pin_function = PIN_SIM_OUT; h.pin_function_set = true; }
+ | F '=' SIM_IN { h.pin_function = PIN_SIM_IN; h.pin_function_set = true; }
+ | F '=' SIM_BOTH { h.pin_function = PIN_SIM_BOTH; h.pin_function_set = true; }
+ ;
+
+pad
+ : '(' PAD { new_record(); } /* deprecated hyperlynx v1.x only */
+ coord_point
+ layer_name
+ S '=' STRING { h.pad1_shape = yylval.strval; }
+ SX '=' FLOAT { h.pad1_sx = yylval.floatval; }
+ SY '=' FLOAT { h.pad1_sy = yylval.floatval; }
+ A '=' FLOAT { h.pad1_angle = yylval.floatval; }
+ ')' { if (hyp->exec_pad(h)) YYERROR; } ;
+ ;
+
+useg
+ : '(' USEG { new_record(); } coord_point1 layer1_name coord_point2 layer2_name useg_param { if (hyp->exec_useg(h)) YYERROR; } ;
+
+useg_param
+ : useg_stackup
+ | useg_impedance
+ ;
+
+useg_stackup
+ : ZL '=' STRING { h.zlayer_name = yylval.strval; h.zlayer_name_set = true; }
+ ZW '=' FLOAT { h.width = yylval.floatval; }
+ ZLEN '=' FLOAT { h.length = yylval.floatval; }
+ ')'
+ ;
+
+useg_impedance
+ : Z '=' FLOAT { h.impedance = yylval.floatval; h.impedance_set = true; }
+ D '=' FLOAT { h.delay = yylval.floatval; }
+ useg_resistance;
+
+useg_resistance
+ : R '=' FLOAT { h.resistance = yylval.floatval; h.resistance_set = true;}
+ ')'
+ | ')'
+ ;
+
+polygon
+ : '{' POLYGON { new_record(); } polygon_param_list coord_point { if (hyp->exec_polygon_begin(h)) YYERROR; }
+ lines_and_curves '}' { if (hyp->exec_polygon_end(h)) YYERROR; } ;
+
+polygon_param_list
+ : polygon_param_list polygon_param
+ | polygon_param
+ ;
+
+polygon_param
+ : layer_name
+ | width
+ | polygon_type
+ | polygon_id
+ ;
+
+polygon_id
+ : ID '=' POSINT { h.id = yylval.intval; h.id_set = true; } /* polygon id is a non-negative integer */
+ ;
+
+polygon_type
+ : T '=' POUR { h.polygon_type = POLYGON_TYPE_POUR; h.polygon_type_set = true; }
+ | T '=' PLANE { h.polygon_type = POLYGON_TYPE_PLANE; h.polygon_type_set = true; }
+ | T '=' COPPER { h.polygon_type = POLYGON_TYPE_COPPER; h.polygon_type_set = true; }
+ ;
+
+polyvoid
+ : '{' POLYVOID { new_record(); } polygon_id coord_point { if (hyp->exec_polyvoid_begin(h)) YYERROR; }
+ lines_and_curves '}' { if (hyp->exec_polyvoid_end(h)) YYERROR; } ;
+
+polyline
+ : '{' POLYLINE { new_record(); } polygon_param_list coord_point { if (hyp->exec_polyline_begin(h)) YYERROR; }
+ lines_and_curves '}' { if (hyp->exec_polyline_end(h)) YYERROR; } ;
+
+lines_and_curves
+ : lines_and_curves line_or_curve
+ | line_or_curve
+ ;
+
+line_or_curve
+ : line
+ | curve
+ | '(' error ')'
+ ;
+
+line
+ : '(' LINE { new_record(); } coord_point ')' { if (hyp->exec_line(h)) YYERROR; } ;
+
+curve
+ : '(' CURVE { new_record(); } coord_arc ')' { if (hyp->exec_curve(h)) YYERROR; } ;
+
+net_attribute
+ : '(' A N '=' STRING { h.name = yylval.strval; } V '=' STRING { h.value = yylval.strval; } ')' { if (hyp->exec_net_attribute(h)) YYERROR; } ;
+
+ /* net class */
+
+netclass
+ : '{' NET_CLASS '=' STRING { h.net_class_name = yylval.strval; if (hyp->exec_net_class(h)) YYERROR; } netclass_subrecords ;
+
+netclass_subrecords
+ : netclass_paramlist '}'
+ | '}'
+ ;
+
+netclass_paramlist
+ : netclass_paramlist netclass_param
+ | netclass_param
+ ;
+
+netclass_param
+ : netclass_attribute
+ | net_name
+ | '(' error ')'
+ ;
+
+net_name
+ : '(' N N '=' STRING { h.net_name = yylval.strval; } ')' { if (hyp->exec_net_class_element(h)) YYERROR; } ;
+
+netclass_attribute
+ : '(' A N '=' STRING { h.name = yylval.strval; } V '=' STRING { h.value = yylval.strval; } ')' { if (hyp->exec_net_class_attribute(h)) YYERROR; } ;
+
+ /* end */
+
+end
+ : '{' END '}' { if (hyp->exec_end(h)) YYERROR; } ;
+
+ /* key */
+
+key
+ : '{' KEY '=' STRING { h.key = yylval.strval; } '}' { if (hyp->exec_key(h)) YYERROR; } ;
+
+ /* coordinates */
+
+coord_point
+ : X '=' FLOAT { h.x = yylval.floatval; } Y '=' FLOAT { h.y = yylval.floatval; } ;
+
+coord_point1
+ : X1 '=' FLOAT { h.x1 = yylval.floatval; } Y1 '=' FLOAT { h.y1 = yylval.floatval; } ;
+
+coord_point2
+ : X2 '=' FLOAT { h.x2 = yylval.floatval; } Y2 '=' FLOAT { h.y2 = yylval.floatval; } ;
+
+coord_line
+ : coord_point1 coord_point2 ;
+
+coord_arc
+ : coord_line XC '=' FLOAT { h.xc = yylval.floatval; } YC '=' FLOAT { h.yc = yylval.floatval; } R '=' FLOAT { h.r = yylval.floatval; } ;
+
+%%
+
+/*
+ * Supporting C routines
+ */
+
+void yyerror(HypFile::Hyp *hyp, const char *msg)
+{
+ std::ostringstream err_msg;
+
+ err_msg << "line " << yylineno << ": " << msg << " at '" << yytext << "'";
+ hyp->error(err_msg.str());
+}
+
+void yyprint(FILE *file, int type, YYSTYPE value)
+{
+ if (type == STRING)
+ fprintf (file, "%s", value.strval);
+ else if (type == FLOAT)
+ fprintf (file, "%g", value.floatval);
+ else if (type == BOOL)
+ fprintf (file, "%i", value.boolval);
+ return;
+}
+
+/*
+ * reset parse_param struct at beginning of record
+ */
+
+void new_record()
+{
+ h.vers = 0;
+ h.detailed = false;
+ h.unit_system_english = false;
+ h.metal_thickness_weight = false;
+ h.default_plane_separation = 0;
+ h.use_die_for_metal = false;
+ h.bulk_resistivity = 0;
+ h.conformal = false;
+ h.epsilon_r = 0;
+ h.layer_name.clear();
+ h.loss_tangent = 0;
+ h.material_name.clear();
+ h.plane_separation = 0;
+ h.plating_thickness = 0;
+ h.prepreg = false;
+ h.temperature_coefficient = 0;
+ h.thickness = 0;
+ h.bulk_resistivity_set = false;
+ h.conformal_set = false;
+ h.epsilon_r_set = false;
+ h.layer_name_set = false;
+ h.loss_tangent_set = false;
+ h.material_name_set = false;
+ h.plane_separation_set = false;
+ h.plating_thickness_set = false;
+ h.prepreg_set = false;
+ h.temperature_coefficient_set = false;
+ h.thickness_set = false;
+ h.device_type.clear();
+ h.ref.clear();
+ h.value_float = 0;
+ h.value_string.clear();
+ h.package.clear();
+ h.name_set = false;
+ h.value_float_set = false;
+ h.value_string_set = false;
+ h.package_set = false;
+ h.voltage_specified = false;
+ h.conversion = false;
+ h.padstack_name.clear();
+ h.drill_size = 0;
+ h.pad_shape = 0;
+ h.pad_sx = 0;
+ h.pad_sy = 0;
+ h.pad_angle = 0;
+ h.thermal_clear_shape = 0;
+ h.thermal_clear_sx = 0;
+ h.thermal_clear_sy = 0;
+ h.thermal_clear_angle = 0;
+ h.pad_type = PAD_TYPE_METAL;
+ h.padstack_name_set = false;
+ h.drill_size_set = false;
+ h.pad_type_set = false;
+ h.width = 0;
+ h.left_plane_separation = 0;
+ h.width_set = false;
+ h.left_plane_separation_set = false;
+ h.layer1_name.clear();
+ h.layer1_name_set = false;
+ h.layer2_name.clear();
+ h.layer2_name_set = false;
+ h.pad1_shape.clear();
+ h.pad1_sx = 0;
+ h.pad1_sy = 0;
+ h.pad1_angle = 0;
+ h.pad2_shape.clear();
+ h.pad2_sx = 0;
+ h.pad2_sy = 0;
+ h.pad2_angle = 0;
+ h.pin_reference.clear();
+ h.pin_reference_set = false;
+ h.pin_function = PIN_SIM_BOTH;
+ h.pin_function_set = false;
+ h.zlayer_name.clear();
+ h.zlayer_name_set = false;
+ h.length = 0;
+ h.impedance = 0;
+ h.impedance_set = false;
+ h.delay = 0;
+ h.resistance = 0;
+ h.resistance_set = false;
+ h.id = -1;
+ h.id_set = false;
+ h.polygon_type = POLYGON_TYPE_PLANE;
+ h.polygon_type_set = false;
+ h.net_class_name.clear();
+ h.net_name.clear();
+ h.key.clear();
+ h.name.clear();
+ h.value.clear();
+ h.x = 0;
+ h.y = 0;
+ h.x1 = 0;
+ h.y1 = 0;
+ h.x2 = 0;
+ h.y2 = 0;
+ h.xc = 0;
+ h.yc = 0;
+ h.r = 0;
+
+ return;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/parse_param.h b/hyp2mat/lib/parse_param.h
new file mode 100644
index 0000000..47ed1bb
--- /dev/null
+++ b/hyp2mat/lib/parse_param.h
@@ -0,0 +1,172 @@
+
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PARSE_PARAM_H
+#define PARSE_PARAM_H
+
+#include <string>
+
+ /*
+ * Parameters passed on by the parser.
+ * All variables added here are initialized in new_record().
+ */
+
+ enum pad_type_enum { PAD_TYPE_METAL, PAD_TYPE_ANTIPAD, PAD_TYPE_THERMAL_RELIEF };
+
+ enum pin_function_enum { PIN_SIM_IN, PIN_SIM_OUT, PIN_SIM_BOTH };
+
+ enum polygon_type_enum { POLYGON_TYPE_POUR, POLYGON_TYPE_PLANE, POLYGON_TYPE_COPPER, POLYGON_TYPE_PAD, POLYGON_TYPE_ANTIPAD };
+
+ struct parse_param {
+ double vers; /* version of the hyp file format */
+ bool detailed; /* data detailed enough for power integrity */
+ bool unit_system_english; /* english or metric units */
+ bool metal_thickness_weight; /* copper by weight or by length */
+ double default_plane_separation; /* trace to plane separation */
+
+ /* stackup record */
+ bool use_die_for_metal; /* dielectric constant and loss tangent of dielectric for metal layers */
+ double bulk_resistivity;
+ bool conformal;
+ double epsilon_r;
+ std::string layer_name;
+ double loss_tangent;
+ std::string material_name;
+ double plane_separation;
+ double plating_thickness;
+ bool prepreg;
+ double temperature_coefficient; /* temperature coefficient of resistivity */
+ double thickness; /* layer thickness */
+
+ /* stackup record flags */
+ bool bulk_resistivity_set;
+ bool conformal_set;
+ bool epsilon_r_set;
+ bool layer_name_set;
+ bool loss_tangent_set;
+ bool material_name_set;
+ bool plane_separation_set;
+ bool plating_thickness_set;
+ bool prepreg_set;
+ bool temperature_coefficient_set;
+ bool thickness_set;
+
+ /* device record */
+ std::string device_type;
+ std::string ref;
+ double value_float;
+ std::string value_string;
+ std::string package;
+
+ /* device record flags */
+ bool name_set;
+ bool value_float_set;
+ bool value_string_set;
+ bool package_set;
+
+ /* supplies record */
+ bool voltage_specified;
+ bool conversion;
+
+ /* padstack record */
+ std::string padstack_name;
+ double drill_size;
+ double pad_shape;
+ double pad_sx;
+ double pad_sy;
+ double pad_angle;
+ double thermal_clear_shape;
+ double thermal_clear_sx;
+ double thermal_clear_sy;
+ double thermal_clear_angle;
+ pad_type_enum pad_type;
+
+ /* padstack record flags */
+ bool padstack_name_set;
+ bool drill_size_set;
+ bool pad_type_set;
+
+ /* net record */
+ double width;
+ double left_plane_separation;
+ bool width_set;
+ bool left_plane_separation_set;
+
+ /* via subrecord of net */
+ std::string layer1_name;
+ bool layer1_name_set;
+ std::string layer2_name;
+ bool layer2_name_set;
+ std::string pad1_shape;
+ double pad1_sx;
+ double pad1_sy;
+ double pad1_angle;
+ std::string pad2_shape;
+ double pad2_sx;
+ double pad2_sy;
+ double pad2_angle;
+
+ /* pin subrecord of net */
+ std::string pin_reference;
+ bool pin_reference_set;
+ pin_function_enum pin_function;
+ bool pin_function_set;
+
+ /* useg subrecord of net */
+ std::string zlayer_name;
+ bool zlayer_name_set;
+ double length;
+ double impedance;
+ bool impedance_set;
+ double delay;
+ double resistance;
+ bool resistance_set;
+
+ /* polygon subrecord of net */
+ int id;
+ bool id_set;
+ polygon_type_enum polygon_type;
+ bool polygon_type_set;
+
+ /* net class record */
+ std::string net_class_name;
+ std::string net_name;
+
+ /* key record */
+ std::string key;
+
+ /* Attributes */
+ std::string name; /* attribute name */
+ std::string value; /* attribute value */
+
+ /* point, line and arc coordinates */
+ double x; /* coordinates point */
+ double y; /* coordinates point */
+ double x1; /* coordinates point 1 */
+ double y1; /* coordinates point 1 */
+ double x2; /* coordinates point 2 */
+ double y2; /* coordinates point 2 */
+ double xc; /* coordinates arc */
+ double yc; /* coordinates arc */
+ double r; /* coordinates arc */
+ };
+
+#endif
diff --git a/hyp2mat/lib/parser.h b/hyp2mat/lib/parser.h
new file mode 100644
index 0000000..217ed8c
--- /dev/null
+++ b/hyp2mat/lib/parser.h
@@ -0,0 +1,40 @@
+
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PARSER_H
+#define PARSER_H
+
+#include <string>
+#include "hypfile.h"
+
+ /*
+ * hooks in bison (parser) and flex (scanner) code
+ */
+
+ extern FILE *yyin; /* file the parser reads from */
+ extern int yy_flex_debug ; /* debug scanner if == 1 */
+ extern int yydebug ; /* debug parser if == 1 */
+ extern int yylineno;
+ extern char *yytext;
+ extern int yyparse(HypFile::Hyp *);
+ extern int yylex(void);
+
+#endif
diff --git a/hyp2mat/lib/pcb.cc b/hyp2mat/lib/pcb.cc
new file mode 100644
index 0000000..86bc657
--- /dev/null
+++ b/hyp2mat/lib/pcb.cc
@@ -0,0 +1,333 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <iostream>
+#include <iomanip>
+
+#include "config.h"
+#include "hyp2mat.h"
+
+#include "hyperlynx.h"
+#include "pdf.h"
+#include "csxcad.h"
+
+using namespace Hyp2Mat;
+
+PCB::PCB()
+{
+ debug = 0;
+ raw = false;
+ via_plating_thickness = 0;
+ _arc_precision = 0;
+ _clearance = -1.0;
+ _epsilon_r = -1.0;
+ _epsilon_r_override = false;
+ _bulk_resistivity = -1.0;
+ _bulk_resistivity_override = false;
+ _loss_tangent = -1.0;
+ _loss_tangent_override = false;
+}
+
+/*
+ * Read a pcb in hyperlynx format.
+ * "filename" is the Hyperlynx file to import.
+ * "layers" is the names of the layers to import. Default is importing all layers.
+ * "nets" is the names of the nets to import. Default is importing all nets.
+ */
+
+void PCB::ReadHyperLynx (std::string filename, std::vector<std::string> layers, std::vector<std::string> nets)
+{
+
+ /* set accuracy with which circle arcs are converted to polygons */
+ Polygon::SetArcPrecision(_arc_precision);
+
+ /* read hyperlynx file */
+ HyperLynx hyperlynx;
+
+ hyperlynx.layers = layers;
+ hyperlynx.nets = nets;
+ hyperlynx.raw = raw;
+ hyperlynx.arc_precision = _arc_precision;
+ hyperlynx.clearance = _clearance;
+ hyperlynx.flood_layers = flood_layers;
+ hyperlynx.bounds = _bounds;
+
+ hyperlynx.Read(filename, *this);
+
+ /* Check for empty board outline; add default board outline if outline empty */
+ _CheckBoardOutline();
+
+ /* Epsilon_r, bulk resistivity and loss tangent overrides */
+ _UserOverrides();
+
+ return;
+}
+
+/*
+ * Write a pcb in pdf format
+ */
+
+void PCB::WritePDF (std::string filename, double hue, double saturation, double brightness)
+{
+ PDF pdf;
+ pdf.SetPalette(hue, saturation, brightness);
+ pdf.Write(filename, *this);
+}
+
+/*
+ * Write a pcb in csxcad format
+ */
+
+void PCB::WriteCSXCAD (std::string filename, bool pcb_outline, bool lossy_copper, bool metal_3d)
+{
+ CSXCAD csxcad;
+ csxcad.Write(filename, *this, pcb_outline, lossy_copper, metal_3d);
+}
+
+/*
+ * Set dielectric epsilon r. Overrides value in Hyperlynx file.
+ */
+
+void PCB::SetEpsilonR(double new_epsilon_r)
+{
+ /* save the new value of epsilon r. run before loading hyperlynx file. */
+ _epsilon_r = new_epsilon_r;
+ _epsilon_r_override = true;
+
+ return;
+}
+
+void PCB::SetLossTangent(double new_loss_tangent)
+{
+ /* save the new value of loss_tangent. run before loading hyperlynx file. */
+ _loss_tangent = new_loss_tangent;
+ _loss_tangent_override = true;
+ return;
+}
+
+void PCB::SetBulkResistance(double new_bulk_resistivity)
+{
+ /* save the new value of bulk_resistivity. run before loading hyperlynx file. */
+ _bulk_resistivity = new_bulk_resistivity;
+ _bulk_resistivity_override = true;
+ return;
+}
+
+/*
+ * Modify the value of epsilon r. Run after loading hyperlynx file.
+ */
+
+void PCB::_UserOverrides()
+{
+
+ /* set all layers to epsilon_r, except outer copper layers, which we assume are in air (er = 1) */
+
+ /* iterate over all layers, setting epsilon */
+ for (LayerList::iterator i = stackup.begin(); i != stackup.end(); ++i) {
+
+ /* override epsilon and loss tangent of dielectric layers */
+ if (_epsilon_r_override && (i->layer_type == LAYER_DIELECTRIC)) i->epsilon_r = _epsilon_r;
+ if (_loss_tangent_override && (i->layer_type == LAYER_DIELECTRIC)) i->loss_tangent = _loss_tangent;
+
+ /* override resistivity of metal layers */
+ if (_bulk_resistivity_override && (i->layer_type != LAYER_DIELECTRIC)) i->bulk_resistivity = _bulk_resistivity;
+
+ /* check for outer copper layer */
+ if (i->layer_type == LAYER_DIELECTRIC)
+ continue;
+
+ /* find dielectric layer above current metal layer */
+ bool upper_layer_found = false;
+ for (LayerList::iterator j = stackup.begin(); j != i; ++j)
+ if (j->layer_type == LAYER_DIELECTRIC) upper_layer_found = true;
+
+ /* find dielectric layer below current metal layer */
+ bool lower_layer_found = false;
+ for (LayerList::iterator j = i; j != stackup.end(); ++j)
+ if (j->layer_type == LAYER_DIELECTRIC) lower_layer_found = true;
+
+ /* assume outer copper layers are in air */
+ if (!upper_layer_found || !lower_layer_found) {
+ i->epsilon_r = 1.0;
+ i->loss_tangent = 0.0;
+ }
+ }
+ return;
+}
+
+/*
+ * set resolution of x and y coordinates
+ */
+
+void PCB::SetGrid(double new_grid)
+{
+ if (new_grid > 0.0) Polygon::SetGrid(new_grid);
+ return;
+}
+
+/*
+ * set maximum difference between perfect circle arc and polygonal approximation
+ */
+
+void PCB::SetArcPrecision(double new_arc_precision)
+{
+ if (new_arc_precision >= 0.0) _arc_precision = new_arc_precision;
+ return;
+}
+
+/*
+ * set track to plane clearance
+ */
+
+void PCB::SetClearance(double new_clearance)
+{
+ if (new_clearance >= 0.0) _clearance = new_clearance;
+ return;
+}
+
+/*
+ * Calculate the bounding rectangle of a pcb.
+ */
+
+Bounds PCB::GetBounds() {
+ Bounds bounds;
+ bounds.x_min = 0;
+ bounds.x_max = 0;
+ bounds.y_min = 0;
+ bounds.y_max = 0;
+ bounds.z_min = 0;
+ bounds.z_max = 0;
+
+ /* iterate over layers; find z_max and z_min */
+ if (!stackup.empty()) {
+ bounds.z_min = stackup.back().z0;
+ bounds.z_max = stackup.front().z1;
+ for (LayerList::iterator l = stackup.begin(); l != stackup.end(); ++l) {
+ if (l->z0 < bounds.z_min) bounds.z_min = l->z0;
+ if (l->z1 > bounds.z_max) bounds.z_max = l->z1;
+ }
+ }
+
+ /* iterate over board edge; find x_min, x_max, y_min, y_max */
+ bool first_point = true;
+
+ for (FloatPolygons::iterator i = board.begin(); i != board.end(); ++i)
+ for (FloatPolygon::iterator j = i->poly.begin(); j != i->poly.end(); ++j) {
+ if ((j->x > bounds.x_max) || first_point) bounds.x_max = j->x;
+ if ((j->y > bounds.y_max) || first_point) bounds.y_max = j->y;
+ if ((j->x < bounds.x_min) || first_point) bounds.x_min = j->x;
+ if ((j->y < bounds.y_min) || first_point) bounds.y_min = j->y;
+ first_point = false;
+ }
+
+ /* iterate over layers */
+ for (LayerList::iterator l = stackup.begin(); l != stackup.end(); ++l)
+ for (FloatPolygons::iterator i = l->metal.begin(); i != l->metal.end(); ++i)
+ for (FloatPolygon::iterator j = i->poly.begin(); j != i->poly.end(); ++j) {
+ if ((j->x > bounds.x_max) || first_point) bounds.x_max = j->x;
+ if ((j->y > bounds.y_max) || first_point) bounds.y_max = j->y;
+ if ((j->x < bounds.x_min) || first_point) bounds.x_min = j->x;
+ if ((j->y < bounds.y_min) || first_point) bounds.y_min = j->y;
+ first_point = false;
+ }
+
+ /* iterate over vias */
+ for (ViaList::iterator i = via.begin(); i != via.end(); ++i) {
+ if ((i->x + i->radius > bounds.x_max) || first_point) bounds.x_max = i->x + i->radius;
+ if ((i->y + i->radius > bounds.y_max) || first_point) bounds.y_max = i->y + i->radius;
+ if ((i->x - i->radius < bounds.x_min) || first_point) bounds.x_min = i->x - i->radius;
+ if ((i->y - i->radius < bounds.y_min) || first_point) bounds.y_min = i->y - i->radius;
+ first_point = false;
+ }
+
+ return bounds;
+ }
+
+/*
+ * Set the bounding rectangle of a pcb.
+ */
+
+void PCB::SetBounds(Bounds new_bounds)
+{
+ _bounds = new_bounds;
+}
+
+void PCB::_CheckBoardOutline()
+{
+ if (!board.empty()) return;
+
+ if (debug) std::cerr << "board outline empty" << std::endl;
+
+ Bounds bounds = GetBounds();
+
+ if ((bounds.x_min != bounds.x_max) && (bounds.y_min != bounds.y_max)) {
+
+ if (debug) std::cerr << "adding default board outline" << std::endl;
+
+ /* add rectangular outline to pcb */
+
+ Hyp2Mat::FloatPoly outline;
+ outline.poly.push_back(Hyp2Mat::FloatPoint(bounds.x_min, bounds.y_min));
+ outline.poly.push_back(Hyp2Mat::FloatPoint(bounds.x_min, bounds.y_max));
+ outline.poly.push_back(Hyp2Mat::FloatPoint(bounds.x_max, bounds.y_max));
+ outline.poly.push_back(Hyp2Mat::FloatPoint(bounds.x_max, bounds.y_min));
+ outline.is_hole = false;
+ outline.nesting_level = 0;
+
+ board.push_back(outline);
+ }
+}
+
+void PCB::PrintSummary() {
+ Bounds bounds = GetBounds();
+
+ /* save stream flags and precision */
+ std::ios::fmtflags saved_flgs = std::cerr.flags();
+ std::streamsize saved_prec = std::cerr.precision();
+ std::cerr.flags(std::ios::fixed);
+ std::cerr.precision(2);
+
+ double m_to_mm = 1000;
+ std::cerr << "board size:" << std::endl;
+ std::cerr << " x = " << std::setw(8) << bounds.x_min*m_to_mm << " : " << std::setw(8) << bounds.x_max*m_to_mm << " mm" << std::endl;
+ std::cerr << " y = " << std::setw(8) << bounds.y_min*m_to_mm << " : " << std::setw(8) << bounds.y_max*m_to_mm << " mm" << std::endl;
+ std::cerr << " z = " << std::setw(8) << bounds.z_min*m_to_mm << " : " << std::setw(8) << bounds.z_max*m_to_mm << " mm" << std::endl;
+
+ std::cerr << "layers:" << std::endl;
+ if (stackup.empty()) std::cerr << "(none)";
+ for (LayerList::iterator it = stackup.begin(); it != stackup.end(); ++it) {
+ std::cerr << " ";
+ switch (it->layer_type) {
+ case LAYER_SIGNAL: std::cerr << "signal " << std::setw(8) << it->thickness * 1E6 << " um" ; break;
+ case LAYER_DIELECTRIC: std::cerr << "dielectric " << std::setw(8) << it->thickness * 1E3 << " mm" ; break;
+ case LAYER_PLANE: std::cerr << "plane " << std::setw(8) << it->thickness * 1E6 << " um" ; break;
+ default: std::cerr << "*** " << std::setw(8) << it->thickness * 1E3 << " mm" ; break;
+ }
+ std::cerr << " eps_r " << it->epsilon_r << " '" << it->layer_name << "'" << std::endl;
+ }
+
+ /* reset flags and precision */
+ std::cerr.flags(saved_flgs);
+ std::cerr.precision(saved_prec);
+
+ return;
+}
+
+/* not truncated */
diff --git a/hyp2mat/lib/pdf.cc b/hyp2mat/lib/pdf.cc
new file mode 100644
index 0000000..a233f29
--- /dev/null
+++ b/hyp2mat/lib/pdf.cc
@@ -0,0 +1,292 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Uses libharu by Takeshi Kanno to generate pdf documents.
+ */
+
+#include <iostream>
+#include <cstdio>
+#include <algorithm>
+#include "pdf.h"
+#include <hpdf_types.h>
+
+using namespace Hyp2Mat;
+
+PDF::PDF()
+{
+ text_height = 10; /* caption size in points */
+ margin = text_height; /* page margin */
+ m_to_points = 1 / (2.54 / 100 / 72); /* convert m to points (1/72 of an inch) */
+ x_max = x_min = y_max = y_min = 0;
+}
+
+/*
+ * PDF error handler
+ */
+
+void error_handler (HPDF_STATUS error_no, HPDF_STATUS detail_no, void *user_data)
+{
+ printf ("ERROR: error_no=%04X, detail_no=%d\n", (unsigned int) error_no, (int) detail_no);
+ throw std::exception (); /* throw exception on error */
+}
+
+/*
+ * set page size to size of pcb board
+ */
+
+void PDF::set_page_size(HPDF_Page page)
+{
+ double width = x_max - x_min + 2 * margin;
+ double height = y_max - y_min + 2 * margin + text_height; /* add margin for caption */
+
+ if (width < 3) width = 3;
+ if (height < 3) height = 3;
+
+ HPDF_Page_SetWidth(page, width);
+ HPDF_Page_SetHeight(page, height);
+}
+
+/*
+ * Generate color palette given hue, saturation and value (brightness).
+ * After Martin Ankerl * "How to Generate Random Colors Programmatically"
+ */
+
+void PDF::set_color(HPDF_Page page, int color_idx)
+{
+ PaletteColor rgb = Color(color_idx);
+
+ HPDF_Page_SetRGBStroke(page, rgb.red, rgb.green, rgb.blue);
+ HPDF_Page_SetRGBFill(page, rgb.red, rgb.green, rgb.blue);
+}
+
+/*
+ * Set stroke and fill color to black
+ */
+
+void PDF::set_color_black(HPDF_Page page)
+{
+ HPDF_Page_SetRGBStroke(page, 0, 0, 0);
+ HPDF_Page_SetRGBFill(page, 0, 0, 0);
+}
+
+/*
+ * fill all polygons
+ */
+
+void PDF::page_fill(HPDF_Page page)
+{
+ if (HPDF_Page_GetGMode(page) == HPDF_GMODE_PATH_OBJECT) HPDF_Page_Fill (page);
+ return;
+}
+
+/*
+ * draw all polygon outlines
+ */
+
+void PDF::page_stroke(HPDF_Page page)
+{
+ if (HPDF_Page_GetGMode(page) == HPDF_GMODE_PATH_OBJECT) HPDF_Page_Stroke (page);
+ return;
+}
+
+/*
+ * Write layer name on top of page
+ */
+
+void PDF::draw_caption(HPDF_Page page, HPDF_Font font, std::string txt)
+{
+ HPDF_Page_SetFontAndSize(page, font, text_height);
+ HPDF_Page_BeginText(page);
+ HPDF_Page_MoveTextPos(page, margin, y_max - y_min + margin + text_height); /* page top left */
+ HPDF_Page_ShowText(page, txt.c_str());
+ HPDF_Page_EndText(page);
+}
+
+/*
+ * Export copper polygon as pdf
+ */
+
+void PDF::draw(HPDF_Page page, FloatPolygon& poly)
+{
+ double old_px, old_py;
+
+ /* pdf requires outer edge of polygon to be clockwise; holes to be counterclockwise */
+
+ bool firstpoint = true;
+ for (FloatPolygon::iterator i = poly.begin(); i != poly.end(); ++i) {
+
+ double px = i->x * m_to_points - x_min + margin;
+ double py = i->y * m_to_points - y_min + margin;
+ if (firstpoint) {
+ HPDF_Page_MoveTo(page, px, py);
+ firstpoint = false;
+ }
+ else {
+ if ((px != old_px) || (py != old_py))
+ HPDF_Page_LineTo(page, px, py);
+ }
+ old_px = px; old_py = py;
+ }
+ HPDF_Page_ClosePath (page);
+ return;
+}
+
+/*
+ * Export all polygons of a copper layer as pdf. May contain holes.
+ */
+
+void PDF::draw(HPDF_Page page, FloatPolygons& polygons)
+{
+ for (FloatPolygons::iterator i = polygons.begin(); i != polygons.end(); ++i)
+ draw(page, i->poly);
+}
+
+/*
+ * Draws a via as a circle.
+ */
+
+void PDF::draw(HPDF_Page page, Hyp2Mat::Via& via)
+{
+ double x = via.x * m_to_points - x_min + margin;
+ double y = via.y * m_to_points - y_min + margin;
+ double r = via.radius * m_to_points;
+ HPDF_Page_Circle(page, x, y, r);
+}
+
+void PDF::draw_composite_view(HPDF_Doc pdf, HPDF_Font font, Hyp2Mat::PCB& pcb)
+{
+ /* new page */
+ HPDF_Page page = HPDF_AddPage(pdf);
+ set_page_size(page);
+ HPDF_Page_SetLineWidth(page, 0);
+ draw_caption(page, font, "Composite");
+
+ /* draw board */
+ set_color_black(page);
+ draw(page, pcb.board);
+ page_stroke (page);
+
+ /* draw layers */
+ int current_color = 0;
+ for (LayerList::reverse_iterator l = pcb.stackup.rbegin(); l != pcb.stackup.rend(); ++l) {
+ if (l->layer_type == LAYER_DIELECTRIC) continue;
+ set_color(page, current_color++);
+ draw(page, l->metal);
+ page_stroke (page);
+ }
+
+ /* draw vias */
+ set_color_black(page);
+ for (ViaList::iterator i = pcb.via.begin(); i != pcb.via.end(); ++i)
+ PDF::draw(page, *i);
+ page_fill (page);
+
+ return;
+}
+
+void PDF::draw_layer_view(HPDF_Doc pdf, HPDF_Font font, Hyp2Mat::PCB& pcb)
+{
+
+ int current_color = 0;
+ for (LayerList::reverse_iterator l = pcb.stackup.rbegin(); l != pcb.stackup.rend(); ++l) {
+ if (l->layer_type == LAYER_DIELECTRIC) continue;
+
+ HPDF_Page page = HPDF_AddPage(pdf);
+ set_page_size(page);
+ HPDF_Page_SetLineWidth(page, 0);
+
+ /* draw caption */
+ draw_caption(page, font, l->layer_name);
+
+ /* draw board */
+ set_color_black(page);
+ draw(page, pcb.board);
+ page_stroke (page);
+
+ /* draw layer */
+ set_color(page, current_color++);
+ draw(page, l->metal);
+ page_fill (page);
+
+ /* draw vias */
+ set_color_black(page);
+ for (ViaList::iterator i = pcb.via.begin(); i != pcb.via.end(); ++i) {
+ /* blind vias: only draw via if via goes through current layer */
+ if ((i->z0 <= l->z0) && (i->z1 >= l->z1)) {
+ draw(page, *i);
+ }
+ }
+ page_fill (page);
+ }
+}
+
+/*
+ * Save current board as pdf.
+ * First page is the composite view of all signal layers,
+ * subsequent pages are one layer each.
+ */
+
+void PDF::Write(const std::string& filename, Hyp2Mat::PCB& pcb)
+{
+ /* choose a color palette. Change HSV values to what suits your taste */
+ int number_of_colors = 0;
+ for (LayerList::reverse_iterator l = pcb.stackup.rbegin(); l != pcb.stackup.rend(); ++l)
+ if (l->layer_type != LAYER_DIELECTRIC) number_of_colors++; /* count layers to determine number of colors needed */
+
+ SetNumberOfColors(number_of_colors);
+
+ HPDF_Doc pdf = HPDF_New (error_handler, NULL); /* set error-handler */
+ if (!pdf) {
+ std::cerr << "cannot create pdf object" << std::endl;
+ return;
+ }
+
+ try {
+ /* get pcb size */
+ Bounds bounds = pcb.GetBounds();
+ x_max = bounds.x_max * m_to_points;
+ y_max = bounds.y_max * m_to_points;
+ x_min = bounds.x_min * m_to_points;
+ y_min = bounds.y_min * m_to_points;
+
+ /* Document settings */
+ HPDF_Font font = HPDF_GetFont(pdf, "Helvetica", NULL);
+ HPDF_SetInfoAttr(pdf, HPDF_INFO_CREATOR, "hyp2mat");
+ HPDF_SetCompressionMode (pdf, HPDF_COMP_ALL);
+
+ /* Draw composite page */
+ draw_composite_view(pdf, font, pcb);
+
+ /* Draw individual layers' pages */
+ draw_layer_view(pdf, font, pcb);
+
+ HPDF_SaveToFile (pdf, filename.c_str());
+ }
+ catch (...) {
+ HPDF_Free (pdf);
+ return;
+ }
+
+ HPDF_Free (pdf);
+
+ return;
+}
+/* not truncated */
diff --git a/hyp2mat/lib/pdf.h b/hyp2mat/lib/pdf.h
new file mode 100644
index 0000000..98d654e
--- /dev/null
+++ b/hyp2mat/lib/pdf.h
@@ -0,0 +1,59 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PDF_H
+#define PDF_H
+
+#include <cmath>
+#include <hpdf.h>
+
+#include "palette.h"
+#include "hyp2mat.h"
+
+class PDF: public Palette {
+public:
+ PDF();
+ void Write(const std::string& filename, Hyp2Mat::PCB& pcb);
+
+private:
+ Palette palette;
+ double x_max; /* board dimensions */
+ double y_max;
+ double x_min;
+ double y_min;
+ double text_height; /* in points */
+ double margin; /* in points */
+ double m_to_points; /* convert meter to points */
+ void set_page_size(HPDF_Page page);
+ void set_color(HPDF_Page page, int color_idx);
+ void set_color_black(HPDF_Page page);
+ void draw_composite_view(HPDF_Doc pdf, HPDF_Font font, Hyp2Mat::PCB& pcb);
+ void draw_layer_view(HPDF_Doc pdf, HPDF_Font font, Hyp2Mat::PCB& pcb);
+ void draw_caption(HPDF_Page page, HPDF_Font font, std::string txt);
+ void draw(HPDF_Page page, Hyp2Mat::FloatPolygons& polygons); /* output a polygon */
+ void draw(HPDF_Page page, Hyp2Mat::FloatPolygon& polygon); /* output a polygon edge */
+ void draw(HPDF_Page page, Hyp2Mat::Via& via); /* output a via */
+ void page_fill(HPDF_Page page); /* fill */
+ void page_stroke(HPDF_Page page); /* draw lines */
+ };
+
+#endif
+
+/* not truncated */
diff --git a/hyp2mat/lib/polygon.cc b/hyp2mat/lib/polygon.cc
new file mode 100644
index 0000000..5d3d9d1
--- /dev/null
+++ b/hyp2mat/lib/polygon.cc
@@ -0,0 +1,335 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cmath>
+#include <iostream>
+#include <algorithm>
+
+#include "polygon.h"
+
+using namespace Hyp2Mat;
+
+double Polygon::_grid = 10E-6; /* 10 micron */
+double Polygon::_arc_precision = 0.0;
+
+Polygon::Polygon()
+{
+ _filltype = ClipperLib::pftNonZero; // Use edge orientation to determine whether or not polygon edge is a hole.
+}
+
+void Polygon::SetGrid(double new_grid)
+{
+ _grid = new_grid;
+}
+
+void Polygon::SetArcPrecision(double new_arc_precision)
+{
+ _arc_precision = new_arc_precision;
+}
+
+/*
+ * Align coordinates to grid
+ */
+
+double Polygon::AlignToGrid(double x)
+{
+ return floor(x / _grid + 0.5) * _grid;
+}
+
+/*
+ * Return true if polygon is empty
+ */
+
+bool Polygon::IsEmpty()
+{
+ return _subject.empty();
+}
+
+/*
+ * Add an outer edge to the polygon
+ */
+
+void Polygon::AddEdge(FloatPolygon edge)
+{
+ _AddEdge(edge, true);
+}
+
+/*
+ * Add an inner edge to the polygon
+ */
+
+void Polygon::AddHole(FloatPolygon hole)
+{
+ _AddEdge(hole, false);
+}
+
+/*
+ * Add an edge - positive or negative - to the polygon
+ */
+
+void Polygon::_AddEdge(FloatPolygon edge, bool orientation)
+{
+
+ /* trivial case */
+ if (edge.empty()) return;
+
+ /* convert edge to Clipper polygon */
+ ClipperLib::Polygon poly = _convert(edge);
+
+ /* orient edge clockwise */
+ if (!Orientation(poly) == orientation) ClipperLib::ReversePolygon(poly);
+
+ /* Add to polygon */
+ _subject.push_back(poly);
+
+ return;
+}
+
+/*
+ * convert from Hyp2Mat floating point point to Clipper long64 point
+ */
+
+ClipperLib::IntPoint Polygon::_convert(FloatPoint p)
+{
+ return ClipperLib::IntPoint(floor(p.x / _grid + 0.5), floor(p.y / _grid + 0.5));
+}
+
+/*
+ * convert from Hyp2Mat floating point polygon to Clipper long64 polygon
+ */
+
+ClipperLib::Polygon Polygon::_convert(FloatPolygon edge)
+{
+ ClipperLib::Polygon poly;
+ for (FloatPolygon::iterator i = edge.begin(); i != edge.end(); ++i)
+ poly.push_back(_convert(*i));
+
+ /* remove repeated vertices */
+ poly = _unique(poly);
+
+ return poly;
+}
+
+/*
+ * Remove repeated vertices from a polygon
+ */
+
+ClipperLib::Polygon Polygon::_unique(ClipperLib::Polygon p)
+{
+ ClipperLib::Polygon q = p;
+ q.clear();
+
+ if (p.empty()) return q;
+
+ /* initial condition: add first vertex */
+ q.push_back(p.front());
+
+ /* create list of non-repeating vertices */
+ for (ClipperLib::Polygon::iterator i = p.begin(); i != p.end(); ++i)
+ if ((i->X != q.back().X) || (i->Y != q.back().Y)) q.push_back(*i);
+
+ /* Remove last vertex if first and last vertex same */
+ while ((q.size() > 1) && ((q.front().X == q.back().X) && (q.front().Y == q.back().Y)))
+ q.pop_back();
+
+ return q;
+}
+
+/*
+ * Expand the polygon by a distance
+ */
+
+void Polygon::Offset(double delta)
+{
+ ClipperLib::Polygons result;
+ double d = delta / _grid;
+ double limit = _arc_precision / _grid;
+
+ if (delta == 0.0) return;
+
+ if (_arc_precision == 0)
+ /* square corners */
+ OffsetPolygons(_subject, result, d, ClipperLib::jtSquare);
+ else
+ /* round corners */
+ OffsetPolygons(_subject, result, d, ClipperLib::jtRound, limit);
+
+ _subject = result;
+
+ return;
+}
+
+/*
+ * Remove self-intersections
+ */
+
+void Polygon::Simplify()
+{
+ ClipperLib::Polygons result;
+ ClipperLib::SimplifyPolygons(_subject, result, _filltype);
+ _subject = result;
+
+}
+
+/*
+ * Basic polygon operations
+ */
+
+void Polygon::Intersection(Polygon clip)
+{
+ _Execute(ClipperLib::ctIntersection, clip);
+}
+
+void Polygon::Union(Polygon clip)
+{
+ _Execute(ClipperLib::ctUnion, clip);
+}
+
+void Polygon::Difference(Polygon clip)
+{
+ _Execute(ClipperLib::ctDifference, clip);
+}
+
+void Polygon::Xor(Polygon clip)
+{
+ _Execute(ClipperLib::ctXor, clip);
+}
+
+void Polygon::_Execute(ClipperLib::ClipType op, Polygon clip)
+{
+ ClipperLib::Polygons result;
+ ClipperLib::Clipper clipper;
+
+ clipper.AddPolygons(_subject, ClipperLib::ptSubject);
+ clipper.AddPolygons(clip._subject, ClipperLib::ptClip);
+
+ clipper.Execute(op, result, _filltype, _filltype);
+ _subject = result;
+
+ return;
+}
+
+/*
+ * Convert from Clipper long64 polygon to Hyp2Mat floating point polygon
+ */
+
+FloatPolygons Polygon::Result()
+{
+ ClipperLib::Clipper clipper;
+ ClipperLib::PolyTree polytree;
+
+ /* convert to PolyTree */
+ clipper.AddPolygons(_subject, ClipperLib::ptSubject);
+ clipper.Execute(ClipperLib::ctUnion, polytree, _filltype, _filltype);
+
+ /* recursively descend polytree, converting nodes */
+ FloatPolygons result;
+ for (int i = 0; i < polytree.ChildCount(); ++i)
+ _AddToPolygonList(*polytree.Childs[i], result, 0);
+
+ return result;
+}
+
+void Polygon::_AddToPolygonList(ClipperLib::PolyNode& polynode, FloatPolygons& polygons, int level)
+{
+ FloatPoly outer_edge;
+
+ outer_edge.poly = _convert(polynode.Contour);
+ outer_edge.is_hole = !ClipperLib::Orientation(polynode.Contour);
+ outer_edge.nesting_level = level;
+ /* make outer edges of polygon clockwise; holes counterclockwise. XXX is this superfluous? */
+ if (outer_edge.is_hole == IsClockwise(outer_edge.poly)) Reverse(outer_edge.poly);
+
+ polygons.push_back(outer_edge);
+
+ for (int i = 0; i < polynode.ChildCount(); ++i) {
+ FloatPoly inner_edge;
+ inner_edge.poly = _convert(polynode.Childs[i]->Contour);
+ inner_edge.is_hole = !ClipperLib::Orientation(polynode.Childs[i]->Contour);
+ inner_edge.nesting_level = level + 1;
+ /* make outer edges of polygon clockwise; holes counterclockwise. XXX is this superfluous? */
+ if (inner_edge.is_hole == IsClockwise(inner_edge.poly)) Reverse(inner_edge.poly);
+ polygons.push_back(inner_edge);
+ }
+
+ /* Add outer polygons nested within holes */
+ for (int i = 0; i < polynode.ChildCount(); ++i)
+ for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j)
+ _AddToPolygonList(*polynode.Childs[i]->Childs[j], polygons, level + 2);
+
+ return;
+}
+
+ /*
+ * Convert edge back from ClipperLib to Hyp2Mat
+ */
+
+FloatPolygon Polygon::_convert (ClipperLib::Polygon clip_poly)
+{
+ FloatPolygon edge;
+
+ for (ClipperLib::Polygon::iterator i = clip_poly.begin(); i != clip_poly.end(); ++i)
+ edge.push_back(_convert(*i));
+
+ return edge;
+}
+
+ /*
+ * Convert point back from ClipperLib to Hyp2Mat
+ */
+
+FloatPoint Polygon::_convert (ClipperLib::IntPoint p)
+{
+ return FloatPoint(p.X * _grid, p.Y * _grid);
+}
+
+/*
+ * Calculate if a polygon is clockwise or not.
+ */
+
+bool Hyp2Mat::IsClockwise(FloatPolygon& poly) {
+
+ /* calculate area of polygon */
+ double area = 0;
+ for (FloatPolygon::iterator i = poly.begin(); i != poly.end(); ++i) {
+ /* i points to current element; j points to next element */
+ FloatPolygon::iterator j = i;
+ ++j;
+ /* polygon is closed */
+ if (j == poly.end()) j = poly.begin();
+ area += i->x * j->y - j->x * i->y;
+ }
+
+ /* positive area is clockwise. Arbitrarily choose clockwise if area 0 */
+ return area >= 0;
+
+}
+
+/*
+ * Reverse order of vertices.
+ */
+
+void Hyp2Mat::Reverse(FloatPolygon& poly)
+{
+ std::reverse(poly.begin(), poly.end());
+}
+
+
+/* not truncated */
diff --git a/hyp2mat/lib/polygon.h b/hyp2mat/lib/polygon.h
new file mode 100644
index 0000000..3df23ef
--- /dev/null
+++ b/hyp2mat/lib/polygon.h
@@ -0,0 +1,74 @@
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef POLYGON_H
+#define POLYGON_H
+
+#include "clipper.hpp"
+#include "hyp2mat.h"
+
+namespace Hyp2Mat {
+
+ bool IsClockwise (FloatPolygon& poly);
+ void Reverse (FloatPolygon& poly);
+
+ class Polygon {
+ public:
+ Polygon();
+ static void SetGrid(double new_grid);
+ static double AlignToGrid(double x);
+ static void SetArcPrecision(double new_arc_precision);
+ void AddEdge(FloatPolygon edge);
+ void AddHole(FloatPolygon hole);
+ void Offset(double delta);
+ void Simplify();
+ void Intersection(Polygon clip);
+ void Union(Polygon clip);
+ void Difference(Polygon clip);
+ void Xor(Polygon clip);
+ bool IsEmpty();
+ FloatPolygons Result();
+ private:
+ /* convert Hyp2Mat to Clipper */
+ void _AddEdge(FloatPolygon edge, bool orientation);
+ ClipperLib::IntPoint _convert(FloatPoint p);
+ ClipperLib::Polygon _convert(FloatPolygon edge);
+ ClipperLib::Polygon _unique(ClipperLib::Polygon p);
+
+ /* Clipper polygon operations */
+ void _Execute(ClipperLib::ClipType op, Polygon clip);
+ ClipperLib::PolyFillType _filltype;
+
+ /* convert Clipper to Hyp2Mat */
+ void _AddToPolygonList(ClipperLib::PolyNode& polynode, FloatPolygons& polygons, int level);
+ FloatPolygon _convert (ClipperLib::Polygon clip_poly);
+ FloatPoint _convert (ClipperLib::IntPoint p);
+
+ /* data */
+ ClipperLib::Polygons _subject;
+ static double _grid; /* resolution of x and y coordinates, identical for all polygons. */
+ static double _arc_precision; /* maximum difference between perfect circle arc and polygonal approximation */
+ };
+
+};
+
+#endif
+
+/* not truncated */
diff --git a/hyp2mat/lib/scan.ll b/hyp2mat/lib/scan.ll
new file mode 100644
index 0000000..3e07902
--- /dev/null
+++ b/hyp2mat/lib/scan.ll
@@ -0,0 +1,371 @@
+
+/*
+ * hyp2mat - convert hyperlynx files to matlab scripts
+ * Copyright 2012 Koen De Vleeschauwer.
+ *
+ * This file is part of hyp2mat.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+%option noyywrap nodefault yylineno debug
+
+%{
+#include <stdlib.h>
+#include "parse.h"
+#include "parser.h"
+
+/* copy a string between quotes */
+char *strunquote(const char *);
+
+/* remember hyperlynx file section we're in */
+int section = -1;
+
+%}
+
+ /*
+ * The scanner knows five states: INITIAL, STATE_STRING, STATE_POSINT, STATE_FLOAT and STATE_COMMENT
+ *
+ * In state INITIAL the scanner recognizes:
+ * - punctuation (such as {}()=, )
+ * - keywords (such as VERSION, BOARD, NET),
+ * - strings (both between double quotes " and unquoted),
+ * - and floating point numbers (with optional suffix, eg. 10 uF),
+ *
+ * In STATE_STRING the scanner recognizes punctuation and strings only.
+ * This avoids unquoted strings being interpreted as numbers, or keywords.
+ *
+ * In STATE_POSINT the scanner recognizes a positive integer.
+ *
+ * In STATE_FLOAT the scanner recognizes a floating point number (without suffix).
+ *
+ * In STATE_COMMENT the scanner discards all text up to the next
+ * right brace } , and then continues in state INITIAL.
+ *
+ */
+
+%x STATE_STRING STATE_POSINT STATE_FLOAT STATE_COMMENT STATE_COMMENT_EOL
+
+ /* whitespace: space, tab, vertical tab, form feed */
+WS [ \t\v\f]
+
+ /* accept windows and unix newlines */
+NEWLINE [\r\n]+
+
+ /*
+ * lines with an asterisk in the first column are comments
+ */
+COMMENT ^\*[^\n\r]*[\n\r]+
+
+ /* Left-hand side of an assignment: check whether next token is equals sign */
+LHS [ \t\v\f]*"="
+
+ /*
+ * Positive integer
+ */
+
+POSINT [0-9]+
+
+ /*
+ * Floating point numbers
+ */
+
+ /* ordinary floating point numbers */
+SIMPLE_FLOAT [-+]?([0-9]+"."[0-9]*|"."?[0-9]+)([Ee][-+]?[0-9]+)?
+
+ /* floating point numbers with suffix, e,g. pF, nH */
+FLOAT_SUFFIX [A-Za-z]*{WS}+
+FLOAT_YOTTA {SIMPLE_FLOAT}{WS}*"Y"{FLOAT_SUFFIX}
+FLOAT_ZETA {SIMPLE_FLOAT}{WS}*"Z"{FLOAT_SUFFIX}
+FLOAT_EXA {SIMPLE_FLOAT}{WS}*"E"{FLOAT_SUFFIX}
+FLOAT_PETA {SIMPLE_FLOAT}{WS}*"P"{FLOAT_SUFFIX}
+FLOAT_TERA {SIMPLE_FLOAT}{WS}*"T"{FLOAT_SUFFIX}
+FLOAT_GIGA {SIMPLE_FLOAT}{WS}*"G"{FLOAT_SUFFIX}
+FLOAT_MEGA {SIMPLE_FLOAT}{WS}*"M"{FLOAT_SUFFIX}
+FLOAT_KILO {SIMPLE_FLOAT}{WS}*[Kk]{FLOAT_SUFFIX}
+FLOAT_MILLI {SIMPLE_FLOAT}{WS}*"m"{FLOAT_SUFFIX}
+FLOAT_MICRO {SIMPLE_FLOAT}{WS}*[uU]{FLOAT_SUFFIX}
+FLOAT_NANO {SIMPLE_FLOAT}{WS}*[nN]{FLOAT_SUFFIX}
+FLOAT_PICO {SIMPLE_FLOAT}{WS}*[pP]{FLOAT_SUFFIX}
+FLOAT_FEMTO {SIMPLE_FLOAT}{WS}*[fF]{FLOAT_SUFFIX}
+FLOAT_ATTO {SIMPLE_FLOAT}{WS}*"a"{FLOAT_SUFFIX}
+FLOAT_ZEPTO {SIMPLE_FLOAT}{WS}*"z"{FLOAT_SUFFIX}
+FLOAT_YOCTO {SIMPLE_FLOAT}{WS}*"y"{FLOAT_SUFFIX}
+
+ /*
+ * Strings
+ */
+
+ /* an unquoted string */
+STRING [^ \t\v\f\r\n\{\}\(\)=\"]+
+
+ /* a string enclosed in double quotes " */
+QUOTED_STRING \"([^\"\n]|\"\")*\"
+
+%%
+
+ /* When in STATE_COMMENT skip all comment until next right brace */
+<STATE_COMMENT>{
+[^\}]* { BEGIN INITIAL; /* skip all comment until next right brace */ }
+}
+
+ /* When in STATE_COMMENT_EOL skip all comment until end-of-line */
+<STATE_COMMENT_EOL>{
+[^\r\n]*{NEWLINE}+ { BEGIN INITIAL; /* skip all comment until next end-of-line */ }
+}
+
+
+ /* skip comments and whitespace */
+<*>{
+
+ {COMMENT} { /* skip comments */ }
+
+ {WS}+ { /* skip whitespace */ }
+
+ {NEWLINE}+ { /* skip newlines */ }
+
+}
+
+ /*
+ * Hyperlynx keywords
+ */
+
+ /* Sections */
+
+"BOARD_FILE" {section = BOARD_FILE; return BOARD_FILE;}
+"VERSION" {section = VERSION; return VERSION;}
+"DATA_MODE" {section = DATA_MODE; return DATA_MODE;}
+"UNITS" {section = UNITS; return UNITS;}
+"PLANE_SEP" {section = PLANE_SEP; return PLANE_SEP;}
+"BOARD" {section = BOARD; BEGIN STATE_COMMENT_EOL; return BOARD;}
+"STACKUP" {section = STACKUP; BEGIN STATE_COMMENT_EOL; return STACKUP;}
+"DEVICES" {section = DEVICES; BEGIN STATE_COMMENT_EOL; return DEVICES;}
+"SUPPLIES" {section = SUPPLIES; BEGIN STATE_COMMENT_EOL; return SUPPLIES;}
+"PADSTACK" {section = PADSTACK; BEGIN STATE_STRING; return PADSTACK;}
+"NET" {section = NET; BEGIN STATE_STRING; return NET;}
+"NET_CLASS" {section = NET_CLASS; return NET_CLASS;}
+"END" {section = END; return END;}
+"KEY" {section = KEY; return KEY;}
+
+ /* Keywords */
+
+"A" {return A;}
+"ARC" {return ARC;}
+"COPPER" {return COPPER;}
+"CURVE" {return CURVE;}
+"DETAILED" {if (section == DATA_MODE) BEGIN STATE_COMMENT; return DETAILED;}
+"DIELECTRIC" {return DIELECTRIC;}
+"ENGLISH" {return ENGLISH;}
+"LENGTH" {if (section == UNITS) BEGIN STATE_COMMENT; return LENGTH;}
+"LINE" {return LINE;}
+"METRIC" {return METRIC;}
+"M" {return M;}
+"N" {return N;}
+"OPTIONS" {return OPTIONS;}
+"PAD" {return PAD;}
+"PERIMETER_ARC" {return PERIMETER_ARC;}
+"PERIMETER_SEGMENT" {return PERIMETER_SEGMENT;}
+"PIN" {return PIN;}
+"PLANE" {return PLANE;}
+"POLYGON" {return POLYGON;}
+"POLYLINE" {return POLYLINE;}
+"POLYVOID" {return POLYVOID;}
+"POUR" {return POUR;}
+"S" {return S;}
+"T" {return T;}
+"SEG" {return SEG;}
+"SIGNAL" {return SIGNAL;}
+"SIMPLIFIED" {if (section == DATA_MODE) BEGIN STATE_COMMENT; return SIMPLIFIED; }
+"SIM_BOTH" {return SIM_BOTH;}
+"SIM_IN" {return SIM_IN;}
+"SIM_OUT" {return SIM_OUT;}
+"USEG" {return USEG;}
+"VIA" {return VIA;}
+"WEIGHT" {if (section == UNITS) BEGIN STATE_COMMENT; return WEIGHT;}
+
+ /* Assignments */
+
+"A"/{LHS} {return A;}
+"A1"/{LHS} {return A1;}
+"A2"/{LHS} {return A2;}
+"BR"/{LHS} {return BR;}
+"C"/{LHS} {return C;}
+"C?"/{LHS} {return C_QM;}
+"CO?"/{LHS} {return CO_QM;}
+"D"/{LHS} {return D;}
+"ER"/{LHS} {return ER;}
+"F"/{LHS} {return F;}
+"ID"/{LHS} {BEGIN STATE_POSINT; return ID;}
+"L"/{LHS} {BEGIN STATE_STRING; return L;}
+"L1"/{LHS} {BEGIN STATE_STRING; return L1;}
+"L2"/{LHS} {BEGIN STATE_STRING; return L2;}
+"LPS"/{LHS} {return LPS;}
+"LT"/{LHS} {return LT;}
+"M"/{LHS} {BEGIN STATE_STRING; return M;}
+"N"/{LHS} {BEGIN STATE_STRING; return N;}
+"NAME"/{LHS} {BEGIN STATE_STRING; return NAME;}
+ /* P is used as "plating thickness" in "stackup/signal" and as "padstack" in "net/via" */
+"P"/{LHS} {if (section == NET) BEGIN STATE_STRING; return P;}
+"PKG"/{LHS} {BEGIN STATE_STRING; return PKG;}
+"PR?"/{LHS} {return PR_QM;}
+"PS"/{LHS} {return PS;}
+"R"/{LHS} {return R;}
+"REF"/{LHS} {BEGIN STATE_STRING; return REF;}
+"S"/{LHS} {BEGIN STATE_STRING; return S;}
+"SX"/{LHS} {return SX;}
+"SY"/{LHS} {return SY;}
+"S1"/{LHS} {BEGIN STATE_STRING; return S1;}
+"S1X"/{LHS} {return S1X;}
+"S1Y"/{LHS} {return S1Y;}
+"S2"/{LHS} {BEGIN STATE_STRING; return S2;}
+"S2X"/{LHS} {return S2X;}
+"S2Y"/{LHS} {return S2Y;}
+"T"/{LHS} {return T;}
+"TC"/{LHS} {return TC;}
+"USE_DIE_FOR_METAL"/{LHS} {return USE_DIE_FOR_METAL;}
+"V"/{LHS} {BEGIN STATE_STRING; return V;}
+"V?"/{LHS} {return V_QM;}
+"VAL"/{LHS} {return VAL;}
+"W"/{LHS} {return W;}
+"X"/{LHS} {return X;}
+"X1"/{LHS} {return X1;}
+"X2"/{LHS} {return X2;}
+"XC"/{LHS} {return XC;}
+"Y"/{LHS} {return Y;}
+"Y1"/{LHS} {return Y1;}
+"Y2"/{LHS} {return Y2;}
+"YC"/{LHS} {return YC;}
+"Z"/{LHS} {return Z;}
+"ZL"/{LHS} {return ZL;}
+"ZLEN"/{LHS} {return ZLEN;}
+"ZW"/{LHS} {return ZW;}
+
+ /* Booleans */
+
+"YES"|"yes" {yylval.boolval = 1; return BOOL; }
+"NO"|"no" {yylval.boolval = 0; return BOOL; }
+
+ /* Floats */
+
+ /* ordinary floating point numbers */
+{SIMPLE_FLOAT} {yylval.floatval = strtod(yytext, NULL); return FLOAT;}
+
+ /* floating point numbers with suffix, e,g. pF, nH */
+{FLOAT_YOTTA} {yylval.floatval = strtod(yytext, NULL) * 1e24; return FLOAT;}
+{FLOAT_ZETA} {yylval.floatval = strtod(yytext, NULL) * 1e21; return FLOAT;}
+{FLOAT_EXA} {yylval.floatval = strtod(yytext, NULL) * 1e18; return FLOAT;}
+{FLOAT_PETA} {yylval.floatval = strtod(yytext, NULL) * 1e15; return FLOAT;}
+{FLOAT_TERA} {yylval.floatval = strtod(yytext, NULL) * 1e12; return FLOAT;}
+{FLOAT_GIGA} {yylval.floatval = strtod(yytext, NULL) * 1e9; return FLOAT;}
+{FLOAT_MEGA} {yylval.floatval = strtod(yytext, NULL) * 1e6; return FLOAT;}
+{FLOAT_KILO} {yylval.floatval = strtod(yytext, NULL) * 1e3; return FLOAT;}
+{FLOAT_MILLI} {yylval.floatval = strtod(yytext, NULL) * 1e-3; return FLOAT;}
+{FLOAT_MICRO} {yylval.floatval = strtod(yytext, NULL) * 1e-6; return FLOAT;}
+{FLOAT_NANO} {yylval.floatval = strtod(yytext, NULL) * 1e-9; return FLOAT;}
+{FLOAT_PICO} {yylval.floatval = strtod(yytext, NULL) * 1e-12; return FLOAT;}
+{FLOAT_FEMTO} {yylval.floatval = strtod(yytext, NULL) * 1e-15; return FLOAT;}
+{FLOAT_ATTO} {yylval.floatval = strtod(yytext, NULL) * 1e-18; return FLOAT;}
+{FLOAT_ZEPTO} {yylval.floatval = strtod(yytext, NULL) * 1e-21; return FLOAT;}
+{FLOAT_YOCTO} {yylval.floatval = strtod(yytext, NULL) * 1e-24; return FLOAT;}
+
+ /* floating point numbers in VERSION and PLANE_SEP have no suffix and are followed by optional comments */
+<STATE_FLOAT>{
+{SIMPLE_FLOAT} {yylval.floatval = strtod(yytext, NULL); BEGIN STATE_COMMENT; return FLOAT;}
+}
+
+ /* A positive integer is used only in polygon/polyline/polyvoid "ID = posint" */
+<STATE_POSINT>{
+{POSINT} { BEGIN INITIAL; yylval.intval = atoi(yytext); return POSINT; }
+}
+
+
+ /*
+ * This is a workaround for syntactically incorrect .hyp files.
+ * We accept the following constructs as representing an empty string:
+ * NAME= L1=somelayer
+ * NAME= )
+ * NAME= }
+ */
+<STATE_STRING>{
+([A-Z][A-Z1-2_]*{WS}*"="|")"|"}") { yyless(0); BEGIN INITIAL; yylval.strval = strdup(""); return STRING; } /* emit empty string and reprocess */
+}
+
+<*>{
+
+ "{" {return '{';}
+
+ "}" {BEGIN STATE_COMMENT_EOL; return '}';}
+
+ "(" {if (section == PADSTACK) BEGIN STATE_STRING; return '(';}
+
+ /* allow for comment after the closing bracket ) */
+ ")" {BEGIN STATE_COMMENT_EOL; return ')';}
+
+ "," {return ',';}
+
+ "=" {if ((section == VERSION) || (section == PLANE_SEP)) BEGIN STATE_FLOAT; return '=';}
+
+ /* string */
+ {STRING} {
+ /*
+ * Commas are not allowed in strings in the padstack section
+ * unless the string is enclosed in double quotes (").
+ */
+
+ if ((section == PADSTACK) && strchr(yytext, ','))
+ REJECT
+ else
+ {
+ BEGIN INITIAL;
+ yylval.strval = strdup(yytext);
+ return STRING;
+ }
+ }
+
+ /* string in double quotes */
+ {QUOTED_STRING} {BEGIN INITIAL; yylval.strval = strunquote(yytext); return STRING;}
+
+ <<EOF>> {yyterminate();}
+
+ /* have bison catch all unrecognized characters with parse errors */
+ . {return yytext[0];}
+
+}
+
+%%
+
+ /*
+ * copy a quoted string.
+ * e.g. "data 0" -> data 0
+ * a double quote inside the string can be escaped by writing two consecutive double quotes
+ * e.g. "net ""hi""" -> net "hi"
+ */
+
+ char *strunquote(const char *src)
+ {
+ char* dst;
+ size_t len = strlen(src) + 1;
+ dst = (char *)malloc(len);
+ if (dst != NULL)
+ {
+ char* p = (char *)src + 1; /* first char after initial quote */
+ char* q = dst;
+ do
+ if (*p == '"') p++;
+ while ((*q++ = *p++) != '\0');
+ }
+ return dst;
+ }
+
+ /* not truncated */