IDF file Linking.
Can read PCB outlines and cutouts, as well as Pin and Mounting holes. A simple PPCB model sans components is added to the assembly.pull/726/head
parent
0a061b6f9e
commit
8cf9d68ecf
|
@ -175,6 +175,7 @@ set(solvespace_core_SOURCES
|
||||||
group.cpp
|
group.cpp
|
||||||
groupmesh.cpp
|
groupmesh.cpp
|
||||||
importdxf.cpp
|
importdxf.cpp
|
||||||
|
importidf.cpp
|
||||||
mesh.cpp
|
mesh.cpp
|
||||||
modify.cpp
|
modify.cpp
|
||||||
mouse.cpp
|
mouse.cpp
|
||||||
|
|
10
src/file.cpp
10
src/file.cpp
|
@ -702,6 +702,16 @@ void SolveSpaceUI::UpgradeLegacyData() {
|
||||||
|
|
||||||
bool SolveSpaceUI::LoadEntitiesFromFile(const Platform::Path &filename, EntityList *le,
|
bool SolveSpaceUI::LoadEntitiesFromFile(const Platform::Path &filename, EntityList *le,
|
||||||
SMesh *m, SShell *sh)
|
SMesh *m, SShell *sh)
|
||||||
|
{
|
||||||
|
if(strcmp(filename.Extension().c_str(), "emn")==0) {
|
||||||
|
return LinkIDF(filename, le, m, sh);
|
||||||
|
} else {
|
||||||
|
return LoadEntitiesFromSlvs(filename, le, m, sh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SolveSpaceUI::LoadEntitiesFromSlvs(const Platform::Path &filename, EntityList *le,
|
||||||
|
SMesh *m, SShell *sh)
|
||||||
{
|
{
|
||||||
SSurface srf = {};
|
SSurface srf = {};
|
||||||
SCurve crv = {};
|
SCurve crv = {};
|
||||||
|
|
|
@ -287,7 +287,7 @@ void Group::MenuGroup(Command id, Platform::Path linkFile) {
|
||||||
g.meshCombine = CombineAs::ASSEMBLE;
|
g.meshCombine = CombineAs::ASSEMBLE;
|
||||||
if(g.linkFile.IsEmpty()) {
|
if(g.linkFile.IsEmpty()) {
|
||||||
Platform::FileDialogRef dialog = Platform::CreateOpenFileDialog(SS.GW.window);
|
Platform::FileDialogRef dialog = Platform::CreateOpenFileDialog(SS.GW.window);
|
||||||
dialog->AddFilters(Platform::SolveSpaceModelFileFilters);
|
dialog->AddFilters(Platform::SolveSpaceLinkFileFilters);
|
||||||
dialog->ThawChoices(settings, "LinkSketch");
|
dialog->ThawChoices(settings, "LinkSketch");
|
||||||
if(!dialog->RunModal()) return;
|
if(!dialog->RunModal()) return;
|
||||||
dialog->FreezeChoices(settings, "LinkSketch");
|
dialog->FreezeChoices(settings, "LinkSketch");
|
||||||
|
|
|
@ -0,0 +1,500 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Intermediate Data Format (IDF) file reader. Reads an IDF file for PCB outlines and creates
|
||||||
|
// an equivalent SovleSpace sketch/extrusion. Supports only Linking, not import.
|
||||||
|
// Part placement is not currently supported.
|
||||||
|
//
|
||||||
|
// Copyright 2020 Paul Kahler.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "solvespace.h"
|
||||||
|
#include "sketch.h"
|
||||||
|
|
||||||
|
// Split a string into substrings separated by spaces.
|
||||||
|
// Allow quotes to enclose spaces within a string
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Functions for linking an IDF file - we need to create entites that
|
||||||
|
// get remapped into a linked group similar to linking .slvs files
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Make a new point - type doesn't matter since we will make a copy later
|
||||||
|
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) {
|
||||||
|
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 = false;
|
||||||
|
en.style.v = 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) {
|
||||||
|
// normals have parameters, but we don't need them to make a NORMAL_N_COPY from this
|
||||||
|
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;
|
||||||
|
// to be visible we need to add a point.
|
||||||
|
en.point[0] = newPoint(el, id, Vector::From(0,0,3), /*visible=*/ 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) {
|
||||||
|
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 = false;
|
||||||
|
en.style.v = 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) {
|
||||||
|
// normals have parameters, but we don't need them to make a NORMAL_N_COPY from this
|
||||||
|
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;
|
||||||
|
// to be visible we'll need to add a point?
|
||||||
|
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) {
|
||||||
|
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 = false;
|
||||||
|
en.style.v = 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) {
|
||||||
|
// locate the center of an arc
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an IDF line or arc to the entity list. According to spec, zero angle indicates a line.
|
||||||
|
// Positive angles are counter clockwise, negative are clockwise. An angle of 360
|
||||||
|
// indicates a circle centered at x1,y1 passing through x2,y2 and is a complete loop.
|
||||||
|
static void CreateEntity(EntityList *el, int *id, hEntity h0, hEntity h1, hEntity hnorm,
|
||||||
|
Vector p0, Vector p1, double angle) {
|
||||||
|
if (angle == 0.0) {
|
||||||
|
//line
|
||||||
|
if(p0.Equals(p1)) return;
|
||||||
|
|
||||||
|
newLine(el, id, h0, h1);
|
||||||
|
|
||||||
|
} else if(angle == 360.0) {
|
||||||
|
// circle
|
||||||
|
double d = p1.Minus(p0).Magnitude();
|
||||||
|
hEntity hd = newDistance(el, id, d);
|
||||||
|
newCircle(el, id, h1, hd, hnorm);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// arc
|
||||||
|
if(angle < 0.0) {
|
||||||
|
swap(p0,p1);
|
||||||
|
swap(h0,h1);
|
||||||
|
}
|
||||||
|
// locate the center of the arc
|
||||||
|
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, /*visible=*/false);
|
||||||
|
newArc(el, id, h0, h1, hc, hnorm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// borrowed from Entity::GenerateBezierCurves because we don't have parameters.
|
||||||
|
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 thetaa, thetab, dtheta;
|
||||||
|
|
||||||
|
if(angle == 360.0) {
|
||||||
|
thetaa = 0;
|
||||||
|
thetab = 2*PI;
|
||||||
|
dtheta = 2*PI;
|
||||||
|
} else {
|
||||||
|
Point2d c2 = center.Project2d(u, v);
|
||||||
|
Point2d pa2 = (pa.Project2d(u, v)).Minus(c2);
|
||||||
|
Point2d pb2 = (pb.Project2d(u, v)).Minus(c2);
|
||||||
|
|
||||||
|
thetaa = atan2(pa2.y, pa2.x);
|
||||||
|
thetab = atan2(pb2.y, pb2.x);
|
||||||
|
dtheta = thetab - thetaa;
|
||||||
|
}
|
||||||
|
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(thetaa);
|
||||||
|
s = sin(thetaa);
|
||||||
|
// The start point of the curve, and the tangent vector at
|
||||||
|
// that start point.
|
||||||
|
Vector p0 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
|
||||||
|
t0 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
|
||||||
|
|
||||||
|
thetaa += dtheta;
|
||||||
|
|
||||||
|
c = cos(thetaa);
|
||||||
|
s = sin(thetaa);
|
||||||
|
Vector p2 = center.Plus(u.ScaledBy( r*c)).Plus(v.ScaledBy(r*s)),
|
||||||
|
t2 = u.ScaledBy(-r*s). Plus(v.ScaledBy(r*c));
|
||||||
|
|
||||||
|
// The control point must lie on both tangents.
|
||||||
|
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 {
|
||||||
|
|
||||||
|
// Here we read the important section of an IDF file. SolveSpace Entities are directly created by
|
||||||
|
// the funcions above, which is only OK because of the way linking works. For example points do
|
||||||
|
// not have handles for solver parameters (coordinates), they only have their actPoint values
|
||||||
|
// set (or actNormal or actDistance). These are incompete entites and would be a problem if
|
||||||
|
// they were part of the sketch, but they are not. After making a list of them here, a new group
|
||||||
|
// gets created from copies of these. Those copies are complete and part of the sketch group.
|
||||||
|
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; //mm
|
||||||
|
|
||||||
|
Quaternion normal = Quaternion::From(Vector::From(1,0,0), Vector::From(0,1,0));
|
||||||
|
hEntity hnorm = newNormal(el, &entityCount, normal);
|
||||||
|
|
||||||
|
// to create the extursion we will need to collect a set of bezier curves defined
|
||||||
|
// by the perimeter, cutouts, and holes.
|
||||||
|
SBezierList sbl = {};
|
||||||
|
|
||||||
|
std::stringstream stream(data);
|
||||||
|
for(std::string line; getline( stream, line ); ) {
|
||||||
|
if (line.find(".END_") == 0) {
|
||||||
|
section = none;
|
||||||
|
}
|
||||||
|
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(".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 board_outline:
|
||||||
|
if (record_number == 2) {
|
||||||
|
board_thickness = std::stod(line) * scale;
|
||||||
|
dbp("IDF board thickness: %lf", board_thickness);
|
||||||
|
} else { // records 3+ are lines, arcs, and circles
|
||||||
|
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) { // start a new curve
|
||||||
|
curve = c;
|
||||||
|
hprev = newPoint(el, &entityCount, point, /*visible=*/false);
|
||||||
|
hprevTop = newPoint(el, &entityCount, pTop, /*visible=*/false);
|
||||||
|
pprev = point;
|
||||||
|
pprevTop = pTop;
|
||||||
|
} else {
|
||||||
|
// create a bezier for the extrusion
|
||||||
|
if (ang == 0) {
|
||||||
|
// straight lines
|
||||||
|
SBezier sb = SBezier::From(pprev, point);
|
||||||
|
sbl.l.Add(&sb);
|
||||||
|
} else if (ang != 360.0) {
|
||||||
|
// Arcs
|
||||||
|
Vector c = ArcCenter(pprev, point, ang);
|
||||||
|
MakeBeziersForArcs(&sbl, c, pprev, point, normal, ang);
|
||||||
|
} else {
|
||||||
|
// circles
|
||||||
|
MakeBeziersForArcs(&sbl, point, pprev, pprev, normal, ang);
|
||||||
|
}
|
||||||
|
// next create the entities
|
||||||
|
// only curves and points at circle centers will be visible
|
||||||
|
bool vis = (ang == 360.0);
|
||||||
|
hEntity hp = newPoint(el, &entityCount, point, /*visible=*/vis);
|
||||||
|
CreateEntity(el, &entityCount, hprev, hp, hnorm, pprev, point, ang);
|
||||||
|
pprev = point;
|
||||||
|
hprev = hp;
|
||||||
|
hp = newPoint(el, &entityCount, pTop, /*visible=*/vis);
|
||||||
|
CreateEntity(el, &entityCount, hprevTop, hp, hnorm, pprevTop, pTop, ang);
|
||||||
|
pprevTop = pTop;
|
||||||
|
hprevTop = hp;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case other_outline:
|
||||||
|
case routing_outline:
|
||||||
|
case placement_outline:
|
||||||
|
case routing_keepout:
|
||||||
|
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]);
|
||||||
|
// Only show holes likely to be useful in MCAD to reduce complexity.
|
||||||
|
if((d > 1.7) || (values[5].compare(0,3,"PIN") == 0)
|
||||||
|
|| (values[5].compare(0,3,"MTG") == 0)) {
|
||||||
|
// create the entity
|
||||||
|
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);
|
||||||
|
// and again for the top
|
||||||
|
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);
|
||||||
|
// create the curves for the extrusion
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
// now we can create an extrusion from all the Bezier curves. We can skip things
|
||||||
|
// like checking for a coplanar sketch because everything is at z=0.
|
||||||
|
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);
|
||||||
|
|
||||||
|
//hack for when there is no sketch yet and the first group is a linked IDF
|
||||||
|
double ctc = SS.chordTolCalculated;
|
||||||
|
if(ctc == 0.0) SS.chordTolCalculated = 0.1; //mm
|
||||||
|
// there should only by one sbls in the sblss unless a board has disjointed parts...
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -85,6 +85,11 @@ std::vector<FileFilter> SolveSpaceModelFileFilters = {
|
||||||
{ CN_("file-type", "SolveSpace models"), { "slvs" } },
|
{ CN_("file-type", "SolveSpace models"), { "slvs" } },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<FileFilter> SolveSpaceLinkFileFilters = {
|
||||||
|
{ CN_("file-type", "SolveSpace models"), { "slvs" } },
|
||||||
|
{ CN_("file-type", "IDF circuit board"), { "emn" } },
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<FileFilter> RasterFileFilters = {
|
std::vector<FileFilter> RasterFileFilters = {
|
||||||
{ CN_("file-type", "PNG image"), { "png" } },
|
{ CN_("file-type", "PNG image"), { "png" } },
|
||||||
};
|
};
|
||||||
|
|
|
@ -329,6 +329,8 @@ struct FileFilter {
|
||||||
|
|
||||||
// SolveSpace's native file format
|
// SolveSpace's native file format
|
||||||
extern std::vector<FileFilter> SolveSpaceModelFileFilters;
|
extern std::vector<FileFilter> SolveSpaceModelFileFilters;
|
||||||
|
// SolveSpace's linkable file formats
|
||||||
|
extern std::vector<FileFilter> SolveSpaceLinkFileFilters;
|
||||||
// Raster image
|
// Raster image
|
||||||
extern std::vector<FileFilter> RasterFileFilters;
|
extern std::vector<FileFilter> RasterFileFilters;
|
||||||
// Triangle mesh
|
// Triangle mesh
|
||||||
|
|
|
@ -679,6 +679,8 @@ public:
|
||||||
void UpgradeLegacyData();
|
void UpgradeLegacyData();
|
||||||
bool LoadEntitiesFromFile(const Platform::Path &filename, EntityList *le,
|
bool LoadEntitiesFromFile(const Platform::Path &filename, EntityList *le,
|
||||||
SMesh *m, SShell *sh);
|
SMesh *m, SShell *sh);
|
||||||
|
bool LoadEntitiesFromSlvs(const Platform::Path &filename, EntityList *le,
|
||||||
|
SMesh *m, SShell *sh);
|
||||||
bool ReloadAllLinked(const Platform::Path &filename, bool canCancel = false);
|
bool ReloadAllLinked(const Platform::Path &filename, bool canCancel = false);
|
||||||
// And the various export options
|
// And the various export options
|
||||||
void ExportAsPngTo(const Platform::Path &filename);
|
void ExportAsPngTo(const Platform::Path &filename);
|
||||||
|
@ -810,6 +812,7 @@ public:
|
||||||
|
|
||||||
void ImportDxf(const Platform::Path &file);
|
void ImportDxf(const Platform::Path &file);
|
||||||
void ImportDwg(const Platform::Path &file);
|
void ImportDwg(const Platform::Path &file);
|
||||||
|
bool LinkIDF(const Platform::Path &filename, EntityList *le, SMesh *m, SShell *sh);
|
||||||
|
|
||||||
extern SolveSpaceUI SS;
|
extern SolveSpaceUI SS;
|
||||||
extern Sketch SK;
|
extern Sketch SK;
|
||||||
|
|
Loading…
Reference in New Issue