//-----------------------------------------------------------------------------
// Entry point in to the program, our registry-stored settings and top-level
// housekeeping when we open, save, and create new files.
//
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include "solvespace.h"
#include "config.h"
namespace SolveSpace {
SolveSpaceUI SS = {};
Sketch SK = {};
void SolveSpaceUI::Init() {
#if !defined(HEADLESS)
// Check that the resource system works.
dbp("%s", LoadString("banner.txt").data());
#endif
Platform::SettingsRef settings = Platform::GetSettings();
SS.tangentArcRadius = 10.0;
SS.explodeDistance = 1.0;
// Then, load the registry settings.
// Default list of colors for the model material
modelColor[0] = settings->ThawColor("ModelColor_0", RGBi(150, 150, 150));
modelColor[1] = settings->ThawColor("ModelColor_1", RGBi(100, 100, 100));
modelColor[2] = settings->ThawColor("ModelColor_2", RGBi( 30, 30, 30));
modelColor[3] = settings->ThawColor("ModelColor_3", RGBi(150, 0, 0));
modelColor[4] = settings->ThawColor("ModelColor_4", RGBi( 0, 100, 0));
modelColor[5] = settings->ThawColor("ModelColor_5", RGBi( 0, 80, 80));
modelColor[6] = settings->ThawColor("ModelColor_6", RGBi( 0, 0, 130));
modelColor[7] = settings->ThawColor("ModelColor_7", RGBi( 80, 0, 80));
// Light intensities
lightIntensity[0] = settings->ThawFloat("LightIntensity_0", 1.0);
lightIntensity[1] = settings->ThawFloat("LightIntensity_1", 0.5);
ambientIntensity = settings->ThawFloat("Light_Ambient", 0.3);
// Light positions
lightDir[0].x = settings->ThawFloat("LightDir_0_Right", -1.0);
lightDir[0].y = settings->ThawFloat("LightDir_0_Up", 1.0);
lightDir[0].z = settings->ThawFloat("LightDir_0_Forward", 0.0);
lightDir[1].x = settings->ThawFloat("LightDir_1_Right", 1.0);
lightDir[1].y = settings->ThawFloat("LightDir_1_Up", 0.0);
lightDir[1].z = settings->ThawFloat("LightDir_1_Forward", 0.0);
exportMode = false;
// Chord tolerance
chordTol = settings->ThawFloat("ChordTolerancePct", 0.1);
// Max pwl segments to generate
maxSegments = settings->ThawInt("MaxSegments", 20);
// Chord tolerance
exportChordTol = settings->ThawFloat("ExportChordTolerance", 0.1);
// Max pwl segments to generate
exportMaxSegments = settings->ThawInt("ExportMaxSegments", 64);
// Timeout value for finding redundant constrains (ms)
timeoutRedundantConstr = settings->ThawInt("TimeoutRedundantConstraints", 1000);
// Animation speed calculation base time (ms)
animationSpeed = settings->ThawInt("AnimationSpeed", 800);
// View units
viewUnits = (Unit)settings->ThawInt("ViewUnits", (uint32_t)Unit::MM);
// Number of digits after the decimal point
afterDecimalMm = settings->ThawInt("AfterDecimalMm", 2);
afterDecimalInch = settings->ThawInt("AfterDecimalInch", 3);
afterDecimalDegree = settings->ThawInt("AfterDecimalDegree", 2);
useSIPrefixes = settings->ThawBool("UseSIPrefixes", false);
// Camera tangent (determines perspective)
cameraTangent = settings->ThawFloat("CameraTangent", 0.3f/1e3);
// Grid spacing
gridSpacing = settings->ThawFloat("GridSpacing", 5.0);
// Export scale factor
exportScale = settings->ThawFloat("ExportScale", 1.0);
// Export offset (cutter radius comp)
exportOffset = settings->ThawFloat("ExportOffset", 0.0);
// Dimensions on arcs default to diameter vs radius
arcDimDefaultDiameter = settings->ThawBool("ArcDimDefaultDiameter", false);
// Show full file path in the menu bar
showFullFilePath = settings->ThawBool("ShowFullFilePath", true);
// Rewrite exported colors close to white into black (assuming white bg)
fixExportColors = settings->ThawBool("FixExportColors", true);
// Export background color
exportBackgroundColor = settings->ThawBool("ExportBackgroundColor", false);
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
drawBackFaces = settings->ThawBool("DrawBackFaces", true);
// Use camera mouse navigation
cameraNav = settings->ThawBool("CameraNav", false);
// Use turntable mouse navigation
turntableNav = settings->ThawBool("TurntableNav", false);
// Immediately edit dimension
immediatelyEditDimension = settings->ThawBool("ImmediatelyEditDimension", true);
// Check that contours are closed and not self-intersecting
checkClosedContour = settings->ThawBool("CheckClosedContour", true);
// Enable automatic constrains for lines
automaticLineConstraints = settings->ThawBool("AutomaticLineConstraints", true);
// Draw closed polygons areas
showContourAreas = settings->ThawBool("ShowContourAreas", false);
// Export shaded triangles in a 2d view
exportShadedTriangles = settings->ThawBool("ExportShadedTriangles", true);
// Export pwl curves (instead of exact) always
exportPwlCurves = settings->ThawBool("ExportPwlCurves", false);
// Background color on-screen
backgroundColor = settings->ThawColor("BackgroundColor", RGBi(0, 0, 0));
// Whether export canvas size is fixed or derived from bbox
exportCanvasSizeAuto = settings->ThawBool("ExportCanvasSizeAuto", true);
// Margins for automatic canvas size
exportMargin.left = settings->ThawFloat("ExportMargin_Left", 5.0);
exportMargin.right = settings->ThawFloat("ExportMargin_Right", 5.0);
exportMargin.bottom = settings->ThawFloat("ExportMargin_Bottom", 5.0);
exportMargin.top = settings->ThawFloat("ExportMargin_Top", 5.0);
// Dimensions for fixed canvas size
exportCanvas.width = settings->ThawFloat("ExportCanvas_Width", 100.0);
exportCanvas.height = settings->ThawFloat("ExportCanvas_Height", 100.0);
exportCanvas.dx = settings->ThawFloat("ExportCanvas_Dx", 5.0);
exportCanvas.dy = settings->ThawFloat("ExportCanvas_Dy", 5.0);
// Extra parameters when exporting G code
gCode.depth = settings->ThawFloat("GCode_Depth", 10.0);
gCode.safeHeight = settings->ThawFloat("GCode_SafeHeight", 5.0);
gCode.passes = settings->ThawInt("GCode_Passes", 1);
gCode.feed = settings->ThawFloat("GCode_Feed", 10.0);
gCode.plungeFeed = settings->ThawFloat("GCode_PlungeFeed", 10.0);
// Show toolbar in the graphics window
showToolbar = settings->ThawBool("ShowToolbar", true);
// Recent files menus
for(size_t i = 0; i < MAX_RECENT; i++) {
std::string rawPath = settings->ThawString("RecentFile_" + std::to_string(i), "");
if(rawPath.empty()) continue;
recentFiles.push_back(Platform::Path::From(rawPath));
}
// Autosave timer
autosaveInterval = settings->ThawInt("AutosaveInterval", 5);
// Locale
std::string locale = settings->ThawString("Locale", "");
if(!locale.empty()) {
SetLocale(locale);
}
refreshTimer = Platform::CreateTimer();
refreshTimer->onTimeout = std::bind(&SolveSpaceUI::Refresh, &SS);
autosaveTimer = Platform::CreateTimer();
autosaveTimer->onTimeout = std::bind(&SolveSpaceUI::Autosave, &SS);
// The default styles (colors, line widths, etc.) are also stored in the
// configuration file, but we will automatically load those as we need
// them.
ScheduleAutosave();
NewFile();
AfterNewFile();
if(TW.window && GW.window) {
TW.window->ThawPosition(settings, "TextWindow");
GW.window->ThawPosition(settings, "GraphicsWindow");
TW.window->SetVisible(true);
GW.window->SetVisible(true);
GW.window->Focus();
// Do this once the window is created.
Request3DConnexionEventsForWindow(GW.window);
}
}
bool SolveSpaceUI::LoadAutosaveFor(const Platform::Path &filename) {
Platform::Path autosaveFile = filename.WithExtension(BACKUP_EXT);
FILE *f = OpenFile(autosaveFile, "rb");
if(!f)
return false;
fclose(f);
Platform::MessageDialogRef dialog = CreateMessageDialog(GW.window);
using Platform::MessageDialog;
dialog->SetType(MessageDialog::Type::QUESTION);
dialog->SetTitle(C_("title", "Autosave Available"));
dialog->SetMessage(C_("dialog", "An autosave file is available for this sketch."));
dialog->SetDescription(C_("dialog", "Do you want to load the autosave file instead?"));
dialog->AddButton(C_("button", "&Load autosave"), MessageDialog::Response::YES,
/*isDefault=*/true);
dialog->AddButton(C_("button", "Do&n't Load"), MessageDialog::Response::NO);
// FIXME(async): asyncify this call
if(dialog->RunModal() == MessageDialog::Response::YES) {
unsaved = true;
return LoadFromFile(autosaveFile, /*canCancel=*/true);
}
return false;
}
bool SolveSpaceUI::Load(const Platform::Path &filename) {
bool autosaveLoaded = LoadAutosaveFor(filename);
bool fileLoaded = autosaveLoaded || LoadFromFile(filename, /*canCancel=*/true);
if(fileLoaded) {
saveFile = filename;
AddToRecentList(filename);
} else {
saveFile.Clear();
NewFile();
}
// Capture whether loading modified the document (e.g. the user relocated
// a missing image, which sets unsaved in ReloadLinkedImage) before
// AfterNewFile() resets it (issue #1238).
bool unsavedDuringLoad = unsaved;
AfterNewFile();
unsaved = autosaveLoaded || unsavedDuringLoad;
return fileLoaded;
}
void SolveSpaceUI::Exit() {
Platform::SettingsRef settings = Platform::GetSettings();
GW.window->FreezePosition(settings, "GraphicsWindow");
TW.window->FreezePosition(settings, "TextWindow");
// Recent files
for(size_t i = 0; i < MAX_RECENT; i++) {
std::string rawPath;
if(recentFiles.size() > i) {
rawPath = recentFiles[i].raw;
}
settings->FreezeString("RecentFile_" + std::to_string(i), rawPath);
}
// Model colors
for(size_t i = 0; i < MODEL_COLORS; i++)
settings->FreezeColor("ModelColor_" + std::to_string(i), modelColor[i]);
// Light intensities
settings->FreezeFloat("LightIntensity_0", (float)lightIntensity[0]);
settings->FreezeFloat("LightIntensity_1", (float)lightIntensity[1]);
settings->FreezeFloat("Light_Ambient", (float)ambientIntensity);
// Light directions
settings->FreezeFloat("LightDir_0_Right", (float)lightDir[0].x);
settings->FreezeFloat("LightDir_0_Up", (float)lightDir[0].y);
settings->FreezeFloat("LightDir_0_Forward", (float)lightDir[0].z);
settings->FreezeFloat("LightDir_1_Right", (float)lightDir[1].x);
settings->FreezeFloat("LightDir_1_Up", (float)lightDir[1].y);
settings->FreezeFloat("LightDir_1_Forward", (float)lightDir[1].z);
// Chord tolerance
settings->FreezeFloat("ChordTolerancePct", (float)chordTol);
// Max pwl segments to generate
settings->FreezeInt("MaxSegments", (uint32_t)maxSegments);
// Export Chord tolerance
settings->FreezeFloat("ExportChordTolerance", (float)exportChordTol);
// Export Max pwl segments to generate
settings->FreezeInt("ExportMaxSegments", (uint32_t)exportMaxSegments);
// Timeout for finding which constraints to fix Jacobian
settings->FreezeInt("TimeoutRedundantConstraints", (uint32_t)timeoutRedundantConstr);
// Animation speed
settings->FreezeInt("AnimationSpeed", (uint32_t)animationSpeed);
// View units
settings->FreezeInt("ViewUnits", (uint32_t)viewUnits);
// Number of digits after the decimal point
settings->FreezeInt("AfterDecimalMm", (uint32_t)afterDecimalMm);
settings->FreezeInt("AfterDecimalInch", (uint32_t)afterDecimalInch);
settings->FreezeInt("AfterDecimalDegree", (uint32_t)afterDecimalDegree);
settings->FreezeBool("UseSIPrefixes", useSIPrefixes);
// Camera tangent (determines perspective)
settings->FreezeFloat("CameraTangent", (float)cameraTangent);
// Grid spacing
settings->FreezeFloat("GridSpacing", gridSpacing);
// Export scale
settings->FreezeFloat("ExportScale", exportScale);
// Export offset (cutter radius comp)
settings->FreezeFloat("ExportOffset", exportOffset);
// Rewrite the default arc dimension setting
settings->FreezeBool("ArcDimDefaultDiameter", arcDimDefaultDiameter);
// Show full file path in the menu bar
settings->FreezeBool("ShowFullFilePath", showFullFilePath);
// Rewrite exported colors close to white into black (assuming white bg)
settings->FreezeBool("FixExportColors", fixExportColors);
// Export background color
settings->FreezeBool("ExportBackgroundColor", exportBackgroundColor);
// Draw back faces of triangles (when mesh is leaky/self-intersecting)
settings->FreezeBool("DrawBackFaces", drawBackFaces);
// Draw closed polygons areas
settings->FreezeBool("ShowContourAreas", showContourAreas);
// Check that contours are closed and not self-intersecting
settings->FreezeBool("CheckClosedContour", checkClosedContour);
// Use camera mouse navigation
settings->FreezeBool("CameraNav", cameraNav);
// Use turntable mouse navigation
settings->FreezeBool("TurntableNav", turntableNav);
// Immediately edit dimensions
settings->FreezeBool("ImmediatelyEditDimension", immediatelyEditDimension);
// Enable automatic constrains for lines
settings->FreezeBool("AutomaticLineConstraints", automaticLineConstraints);
// Export shaded triangles in a 2d view
settings->FreezeBool("ExportShadedTriangles", exportShadedTriangles);
// Export pwl curves (instead of exact) always
settings->FreezeBool("ExportPwlCurves", exportPwlCurves);
// Background color on-screen
settings->FreezeColor("BackgroundColor", backgroundColor);
// Whether export canvas size is fixed or derived from bbox
settings->FreezeBool("ExportCanvasSizeAuto", exportCanvasSizeAuto);
// Margins for automatic canvas size
settings->FreezeFloat("ExportMargin_Left", exportMargin.left);
settings->FreezeFloat("ExportMargin_Right", exportMargin.right);
settings->FreezeFloat("ExportMargin_Bottom", exportMargin.bottom);
settings->FreezeFloat("ExportMargin_Top", exportMargin.top);
// Dimensions for fixed canvas size
settings->FreezeFloat("ExportCanvas_Width", exportCanvas.width);
settings->FreezeFloat("ExportCanvas_Height", exportCanvas.height);
settings->FreezeFloat("ExportCanvas_Dx", exportCanvas.dx);
settings->FreezeFloat("ExportCanvas_Dy", exportCanvas.dy);
// Extra parameters when exporting G code
settings->FreezeFloat("GCode_Depth", gCode.depth);
settings->FreezeInt("GCode_Passes", gCode.passes);
settings->FreezeFloat("GCode_Feed", gCode.feed);
settings->FreezeFloat("GCode_PlungeFeed", gCode.plungeFeed);
// Show toolbar in the graphics window
settings->FreezeBool("ShowToolbar", showToolbar);
// Autosave timer
settings->FreezeInt("AutosaveInterval", autosaveInterval);
// And the default styles, colors and line widths and such.
Style::FreezeDefaultStyles(settings);
Platform::ExitGui();
}
void SolveSpaceUI::Refresh() {
// generateAll must happen bfore updating displays
if(scheduledGenerateAll) {
// Clear the flag so that if the call to GenerateAll is blocked by a Message or Error,
// subsequent refreshes do not try to Generate again.
scheduledGenerateAll = false;
GenerateAll(Generate::DIRTY, /*andFindFree=*/false, /*genForBBox=*/false);
}
if(scheduledShowTW) {
scheduledShowTW = false;
TW.Show();
}
}
void SolveSpaceUI::ScheduleGenerateAll() {
scheduledGenerateAll = true;
refreshTimer->RunAfterProcessingEvents();
}
void SolveSpaceUI::ScheduleShowTW() {
scheduledShowTW = true;
refreshTimer->RunAfterProcessingEvents();
}
void SolveSpaceUI::ScheduleAutosave() {
autosaveTimer->RunAfter(autosaveInterval * 60 * 1000);
}
double SolveSpaceUI::MmPerUnit() {
switch(viewUnits) {
case Unit::INCHES: return 25.4;
case Unit::FEET_INCHES: return 25.4; // The 'unit' is still inches
case Unit::METERS: return 1000.0;
case Unit::MM: return 1.0;
}
return 1.0;
}
const char *SolveSpaceUI::UnitName() {
switch(viewUnits) {
case Unit::INCHES: return "in";
case Unit::FEET_INCHES: return "in";
case Unit::METERS: return "m";
case Unit::MM: return "mm";
}
return "";
}
std::string SolveSpaceUI::MmToString(double v, bool editable) {
v /= MmPerUnit();
// The syntax 2' 6" for feet and inches is not something we can (currently)
// parse back from a string so if editable is true, we treat FEET_INCHES the
// same as INCHES and just return the unadorned decimal number of inches.
if(viewUnits == Unit::FEET_INCHES && !editable) {
// Now convert v from inches to 64'ths of an inch, to make rounding easier.
v = floor((v + (1.0 / 128.0)) * 64.0);
int feet = (int)(v / (12.0 * 64.0));
v = v - (feet * 12.0 * 64.0);
// v is now the feet-less remainder in 1/64 inches
int inches = (int)(v / 64.0);
int numerator = (int)(v - ((double)inches * 64.0));
int denominator = 64;
// Divide down to smallest denominator where the numerator is still a whole number
while ((numerator != 0) && ((numerator & 1) == 0)) {
numerator /= 2;
denominator /= 2;
}
std::ostringstream str;
if(feet != 0) {
str