Add feature to trace a point; so I can show the path that a
linkage traces out, and export the coordinates. [git-p4: depot-paths = "//depot/solvespace/": change = 1846]solver
parent
d2c4d2cdb7
commit
8fe910da4d
|
@ -801,6 +801,67 @@ for new users; to learn about this program, see the video tutorials.
|
|||
computer, it's necessary to transfer all of the imported files
|
||||
as well.
|
||||
|
||||
<h2>Analysis</h2>
|
||||
|
||||
<h3>Trace Point</h3>
|
||||
|
||||
MechSketch can draw a "trail" behind a point as it moves. This
|
||||
is useful when designing mechanisms. The sketch shown below is
|
||||
a four-bar linkage:
|
||||
|
||||
<img src="/pics/ref-point-traced.png" />
|
||||
|
||||
As the linkage is worked, the midpoint of the top link moves
|
||||
along the cyan curve. This image was produced by drawing the
|
||||
linkage, tracing that midpoint, and then dragging the linkage
|
||||
through its full range of motion with the mouse.
|
||||
|
||||
To start tracing, select a point, and choose Analyze -> Trace
|
||||
Point. When the point moves, a cyan trail will be drawn behind it.
|
||||
|
||||
To stop tracing, choose Analyze -> Stop Tracing. A dialog
|
||||
will appear, with the option to save the trace as a CSV
|
||||
(comma-separated value) file. To save the trace, enter a
|
||||
filename. To abandon the trace, choose Cancel or hit Esc.
|
||||
|
||||
The trace is saved as a text file, with one point per line. Each
|
||||
point appears in the format x, y, z, separated by commas. Many
|
||||
programs, including spreadsheets like Excel, can read this format.
|
||||
The units for the coordinates are determined by the export
|
||||
scale factor. (If the export scale factor is 1, then they are
|
||||
millimeters, and if it's 25.4, then they're inches.)
|
||||
|
||||
If the mechanism is worked by dragging it with the mouse, then
|
||||
the points in the trace will be unevenly spaced, because the
|
||||
motion of the mouse is irregular. A plot of x vs. y (like the
|
||||
cyan trace above) is not affected, but a plot of x or y vs. t
|
||||
is useless, because the "speed" along the curve is not constant.
|
||||
|
||||
To avoid this problem, move the point by stepping a dimension,
|
||||
rather than by dragging with the mouse. Select the dimension to
|
||||
be stepped; this can be any distance or angle. Choose Analyze ->
|
||||
Step Dimension. Enter the new final value for that dimension,
|
||||
and the number of steps; then click "step dimension now".
|
||||
|
||||
The dimension will be modified in multiple steps, and solved
|
||||
at each intermediate value. For example, consider a dimension
|
||||
that is now set to 10 degrees. The user steps this dimension to
|
||||
30 degrees, in 10 steps. This means that MechSketch will solve
|
||||
at 12 degrees, then 14 degrees, then 16, and so on, until it
|
||||
reaches 30 degrees.
|
||||
|
||||
The position of the traced point will be recorded at each
|
||||
intermediate value. When the trace is exported, it represents
|
||||
the position of that point, as the dimensioned link rotates with
|
||||
constant angular speed.
|
||||
|
||||
The step dimension feature can also improve convergence. In some
|
||||
difficult cases, the solver will fail to find a solution when a
|
||||
dimension is changed. If the specified constraints have multiple
|
||||
solutions, then the solver may also find an undesired solution. In
|
||||
this case, it may be useful to try stepping the dimension to
|
||||
its new value, instead of changing it in a single step.
|
||||
|
||||
|
||||
<h2>Export</h2>
|
||||
|
||||
|
|
20
draw.cpp
20
draw.cpp
|
@ -918,13 +918,7 @@ void GraphicsWindow::Paint(int w, int h) {
|
|||
// At the same depth, we want later lines drawn over earlier.
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
bool allOk = true;
|
||||
for(i = 0; i < SS.group.n; i++) {
|
||||
if(SS.group.elem[i].solved.how != Group::SOLVED_OKAY) {
|
||||
allOk = false;
|
||||
}
|
||||
}
|
||||
if(allOk) {
|
||||
if(SS.AllGroupsOkay()) {
|
||||
glClearColor(0, 0, 0, 1.0f);
|
||||
} else {
|
||||
// Draw a red background whenever we're having solve problems.
|
||||
|
@ -974,7 +968,7 @@ void GraphicsWindow::Paint(int w, int h) {
|
|||
|
||||
glxUnlockColor();
|
||||
|
||||
// Draw the groups; this fills the polygons in a drawing group, and
|
||||
// Draw the active group; this fills the polygons in a drawing group, and
|
||||
// draws the solid mesh.
|
||||
(SS.GetGroup(activeGroup))->Draw();
|
||||
|
||||
|
@ -988,6 +982,16 @@ void GraphicsWindow::Paint(int w, int h) {
|
|||
SS.constraint.elem[i].Draw();
|
||||
}
|
||||
|
||||
// Draw the traced path, if one exists
|
||||
glLineWidth(1);
|
||||
glColor3d(0, 1, 1);
|
||||
SContour *sc = &(SS.traced.path);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for(i = 0; i < sc->l.n; i++) {
|
||||
glxVertex3v(sc->l.elem[i].p);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
// Then redraw whatever the mouse is hovering over, highlighted.
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glxLockColorTo(1, 1, 0);
|
||||
|
|
24
generate.cpp
24
generate.cpp
|
@ -148,6 +148,9 @@ void SolveSpace::GenerateAll(void) {
|
|||
void SolveSpace::GenerateAll(int first, int last) {
|
||||
int i, j;
|
||||
|
||||
// Remove any requests or constraints that refer to a nonexistent
|
||||
// group; can check those immediately, since we know what the list
|
||||
// of groups should be.
|
||||
while(PruneOrphans())
|
||||
;
|
||||
|
||||
|
@ -222,6 +225,16 @@ void SolveSpace::GenerateAll(int first, int last) {
|
|||
}
|
||||
}
|
||||
|
||||
// Make sure the point that we're tracing exists.
|
||||
if(traced.point.v && !entity.FindByIdNoOops(traced.point)) {
|
||||
traced.point = Entity::NO_ENTITY;
|
||||
}
|
||||
// And if we're tracing a point, add its new value to the path
|
||||
if(traced.point.v) {
|
||||
Entity *pt = GetEntity(traced.point);
|
||||
traced.path.AddPoint(pt->PointGetNum());
|
||||
}
|
||||
|
||||
prev.Clear();
|
||||
InvalidateGraphics();
|
||||
|
||||
|
@ -325,3 +338,14 @@ void SolveSpace::SolveGroup(hGroup hg) {
|
|||
FreeAllTemporary();
|
||||
}
|
||||
|
||||
bool SolveSpace::AllGroupsOkay(void) {
|
||||
int i;
|
||||
bool allOk = true;
|
||||
for(i = 0; i < group.n; i++) {
|
||||
if(group.elem[i].solved.how != Group::SOLVED_OKAY) {
|
||||
allOk = false;
|
||||
}
|
||||
}
|
||||
return allOk;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define mCon (&Constraint::MenuConstrain)
|
||||
#define mFile (&SolveSpace::MenuFile)
|
||||
#define mGrp (&Group::MenuGroup)
|
||||
#define mAna (&SolveSpace::MenuAnalyze)
|
||||
#define S 0x100
|
||||
#define C 0x200
|
||||
const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
||||
|
@ -93,6 +94,13 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = {
|
|||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "Comment\t;", MNU_COMMENT, ';', mCon },
|
||||
|
||||
{ 0, "&Analyze", 0, NULL },
|
||||
{ 1, "Measure &Volume", MNU_VOLUME, 0, mAna },
|
||||
{ 1, NULL, 0, NULL },
|
||||
{ 1, "&Trace Point\tCtrl+Shift+T", MNU_TRACE_PT, 'T'|S|C,mAna },
|
||||
{ 1, "&Stop Tracing...\tCtrl+Shift+S", MNU_STOP_TRACING, 'S'|S|C,mAna },
|
||||
{ 1, "Step &Dimension...\tCtrl+Shift+D", MNU_STEP_DIM, 'D'|S|C,mAna },
|
||||
|
||||
{ 0, "&Help", 0, NULL },
|
||||
{ 1, "&Load License...", 0, NULL },
|
||||
{ 1, NULL, 0, NULL },
|
||||
|
|
|
@ -140,9 +140,20 @@ double SolveSpace::ExprToMm(Expr *e) {
|
|||
return e->Eval();
|
||||
}
|
||||
}
|
||||
double SolveSpace::StringToMm(char *str) {
|
||||
if(viewUnits == UNIT_INCHES) {
|
||||
return atof(str)*25.4;
|
||||
} else {
|
||||
return atof(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SolveSpace::AfterNewFile(void) {
|
||||
// Clear out the traced point, which is no longer valid
|
||||
traced.point = Entity::NO_ENTITY;
|
||||
traced.path.l.Clear();
|
||||
|
||||
ReloadAllImported();
|
||||
GenerateAll(-1, -1);
|
||||
|
||||
|
@ -318,3 +329,73 @@ void SolveSpace::MenuFile(int id) {
|
|||
|
||||
SS.UpdateWindowTitle();
|
||||
}
|
||||
|
||||
void SolveSpace::MenuAnalyze(int id) {
|
||||
SS.GW.GroupSelection();
|
||||
#define gs (SS.GW.gs)
|
||||
|
||||
switch(id) {
|
||||
case GraphicsWindow::MNU_STEP_DIM:
|
||||
if(gs.constraints == 1 && gs.n == 0) {
|
||||
Constraint *c = SS.GetConstraint(gs.constraint[0]);
|
||||
if(c->HasLabel() && !c->reference) {
|
||||
SS.TW.shown.dimFinish = c->valA;
|
||||
SS.TW.shown.dimSteps = 10;
|
||||
SS.TW.shown.dimIsDistance =
|
||||
(c->type != Constraint::ANGLE) &&
|
||||
(c->type != Constraint::LENGTH_RATIO);
|
||||
SS.TW.shown.constraint = c->h;
|
||||
SS.TW.shown.screen = TextWindow::SCREEN_STEP_DIMENSION;
|
||||
|
||||
SS.later.showTW = true;
|
||||
SS.GW.ClearSelection();
|
||||
} else {
|
||||
Error("Constraint must have a label, and must not be "
|
||||
"a reference dimension.");
|
||||
}
|
||||
} else {
|
||||
Error("Bad selection for step dimension; select a constraint.");
|
||||
}
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_VOLUME:
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_TRACE_PT:
|
||||
if(gs.points == 1 && gs.n == 1) {
|
||||
SS.traced.point = gs.point[0];
|
||||
SS.GW.ClearSelection();
|
||||
} else {
|
||||
Error("Bad selection for trace; select a single point.");
|
||||
}
|
||||
break;
|
||||
|
||||
case GraphicsWindow::MNU_STOP_TRACING: {
|
||||
char exportFile[MAX_PATH] = "";
|
||||
if(GetSaveFile(exportFile, CSV_EXT, CSV_PATTERN)) {
|
||||
FILE *f = fopen(exportFile, "w");
|
||||
if(f) {
|
||||
int i;
|
||||
SContour *sc = &(SS.traced.path);
|
||||
for(i = 0; i < sc->l.n; i++) {
|
||||
Vector p = sc->l.elem[i].p;
|
||||
double s = SS.exportScale;
|
||||
fprintf(f, "%.10f, %.10f, %.10f\n",
|
||||
p.x/s, p.y/s, p.z/s);
|
||||
}
|
||||
fclose(f);
|
||||
} else {
|
||||
Error("Couldn't write to '%s'", exportFile);
|
||||
}
|
||||
}
|
||||
// Clear the trace, and stop tracing
|
||||
SS.traced.point = Entity::NO_ENTITY;
|
||||
SS.traced.path.l.Clear();
|
||||
InvalidateGraphics();
|
||||
break;
|
||||
}
|
||||
|
||||
default: oops();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
11
solvespace.h
11
solvespace.h
|
@ -62,6 +62,8 @@ int SaveFileYesNoCancel(void);
|
|||
#define STL_EXT "stl"
|
||||
#define DXF_PATTERN "DXF File (*.dxf)\0*.dxf\0All Files (*)\0*\0\0"
|
||||
#define DXF_EXT "dxf"
|
||||
#define CSV_PATTERN "CSV File (*.csv)\0*.csv\0All Files (*)\0*\0\0"
|
||||
#define CSV_EXT "csv"
|
||||
BOOL GetSaveFile(char *file, char *defExtension, char *selPattern);
|
||||
BOOL GetOpenFile(char *file, char *defExtension, char *selPattern);
|
||||
void GetAbsoluteFilename(char *file);
|
||||
|
@ -370,6 +372,7 @@ public:
|
|||
Unit viewUnits;
|
||||
char *MmToString(double v);
|
||||
double ExprToMm(Expr *e);
|
||||
double StringToMm(char *s);
|
||||
|
||||
// The platform-dependent code calls this before entering the msg loop
|
||||
void Init(char *cmdLine);
|
||||
|
@ -414,6 +417,12 @@ public:
|
|||
void ExportDxfTo(char *file);
|
||||
void ExportMeshTo(char *file);
|
||||
|
||||
static void MenuAnalyze(int id);
|
||||
struct {
|
||||
SContour path;
|
||||
hEntity point;
|
||||
} traced;
|
||||
|
||||
void MarkGroupDirty(hGroup hg);
|
||||
void MarkGroupDirtyByEntity(hEntity he);
|
||||
|
||||
|
@ -437,6 +446,8 @@ public:
|
|||
void SolveGroup(hGroup hg);
|
||||
void ForceReferences(void);
|
||||
|
||||
bool AllGroupsOkay(void);
|
||||
|
||||
// The system to be solved.
|
||||
System sys;
|
||||
|
||||
|
|
|
@ -604,6 +604,76 @@ void TextWindow::ShowConfiguration(void) {
|
|||
&ScreenChangeExportScale, 0);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// When we're stepping a dimension. User specifies the finish value, and
|
||||
// how many steps to take in between current and finish, re-solving each
|
||||
// time.
|
||||
//-----------------------------------------------------------------------------
|
||||
void TextWindow::ScreenStepDimFinish(int link, DWORD v) {
|
||||
SS.TW.edit.meaning = EDIT_STEP_DIM_FINISH;
|
||||
char s[1024];
|
||||
if(SS.TW.shown.dimIsDistance) {
|
||||
strcpy(s, SS.MmToString(SS.TW.shown.dimFinish));
|
||||
} else {
|
||||
sprintf(s, "%.3f", SS.TW.shown.dimFinish);
|
||||
}
|
||||
ShowTextEditControl(12, 11, s);
|
||||
}
|
||||
void TextWindow::ScreenStepDimSteps(int link, DWORD v) {
|
||||
char str[1024];
|
||||
sprintf(str, "%d", SS.TW.shown.dimSteps);
|
||||
SS.TW.edit.meaning = EDIT_STEP_DIM_STEPS;
|
||||
ShowTextEditControl(14, 11, str);
|
||||
}
|
||||
void TextWindow::ScreenStepDimGo(int link, DWORD v) {
|
||||
hConstraint hc = SS.TW.shown.constraint;
|
||||
Constraint *c = SS.constraint.FindByIdNoOops(hc);
|
||||
if(c) {
|
||||
SS.UndoRemember();
|
||||
double start = c->valA, finish = SS.TW.shown.dimFinish;
|
||||
int i, n = SS.TW.shown.dimSteps;
|
||||
for(i = 1; i <= n; i++) {
|
||||
c = SS.GetConstraint(hc);
|
||||
c->valA = start + ((finish - start)*i)/n;
|
||||
SS.MarkGroupDirty(c->group);
|
||||
SS.GenerateAll();
|
||||
if(!SS.AllGroupsOkay()) {
|
||||
// Failed to solve, so quit
|
||||
break;
|
||||
}
|
||||
PaintGraphics();
|
||||
}
|
||||
}
|
||||
InvalidateGraphics();
|
||||
SS.TW.GoToScreen(SCREEN_LIST_OF_GROUPS);
|
||||
}
|
||||
void TextWindow::ShowStepDimension(void) {
|
||||
Constraint *c = SS.constraint.FindByIdNoOops(shown.constraint);
|
||||
if(!c) {
|
||||
shown.screen = SCREEN_LIST_OF_GROUPS;
|
||||
Show();
|
||||
return;
|
||||
}
|
||||
|
||||
Printf(true, "%FtSTEP DIMENSION%E %s", c->DescriptionString());
|
||||
|
||||
if(shown.dimIsDistance) {
|
||||
Printf(true, "%Ba %FtSTART%E %s", SS.MmToString(c->valA));
|
||||
Printf(false, "%Bd %FtFINISH%E %s %Fl%Ll%f[change]%E",
|
||||
SS.MmToString(shown.dimFinish), &ScreenStepDimFinish);
|
||||
} else {
|
||||
Printf(true, "%Ba %FtSTART%E %@", c->valA);
|
||||
Printf(false, "%Bd %FtFINISH%E %@ %Fl%Ll%f[change]%E",
|
||||
shown.dimFinish, &ScreenStepDimFinish);
|
||||
}
|
||||
Printf(false, "%Ba %FtSTEPS%E %d %Fl%Ll%f%D[change]%E",
|
||||
shown.dimSteps, &ScreenStepDimSteps);
|
||||
|
||||
Printf(true, " %Fl%Ll%fstep dimension now%E", &ScreenStepDimGo);
|
||||
|
||||
Printf(true, "(or %Fl%Ll%fcancel operation%E)", &ScreenHome);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// The edit control is visible, and the user just pressed enter.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -752,6 +822,18 @@ void TextWindow::EditControlDone(char *s) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case EDIT_STEP_DIM_FINISH:
|
||||
if(shown.dimIsDistance) {
|
||||
shown.dimFinish = SS.StringToMm(s);
|
||||
} else {
|
||||
shown.dimFinish = atof(s);
|
||||
}
|
||||
break;
|
||||
|
||||
case EDIT_STEP_DIM_STEPS:
|
||||
shown.dimSteps = min(300, max(1, atoi(s)));
|
||||
break;
|
||||
}
|
||||
InvalidateGraphics();
|
||||
SS.later.showTW = true;
|
||||
|
|
|
@ -221,6 +221,7 @@ void TextWindow::Show(void) {
|
|||
case SCREEN_GROUP_INFO: ShowGroupInfo(); break;
|
||||
case SCREEN_GROUP_SOLVE_INFO: ShowGroupSolveInfo(); break;
|
||||
case SCREEN_CONFIGURATION: ShowConfiguration(); break;
|
||||
case SCREEN_STEP_DIMENSION: ShowStepDimension(); break;
|
||||
}
|
||||
}
|
||||
Printf(false, "");
|
||||
|
|
19
ui.h
19
ui.h
|
@ -46,9 +46,16 @@ public:
|
|||
static const int SCREEN_GROUP_INFO = 1;
|
||||
static const int SCREEN_GROUP_SOLVE_INFO = 2;
|
||||
static const int SCREEN_CONFIGURATION = 3;
|
||||
static const int SCREEN_STEP_DIMENSION = 4;
|
||||
typedef struct {
|
||||
int screen;
|
||||
|
||||
hGroup group;
|
||||
|
||||
hConstraint constraint;
|
||||
bool dimIsDistance;
|
||||
double dimFinish;
|
||||
int dimSteps;
|
||||
} ShownState;
|
||||
ShownState shown;
|
||||
|
||||
|
@ -66,6 +73,8 @@ public:
|
|||
static const int EDIT_HELIX_PITCH = 21;
|
||||
static const int EDIT_HELIX_DRADIUS = 22;
|
||||
static const int EDIT_TTF_TEXT = 23;
|
||||
static const int EDIT_STEP_DIM_FINISH = 30;
|
||||
static const int EDIT_STEP_DIM_STEPS = 31;
|
||||
struct {
|
||||
int meaning;
|
||||
int i;
|
||||
|
@ -84,6 +93,7 @@ public:
|
|||
void ShowGroupInfo(void);
|
||||
void ShowGroupSolveInfo(void);
|
||||
void ShowConfiguration(void);
|
||||
void ShowStepDimension(void);
|
||||
// Special screen, based on selection
|
||||
void DescribeSelection(void);
|
||||
|
||||
|
@ -117,6 +127,10 @@ public:
|
|||
|
||||
static void ScreenShowConfiguration(int link, DWORD v);
|
||||
|
||||
static void ScreenStepDimSteps(int link, DWORD v);
|
||||
static void ScreenStepDimFinish(int link, DWORD v);
|
||||
static void ScreenStepDimGo(int link, DWORD v);
|
||||
|
||||
static void ScreenHome(int link, DWORD v);
|
||||
|
||||
// These ones do stuff with the edit control
|
||||
|
@ -201,6 +215,11 @@ public:
|
|||
MNU_PERPENDICULAR,
|
||||
MNU_ORIENTED_SAME,
|
||||
MNU_COMMENT,
|
||||
// Analyze
|
||||
MNU_VOLUME,
|
||||
MNU_TRACE_PT,
|
||||
MNU_STOP_TRACING,
|
||||
MNU_STEP_DIM,
|
||||
} MenuId;
|
||||
typedef void MenuHandler(int id);
|
||||
typedef struct {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
get rid of the oops() calls in the mesh codes
|
||||
trace point feature
|
||||
smooth shading
|
||||
leak fixing
|
||||
cylindrical faces?
|
||||
|
|
Loading…
Reference in New Issue