From 4bd44bf18a4c0ce6a9968e159cb266cf655e6f81 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Thu, 29 May 2008 02:10:12 -0800 Subject: [PATCH] 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] --- entity.cpp | 16 ++++++- file.cpp | 111 ++++++++++++++++++++++++++++++++++++++++++++++--- sketch.cpp | 84 ++++++++++++++++++++++++++++++++----- sketch.h | 23 ++++++++-- solvespace.cpp | 16 ++++--- solvespace.h | 10 +++++ textwin.cpp | 4 ++ 7 files changed, 233 insertions(+), 31 deletions(-) diff --git a/entity.cpp b/entity.cpp index f0568312..f85a5018 100644 --- a/entity.cpp +++ b/entity.cpp @@ -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 *l) { } } +void Entity::CalculateNumerical(void) { + if(IsPoint()) actPoint = PointGetNum(); + if(IsNormal()) actNormal = NormalGetNum(); + if(type == DISTANCE || type == DISTANCE_N_COPY) { + actDistance = DistanceGetNum(); + } +} diff --git a/file.cpp b/file.cpp index b3dcc180..a2c9c3e7 100644 --- a/file.cpp +++ b/file.cpp @@ -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 *m = (IdList *)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(); + } + } +} + diff --git a/sketch.cpp b/sketch.cpp index fabafae5..b91bc374 100644 --- a/sketch.cpp +++ b/sketch.cpp @@ -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, 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, 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, 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, void Group::GenerateEquations(IdList *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 *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(); diff --git a/sketch.h b/sketch.h index dd334e87..0c106844 100644 --- a/sketch.h +++ b/sketch.h @@ -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 remap; + + char impFile[MAX_PATH]; + SMesh impMesh; + EntityList impEntity; + NameStr name; + + char *DescriptionString(void); static void AddParam(IdList *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 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 *l, Expr *expr, int index); void GenerateEquations(IdList *l); + void CalculateNumerical(void); + char *DescriptionString(void); }; diff --git a/solvespace.cpp b/solvespace.cpp index bc775a99..4a1e1dec 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -3,9 +3,10 @@ SolveSpace SS; void SolveSpace::Init(char *cmdLine) { - if(strlen(cmdLine) == 0) { - NewFile(); - } else { + NewFile(); + 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)) { diff --git a/solvespace.h b/solvespace.h index 5d5bcfbf..7b73deb2 100644 --- a/solvespace.h +++ b/solvespace.h @@ -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 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; diff --git a/textwin.cpp b/textwin.cpp index c970b1e7..4068fbcb 100644 --- a/textwin.cpp +++ b/textwin.cpp @@ -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) {