321 lines
9.3 KiB
C++
321 lines
9.3 KiB
C++
#include "solvespace.h"
|
|
|
|
SolveSpace SS;
|
|
|
|
void SolveSpace::Init(char *cmdLine) {
|
|
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
|
|
lightIntensity[0] = CnfThawFloat(1.0f, "LightIntensity_0");
|
|
lightIntensity[1] = CnfThawFloat(0.5f, "LightIntensity_1");
|
|
// Light positions
|
|
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" );
|
|
// Chord tolerance
|
|
chordTol = CnfThawFloat(2.0f, "ChordTolerance");
|
|
// View units
|
|
viewUnits = (Unit)CnfThawDWORD((DWORD)UNIT_MM, "ViewUnits");
|
|
// Camera tangent (determines perspective)
|
|
cameraTangent = CnfThawFloat(0.0f, "CameraTangent");
|
|
// Color for edges (drawn as lines for emphasis)
|
|
edgeColor = CnfThawDWORD(RGB(0, 0, 0), "EdgeColor");
|
|
// Export scale factor
|
|
exportScale = CnfThawFloat(1.0f, "ExportScale");
|
|
// 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();
|
|
|
|
// Start with either an empty file, or the file specified on the
|
|
// command line.
|
|
NewFile();
|
|
AfterNewFile();
|
|
if(strlen(cmdLine) != 0) {
|
|
if(LoadFromFile(cmdLine)) {
|
|
strcpy(saveFile, cmdLine);
|
|
} else {
|
|
NewFile();
|
|
}
|
|
}
|
|
AfterNewFile();
|
|
}
|
|
|
|
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
|
|
CnfFreezeFloat((float)lightIntensity[0], "LightIntensity_0");
|
|
CnfFreezeFloat((float)lightIntensity[1], "LightIntensity_1");
|
|
// Light directions
|
|
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");
|
|
// Chord tolerance
|
|
CnfFreezeFloat((float)chordTol, "ChordTolerance");
|
|
// Display/entry units
|
|
CnfFreezeDWORD((DWORD)viewUnits, "ViewUnits");
|
|
// Camera tangent (determines perspective)
|
|
CnfFreezeFloat((float)cameraTangent, "CameraTangent");
|
|
// Color for edges (drawn as lines for emphasis)
|
|
CnfFreezeDWORD(edgeColor, "EdgeColor");
|
|
// Export scale (a float, stored as a DWORD)
|
|
CnfFreezeFloat(exportScale, "ExportScale");
|
|
|
|
ExitNow();
|
|
}
|
|
|
|
void SolveSpace::DoLater(void) {
|
|
if(later.generateAll) GenerateAll();
|
|
if(later.showTW) TW.Show();
|
|
ZERO(&later);
|
|
}
|
|
|
|
int SolveSpace::CircleSides(double r) {
|
|
// 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));
|
|
|
|
return max(7, min(n, 40));
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
|
|
void SolveSpace::AfterNewFile(void) {
|
|
ReloadAllImported();
|
|
GenerateAll(-1, -1);
|
|
|
|
TW.Init();
|
|
GW.Init();
|
|
|
|
unsaved = false;
|
|
|
|
int w, h;
|
|
GetGraphicsWindowSize(&w, &h);
|
|
GW.width = w;
|
|
GW.height = h;
|
|
|
|
// The triangles haven't been generated yet, but zoom to fit the entities
|
|
// roughly in the window, since that sets the mesh tolerance.
|
|
GW.ZoomToFit();
|
|
|
|
GenerateAll(0, INT_MAX);
|
|
later.showTW = true;
|
|
// Then zoom to fit again, to fit the triangles
|
|
GW.ZoomToFit();
|
|
|
|
UpdateWindowTitle();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
bool SolveSpace::GetFilenameAndSave(bool saveAs) {
|
|
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();
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void SolveSpace::MenuFile(int id) {
|
|
if(id >= RECENT_OPEN && id < (RECENT_OPEN+MAX_RECENT)) {
|
|
if(!SS.OkayToStartNewFile()) return;
|
|
|
|
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;
|
|
}
|
|
|
|
switch(id) {
|
|
case GraphicsWindow::MNU_NEW:
|
|
if(!SS.OkayToStartNewFile()) break;
|
|
|
|
strcpy(SS.saveFile, "");
|
|
SS.NewFile();
|
|
SS.AfterNewFile();
|
|
break;
|
|
|
|
case GraphicsWindow::MNU_OPEN: {
|
|
if(!SS.OkayToStartNewFile()) break;
|
|
|
|
char newFile[MAX_PATH] = "";
|
|
if(GetOpenFile(newFile, SLVS_EXT, SLVS_PATTERN)) {
|
|
if(SS.LoadFromFile(newFile)) {
|
|
strcpy(SS.saveFile, newFile);
|
|
AddToRecentList(newFile);
|
|
} else {
|
|
strcpy(SS.saveFile, "");
|
|
SS.NewFile();
|
|
}
|
|
SS.AfterNewFile();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GraphicsWindow::MNU_SAVE:
|
|
SS.GetFilenameAndSave(false);
|
|
break;
|
|
|
|
case GraphicsWindow::MNU_SAVE_AS:
|
|
SS.GetFilenameAndSave(true);
|
|
break;
|
|
|
|
case GraphicsWindow::MNU_EXPORT_PNG: {
|
|
char exportFile[MAX_PATH] = "";
|
|
if(!GetSaveFile(exportFile, PNG_EXT, PNG_PATTERN)) break;
|
|
SS.ExportAsPngTo(exportFile);
|
|
break;
|
|
}
|
|
|
|
case GraphicsWindow::MNU_EXPORT_DXF: {
|
|
char exportFile[MAX_PATH] = "";
|
|
if(!GetSaveFile(exportFile, DXF_EXT, DXF_PATTERN)) break;
|
|
SS.ExportDxfTo(exportFile);
|
|
break;
|
|
}
|
|
|
|
case GraphicsWindow::MNU_EXPORT_MESH: {
|
|
char exportFile[MAX_PATH] = "";
|
|
if(!GetSaveFile(exportFile, STL_EXT, STL_PATTERN)) break;
|
|
SS.ExportMeshTo(exportFile);
|
|
break;
|
|
}
|
|
|
|
case GraphicsWindow::MNU_EXIT:
|
|
if(!SS.OkayToStartNewFile()) break;
|
|
SS.Exit();
|
|
break;
|
|
|
|
default: oops();
|
|
}
|
|
|
|
SS.UpdateWindowTitle();
|
|
}
|