diff --git a/Makefile b/Makefile index 5e9874b..3cafb8e 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ SSOBJS = $(OBJDIR)\solvespace.obj \ $(OBJDIR)\graphicswin.obj \ $(OBJDIR)\modify.obj \ $(OBJDIR)\util.obj \ + $(OBJDIR)\style.obj \ $(OBJDIR)\entity.obj \ $(OBJDIR)\drawentity.obj \ $(OBJDIR)\group.obj \ diff --git a/file.cpp b/file.cpp index 11afb7c..353b0d5 100644 --- a/file.cpp +++ b/file.cpp @@ -103,12 +103,14 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'r', "Request.workplane.v", 'x', &(SS.sv.r.workplane.v) }, { 'r', "Request.group.v", 'x', &(SS.sv.r.group.v) }, { 'r', "Request.construction", 'b', &(SS.sv.r.construction) }, + { 'r', "Request.style", 'x', &(SS.sv.r.style) }, { 'r', "Request.str", 'N', &(SS.sv.r.str) }, { 'r', "Request.font", 'N', &(SS.sv.r.font) }, { 'e', "Entity.h.v", 'x', &(SS.sv.e.h.v) }, { 'e', "Entity.type", 'd', &(SS.sv.e.type) }, { 'e', "Entity.construction", 'b', &(SS.sv.e.construction) }, + { 'e', "Entity.style", 'x', &(SS.sv.e.style) }, { 'e', "Entity.str", 'N', &(SS.sv.e.str) }, { 'e', "Entity.font", 'N', &(SS.sv.e.font) }, { 'e', "Entity.point[0].v", 'x', &(SS.sv.e.point[0].v) }, @@ -146,6 +148,7 @@ const SolveSpace::SaveTable SolveSpace::SAVED[] = { { 'c', "Constraint.disp.offset.x", 'f', &(SS.sv.c.disp.offset.x) }, { 'c', "Constraint.disp.offset.y", 'f', &(SS.sv.c.disp.offset.y) }, { 'c', "Constraint.disp.offset.z", 'f', &(SS.sv.c.disp.offset.z) }, + { 'c', "Constraint.disp.style", 'x', &(SS.sv.c.disp.style) }, { 0, NULL, NULL, NULL }, }; diff --git a/groupmesh.cpp b/groupmesh.cpp index ed24a42..b7d9512 100644 --- a/groupmesh.cpp +++ b/groupmesh.cpp @@ -75,12 +75,11 @@ void SMesh::RemapFaces(Group *g, int remap) { } template -void Group::GenerateForStepAndRepeat(T *prevs, T *steps, T *outs, int how) { +void Group::GenerateForStepAndRepeat(T *steps, T *outs) { T workA, workB; ZERO(&workA); ZERO(&workB); T *soFar = &workA, *scratch = &workB; - soFar->MakeFromCopyOf(prevs); int n = (int)valA, a0 = 0; if(subtype == ONE_SIDED && skipFirst) { @@ -111,15 +110,14 @@ void Group::GenerateForStepAndRepeat(T *prevs, T *steps, T *outs, int how) { // We need to rewrite any plane face entities to the transformed ones. transd.RemapFaces(this, remap); - if(how == COMBINE_AS_DIFFERENCE) { - scratch->MakeFromDifferenceOf(soFar, &transd); - } else if(how == COMBINE_AS_UNION) { - scratch->MakeFromUnionOf(soFar, &transd); + // And tack this transformed copy on to the return. + if(soFar->IsEmpty()) { + scratch->MakeFromCopyOf(&transd); } else { - scratch->MakeFromAssemblyOf(soFar, &transd); + scratch->MakeFromUnionOf(soFar, &transd); } - SWAP(T *, scratch, soFar); + SWAP(T *, scratch, soFar); scratch->Clear(); transd.Clear(); } @@ -129,7 +127,7 @@ void Group::GenerateForStepAndRepeat(T *prevs, T *steps, T *outs, int how) { } template -void Group::GenerateForBoolean(T *prevs, T *thiss, T *outs) { +void Group::GenerateForBoolean(T *prevs, T *thiss, T *outs, int how) { // If this group contributes no new mesh, then our running mesh is the // same as last time, no combining required. Likewise if we have a mesh // but it's suppressed. @@ -140,9 +138,9 @@ void Group::GenerateForBoolean(T *prevs, T *thiss, T *outs) { // So our group's shell appears in thisShell. Combine this with the // previous group's shell, using the requested operation. - if(meshCombine == COMBINE_AS_UNION) { + if(how == COMBINE_AS_UNION) { outs->MakeFromUnionOf(prevs, thiss); - } else if(meshCombine == COMBINE_AS_DIFFERENCE) { + } else if(how == COMBINE_AS_DIFFERENCE) { outs->MakeFromDifferenceOf(prevs, thiss); } else { outs->MakeFromAssemblyOf(prevs, thiss); @@ -153,63 +151,21 @@ void Group::GenerateShellAndMesh(void) { bool prevBooleanFailed = booleanFailed; booleanFailed = false; + Group *srcg = this; + thisShell.Clear(); thisMesh.Clear(); runningShell.Clear(); runningMesh.Clear(); if(type == TRANSLATE || type == ROTATE) { - Group *src = SK.GetGroup(opA); - Group *pg = src->PreviousGroup(); + // A step and repeat gets merged against the group's prevous group, + // not our own previous group. + srcg = SK.GetGroup(opA); - if(src->thisMesh.IsEmpty() && pg->runningMesh.IsEmpty() && !forceToMesh) - { - SShell *toStep = &(src->thisShell), - *prev = &(pg->runningShell); - - // This isn't used, but it makes sure the display and calculation - // of our shell doesn't get optimized out because it looks like - // we contribute no solid model. - thisShell.MakeFromCopyOf(toStep); - - GenerateForStepAndRepeat - (prev, toStep, &runningShell, src->meshCombine); - if(meshCombine != COMBINE_AS_ASSEMBLE) { - runningShell.MergeCoincidentSurfaces(); - } - } else { - SMesh prevm, stepm; - ZERO(&prevm); - ZERO(&stepm); - - prevm.MakeFromCopyOf(&(pg->runningMesh)); - pg->runningShell.TriangulateInto(&prevm); - // Setting thisMesh for same reasons as thisShell above. - thisMesh.MakeFromCopyOf(&prevm); - - stepm.MakeFromCopyOf(&(src->thisMesh)); - src->thisShell.TriangulateInto(&stepm); - - SMesh outm; - ZERO(&outm); - GenerateForStepAndRepeat - (&prevm, &stepm, &outm, src->meshCombine); - - // And make sure that the output mesh is vertex-to-vertex. - SKdNode *root = SKdNode::From(&outm); - root->SnapToMesh(&outm); - root->MakeMeshInto(&runningMesh); - - outm.Clear(); - stepm.Clear(); - prevm.Clear(); - } - - displayDirty = true; - return; - } - - if(type == EXTRUDE) { + GenerateForStepAndRepeat(&(srcg->thisShell), &thisShell); + GenerateForStepAndRepeat (&(srcg->thisMesh), &thisMesh); + } else if(type == EXTRUDE) { Group *src = SK.GetGroup(opA); Vector translate = Vector::From(h.param(0), h.param(1), h.param(2)); @@ -298,7 +254,7 @@ void Group::GenerateShellAndMesh(void) { thisShell.RemapFaces(this, 0); } - if(meshCombine != COMBINE_AS_ASSEMBLE) { + if(srcg->meshCombine != COMBINE_AS_ASSEMBLE) { thisShell.MergeCoincidentSurfaces(); } @@ -306,12 +262,14 @@ void Group::GenerateShellAndMesh(void) { // the previous group's mesh or shell with the requested Boolean, and // we're done. - Group *pg = PreviousGroup(); - if(pg->runningMesh.IsEmpty() && thisMesh.IsEmpty() && !forceToMesh) { - SShell *prevs = &(pg->runningShell); - GenerateForBoolean(prevs, &thisShell, &runningShell); + Group *prevg = srcg->RunningMeshGroup(); - if(meshCombine != COMBINE_AS_ASSEMBLE) { + if(prevg->runningMesh.IsEmpty() && thisMesh.IsEmpty() && !forceToMesh) { + SShell *prevs = &(prevg->runningShell); + GenerateForBoolean(prevs, &thisShell, &runningShell, + srcg->meshCombine); + + if(srcg->meshCombine != COMBINE_AS_ASSEMBLE) { runningShell.MergeCoincidentSurfaces(); } @@ -326,15 +284,15 @@ void Group::GenerateShellAndMesh(void) { ZERO(&prevm); ZERO(&thism); - prevm.MakeFromCopyOf(&(pg->runningMesh)); - pg->runningShell.TriangulateInto(&prevm); + prevm.MakeFromCopyOf(&(prevg->runningMesh)); + prevg->runningShell.TriangulateInto(&prevm); thism.MakeFromCopyOf(&thisMesh); thisShell.TriangulateInto(&thism); SMesh outm; ZERO(&outm); - GenerateForBoolean(&prevm, &thism, &outm); + GenerateForBoolean(&prevm, &thism, &outm, srcg->meshCombine); // And make sure that the output mesh is vertex-to-vertex. SKdNode *root = SKdNode::From(&outm); @@ -387,6 +345,14 @@ Group *Group::PreviousGroup(void) { return &(SK.group.elem[i-1]); } +Group *Group::RunningMeshGroup(void) { + if(type == TRANSLATE || type == ROTATE) { + return SK.GetGroup(opA)->RunningMeshGroup(); + } else { + return PreviousGroup(); + } +} + void Group::DrawDisplayItems(int t) { int specColor; if(t == DRAWING_3D || t == DRAWING_WORKPLANE) { @@ -432,7 +398,7 @@ void Group::Draw(void) { // can control this stuff independently, with show/hide solids, edges, // mesh, etc. - Group *pg = PreviousGroup(); + Group *pg = RunningMeshGroup(); if(pg && thisMesh.IsEmpty() && thisShell.IsEmpty()) { // We don't contribute any new solid model in this group, so our // display items are identical to the previous group's; which means diff --git a/sketch.h b/sketch.h index e47ba78..e0683c2 100644 --- a/sketch.h +++ b/sketch.h @@ -6,14 +6,15 @@ class hGroup; class hRequest; class hEntity; class hParam; +class hStyle; +class hConstraint; +class hEquation; class Entity; class Param; - -class hConstraint; -class hEquation; class Equation; + // All of the hWhatever handles are a 32-bit ID, that is used to represent // some data structure in the sketch. class hGroup { @@ -55,6 +56,11 @@ public: inline hRequest request(void); }; +class hStyle { +public: + DWORD v; +}; + class EntityId { public: @@ -210,9 +216,10 @@ public: void GenerateLoops(void); // And the mesh stuff Group *PreviousGroup(void); + Group *RunningMeshGroup(void); void GenerateShellAndMesh(void); - template void GenerateForStepAndRepeat(T *a, T *b, T *o, int how); - template void GenerateForBoolean(T *a, T *b, T *o); + template void GenerateForStepAndRepeat(T *steps, T *outs); + template void GenerateForBoolean(T *a, T *b, T *o, int how); void GenerateDisplayItems(void); void DrawDisplayItems(int t); void Draw(void); @@ -247,6 +254,7 @@ public: hEntity workplane; // or Entity::FREE_IN_3D hGroup group; + hStyle style; bool construction; NameStr str; @@ -395,6 +403,7 @@ public: // and the shown state also gets saved here, for later import bool actVisible; + hStyle style; bool construction; // Routines to draw and hit-test the representation of the entity @@ -523,6 +532,7 @@ public: // These define how the constraint is drawn on-screen. struct { Vector offset; + hStyle style; } disp; // State for drawing or getting distance (for hit testing). @@ -583,6 +593,32 @@ public: }; +class Style { + int tag; + hStyle h; + + static const int ACTIVE = 1; + static const int CONSTRUCTION = 2; + static const int INACTIVE = 3; + static const int DATUM = 4; + static const int SOLID_EDGE = 5; + static const int CONSTRAINT = 6; + static const int SELECTED = 7; + static const int HOVERED = 8; + + static const int FIRST_CUSTOM = 0x1000; + + static const int WIDTH_ABSOLUTE = 0; + static const int WIDTH_RELATIVE = 1; + static const int WIDTH_PIXELS = 2; + double width; + int widthHow; + DWORD color; + bool visible; + bool exportable; +}; + + inline hEntity hGroup::entity(int i) { hEntity r; r.v = 0x80000000 | (v << 16) | i; return r; } inline hParam hGroup::param(int i) diff --git a/style.cpp b/style.cpp new file mode 100644 index 0000000..aeebeb5 --- /dev/null +++ b/style.cpp @@ -0,0 +1,5 @@ +#include "solvespace.h" + +//DWORD Style::Color(hStyle h) { +//} + diff --git a/util.cpp b/util.cpp index f22c5b4..9a462a7 100644 --- a/util.cpp +++ b/util.cpp @@ -518,7 +518,7 @@ Vector Vector::WithMagnitude(double v) { if(m == 0) { // We can do a zero vector with zero magnitude, but not any other cases. if(fabs(v) > 1e-100) { - dbp("Vector::WithMagnitude of zero vector!"); + dbp("Vector::WithMagnitude(%g) of zero vector!", v); } return From(0, 0, 0); } else { diff --git a/wishlist.txt b/wishlist.txt index 566f30a..4eb82c9 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,12 +1,18 @@ grid -associative entities from solid model, as a special group -rounding, as a special group +line styles (color, thickness) +margins in exported vector art +background color setting +better text +better drawing of dimensions +faster triangulation +SpaceNavigator support ----- -line styles (color, thickness) loop detection IGES export incremental regen of entities +associative entities from solid model, as a special group +rounding, as a special group