Initial work on the assembly (`import') feature. I can import a

part, including all of its entities and the triangle mesh. These
are transformed by a rotation and translation, and appear in the
sketch; the transformation may be set with constraints.

[git-p4: depot-paths = "//depot/solvespace/": change = 1756]
solver
Jonathan Westhues 2008-05-29 02:10:12 -08:00
parent d5a8c431da
commit 4bd44bf18a
7 changed files with 233 additions and 31 deletions

View File

@ -240,8 +240,15 @@ void Entity::NormalForceTo(Quaternion q) {
// There's absolutely nothing to do; these are locked.
break;
case NORMAL_N_ROT:
case NORMAL_N_ROT: {
Quaternion qp = q.Times(numNormal.Inverse());
SS.GetParam(param[0])->val = qp.w;
SS.GetParam(param[1])->val = qp.vx;
SS.GetParam(param[2])->val = qp.vy;
SS.GetParam(param[3])->val = qp.vz;
break;
}
default: oops();
}
@ -852,4 +859,11 @@ void Entity::GenerateEquations(IdList<Equation,hEquation> *l) {
}
}
void Entity::CalculateNumerical(void) {
if(IsPoint()) actPoint = PointGetNum();
if(IsNormal()) actNormal = NormalGetNum();
if(type == DISTANCE || type == DISTANCE_N_COPY) {
actDistance = DistanceGetNum();
}
}

111
file.cpp
View File

@ -1,5 +1,7 @@
#include "solvespace.h"
#define VERSION_STRING "±²³SolveSpaceREVa"
void SolveSpace::NewFile(void) {
constraint.Clear();
request.Clear();
@ -54,7 +56,6 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 'g', "Group.name", 'N', &(SS.sv.g.name) },
{ 'g', "Group.activeWorkplane.v", 'x', &(SS.sv.g.activeWorkplane.v) },
{ 'g', "Group.opA.v", 'x', &(SS.sv.g.opA.v) },
{ 'g', "Group.opB.v", 'x', &(SS.sv.g.opB.v) },
{ 'g', "Group.exprA", 'E', &(SS.sv.g.exprA) },
{ 'g', "Group.subtype", 'd', &(SS.sv.g.subtype) },
{ 'g', "Group.meshCombine", 'd', &(SS.sv.g.meshCombine) },
@ -70,6 +71,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 'g', "Group.wrkpl.negateV", 'b', &(SS.sv.g.wrkpl.negateV) },
{ 'g', "Group.visible", 'b', &(SS.sv.g.visible) },
{ 'g', "Group.remap", 'M', &(SS.sv.g.remap) },
{ 'g', "Group.impFile", 'P', &(SS.sv.g.impFile) },
{ 'p', "Param.h.v.", 'x', &(SS.sv.p.h.v) },
{ 'p', "Param.val", 'f', &(SS.sv.p.val) },
@ -107,6 +109,15 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = {
{ 'e', "Entity.numNormal.vy", 'f', &(SS.sv.e.numNormal.vy) },
{ 'e', "Entity.numNormal.vz", 'f', &(SS.sv.e.numNormal.vz) },
{ 'e', "Entity.numDistance", 'f', &(SS.sv.e.numDistance) },
{ 'e', "Entity.actPoint.x", 'f', &(SS.sv.e.actPoint.x) },
{ 'e', "Entity.actPoint.y", 'f', &(SS.sv.e.actPoint.y) },
{ 'e', "Entity.actPoint.z", 'f', &(SS.sv.e.actPoint.z) },
{ 'e', "Entity.actNormal.w", 'f', &(SS.sv.e.actNormal.w) },
{ 'e', "Entity.actNormal.vx", 'f', &(SS.sv.e.actNormal.vx) },
{ 'e', "Entity.actNormal.vy", 'f', &(SS.sv.e.actNormal.vy) },
{ 'e', "Entity.actNormal.vz", 'f', &(SS.sv.e.actNormal.vz) },
{ 'e', "Entity.actDistance", 'f', &(SS.sv.e.actDistance) },
{ 'c', "Constraint.h.v", 'x', &(SS.sv.c.h.v) },
{ 'c', "Constraint.type", 'd', &(SS.sv.c.type) },
@ -131,14 +142,22 @@ void SolveSpace::SaveUsingTable(int type) {
int i;
for(i = 0; SAVED[i].type != 0; i++) {
if(SAVED[i].type != type) continue;
fprintf(fh, "%s=", SAVED[i].desc);
int fmt = SAVED[i].fmt;
void *p = SAVED[i].ptr;
switch(SAVED[i].fmt) {
// Any items that aren't specified are assumed to be zero
if(fmt == 'd' && *((int *)p) == 0) continue;
if(fmt == 'x' && *((DWORD *)p) == 0) continue;
if(fmt == 'f' && *((double *)p) == 0.0) continue;
fprintf(fh, "%s=", SAVED[i].desc);
switch(fmt) {
case 'd': fprintf(fh, "%d", *((int *)p)); break;
case 'b': fprintf(fh, "%d", *((bool *)p) ? 1 : 0); break;
case 'x': fprintf(fh, "%08x", *((DWORD *)p)); break;
case 'f': fprintf(fh, "%.20f", *((double *)p)); break;
case 'N': fprintf(fh, "%s", ((NameStr *)p)->str); break;
case 'P': fprintf(fh, "%s", (char *)p); break;
case 'E': fprintf(fh, "%s", (*((Expr **)p))->Print()); break;
case 'M': {
@ -167,7 +186,7 @@ bool SolveSpace::SaveToFile(char *filename) {
return false;
}
fprintf(fh, "ñ÷åò±²³´SolveSpaceREVa\n\n\n");
fprintf(fh, "%s\n\n\n", VERSION_STRING);
int i;
for(i = 0; i < group.n; i++) {
@ -189,6 +208,7 @@ bool SolveSpace::SaveToFile(char *filename) {
}
for(i = 0; i < entity.n; i++) {
(entity.elem[i]).CalculateNumerical();
sv.e = entity.elem[i];
SaveUsingTable('e');
fprintf(fh, "AddEntity\n\n");
@ -200,6 +220,14 @@ bool SolveSpace::SaveToFile(char *filename) {
fprintf(fh, "AddConstraint\n\n");
}
SMesh *m = &(group.elem[group.n-1].mesh);
for(i = 0; i < m->l.n; i++) {
STriangle *tr = &(m->l.elem[i]);
fprintf(fh, "Triangle "
"%.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f\n",
CO(tr->a), CO(tr->b), CO(tr->c));
}
fclose(fh);
return true;
@ -223,6 +251,10 @@ void SolveSpace::LoadUsingTable(char *key, char *val) {
*((Expr **)p) = e->DeepCopyKeep();
break;
case 'P':
if(strlen(val)+1 < MAX_PATH) strcpy((char *)p, val);
break;
case 'M': {
IdList<EntityMap,EntityId> *m =
(IdList<EntityMap,EntityId> *)p;
@ -264,9 +296,9 @@ bool SolveSpace::LoadFromFile(char *filename) {
constraint.Clear();
request.Clear();
group.Clear();
entity.Clear();
param.Clear();
memset(&sv, 0, sizeof(sv));
char line[1024];
while(fgets(line, sizeof(line), fh)) {
@ -296,8 +328,10 @@ bool SolveSpace::LoadFromFile(char *filename) {
} else if(strcmp(line, "AddConstraint")==0) {
SS.constraint.Add(&(sv.c));
memset(&(sv.c), 0, sizeof(sv.c));
} else if(strcmp(line, "ñ÷åò±²³´SolveSpaceREVa")==0) {
} else if(strcmp(line, VERSION_STRING)==0) {
// do nothing, version string
} else if(memcmp(line, "Triangle", 8)==0) {
// likewise ignore the triangles; we generate those
} else {
oops();
}
@ -308,3 +342,68 @@ bool SolveSpace::LoadFromFile(char *filename) {
return true;
}
bool SolveSpace::LoadEntitiesFromFile(char *file, EntityList *le, SMesh *m) {
fh = fopen(file, "r");
if(!fh) return false;
le->Clear();
memset(&sv, 0, sizeof(sv));
char line[1024];
while(fgets(line, sizeof(line), fh)) {
char *s = strchr(line, '\n');
if(s) *s = '\0';
if(*line == '\0') continue;
char *e = strchr(line, '=');
if(e) {
*e = '\0';
char *key = line, *val = e+1;
LoadUsingTable(key, val);
} else if(strcmp(line, "AddGroup")==0) {
} else if(strcmp(line, "AddParam")==0) {
} else if(strcmp(line, "AddEntity")==0) {
le->Add(&(sv.e));
memset(&(sv.e), 0, sizeof(sv.e));
} else if(strcmp(line, "AddRequest")==0) {
} else if(strcmp(line, "AddConstraint")==0) {
} else if(strcmp(line, VERSION_STRING)==0) {
} else if(memcmp(line, "Triangle", 8)==0) {
STriangle tr; ZERO(&tr);
if(sscanf(line, "Triangle %lf %lf %lf %lf %lf %lf %lf %lf %lf",
&(tr.a.x), &(tr.a.y), &(tr.a.z),
&(tr.b.x), &(tr.b.y), &(tr.b.z),
&(tr.c.x), &(tr.c.y), &(tr.c.z)) != 9)
{
oops();
}
m->AddTriangle(&tr);
} else {
oops();
}
}
fclose(fh);
return true;
}
void SolveSpace::ReloadAllImported(void) {
int i;
for(i = 0; i < group.n; i++) {
Group *g = &(group.elem[i]);
if(g->type != Group::IMPORTED) continue;
g->impEntity.Clear();
g->impMesh.Clear();
if(!LoadEntitiesFromFile(g->impFile, &(g->impEntity), &(g->impMesh))) {
oops();
}
}
}

View File

@ -24,6 +24,11 @@ void Group::MenuGroup(int id) {
memset(&g, 0, sizeof(g));
g.visible = true;
if(id >= RECENT_IMPORT && id < (RECENT_IMPORT + MAX_RECENT)) {
strcpy(g.impFile, RecentFile[id-RECENT_IMPORT]);
id = GraphicsWindow::MNU_GROUP_IMPORT;
}
SS.GW.GroupSelection();
#define gs (SS.GW.gs)
@ -95,10 +100,23 @@ void Group::MenuGroup(int id) {
g.name.strcpy("translate");
break;
case GraphicsWindow::MNU_GROUP_IMPORT: {
g.type = IMPORTED;
g.opA = SS.GW.activeGroup;
if(strlen(g.impFile) == 0) {
if(!GetOpenFile(g.impFile, SLVS_EXT, SLVS_PATTERN)) return;
}
g.name.strcpy("import");
break;
}
default: oops();
}
SS.group.AddAndAssignId(&g);
if(g.type == IMPORTED) {
SS.ReloadAllImported();
}
SS.GenerateAll(SS.GW.solving == GraphicsWindow::SOLVE_ALWAYS);
SS.GW.activeGroup = g.h;
if(g.type == DRAWING_WORKPLANE) {
@ -191,14 +209,15 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
Entity *e = &(entity->elem[i]);
if(e->group.v != opA.v) continue;
e->CalculateNumerical();
hEntity he = e->h; e = NULL;
// As soon as I call CopyEntity, e may become invalid! That
// adds entities, which may cause a realloc.
CopyEntity(he, ai,
CopyEntity(SS.GetEntity(he), ai,
h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
true);
CopyEntity(he, af,
CopyEntity(SS.GetEntity(he), af,
h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
true);
@ -218,7 +237,8 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
Entity *e = &(entity->elem[i]);
if(e->group.v != opA.v) continue;
CopyEntity(e->h, a*2 - (subtype == ONE_SIDED ? 0 : (n-1)),
e->CalculateNumerical();
CopyEntity(e, a*2 - (subtype == ONE_SIDED ? 0 : (n-1)),
h.param(0), h.param(1), h.param(2),
NO_PARAM, NO_PARAM, NO_PARAM, NO_PARAM,
true);
@ -241,7 +261,29 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
Entity *e = &(entity->elem[i]);
if(e->group.v != opA.v) continue;
CopyEntity(e->h, 0,
e->CalculateNumerical();
CopyEntity(e, 0,
h.param(0), h.param(1), h.param(2),
h.param(3), h.param(4), h.param(5), h.param(6),
false);
}
break;
case IMPORTED:
// The translation vector
AddParam(param, h.param(0), gp.x);
AddParam(param, h.param(1), gp.y);
AddParam(param, h.param(2), gp.z);
// The rotation quaternion
AddParam(param, h.param(3), 1);
AddParam(param, h.param(4), 0);
AddParam(param, h.param(5), 0);
AddParam(param, h.param(6), 0);
for(i = 0; i < impEntity.n; i++) {
Entity *ie = &(impEntity.elem[i]);
CopyEntity(ie, 0,
h.param(0), h.param(1), h.param(2),
h.param(3), h.param(4), h.param(5), h.param(6),
false);
@ -254,7 +296,7 @@ void Group::Generate(IdList<Entity,hEntity> *entity,
void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
Equation eq;
if(type == ROTATE) {
if(type == ROTATE || type == IMPORTED) {
// Normalize the quaternion
ExprQuaternion q = {
Expr::FromParam(h.param(3)),
@ -266,6 +308,8 @@ void Group::GenerateEquations(IdList<Equation,hEquation> *l) {
l->Add(&eq);
} else if(type == EXTRUDE) {
if(wrkpl.entityB.v != Entity::FREE_IN_3D.v) {
// The extrusion path is locked along a line, normal to the
// specified workplane.
Entity *w = SS.GetEntity(wrkpl.entityB);
ExprVector u = w->Normal()->NormalExprsU();
ExprVector v = w->Normal()->NormalExprsV();
@ -315,12 +359,10 @@ void Group::MakeExtrusionLines(hEntity in, int ai, int af) {
SS.entity.Add(&en);
}
void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz,
void Group::CopyEntity(Entity *ep, int a, hParam dx, hParam dy, hParam dz,
hParam qw, hParam qvx, hParam qvy, hParam qvz,
bool transOnly)
{
Entity *ep = SS.GetEntity(in);
Entity en;
memset(&en, 0, sizeof(en));
en.type = ep->type;
@ -378,7 +420,7 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz,
en.param[5] = qvy;
en.param[6] = qvz;
}
en.numPoint = ep->PointGetNum();
en.numPoint = ep->actPoint;
en.timesApplied = a;
break;
@ -395,7 +437,7 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz,
en.param[2] = qvy;
en.param[3] = qvz;
}
en.numNormal = ep->NormalGetNum();
en.numNormal = ep->actNormal;
en.point[0] = Remap(ep->point[0], a);
en.timesApplied = a;
break;
@ -403,7 +445,7 @@ void Group::CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz,
case Entity::DISTANCE_N_COPY:
case Entity::DISTANCE:
en.type = Entity::DISTANCE_N_COPY;
en.numDistance = ep->DistanceGetNum();
en.numDistance = ep->actDistance;
break;
default:
@ -512,6 +554,26 @@ void Group::MakePolygons(void) {
outm.AddTriangle(bbot, btop, atop);
}
}
} else if(type == IMPORTED) {
// Triangles are just copied over, with the appropriate transformation
// applied.
Vector offset = {
SS.GetParam(h.param(0))->val,
SS.GetParam(h.param(1))->val,
SS.GetParam(h.param(2))->val };
Quaternion q = {
SS.GetParam(h.param(3))->val,
SS.GetParam(h.param(4))->val,
SS.GetParam(h.param(5))->val,
SS.GetParam(h.param(6))->val };
for(int i = 0; i < impMesh.l.n; i++) {
STriangle st = impMesh.l.elem[i];
st.a = q.Rotate(st.a).Plus(offset);
st.b = q.Rotate(st.b).Plus(offset);
st.c = q.Rotate(st.c).Plus(offset);
outm.AddTriangle(&st);
}
}
edges.Clear();

View File

@ -87,7 +87,6 @@ public:
int type;
hGroup opA;
hGroup opB;
bool visible;
hEntity activeWorkplane;
Expr *exprA;
@ -127,7 +126,15 @@ public:
static const int COMBINE_AS_DIFFERENCE = 1;
int meshCombine;
IdList<EntityMap,EntityId> remap;
char impFile[MAX_PATH];
SMesh impMesh;
EntityList impEntity;
NameStr name;
char *DescriptionString(void);
static void AddParam(IdList<Param,hParam> *param, hParam hp, double v);
@ -136,10 +143,9 @@ public:
// entities may have come from multiple requests, it's necessary to
// remap the entity ID so that it's still unique. We do this with a
// mapping list.
IdList<EntityMap,EntityId> remap;
hEntity Remap(hEntity in, int copyNumber);
void MakeExtrusionLines(hEntity in, int ai, int af);
void CopyEntity(hEntity in, int a, hParam dx, hParam dy, hParam dz,
void CopyEntity(Entity *ep, int a, hParam dx, hParam dy, hParam dz,
hParam qw, hParam qvx, hParam qvy, hParam qvz,
bool transOnly);
@ -235,11 +241,18 @@ public:
// and directions.
hParam param[7];
// Transformed points/normals/distances have their numerical value.
// Transformed points/normals/distances have their numerical base
Vector numPoint;
Quaternion numNormal;
double numDistance;
// All points/normals/distances have their numerical value; this is
// a convenience, to simplify the import/assembly code, so that the
// part is entirely described by the entities.
Vector actPoint;
Quaternion actNormal;
double actDistance;
hGroup group;
hEntity workplane; // or Entity::FREE_IN_3D
@ -316,6 +329,8 @@ public:
void AddEq(IdList<Equation,hEquation> *l, Expr *expr, int index);
void GenerateEquations(IdList<Equation,hEquation> *l);
void CalculateNumerical(void);
char *DescriptionString(void);
};

View File

@ -3,9 +3,10 @@
SolveSpace SS;
void SolveSpace::Init(char *cmdLine) {
if(strlen(cmdLine) == 0) {
NewFile();
} else {
AfterNewFile();
if(strlen(cmdLine) != 0) {
if(LoadFromFile(cmdLine)) {
strcpy(saveFile, cmdLine);
} else {
@ -17,6 +18,7 @@ void SolveSpace::Init(char *cmdLine) {
}
void SolveSpace::AfterNewFile(void) {
ReloadAllImported();
GenerateAll(false, 0, INT_MAX);
TW.Init();
@ -78,7 +80,6 @@ bool SolveSpace::EntityExists(hEntity he) {
bool SolveSpace::PruneGroups(hGroup hg) {
Group *g = GetGroup(hg);
if(GroupsInOrder(g->opA, hg) &&
GroupsInOrder(g->opB, hg) &&
EntityExists(g->wrkpl.origin) &&
EntityExists(g->wrkpl.entityB) &&
EntityExists(g->wrkpl.entityC))
@ -334,9 +335,6 @@ void SolveSpace::AddToRecentList(char *file) {
}
void SolveSpace::MenuFile(int id) {
char *slvsPattern =
"SolveSpace Models (*.slvs)\0*.slvs\0All Files (*)\0*\0\0";
char *slvsExt = "slvs";
if(id >= RECENT_OPEN && id < (RECENT_OPEN+MAX_RECENT)) {
char newFile[MAX_PATH];
@ -362,7 +360,7 @@ void SolveSpace::MenuFile(int id) {
case GraphicsWindow::MNU_OPEN: {
char newFile[MAX_PATH] = "";
if(GetOpenFile(newFile, slvsExt, slvsPattern)) {
if(GetOpenFile(newFile, SLVS_EXT, SLVS_PATTERN)) {
if(SS.LoadFromFile(newFile)) {
strcpy(SS.saveFile, newFile);
AddToRecentList(newFile);
@ -380,7 +378,7 @@ void SolveSpace::MenuFile(int id) {
char newFile[MAX_PATH];
strcpy(newFile, SS.saveFile);
if(id == GraphicsWindow::MNU_SAVE_AS || strlen(newFile)==0) {
if(!GetSaveFile(newFile, slvsExt, slvsPattern)) break;
if(!GetSaveFile(newFile, SLVS_EXT, SLVS_PATTERN)) break;
}
if(SS.SaveToFile(newFile)) {

View File

@ -41,6 +41,7 @@ class Expr;
class ExprVector;
class ExprQuaternion;
// From the platform-specific code.
#define MAX_RECENT 8
#define RECENT_OPEN (0xf000)
@ -49,6 +50,8 @@ extern char RecentFile[MAX_RECENT][MAX_PATH];
void RefreshRecentMenus(void);
int SaveFileYesNoCancel(void);
#define SLVS_PATTERN "SolveSpace Models (*.slvs)\0*.slvs\0All Files (*)\0*\0\0"
#define SLVS_EXT "slvs"
BOOL GetSaveFile(char *file, char *defExtension, char *selPattern);
BOOL GetOpenFile(char *file, char *defExtension, char *selPattern);
@ -81,6 +84,11 @@ void vl(void); // debug function to validate
#include "dsc.h"
#include "polygon.h"
class Entity;
class hEntity;
typedef IdList<Entity,hEntity> EntityList;
#include "sketch.h"
#include "ui.h"
#include "expr.h"
@ -234,6 +242,8 @@ public:
void NewFile(void);
bool SaveToFile(char *filename);
bool LoadFromFile(char *filename);
bool LoadEntitiesFromFile(char *filename, EntityList *le, SMesh *m);
void ReloadAllImported(void);
struct {
int requests;

View File

@ -407,6 +407,10 @@ void TextWindow::ShowGroupInfo(void) {
g->DescriptionString(),
g->h.v, &TextWindow::ScreenChangeGroupName, s);
if(g->type == Group::IMPORTED) {
Printf(true, "%FtIMPORT %E '%s'", g->impFile);
}
if(g->type == Group::EXTRUDE) {
s = "EXTRUDE";
} else if(g->type == Group::TRANSLATE) {