2008-03-25 10:02:13 +00:00
|
|
|
|
#include "solvespace.h"
|
|
|
|
|
|
|
|
|
|
SolveSpace SS;
|
2009-04-19 05:53:16 +00:00
|
|
|
|
Sketch SK;
|
2008-03-25 10:02:13 +00:00
|
|
|
|
|
2008-02-09 13:52:01 +00:00
|
|
|
|
void SolveSpace::CheckLicenseFromRegistry(void) {
|
|
|
|
|
// First, let's see if we're running licensed or free
|
|
|
|
|
CnfThawString(license.line1, sizeof(license.line1), "LicenseLine1");
|
|
|
|
|
CnfThawString(license.line2, sizeof(license.line2), "LicenseLine2");
|
|
|
|
|
CnfThawString(license.users, sizeof(license.users), "LicenseUsers");
|
|
|
|
|
license.key = CnfThawDWORD(0, "LicenseKey");
|
|
|
|
|
|
|
|
|
|
license.licensed =
|
|
|
|
|
LicenseValid(license.line1, license.line2, license.users, license.key);
|
2009-06-10 06:57:27 +00:00
|
|
|
|
|
|
|
|
|
// Now see if we've recorded a previous first use time in the registry. If
|
|
|
|
|
// yes then we use that, otherwise we record the current time.
|
|
|
|
|
SQWORD now = GetUnixTime();
|
|
|
|
|
DWORD timeLow = CnfThawDWORD(0, "FirstUseLow");
|
|
|
|
|
DWORD timeHigh = CnfThawDWORD(0, "FirstUseHigh");
|
|
|
|
|
if(timeHigh == 0 && timeLow == 0) {
|
|
|
|
|
CnfFreezeDWORD((DWORD)((now ) & 0xffffffff), "FirstUseLow");
|
|
|
|
|
CnfFreezeDWORD((DWORD)((now >> 32) & 0xffffffff), "FirstUseHigh");
|
|
|
|
|
license.firstUse = now;
|
|
|
|
|
} else {
|
|
|
|
|
license.firstUse = (((SQWORD)timeHigh) << 32) | ((SQWORD)timeLow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int SECONDS_IN_DAY = 60*60*24;
|
2009-09-23 10:59:59 +00:00
|
|
|
|
license.trialDaysRemaining = 30 -
|
2009-06-10 06:57:27 +00:00
|
|
|
|
(int)(((now - license.firstUse))/SECONDS_IN_DAY);
|
2008-02-09 13:52:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-04-24 06:22:16 +00:00
|
|
|
|
void SolveSpace::Init(char *cmdLine) {
|
2008-02-09 13:52:01 +00:00
|
|
|
|
CheckLicenseFromRegistry();
|
|
|
|
|
|
|
|
|
|
// Then, load the registry settings.
|
2008-06-11 04:22:52 +00:00
|
|
|
|
int i;
|
|
|
|
|
// Default list of colors for the model material
|
|
|
|
|
modelColor[0] = CnfThawDWORD(RGB(150, 150, 150), "ModelColor_0");
|
|
|
|
|
modelColor[1] = CnfThawDWORD(RGB(100, 100, 100), "ModelColor_1");
|
|
|
|
|
modelColor[2] = CnfThawDWORD(RGB( 30, 30, 30), "ModelColor_2");
|
|
|
|
|
modelColor[3] = CnfThawDWORD(RGB(150, 0, 0), "ModelColor_3");
|
|
|
|
|
modelColor[4] = CnfThawDWORD(RGB( 0, 100, 0), "ModelColor_4");
|
|
|
|
|
modelColor[5] = CnfThawDWORD(RGB( 0, 80, 80), "ModelColor_5");
|
|
|
|
|
modelColor[6] = CnfThawDWORD(RGB( 0, 0, 130), "ModelColor_6");
|
|
|
|
|
modelColor[7] = CnfThawDWORD(RGB( 80, 0, 80), "ModelColor_7");
|
|
|
|
|
// Light intensities
|
2008-07-08 07:41:29 +00:00
|
|
|
|
lightIntensity[0] = CnfThawFloat(1.0f, "LightIntensity_0");
|
|
|
|
|
lightIntensity[1] = CnfThawFloat(0.5f, "LightIntensity_1");
|
2009-03-17 16:33:46 +00:00
|
|
|
|
ambientIntensity = 0.3; // no setting for that yet
|
2008-06-11 04:22:52 +00:00
|
|
|
|
// Light positions
|
2008-07-08 07:41:29 +00:00
|
|
|
|
lightDir[0].x = CnfThawFloat(-1.0f, "LightDir_0_Right" );
|
|
|
|
|
lightDir[0].y = CnfThawFloat( 1.0f, "LightDir_0_Up" );
|
|
|
|
|
lightDir[0].z = CnfThawFloat( 0.0f, "LightDir_0_Forward" );
|
|
|
|
|
lightDir[1].x = CnfThawFloat( 1.0f, "LightDir_1_Right" );
|
|
|
|
|
lightDir[1].y = CnfThawFloat( 0.0f, "LightDir_1_Up" );
|
|
|
|
|
lightDir[1].z = CnfThawFloat( 0.0f, "LightDir_1_Forward" );
|
2008-07-10 05:26:08 +00:00
|
|
|
|
// Chord tolerance
|
2009-06-10 08:04:35 +00:00
|
|
|
|
chordTol = CnfThawFloat(2.0f, "ChordTolerance");
|
2008-02-12 13:00:26 +00:00
|
|
|
|
// Max pwl segments to generate
|
2009-05-08 08:33:04 +00:00
|
|
|
|
maxSegments = CnfThawDWORD(10, "MaxSegments");
|
2008-06-14 09:51:25 +00:00
|
|
|
|
// View units
|
|
|
|
|
viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits");
|
2008-06-17 19:12:25 +00:00
|
|
|
|
// Camera tangent (determines perspective)
|
2008-07-08 07:41:29 +00:00
|
|
|
|
cameraTangent = CnfThawFloat(0.0f, "CameraTangent");
|
|
|
|
|
// Export scale factor
|
|
|
|
|
exportScale = CnfThawFloat(1.0f, "ExportScale");
|
2008-08-14 08:28:25 +00:00
|
|
|
|
// Export offset (cutter radius comp)
|
|
|
|
|
exportOffset = CnfThawFloat(0.0f, "ExportOffset");
|
2009-09-22 05:46:30 +00:00
|
|
|
|
// Rewrite exported colors close to white into black (assuming white bg)
|
|
|
|
|
fixExportColors = CnfThawDWORD(1, "FixExportColors");
|
2008-08-11 10:56:08 +00:00
|
|
|
|
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
|
|
|
|
|
drawBackFaces = CnfThawDWORD(1, "DrawBackFaces");
|
2009-03-18 04:26:04 +00:00
|
|
|
|
// Export shaded triangles in a 2d view
|
|
|
|
|
exportShadedTriangles = CnfThawDWORD(1, "ExportShadedTriangles");
|
2009-04-14 04:19:23 +00:00
|
|
|
|
// Export pwl curves (instead of exact) always
|
|
|
|
|
exportPwlCurves = CnfThawDWORD(0, "ExportPwlCurves");
|
2009-09-18 08:14:15 +00:00
|
|
|
|
// Background color on-screen
|
|
|
|
|
backgroundColor = CnfThawDWORD(RGB(0, 0, 0), "BackgroundColor");
|
2009-09-03 08:13:09 +00:00
|
|
|
|
// Whether export canvas size is fixed or derived from bbox
|
|
|
|
|
exportCanvasSizeAuto = CnfThawDWORD(1, "ExportCanvasSizeAuto");
|
|
|
|
|
// Margins for automatic canvas size
|
|
|
|
|
exportMargin.left = CnfThawFloat(5.0f, "ExportMargin_Left");
|
|
|
|
|
exportMargin.right = CnfThawFloat(5.0f, "ExportMargin_Right");
|
|
|
|
|
exportMargin.bottom = CnfThawFloat(5.0f, "ExportMargin_Bottom");
|
|
|
|
|
exportMargin.top = CnfThawFloat(5.0f, "ExportMargin_Top");
|
|
|
|
|
// Dimensions for fixed canvas size
|
|
|
|
|
exportCanvas.width = CnfThawFloat(100.0f, "ExportCanvas_Width");
|
|
|
|
|
exportCanvas.height = CnfThawFloat(100.0f, "ExportCanvas_Height");
|
|
|
|
|
exportCanvas.dx = CnfThawFloat( 5.0f, "ExportCanvas_Dx");
|
|
|
|
|
exportCanvas.dy = CnfThawFloat( 5.0f, "ExportCanvas_Dy");
|
2009-01-02 10:38:36 +00:00
|
|
|
|
// Show toolbar in the graphics window
|
|
|
|
|
showToolbar = CnfThawDWORD(1, "ShowToolbar");
|
2008-06-11 04:22:52 +00:00
|
|
|
|
// Recent files menus
|
|
|
|
|
for(i = 0; i < MAX_RECENT; i++) {
|
|
|
|
|
char name[100];
|
|
|
|
|
sprintf(name, "RecentFile_%d", i);
|
|
|
|
|
strcpy(RecentFile[i], "");
|
|
|
|
|
CnfThawString(RecentFile[i], MAX_PATH, name);
|
|
|
|
|
}
|
|
|
|
|
RefreshRecentMenus();
|
|
|
|
|
|
2009-09-17 07:32:36 +00:00
|
|
|
|
// The default styles (colors, line widths, etc.) are also stored in the
|
|
|
|
|
// configuration file, but we will automatically load those as we need
|
|
|
|
|
// them.
|
|
|
|
|
|
2008-06-11 04:22:52 +00:00
|
|
|
|
// Start with either an empty file, or the file specified on the
|
|
|
|
|
// command line.
|
2008-05-29 10:10:12 +00:00
|
|
|
|
NewFile();
|
|
|
|
|
AfterNewFile();
|
|
|
|
|
if(strlen(cmdLine) != 0) {
|
2008-05-28 10:10:31 +00:00
|
|
|
|
if(LoadFromFile(cmdLine)) {
|
|
|
|
|
strcpy(saveFile, cmdLine);
|
|
|
|
|
} else {
|
|
|
|
|
NewFile();
|
|
|
|
|
}
|
2008-04-24 06:22:16 +00:00
|
|
|
|
}
|
2008-05-28 10:10:31 +00:00
|
|
|
|
AfterNewFile();
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-11 04:22:52 +00:00
|
|
|
|
void SolveSpace::Exit(void) {
|
|
|
|
|
int i;
|
|
|
|
|
char name[100];
|
|
|
|
|
// Recent files
|
|
|
|
|
for(i = 0; i < MAX_RECENT; i++) {
|
|
|
|
|
sprintf(name, "RecentFile_%d", i);
|
|
|
|
|
CnfFreezeString(RecentFile[i], name);
|
|
|
|
|
}
|
|
|
|
|
// Model colors
|
|
|
|
|
for(i = 0; i < MODEL_COLORS; i++) {
|
|
|
|
|
sprintf(name, "ModelColor_%d", i);
|
|
|
|
|
CnfFreezeDWORD(modelColor[i], name);
|
|
|
|
|
}
|
|
|
|
|
// Light intensities
|
2008-07-08 07:41:29 +00:00
|
|
|
|
CnfFreezeFloat((float)lightIntensity[0], "LightIntensity_0");
|
|
|
|
|
CnfFreezeFloat((float)lightIntensity[1], "LightIntensity_1");
|
2008-06-11 04:30:18 +00:00
|
|
|
|
// Light directions
|
2008-07-08 07:41:29 +00:00
|
|
|
|
CnfFreezeFloat((float)lightDir[0].x, "LightDir_0_Right");
|
|
|
|
|
CnfFreezeFloat((float)lightDir[0].y, "LightDir_0_Up");
|
|
|
|
|
CnfFreezeFloat((float)lightDir[0].z, "LightDir_0_Forward");
|
|
|
|
|
CnfFreezeFloat((float)lightDir[1].x, "LightDir_1_Right");
|
|
|
|
|
CnfFreezeFloat((float)lightDir[1].y, "LightDir_1_Up");
|
|
|
|
|
CnfFreezeFloat((float)lightDir[1].z, "LightDir_1_Forward");
|
2008-07-10 05:26:08 +00:00
|
|
|
|
// Chord tolerance
|
|
|
|
|
CnfFreezeFloat((float)chordTol, "ChordTolerance");
|
2008-02-12 13:00:26 +00:00
|
|
|
|
// Max pwl segments to generate
|
|
|
|
|
CnfFreezeDWORD((DWORD)maxSegments, "MaxSegments");
|
2008-06-14 09:51:25 +00:00
|
|
|
|
// Display/entry units
|
2008-07-08 07:41:29 +00:00
|
|
|
|
CnfFreezeDWORD((DWORD)viewUnits, "ViewUnits");
|
2008-06-17 19:12:25 +00:00
|
|
|
|
// Camera tangent (determines perspective)
|
2008-07-08 07:41:29 +00:00
|
|
|
|
CnfFreezeFloat((float)cameraTangent, "CameraTangent");
|
|
|
|
|
// Export scale (a float, stored as a DWORD)
|
|
|
|
|
CnfFreezeFloat(exportScale, "ExportScale");
|
2008-08-14 08:28:25 +00:00
|
|
|
|
// Export offset (cutter radius comp)
|
|
|
|
|
CnfFreezeFloat(exportOffset, "ExportOffset");
|
2009-09-22 05:46:30 +00:00
|
|
|
|
// Rewrite exported colors close to white into black (assuming white bg)
|
|
|
|
|
CnfFreezeDWORD(fixExportColors, "FixExportColors");
|
2008-08-11 10:56:08 +00:00
|
|
|
|
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
|
|
|
|
|
CnfFreezeDWORD(drawBackFaces, "DrawBackFaces");
|
2009-03-18 04:26:04 +00:00
|
|
|
|
// Export shaded triangles in a 2d view
|
|
|
|
|
CnfFreezeDWORD(exportShadedTriangles, "ExportShadedTriangles");
|
2009-04-14 04:19:23 +00:00
|
|
|
|
// Export pwl curves (instead of exact) always
|
|
|
|
|
CnfFreezeDWORD(exportPwlCurves, "ExportPwlCurves");
|
2009-09-18 08:14:15 +00:00
|
|
|
|
// Background color on-screen
|
|
|
|
|
CnfFreezeDWORD(backgroundColor, "BackgroundColor");
|
2009-09-03 08:13:09 +00:00
|
|
|
|
// Whether export canvas size is fixed or derived from bbox
|
|
|
|
|
CnfFreezeDWORD(exportCanvasSizeAuto, "ExportCanvasSizeAuto");
|
|
|
|
|
// Margins for automatic canvas size
|
|
|
|
|
CnfFreezeFloat(exportMargin.left, "ExportMargin_Left");
|
|
|
|
|
CnfFreezeFloat(exportMargin.right, "ExportMargin_Right");
|
|
|
|
|
CnfFreezeFloat(exportMargin.bottom, "ExportMargin_Bottom");
|
|
|
|
|
CnfFreezeFloat(exportMargin.top, "ExportMargin_Top");
|
|
|
|
|
// Dimensions for fixed canvas size
|
|
|
|
|
CnfFreezeFloat(exportCanvas.width, "ExportCanvas_Width");
|
|
|
|
|
CnfFreezeFloat(exportCanvas.height, "ExportCanvas_Height");
|
|
|
|
|
CnfFreezeFloat(exportCanvas.dx, "ExportCanvas_Dx");
|
|
|
|
|
CnfFreezeFloat(exportCanvas.dy, "ExportCanvas_Dy");
|
2009-01-02 10:38:36 +00:00
|
|
|
|
// Show toolbar in the graphics window
|
|
|
|
|
CnfFreezeDWORD(showToolbar, "ShowToolbar");
|
2008-07-08 07:41:29 +00:00
|
|
|
|
|
2009-09-17 07:32:36 +00:00
|
|
|
|
// And the default styles, colors and line widths and such.
|
|
|
|
|
Style::FreezeDefaultStyles();
|
|
|
|
|
|
2008-06-11 04:22:52 +00:00
|
|
|
|
ExitNow();
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-03 18:28:41 +00:00
|
|
|
|
void SolveSpace::DoLater(void) {
|
|
|
|
|
if(later.generateAll) GenerateAll();
|
|
|
|
|
if(later.showTW) TW.Show();
|
|
|
|
|
ZERO(&later);
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-11 04:22:52 +00:00
|
|
|
|
int SolveSpace::CircleSides(double r) {
|
2008-07-10 05:26:08 +00:00
|
|
|
|
// Let the pwl segment be symmetric about the x axis; then the curve
|
|
|
|
|
// goes out to r, and if there's n segments, then the endpoints are
|
|
|
|
|
// at +/- (2pi/n)/2 = +/- pi/n. So the chord goes to x = r cos pi/n,
|
|
|
|
|
// from x = r, so it's
|
|
|
|
|
// tol = r - r cos pi/n
|
|
|
|
|
// tol = r(1 - cos pi/n)
|
|
|
|
|
// tol ~ r(1 - (1 - (pi/n)^2/2)) (Taylor expansion)
|
|
|
|
|
// tol = r((pi/n)^2/2)
|
|
|
|
|
// 2*tol/r = (pi/n)^2
|
|
|
|
|
// sqrt(2*tol/r) = pi/n
|
|
|
|
|
// n = pi/sqrt(2*tol/r);
|
|
|
|
|
|
|
|
|
|
double tol = chordTol/GW.scale;
|
|
|
|
|
int n = 3 + (int)(PI/sqrt(2*tol/r));
|
|
|
|
|
|
2008-02-12 13:00:26 +00:00
|
|
|
|
return max(7, min(n, maxSegments));
|
2008-06-11 04:22:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-06-14 09:51:25 +00:00
|
|
|
|
char *SolveSpace::MmToString(double v) {
|
|
|
|
|
static int WhichBuf;
|
|
|
|
|
static char Bufs[8][128];
|
|
|
|
|
|
|
|
|
|
WhichBuf++;
|
|
|
|
|
if(WhichBuf >= 8 || WhichBuf < 0) WhichBuf = 0;
|
|
|
|
|
|
|
|
|
|
char *s = Bufs[WhichBuf];
|
|
|
|
|
if(viewUnits == UNIT_INCHES) {
|
|
|
|
|
sprintf(s, "%.3f", v/25.4);
|
|
|
|
|
} else {
|
|
|
|
|
sprintf(s, "%.2f", v);
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
double SolveSpace::ExprToMm(Expr *e) {
|
|
|
|
|
if(viewUnits == UNIT_INCHES) {
|
|
|
|
|
return (e->Eval())*25.4;
|
|
|
|
|
} else {
|
|
|
|
|
return e->Eval();
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-07-20 11:27:22 +00:00
|
|
|
|
double SolveSpace::StringToMm(char *str) {
|
|
|
|
|
if(viewUnits == UNIT_INCHES) {
|
|
|
|
|
return atof(str)*25.4;
|
|
|
|
|
} else {
|
|
|
|
|
return atof(str);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-03-08 10:59:57 +00:00
|
|
|
|
double SolveSpace::ChordTolMm(void) {
|
|
|
|
|
return SS.chordTol / SS.GW.scale;
|
|
|
|
|
}
|
2008-06-14 09:51:25 +00:00
|
|
|
|
|
|
|
|
|
|
2008-05-28 10:10:31 +00:00
|
|
|
|
void SolveSpace::AfterNewFile(void) {
|
2008-07-20 11:27:22 +00:00
|
|
|
|
// Clear out the traced point, which is no longer valid
|
|
|
|
|
traced.point = Entity::NO_ENTITY;
|
|
|
|
|
traced.path.l.Clear();
|
2009-01-25 09:19:59 +00:00
|
|
|
|
// and the naked edges
|
|
|
|
|
nakedEdges.Clear();
|
2008-07-20 11:27:22 +00:00
|
|
|
|
|
2009-06-04 03:59:40 +00:00
|
|
|
|
// GenerateAll() expects the view to be valid, because it uses that to
|
|
|
|
|
// fill in default values for extrusion depths etc. (which won't matter
|
|
|
|
|
// here, but just don't let it work on garbage)
|
|
|
|
|
SS.GW.offset = Vector::From(0, 0, 0);
|
|
|
|
|
SS.GW.projRight = Vector::From(1, 0, 0);
|
|
|
|
|
SS.GW.projUp = Vector::From(0, 1, 0);
|
|
|
|
|
|
2008-05-29 10:10:12 +00:00
|
|
|
|
ReloadAllImported();
|
2008-06-02 09:31:26 +00:00
|
|
|
|
GenerateAll(-1, -1);
|
2008-05-25 13:11:44 +00:00
|
|
|
|
|
2008-04-13 10:57:41 +00:00
|
|
|
|
TW.Init();
|
|
|
|
|
GW.Init();
|
|
|
|
|
|
2008-06-03 18:28:41 +00:00
|
|
|
|
unsaved = false;
|
2008-06-12 07:31:41 +00:00
|
|
|
|
|
|
|
|
|
int w, h;
|
|
|
|
|
GetGraphicsWindowSize(&w, &h);
|
|
|
|
|
GW.width = w;
|
|
|
|
|
GW.height = h;
|
2008-06-21 10:18:20 +00:00
|
|
|
|
|
|
|
|
|
// The triangles haven't been generated yet, but zoom to fit the entities
|
2009-05-29 05:40:17 +00:00
|
|
|
|
// roughly in the window, since that sets the mesh tolerance. Consider
|
|
|
|
|
// invisible entities, so we still get something reasonable if the only
|
|
|
|
|
// thing visible is the not-yet-generated surfaces.
|
|
|
|
|
GW.ZoomToFit(true);
|
2008-06-12 08:58:58 +00:00
|
|
|
|
|
|
|
|
|
GenerateAll(0, INT_MAX);
|
|
|
|
|
later.showTW = true;
|
2008-06-21 10:18:20 +00:00
|
|
|
|
// Then zoom to fit again, to fit the triangles
|
2009-05-29 05:40:17 +00:00
|
|
|
|
GW.ZoomToFit(false);
|
2008-07-08 08:02:22 +00:00
|
|
|
|
|
2009-09-18 08:14:15 +00:00
|
|
|
|
// Create all the default styles; they'll get created on the fly anyways,
|
|
|
|
|
// but can't hurt to do it now.
|
|
|
|
|
Style::CreateAllDefaultStyles();
|
|
|
|
|
|
2008-07-08 08:02:22 +00:00
|
|
|
|
UpdateWindowTitle();
|
2008-04-08 12:54:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-05-28 10:10:31 +00:00
|
|
|
|
void SolveSpace::RemoveFromRecentList(char *file) {
|
|
|
|
|
int src, dest;
|
|
|
|
|
dest = 0;
|
|
|
|
|
for(src = 0; src < MAX_RECENT; src++) {
|
|
|
|
|
if(strcmp(file, RecentFile[src]) != 0) {
|
|
|
|
|
if(src != dest) strcpy(RecentFile[dest], RecentFile[src]);
|
|
|
|
|
dest++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while(dest < MAX_RECENT) strcpy(RecentFile[dest++], "");
|
|
|
|
|
RefreshRecentMenus();
|
|
|
|
|
}
|
|
|
|
|
void SolveSpace::AddToRecentList(char *file) {
|
|
|
|
|
RemoveFromRecentList(file);
|
|
|
|
|
|
|
|
|
|
int src;
|
|
|
|
|
for(src = MAX_RECENT - 2; src >= 0; src--) {
|
|
|
|
|
strcpy(RecentFile[src+1], RecentFile[src]);
|
|
|
|
|
}
|
|
|
|
|
strcpy(RecentFile[0], file);
|
|
|
|
|
RefreshRecentMenus();
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-03 18:28:41 +00:00
|
|
|
|
bool SolveSpace::GetFilenameAndSave(bool saveAs) {
|
2009-06-10 06:57:27 +00:00
|
|
|
|
|
2008-06-03 18:28:41 +00:00
|
|
|
|
char newFile[MAX_PATH];
|
|
|
|
|
strcpy(newFile, saveFile);
|
|
|
|
|
if(saveAs || strlen(newFile)==0) {
|
|
|
|
|
if(!GetSaveFile(newFile, SLVS_EXT, SLVS_PATTERN)) return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SaveToFile(newFile)) {
|
|
|
|
|
AddToRecentList(newFile);
|
|
|
|
|
strcpy(saveFile, newFile);
|
|
|
|
|
unsaved = false;
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SolveSpace::OkayToStartNewFile(void) {
|
|
|
|
|
if(!unsaved) return true;
|
|
|
|
|
|
|
|
|
|
switch(SaveFileYesNoCancel()) {
|
|
|
|
|
case IDYES:
|
|
|
|
|
return GetFilenameAndSave(false);
|
|
|
|
|
|
|
|
|
|
case IDNO:
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
case IDCANCEL:
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
default: oops();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-08 08:02:22 +00:00
|
|
|
|
void SolveSpace::UpdateWindowTitle(void) {
|
|
|
|
|
if(strlen(saveFile) == 0) {
|
|
|
|
|
SetWindowTitle("SolveSpace - (not yet saved)");
|
|
|
|
|
} else {
|
|
|
|
|
char buf[MAX_PATH+100];
|
|
|
|
|
sprintf(buf, "SolveSpace - %s", saveFile);
|
|
|
|
|
SetWindowTitle(buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-18 11:11:48 +00:00
|
|
|
|
void SolveSpace::MenuFile(int id) {
|
2008-05-28 10:10:31 +00:00
|
|
|
|
if(id >= RECENT_OPEN && id < (RECENT_OPEN+MAX_RECENT)) {
|
2008-07-10 06:11:56 +00:00
|
|
|
|
if(!SS.OkayToStartNewFile()) return;
|
|
|
|
|
|
2008-05-28 10:10:31 +00:00
|
|
|
|
char newFile[MAX_PATH];
|
|
|
|
|
strcpy(newFile, RecentFile[id-RECENT_OPEN]);
|
|
|
|
|
RemoveFromRecentList(newFile);
|
|
|
|
|
if(SS.LoadFromFile(newFile)) {
|
|
|
|
|
strcpy(SS.saveFile, newFile);
|
|
|
|
|
AddToRecentList(newFile);
|
|
|
|
|
} else {
|
|
|
|
|
strcpy(SS.saveFile, "");
|
|
|
|
|
SS.NewFile();
|
|
|
|
|
}
|
|
|
|
|
SS.AfterNewFile();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-18 11:11:48 +00:00
|
|
|
|
switch(id) {
|
|
|
|
|
case GraphicsWindow::MNU_NEW:
|
2008-06-03 18:28:41 +00:00
|
|
|
|
if(!SS.OkayToStartNewFile()) break;
|
|
|
|
|
|
2008-05-28 10:10:31 +00:00
|
|
|
|
strcpy(SS.saveFile, "");
|
2008-04-24 06:22:16 +00:00
|
|
|
|
SS.NewFile();
|
2008-05-28 10:10:31 +00:00
|
|
|
|
SS.AfterNewFile();
|
2008-04-24 06:22:16 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2008-05-28 10:10:31 +00:00
|
|
|
|
case GraphicsWindow::MNU_OPEN: {
|
2008-06-03 18:28:41 +00:00
|
|
|
|
if(!SS.OkayToStartNewFile()) break;
|
|
|
|
|
|
2008-05-28 10:10:31 +00:00
|
|
|
|
char newFile[MAX_PATH] = "";
|
2008-05-29 10:10:12 +00:00
|
|
|
|
if(GetOpenFile(newFile, SLVS_EXT, SLVS_PATTERN)) {
|
2008-05-28 10:10:31 +00:00
|
|
|
|
if(SS.LoadFromFile(newFile)) {
|
|
|
|
|
strcpy(SS.saveFile, newFile);
|
|
|
|
|
AddToRecentList(newFile);
|
|
|
|
|
} else {
|
|
|
|
|
strcpy(SS.saveFile, "");
|
|
|
|
|
SS.NewFile();
|
|
|
|
|
}
|
|
|
|
|
SS.AfterNewFile();
|
|
|
|
|
}
|
2008-04-24 06:22:16 +00:00
|
|
|
|
break;
|
2008-05-28 10:10:31 +00:00
|
|
|
|
}
|
2008-04-18 11:11:48 +00:00
|
|
|
|
|
|
|
|
|
case GraphicsWindow::MNU_SAVE:
|
2008-06-03 18:28:41 +00:00
|
|
|
|
SS.GetFilenameAndSave(false);
|
|
|
|
|
break;
|
2008-04-18 11:11:48 +00:00
|
|
|
|
|
2008-06-03 18:28:41 +00:00
|
|
|
|
case GraphicsWindow::MNU_SAVE_AS:
|
|
|
|
|
SS.GetFilenameAndSave(true);
|
2008-04-18 11:11:48 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2008-06-18 08:35:14 +00:00
|
|
|
|
case GraphicsWindow::MNU_EXPORT_PNG: {
|
|
|
|
|
char exportFile[MAX_PATH] = "";
|
|
|
|
|
if(!GetSaveFile(exportFile, PNG_EXT, PNG_PATTERN)) break;
|
|
|
|
|
SS.ExportAsPngTo(exportFile);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-14 05:10:42 +00:00
|
|
|
|
case GraphicsWindow::MNU_EXPORT_VIEW: {
|
2008-07-08 06:30:13 +00:00
|
|
|
|
char exportFile[MAX_PATH] = "";
|
2009-01-27 05:48:40 +00:00
|
|
|
|
if(!GetSaveFile(exportFile, VEC_EXT, VEC_PATTERN)) break;
|
2009-01-14 05:10:42 +00:00
|
|
|
|
SS.ExportViewTo(exportFile);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case GraphicsWindow::MNU_EXPORT_SECTION: {
|
|
|
|
|
char exportFile[MAX_PATH] = "";
|
2009-01-27 05:48:40 +00:00
|
|
|
|
if(!GetSaveFile(exportFile, VEC_EXT, VEC_PATTERN)) break;
|
2009-01-14 05:10:42 +00:00
|
|
|
|
SS.ExportSectionTo(exportFile);
|
2008-07-08 06:30:13 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2008-07-06 09:24:31 +00:00
|
|
|
|
case GraphicsWindow::MNU_EXPORT_MESH: {
|
|
|
|
|
char exportFile[MAX_PATH] = "";
|
|
|
|
|
if(!GetSaveFile(exportFile, STL_EXT, STL_PATTERN)) break;
|
|
|
|
|
SS.ExportMeshTo(exportFile);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-08 06:50:16 +00:00
|
|
|
|
case GraphicsWindow::MNU_EXPORT_SURFACES: {
|
|
|
|
|
char exportFile[MAX_PATH] = "";
|
|
|
|
|
if(!GetSaveFile(exportFile, SRF_EXT, SRF_PATTERN)) break;
|
|
|
|
|
StepFileWriter sfw;
|
|
|
|
|
ZERO(&sfw);
|
2009-06-11 05:57:23 +00:00
|
|
|
|
sfw.ExportSurfacesTo(exportFile);
|
2009-06-08 06:50:16 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2008-04-18 11:11:48 +00:00
|
|
|
|
case GraphicsWindow::MNU_EXIT:
|
2008-06-03 18:28:41 +00:00
|
|
|
|
if(!SS.OkayToStartNewFile()) break;
|
2008-06-11 04:22:52 +00:00
|
|
|
|
SS.Exit();
|
2008-04-18 11:11:48 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default: oops();
|
|
|
|
|
}
|
2008-07-08 08:02:22 +00:00
|
|
|
|
|
|
|
|
|
SS.UpdateWindowTitle();
|
2008-04-18 11:11:48 +00:00
|
|
|
|
}
|
2008-07-20 11:27:22 +00:00
|
|
|
|
|
|
|
|
|
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) {
|
2009-04-19 05:53:16 +00:00
|
|
|
|
Constraint *c = SK.GetConstraint(gs.constraint[0]);
|
2008-07-20 11:27:22 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2008-09-17 10:13:37 +00:00
|
|
|
|
// The step params are specified in the text window,
|
|
|
|
|
// so force that to be shown.
|
|
|
|
|
SS.GW.ForceTextWindowShown();
|
|
|
|
|
|
2008-07-20 11:27:22 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2009-01-19 10:37:10 +00:00
|
|
|
|
case GraphicsWindow::MNU_NAKED_EDGES: {
|
2009-01-25 09:19:59 +00:00
|
|
|
|
SS.nakedEdges.Clear();
|
|
|
|
|
|
2009-06-07 23:00:57 +00:00
|
|
|
|
Group *g = SK.GetGroup(SS.GW.activeGroup);
|
|
|
|
|
SMesh *m = &(g->displayMesh);
|
2009-01-25 09:19:59 +00:00
|
|
|
|
SKdNode *root = SKdNode::From(m);
|
2009-01-28 07:09:01 +00:00
|
|
|
|
bool inters, leaks;
|
2009-05-29 05:40:17 +00:00
|
|
|
|
root->MakeCertainEdgesInto(&(SS.nakedEdges),
|
|
|
|
|
SKdNode::NAKED_OR_SELF_INTER_EDGES, true, &inters, &leaks);
|
|
|
|
|
|
2009-01-25 09:19:59 +00:00
|
|
|
|
InvalidateGraphics();
|
|
|
|
|
|
2009-01-28 07:09:01 +00:00
|
|
|
|
char *intersMsg = inters ?
|
|
|
|
|
"The mesh is self-intersecting (NOT okay, invalid)." :
|
|
|
|
|
"The mesh is not self-intersecting (okay, valid).";
|
|
|
|
|
char *leaksMsg = leaks ?
|
|
|
|
|
"The mesh has naked edges (NOT okay, invalid)." :
|
|
|
|
|
"The mesh is watertight (okay, valid).";
|
|
|
|
|
|
2009-06-07 23:00:57 +00:00
|
|
|
|
char cntMsg[1024];
|
|
|
|
|
sprintf(cntMsg, "\r\n\r\nThe model contains %d triangles, from "
|
|
|
|
|
"%d surfaces.",
|
|
|
|
|
g->displayMesh.l.n, g->runningShell.surface.n);
|
|
|
|
|
|
2009-01-25 09:19:59 +00:00
|
|
|
|
if(SS.nakedEdges.l.n == 0) {
|
2009-06-07 23:00:57 +00:00
|
|
|
|
Message("%s\r\n\r\n%s\r\n\r\nZero problematic edges, good.%s",
|
|
|
|
|
intersMsg, leaksMsg, cntMsg);
|
2009-01-25 09:19:59 +00:00
|
|
|
|
} else {
|
2009-06-07 23:00:57 +00:00
|
|
|
|
Error("%s\r\n\r\n%s\r\n\r\n%d problematic edges, bad.%s",
|
|
|
|
|
intersMsg, leaksMsg, SS.nakedEdges.l.n, cntMsg);
|
2009-01-25 09:19:59 +00:00
|
|
|
|
}
|
2009-01-19 10:37:10 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-22 10:02:02 +00:00
|
|
|
|
case GraphicsWindow::MNU_INTERFERENCE: {
|
|
|
|
|
SS.nakedEdges.Clear();
|
|
|
|
|
|
|
|
|
|
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
|
|
|
|
|
SKdNode *root = SKdNode::From(m);
|
|
|
|
|
bool inters, leaks;
|
2009-05-29 05:40:17 +00:00
|
|
|
|
root->MakeCertainEdgesInto(&(SS.nakedEdges),
|
2009-06-06 09:44:58 +00:00
|
|
|
|
SKdNode::SELF_INTER_EDGES, false, &inters, &leaks);
|
2009-05-29 05:40:17 +00:00
|
|
|
|
|
2009-05-22 10:02:02 +00:00
|
|
|
|
InvalidateGraphics();
|
|
|
|
|
|
|
|
|
|
if(inters) {
|
|
|
|
|
Error("%d edges interfere with other triangles, bad.",
|
|
|
|
|
SS.nakedEdges.l.n);
|
|
|
|
|
} else {
|
|
|
|
|
Message("The assembly does not interfere, good.");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2008-02-07 09:53:52 +00:00
|
|
|
|
case GraphicsWindow::MNU_VOLUME: {
|
2009-05-21 09:06:26 +00:00
|
|
|
|
SMesh *m = &(SK.GetGroup(SS.GW.activeGroup)->displayMesh);
|
2008-02-07 09:53:52 +00:00
|
|
|
|
|
|
|
|
|
double vol = 0;
|
|
|
|
|
int i;
|
|
|
|
|
for(i = 0; i < m->l.n; i++) {
|
|
|
|
|
STriangle tr = m->l.elem[i];
|
|
|
|
|
|
|
|
|
|
// Translate to place vertex A at (x, y, 0)
|
|
|
|
|
Vector trans = Vector::From(tr.a.x, tr.a.y, 0);
|
|
|
|
|
tr.a = (tr.a).Minus(trans);
|
|
|
|
|
tr.b = (tr.b).Minus(trans);
|
|
|
|
|
tr.c = (tr.c).Minus(trans);
|
|
|
|
|
|
|
|
|
|
// Rotate to place vertex B on the y-axis. Depending on
|
|
|
|
|
// whether the triangle is CW or CCW, C is either to the
|
|
|
|
|
// right or to the left of the y-axis. This handles the
|
|
|
|
|
// sign of our normal.
|
|
|
|
|
Vector u = Vector::From(-tr.b.y, tr.b.x, 0);
|
|
|
|
|
u = u.WithMagnitude(1);
|
|
|
|
|
Vector v = Vector::From(tr.b.x, tr.b.y, 0);
|
|
|
|
|
v = v.WithMagnitude(1);
|
|
|
|
|
Vector n = Vector::From(0, 0, 1);
|
|
|
|
|
|
|
|
|
|
tr.a = (tr.a).DotInToCsys(u, v, n);
|
|
|
|
|
tr.b = (tr.b).DotInToCsys(u, v, n);
|
|
|
|
|
tr.c = (tr.c).DotInToCsys(u, v, n);
|
|
|
|
|
|
|
|
|
|
n = tr.Normal().WithMagnitude(1);
|
|
|
|
|
|
|
|
|
|
// Triangles on edge don't contribute
|
|
|
|
|
if(fabs(n.z) < LENGTH_EPS) continue;
|
|
|
|
|
|
|
|
|
|
// The plane has equation p dot n = a dot n
|
|
|
|
|
double d = (tr.a).Dot(n);
|
|
|
|
|
// nx*x + ny*y + nz*z = d
|
|
|
|
|
// nz*z = d - nx*x - ny*y
|
|
|
|
|
double A = -n.x/n.z, B = -n.y/n.z, C = d/n.z;
|
|
|
|
|
|
|
|
|
|
double mac = tr.c.y/tr.c.x, mbc = (tr.c.y - tr.b.y)/tr.c.x;
|
|
|
|
|
double xc = tr.c.x, yb = tr.b.y;
|
|
|
|
|
|
|
|
|
|
// I asked Maple for
|
|
|
|
|
// int(int(A*x + B*y +C, y=mac*x..(mbc*x + yb)), x=0..xc);
|
|
|
|
|
double integral =
|
|
|
|
|
(1.0/3)*(
|
|
|
|
|
A*(mbc-mac)+
|
|
|
|
|
(1.0/2)*B*(mbc*mbc-mac*mac)
|
|
|
|
|
)*(xc*xc*xc)+
|
|
|
|
|
(1.0/2)*(A*yb+B*yb*mbc+C*(mbc-mac))*xc*xc+
|
|
|
|
|
C*yb*xc+
|
|
|
|
|
(1.0/2)*B*yb*yb*xc;
|
|
|
|
|
|
|
|
|
|
vol += integral;
|
|
|
|
|
}
|
|
|
|
|
SS.TW.shown.volume = vol;
|
|
|
|
|
SS.TW.GoToScreen(TextWindow::SCREEN_MESH_VOLUME);
|
|
|
|
|
SS.later.showTW = true;
|
2008-07-20 11:27:22 +00:00
|
|
|
|
break;
|
2008-02-07 09:53:52 +00:00
|
|
|
|
}
|
2008-07-20 11:27:22 +00:00
|
|
|
|
|
2009-01-04 12:01:46 +00:00
|
|
|
|
case GraphicsWindow::MNU_SHOW_DOF:
|
|
|
|
|
// This works like a normal solve, except that it calculates
|
|
|
|
|
// which variables are free/bound at the same time.
|
|
|
|
|
SS.GenerateAll(0, INT_MAX, true);
|
|
|
|
|
break;
|
|
|
|
|
|
2008-07-20 11:27:22 +00:00
|
|
|
|
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)) {
|
2008-02-09 13:52:01 +00:00
|
|
|
|
FILE *f = fopen(exportFile, "wb");
|
2008-07-20 11:27:22 +00:00
|
|
|
|
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;
|
2008-02-09 13:52:01 +00:00
|
|
|
|
fprintf(f, "%.10f, %.10f, %.10f\r\n",
|
2008-07-20 11:27:22 +00:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-02-09 13:52:01 +00:00
|
|
|
|
void SolveSpace::Crc::ProcessBit(int bit) {
|
|
|
|
|
bool topWasSet = ((shiftReg & (1 << 31)) != 0);
|
|
|
|
|
|
|
|
|
|
shiftReg <<= 1;
|
|
|
|
|
if(bit) {
|
|
|
|
|
shiftReg |= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(topWasSet) {
|
|
|
|
|
shiftReg ^= POLY;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SolveSpace::Crc::ProcessByte(BYTE b) {
|
|
|
|
|
int i;
|
|
|
|
|
for(i = 0; i < 8; i++) {
|
|
|
|
|
ProcessBit(b & (1 << i));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SolveSpace::Crc::ProcessString(char *s) {
|
|
|
|
|
for(; *s; s++) {
|
|
|
|
|
if(*s != '\n' && *s != '\r') {
|
|
|
|
|
ProcessByte((BYTE)*s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SolveSpace::LicenseValid(char *line1, char *line2, char *users, DWORD key)
|
|
|
|
|
{
|
|
|
|
|
BYTE magic[17] = {
|
|
|
|
|
203, 244, 134, 225, 45, 250, 70, 65,
|
|
|
|
|
224, 189, 35, 3, 228, 51, 77, 169,
|
|
|
|
|
0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
crc.shiftReg = 0;
|
|
|
|
|
crc.ProcessString(line1);
|
|
|
|
|
crc.ProcessString(line2);
|
|
|
|
|
crc.ProcessString(users);
|
|
|
|
|
crc.ProcessString((char *)magic);
|
|
|
|
|
|
|
|
|
|
return (key == crc.shiftReg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SolveSpace::CleanEol(char *in) {
|
|
|
|
|
char *s;
|
|
|
|
|
s = strchr(in, '\r');
|
|
|
|
|
if(s) *s = '\0';
|
|
|
|
|
s = strchr(in, '\n');
|
|
|
|
|
if(s) *s = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SolveSpace::LoadLicenseFile(char *filename) {
|
|
|
|
|
FILE *f = fopen(filename, "rb");
|
|
|
|
|
if(!f) {
|
|
|
|
|
Error("Couldn't open file '%s'", filename);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char buf[100];
|
|
|
|
|
fgets(buf, sizeof(buf), f);
|
2008-02-13 16:13:08 +00:00
|
|
|
|
char *str = "<EFBFBD><EFBFBD><EFBFBD>SolveSpaceLicense";
|
2008-02-09 13:52:01 +00:00
|
|
|
|
if(memcmp(buf, str, strlen(str)) != 0) {
|
|
|
|
|
fclose(f);
|
|
|
|
|
Error("This is not a license file,");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char line1[512], line2[512], users[512];
|
|
|
|
|
fgets(line1, sizeof(line1), f);
|
|
|
|
|
CleanEol(line1);
|
|
|
|
|
fgets(line2, sizeof(line2), f);
|
|
|
|
|
CleanEol(line2);
|
|
|
|
|
fgets(users, sizeof(users), f);
|
|
|
|
|
CleanEol(users);
|
|
|
|
|
|
|
|
|
|
fgets(buf, sizeof(buf), f);
|
|
|
|
|
DWORD key = 0;
|
|
|
|
|
sscanf(buf, "%x", &key);
|
|
|
|
|
|
|
|
|
|
if(LicenseValid(line1, line2, users, key)) {
|
|
|
|
|
// Install the new key
|
|
|
|
|
CnfFreezeString(line1, "LicenseLine1");
|
|
|
|
|
CnfFreezeString(line2, "LicenseLine2");
|
|
|
|
|
CnfFreezeString(users, "LicenseUsers");
|
|
|
|
|
CnfFreezeDWORD(key, "LicenseKey");
|
|
|
|
|
Message("License key successfully installed.");
|
|
|
|
|
|
|
|
|
|
// This updates our display in the text window to show that we're
|
|
|
|
|
// licensed now.
|
|
|
|
|
CheckLicenseFromRegistry();
|
|
|
|
|
SS.later.showTW = true;
|
|
|
|
|
} else {
|
|
|
|
|
Error("License key invalid.");
|
|
|
|
|
}
|
|
|
|
|
fclose(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SolveSpace::MenuHelp(int id) {
|
|
|
|
|
switch(id) {
|
|
|
|
|
case GraphicsWindow::MNU_WEBSITE:
|
2008-02-13 16:13:08 +00:00
|
|
|
|
OpenWebsite("http://www.solvespace.com/helpmenu");
|
2008-02-09 13:52:01 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GraphicsWindow::MNU_ABOUT:
|
2009-06-14 04:36:38 +00:00
|
|
|
|
Message("This is SolveSpace version 1.4.\r\n\r\n"
|
2009-09-28 09:48:56 +00:00
|
|
|
|
"For more information, see http://www.solvespace.com/\r\n\r\n"
|
|
|
|
|
"Built " __TIME__ " " __DATE__ ".\r\n\r\n"
|
|
|
|
|
"Copyright 2008-2009 Useful Subset, LLC. All Rights Reserved.");
|
2008-02-09 13:52:01 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GraphicsWindow::MNU_LICENSE: {
|
|
|
|
|
char licenseFile[MAX_PATH] = "";
|
|
|
|
|
if(GetOpenFile(licenseFile, LICENSE_EXT, LICENSE_PATTERN)) {
|
|
|
|
|
SS.LoadLicenseFile(licenseFile);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default: oops();
|
|
|
|
|
}
|
|
|
|
|
}
|