#include "solvespace.h"
#include "sketch.h"
static std::vector <std::string> splitString(const std::string line) {
std::vector <std::string> v = {};
if(line.length() == 0) return v;
std::string s = "";
bool inString = false;
bool inQuotes = false;
for (size_t i=0; i<line.length(); i++) {
char c = line.at(i);
if (inQuotes) {
if (c != '"') {
s.push_back(c);
} else {
v.push_back(s);
inQuotes = false;
inString = false;
s = "";
}
} else if (inString) {
if (c != ' ') {
s.push_back(c);
} else {
v.push_back(s);
inString = false;
s = "";
}
} else if(c == '"') {
inString = true;
inQuotes = true;
} else if(c != ' ') {
s = "";
s.push_back(c);
inString = true;
}
}
if(s.length() > 0)
v.push_back(s);
return v;
}
static hEntity newPoint(EntityList *el, int *id, Vector p, bool visible = true) {
Entity en = {};
en.type = Entity::Type::POINT_N_COPY;
en.extraPoints = 0;
en.timesApplied = 0;
en.group.v = 462;
en.actPoint = p;
en.construction = false;
en.style.v = Style::DATUM;
en.actVisible = visible;
en.forceHidden = false;
*id = *id+1;
en.h.v = *id + en.group.v*65536;
el->Add(&en);
return en.h;
}
static hEntity newLine(EntityList *el, int *id, hEntity p0, hEntity p1, bool keepout) {
Entity en = {};
en.type = Entity::Type::LINE_SEGMENT;
en.point[0] = p0;
en.point[1] = p1;
en.extraPoints = 0;
en.timesApplied = 0;
en.group.v = 493;
en.construction = keepout;
en.style.v = keepout? Style::CONSTRUCTION : Style::ACTIVE_GRP;
en.actVisible = true;
en.forceHidden = false;
*id = *id+1;
en.h.v = *id + en.group.v*65536;
el->Add(&en);
return en.h;
}
static hEntity newNormal(EntityList *el, int *id, Quaternion normal) {
Entity en = {};
en.type = Entity::Type::NORMAL_N_COPY;
en.extraPoints = 0;
en.timesApplied = 0;
en.group.v = 472;
en.actNormal = normal;
en.construction = false;
en.style.v = Style::ACTIVE_GRP;
en.point[0] = newPoint(el, id, Vector::From(0,0,3), true);
en.actVisible = true;
en.forceHidden = false;
*id = *id+1;
en.h.v = *id + en.group.v*65536;
el->Add(&en);
return en.h;
}
static hEntity newArc(EntityList *el, int *id, hEntity p0, hEntity p1, hEntity pc, hEntity hnorm, bool keepout) {
Entity en = {};
en.type = Entity::Type::ARC_OF_CIRCLE;
en.point[0] = pc;
en.point[1] = p0;
en.point[2] = p1;
en.normal = hnorm;
en.extraPoints = 0;
en.timesApplied = 0;
en.group.v = 403;
en.construction = keepout;
en.style.v = keepout? Style::CONSTRUCTION : Style::ACTIVE_GRP;
en.actVisible = true;
en.forceHidden = false; *id = *id+1;
*id = *id + 1;
en.h.v = *id + en.group.v*65536;
el->Add(&en);
return en.h;
}
static hEntity newDistance(EntityList *el, int *id, double distance) {
Entity en = {};
en.type = Entity::Type::DISTANCE;
en.extraPoints = 0;
en.timesApplied = 0;
en.group.v = 472;
en.actDistance = distance;
en.construction = false;
en.style.v = Style::ACTIVE_GRP;
en.actVisible = false;
en.forceHidden = false;
*id = *id+1;
en.h.v = *id + en.group.v*65536;
el->Add(&en);
return en.h;
}
static hEntity newCircle(EntityList *el, int *id, hEntity p0, hEntity hdist, hEntity hnorm, bool keepout) {
Entity en = {};
en.type = Entity::Type::CIRCLE;
en.point[0] = p0;
en.normal = hnorm;
en.distance = hdist;
en.extraPoints = 0;
en.timesApplied = 0;
en.group.v = 399;
en.construction = keepout;
en.style.v = keepout? Style::CONSTRUCTION : Style::ACTIVE_GRP;
en.actVisible = true;
en.forceHidden = false;
*id = *id+1;
en.h.v = *id + en.group.v*65536;
el->Add(&en);
return en.h;
}
static Vector ArcCenter(Vector p0, Vector p1, double angle) {
Vector m = p0.Plus(p1).ScaledBy(0.5);
Vector perp = Vector::From(p1.y-p0.y, p0.x-p1.x, 0.0).WithMagnitude(1.0);
double dist = 0;
if (angle != 180) {
dist = (p1.Minus(m).Magnitude())/tan(0.5*angle*3.141592653589793/180.0);
} else {
dist = 0.0;
}
Vector c = m.Minus(perp.ScaledBy(dist));
return c;
}
static void CreateEntity(EntityList *el, int *id, hEntity h0, hEntity h1, hEntity hnorm,
Vector p0, Vector p1, double angle, bool keepout) {
if (angle == 0.0) {
if(p0.Equals(p1)) return;
newLine(el, id, h0, h1, keepout);
} else if(angle == 360.0) {
double d = p1.Minus(p0).Magnitude();
hEntity hd = newDistance(el, id, d);
newCircle(el, id, h1, hd, hnorm, keepout);
} else {
if(angle < 0.0) {
swap(p0,p1);
swap(h0,h1);
}
Vector m = p0.Plus(p1).ScaledBy(0.5);
Vector perp = Vector::From(p1.y-p0.y, p0.x-p1.x, 0.0).WithMagnitude(1.0);
double dist = 0;
if (angle != 180) {
dist = (p1.Minus(m).Magnitude())/tan(0.5*angle*3.141592653589793/180.0);
} else {
dist = 0.0;
}
Vector c = m.Minus(perp.ScaledBy(dist));
hEntity hc = newPoint(el, id, c, false);
newArc(el, id, h0, h1, hc, hnorm, keepout);
}
}
static void MakeBeziersForArcs(SBezierList *sbl, Vector center, Vector pa, Vector pb,
Quaternion q, double angle) {
Vector u = q.RotationU(), v = q.RotationV();
double r = pa.Minus(center).Magnitude();
double theta, dtheta;
if(angle == 360.0) {
theta = 0;
} else {
Point2d c2 = center.Project2d(u, v);
Point2d pa2 = (pa.Project2d(u, v)).Minus(c2);
theta = atan2(pa2.y, pa2.x);
}
dtheta = angle * PI/180;
int i, n;
if(dtheta > (3*PI/2 + 0.01)) {
n = 4;
} else if(dtheta > (PI + 0.01)) {
n = 3;
} else if(dtheta > (PI/2 + 0.01)) {
n = 2;
} else {
n = 1;
}
dtheta /= n;
for(i = 0; i < n; i++) {
double s, c;
c = cos(theta);
s = sin(theta);
Vector p0 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
t0 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
theta += dtheta;
c = cos(theta);
s = sin(theta);
Vector p2 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
t2 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
Vector p1 = Vector::AtIntersectionOfLines(p0, p0.Plus(t0),
p2, p2.Plus(t2),
NULL);
SBezier sb = SBezier::From(p0, p1, p2);
sb.weight[1] = cos(dtheta/2);
sbl->l.Add(&sb);
}
}
namespace SolveSpace {
bool LinkIDF(const Platform::Path &filename, EntityList *el, SMesh *m, SShell *sh) {
dbp("\nLink IDF board outline.");
el->Clear();
std::string data;
if(!ReadFile(filename, &data)) {
Error("Couldn't read from '%s'", filename.raw.c_str());
return false;
}
enum IDF_SECTION {
none,
header,
board_outline,
other_outline,
routing_outline,
placement_outline,
routing_keepout,
via_keepout,
placement_group,
drilled_holes,
notes,
component_placement
} section;
section = IDF_SECTION::none;
int record_number = 0;
int curve = -1;
int entityCount = 0;
hEntity hprev;
hEntity hprevTop;
Vector pprev = Vector::From(0,0,0);
Vector pprevTop = Vector::From(0,0,0);
double board_thickness = 10.0;
double scale = 1.0; bool topEntities = false;
bool bottomEntities = false;
Quaternion normal = Quaternion::From(Vector::From(1,0,0), Vector::From(0,1,0));
hEntity hnorm = newNormal(el, &entityCount, normal);
SBezierList sbl = {};
std::stringstream stream(data);
for(std::string line; getline( stream, line ); ) {
if (line.find(".END_") == 0) {
section = none;
curve = -1;
}
switch (section) {
case none:
if(line.find(".HEADER") == 0) {
section = header;
record_number = 1;
} else if (line.find(".BOARD_OUTLINE") == 0) {
section = board_outline;
record_number = 1;
} else if (line.find(".ROUTE_KEEPOUT") == 0) {
section = routing_keepout;
record_number = 1;
} else if(line.find(".DRILLED_HOLES") == 0) {
section = drilled_holes;
record_number = 1;
}
break;
case header:
if(record_number == 3) {
if(line.find("MM") != std::string::npos) {
dbp("IDF units are MM");
scale = 1.0;
} else if(line.find("THOU") != std::string::npos) {
dbp("IDF units are thousandths of an inch");
scale = 0.0254;
} else {
dbp("IDF import, no units found in file.");
}
}
break;
case routing_keepout:
case board_outline:
if (record_number == 2) {
if(section == board_outline) {
topEntities = true;
bottomEntities = true;
board_thickness = std::stod(line) * scale;
dbp("IDF board thickness: %lf", board_thickness);
} else if (section == routing_keepout) {
topEntities = false;
bottomEntities = false;
if(line.find("TOP") == 0 || line.find("BOTH") == 0)
topEntities = true;
if(line.find("BOTTOM") == 0 || line.find("BOTH") == 0)
bottomEntities = true;
}
} else { std::vector <std::string> values = splitString(line);
if(values.size() != 4) continue;
int c = stoi(values[0]);
double x = stof(values[1]);
double y = stof(values[2]);
double ang = stof(values[3]);
Vector point = Vector::From(x,y,0.0);
Vector pTop = Vector::From(x,y,board_thickness);
if(c != curve) { curve = c;
if (bottomEntities)
hprev = newPoint(el, &entityCount, point, false);
if (topEntities)
hprevTop = newPoint(el, &entityCount, pTop, false);
pprev = point;
pprevTop = pTop;
} else {
if(section == board_outline) {
if (ang == 0) {
SBezier sb = SBezier::From(pprev, point);
sbl.l.Add(&sb);
} else if (ang != 360.0) {
Vector c = ArcCenter(pprev, point, ang);
MakeBeziersForArcs(&sbl, c, pprev, point, normal, ang);
} else {
MakeBeziersForArcs(&sbl, point, pprev, pprev, normal, ang);
}
}
bool vis = (ang == 360.0);
if (bottomEntities) {
hEntity hp = newPoint(el, &entityCount, point, vis);
CreateEntity(el, &entityCount, hprev, hp, hnorm, pprev, point, ang,
(section == routing_keepout) );
pprev = point;
hprev = hp;
}
if (topEntities) {
hEntity hp = newPoint(el, &entityCount, pTop, vis);
CreateEntity(el, &entityCount, hprevTop, hp, hnorm, pprevTop, pTop,
ang, (section == routing_keepout) );
pprevTop = pTop;
hprevTop = hp;
}
}
}
break;
case other_outline:
case routing_outline:
case placement_outline:
case via_keepout:
case placement_group:
break;
case drilled_holes: {
std::vector <std::string> values = splitString(line);
if(values.size() < 6) continue;
double d = stof(values[0]);
double x = stof(values[1]);
double y = stof(values[2]);
if((d > 1.7) || (values[5].compare(0,3,"PIN") == 0)
|| (values[5].compare(0,3,"MTG") == 0)) {
Vector cent = Vector::From(x,y,0.0);
hEntity hcent = newPoint(el, &entityCount, cent);
hEntity hdist = newDistance(el, &entityCount, d/2);
newCircle(el, &entityCount, hcent, hdist, hnorm, false);
Vector cTop = Vector::From(x,y,board_thickness);
hcent = newPoint(el, &entityCount, cTop);
hdist = newDistance(el, &entityCount, d/2);
newCircle(el, &entityCount, hcent, hdist, hnorm, false);
Vector pt = Vector::From(x+d/2, y, 0.0);
MakeBeziersForArcs(&sbl, cent, pt, pt, normal, 360.0);
}
break;
}
case notes:
case component_placement:
break;
default:
section = none;
break;
}
record_number++;
}
SPolygon polyLoops = {};
bool allClosed;
bool allCoplanar;
Vector errorPointAt = Vector::From(0,0,0);
SEdge errorAt = {};
SBezierLoopSetSet sblss = {};
sblss.FindOuterFacesFrom(&sbl, &polyLoops, NULL,
100.0, &allClosed, &errorAt,
&allCoplanar, &errorPointAt, NULL);
double ctc = SS.chordTolCalculated;
if(ctc == 0.0) SS.chordTolCalculated = 0.1; sh->MakeFromExtrusionOf(sblss.l.First(), Vector::From(0.0, 0.0, 0.0),
Vector::From(0.0, 0.0, board_thickness),
RgbaColor::From(0, 180, 0) );
SS.chordTolCalculated = ctc;
sblss.Clear();
sbl.Clear();
sh->booleanFailed = false;
return true;
}
}