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
Jonathan Westhues 2008-07-20 03:27:22 -08:00
parent d2c4d2cdb7
commit 8fe910da4d
10 changed files with 299 additions and 9 deletions

View File

@ -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>

View File

@ -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);

View File

@ -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;
}

View File

@ -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 },

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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
View File

@ -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 {

View File

@ -1,6 +1,5 @@
get rid of the oops() calls in the mesh codes
trace point feature
smooth shading
leak fixing
cylindrical faces?