#include "lc_global.h" #include #include "lc_application.h" #include "lc_library.h" #include "lc_profile.h" #include "project.h" #include "lc_mainwindow.h" #include "lc_qpreferencesdialog.h" #include "lc_partselectionwidget.h" #include "lc_shortcuts.h" #include "lc_view.h" #include "camera.h" #include "lc_previewwidget.h" lcApplication* gApplication; void lcPreferences::LoadDefaults() { mFixedAxes = lcGetProfileInt(LC_PROFILE_FIXED_AXES); mMouseSensitivity = lcGetProfileInt(LC_PROFILE_MOUSE_SENSITIVITY); mShadingMode = static_cast(lcGetProfileInt(LC_PROFILE_SHADING_MODE)); mBackgroundGradient = lcGetProfileInt(LC_PROFILE_BACKGROUND_GRADIENT); mBackgroundSolidColor = lcGetProfileInt(LC_PROFILE_BACKGROUND_COLOR); mBackgroundGradientColorTop = lcGetProfileInt(LC_PROFILE_GRADIENT_COLOR_TOP); mBackgroundGradientColorBottom = lcGetProfileInt(LC_PROFILE_GRADIENT_COLOR_BOTTOM); mDrawAxes = lcGetProfileInt(LC_PROFILE_DRAW_AXES); mAxesColor = lcGetProfileInt(LC_PROFILE_AXES_COLOR); mTextColor = lcGetProfileInt(LC_PROFILE_TEXT_COLOR); mMarqueeBorderColor = lcGetProfileInt(LC_PROFILE_MARQUEE_BORDER_COLOR); mMarqueeFillColor = lcGetProfileInt(LC_PROFILE_MARQUEE_FILL_COLOR); mOverlayColor = lcGetProfileInt(LC_PROFILE_OVERLAY_COLOR); mActiveViewColor = lcGetProfileInt(LC_PROFILE_ACTIVE_VIEW_COLOR); mInactiveViewColor = lcGetProfileInt(LC_PROFILE_INACTIVE_VIEW_COLOR); mDrawEdgeLines = lcGetProfileInt(LC_PROFILE_DRAW_EDGE_LINES); mLineWidth = lcGetProfileFloat(LC_PROFILE_LINE_WIDTH); mAllowLOD = lcGetProfileInt(LC_PROFILE_ALLOW_LOD); mMeshLODDistance = lcGetProfileFloat(LC_PROFILE_LOD_DISTANCE); mFadeSteps = lcGetProfileInt(LC_PROFILE_FADE_STEPS); mFadeStepsColor = lcGetProfileInt(LC_PROFILE_FADE_STEPS_COLOR); mHighlightNewParts = lcGetProfileInt(LC_PROFILE_HIGHLIGHT_NEW_PARTS); mHighlightNewPartsColor = lcGetProfileInt(LC_PROFILE_HIGHLIGHT_NEW_PARTS_COLOR); mDrawGridStuds = lcGetProfileInt(LC_PROFILE_GRID_STUDS); mGridStudColor = lcGetProfileInt(LC_PROFILE_GRID_STUD_COLOR); mDrawGridLines = lcGetProfileInt(LC_PROFILE_GRID_LINES); mGridLineSpacing = lcGetProfileInt(LC_PROFILE_GRID_LINE_SPACING); mGridLineColor = lcGetProfileInt(LC_PROFILE_GRID_LINE_COLOR); mViewSphereEnabled = lcGetProfileInt(LC_PROFILE_VIEW_SPHERE_ENABLED); mViewSphereLocation = static_cast(lcGetProfileInt(LC_PROFILE_VIEW_SPHERE_LOCATION)); mViewSphereSize = lcGetProfileInt(LC_PROFILE_VIEW_SPHERE_SIZE); mViewSphereColor = lcGetProfileInt(LC_PROFILE_VIEW_SPHERE_COLOR); mViewSphereTextColor = lcGetProfileInt(LC_PROFILE_VIEW_SPHERE_TEXT_COLOR); mViewSphereHighlightColor = lcGetProfileInt(LC_PROFILE_VIEW_SPHERE_HIGHLIGHT_COLOR); mAutoLoadMostRecent = lcGetProfileInt(LC_PROFILE_AUTOLOAD_MOSTRECENT); mRestoreTabLayout = lcGetProfileInt(LC_PROFILE_RESTORE_TAB_LAYOUT); mColorTheme = static_cast(lcGetProfileInt(LC_PROFILE_COLOR_THEME)); mPreviewViewSphereEnabled = lcGetProfileInt(LC_PROFILE_PREVIEW_VIEW_SPHERE_ENABLED); mPreviewViewSphereSize = lcGetProfileInt(LC_PROFILE_PREVIEW_VIEW_SPHERE_SIZE); mPreviewViewSphereLocation = static_cast(lcGetProfileInt(LC_PROFILE_PREVIEW_VIEW_SPHERE_LOCATION)); mDrawPreviewAxis = lcGetProfileInt(LC_PROFILE_PREVIEW_DRAW_AXES); } void lcPreferences::SaveDefaults() { lcSetProfileInt(LC_PROFILE_FIXED_AXES, mFixedAxes); lcSetProfileInt(LC_PROFILE_MOUSE_SENSITIVITY, mMouseSensitivity); lcSetProfileInt(LC_PROFILE_SHADING_MODE, static_cast(mShadingMode)); lcSetProfileInt(LC_PROFILE_DRAW_AXES, mDrawAxes); lcSetProfileInt(LC_PROFILE_AXES_COLOR, mAxesColor); lcSetProfileInt(LC_PROFILE_TEXT_COLOR, mTextColor); lcSetProfileInt(LC_PROFILE_BACKGROUND_GRADIENT, mBackgroundGradient); lcSetProfileInt(LC_PROFILE_BACKGROUND_COLOR, mBackgroundSolidColor); lcSetProfileInt(LC_PROFILE_GRADIENT_COLOR_TOP, mBackgroundGradientColorTop); lcSetProfileInt(LC_PROFILE_GRADIENT_COLOR_BOTTOM, mBackgroundGradientColorBottom); lcSetProfileInt(LC_PROFILE_MARQUEE_BORDER_COLOR, mMarqueeBorderColor); lcSetProfileInt(LC_PROFILE_MARQUEE_FILL_COLOR, mMarqueeFillColor); lcSetProfileInt(LC_PROFILE_OVERLAY_COLOR, mOverlayColor); lcSetProfileInt(LC_PROFILE_ACTIVE_VIEW_COLOR, mActiveViewColor); lcSetProfileInt(LC_PROFILE_INACTIVE_VIEW_COLOR, mInactiveViewColor); lcSetProfileInt(LC_PROFILE_DRAW_EDGE_LINES, mDrawEdgeLines); lcSetProfileFloat(LC_PROFILE_LINE_WIDTH, mLineWidth); lcSetProfileInt(LC_PROFILE_ALLOW_LOD, mAllowLOD); lcSetProfileFloat(LC_PROFILE_LOD_DISTANCE, mMeshLODDistance); lcSetProfileInt(LC_PROFILE_FADE_STEPS, mFadeSteps); lcSetProfileInt(LC_PROFILE_FADE_STEPS_COLOR, mFadeStepsColor); lcSetProfileInt(LC_PROFILE_HIGHLIGHT_NEW_PARTS, mHighlightNewParts); lcSetProfileInt(LC_PROFILE_HIGHLIGHT_NEW_PARTS_COLOR, mHighlightNewPartsColor); lcSetProfileInt(LC_PROFILE_GRID_STUDS, mDrawGridStuds); lcSetProfileInt(LC_PROFILE_GRID_STUD_COLOR, mGridStudColor); lcSetProfileInt(LC_PROFILE_GRID_LINES, mDrawGridLines); lcSetProfileInt(LC_PROFILE_GRID_LINE_SPACING, mGridLineSpacing); lcSetProfileInt(LC_PROFILE_GRID_LINE_COLOR, mGridLineColor); lcSetProfileInt(LC_PROFILE_VIEW_SPHERE_ENABLED, mViewSphereSize ? 1 : 0); lcSetProfileInt(LC_PROFILE_VIEW_SPHERE_LOCATION, static_cast(mViewSphereLocation)); lcSetProfileInt(LC_PROFILE_VIEW_SPHERE_SIZE, mViewSphereSize); lcSetProfileInt(LC_PROFILE_VIEW_SPHERE_COLOR, mViewSphereColor); lcSetProfileInt(LC_PROFILE_VIEW_SPHERE_TEXT_COLOR, mViewSphereTextColor); lcSetProfileInt(LC_PROFILE_VIEW_SPHERE_HIGHLIGHT_COLOR, mViewSphereHighlightColor); lcSetProfileInt(LC_PROFILE_AUTOLOAD_MOSTRECENT, mAutoLoadMostRecent); lcSetProfileInt(LC_PROFILE_RESTORE_TAB_LAYOUT, mRestoreTabLayout); lcSetProfileInt(LC_PROFILE_COLOR_THEME, static_cast(mColorTheme)); lcSetProfileInt(LC_PROFILE_PREVIEW_VIEW_SPHERE_SIZE, mPreviewViewSphereSize); lcSetProfileInt(LC_PROFILE_PREVIEW_VIEW_SPHERE_LOCATION, static_cast(mPreviewViewSphereLocation)); lcSetProfileInt(LC_PROFILE_PREVIEW_DRAW_AXES, mDrawPreviewAxis); } void lcPreferences::SetInterfaceColors(lcColorTheme ColorTheme) { if (ColorTheme == lcColorTheme::Dark) { mAxesColor = LC_RGBA(160, 160, 160, 255); mTextColor = LC_RGBA(160, 160, 160, 255); mBackgroundSolidColor = LC_RGB(49, 52, 55); mBackgroundGradientColorTop = LC_RGB(0, 0, 191); mBackgroundGradientColorBottom = LC_RGB(255, 255, 255); mOverlayColor = lcGetProfileInt(LC_PROFILE_OVERLAY_COLOR); mActiveViewColor = LC_RGBA(41, 128, 185, 255); mGridStudColor = LC_RGBA(24, 24, 24, 192); mGridLineColor = LC_RGBA(24, 24, 24, 255); mViewSphereColor = LC_RGBA(35, 38, 41, 255); mViewSphereTextColor = LC_RGBA(224, 224, 224, 255); mViewSphereHighlightColor = LC_RGBA(41, 128, 185, 255); } else { mAxesColor = LC_RGBA(0, 0, 0, 255); mTextColor = LC_RGBA(0, 0, 0, 255); mBackgroundSolidColor = LC_RGB(255, 255, 255); mBackgroundGradientColorTop = LC_RGB(54, 72, 95); mBackgroundGradientColorBottom = LC_RGB(49, 52, 55); mOverlayColor = LC_RGBA(0, 0, 0, 255); mActiveViewColor = LC_RGBA(255, 0, 0, 255); mGridStudColor = LC_RGBA(64, 64, 64, 192); mGridLineColor = LC_RGBA(0, 0, 0, 255); mViewSphereColor = LC_RGBA(255, 255, 255, 255); mViewSphereTextColor = LC_RGBA(0, 0, 0, 255); mViewSphereHighlightColor = LC_RGBA(255, 0, 0, 255); } } lcApplication::lcApplication(int& Argc, char** Argv) : QApplication(Argc, Argv) { setApplicationDisplayName("LeoCAD"); setOrganizationDomain("leocad.org"); setOrganizationName("LeoCAD Software"); setApplicationName("LeoCAD"); setApplicationVersion(LC_VERSION_TEXT); gApplication = this; mProject = nullptr; mLibrary = nullptr; mDefaultStyle = style()->objectName(); mPreferences.LoadDefaults(); UpdateStyle(); } lcApplication::~lcApplication() { delete mProject; delete mLibrary; gApplication = nullptr; } void lcApplication::UpdateStyle() { if (mPreferences.mColorTheme == lcColorTheme::Dark) { if (!QApplication::setStyle("fusion")) return; QPalette Palette = QApplication::palette(); Palette.setColor(QPalette::Window, QColor(49, 52, 55)); Palette.setColor(QPalette::WindowText, QColor(240, 240, 240)); Palette.setColor(QPalette::Base, QColor(35, 38, 41)); Palette.setColor(QPalette::AlternateBase, QColor(44, 47, 50)); Palette.setColor(QPalette::ToolTipBase, QColor(224, 224, 244)); Palette.setColor(QPalette::ToolTipText, QColor(58, 58, 58)); #if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) Palette.setColor(QPalette::PlaceholderText, QColor(100, 100, 100)); #endif Palette.setColor(QPalette::Text, QColor(224, 224, 224)); Palette.setColor(QPalette::Button, QColor(45, 48, 51)); Palette.setColor(QPalette::ButtonText, QColor(224, 224, 244)); Palette.setColor(QPalette::Light, QColor(65, 65, 65)); Palette.setColor(QPalette::Midlight, QColor(62, 62, 62)); Palette.setColor(QPalette::Dark, QColor(35, 35, 35)); Palette.setColor(QPalette::Mid, QColor(50, 50, 50)); Palette.setColor(QPalette::Shadow, QColor(20, 20, 20)); // Palette.setColor(QPalette::Highlight, QColor(46, 108, 219)); Palette.setColor(QPalette::Highlight, QColor(41, 128, 185)); Palette.setColor(QPalette::HighlightedText, QColor(232, 232, 232)); Palette.setColor(QPalette::Link, QColor(41, 128, 185)); Palette.setColor(QPalette::Disabled, QPalette::Text, QColor(128, 128, 128)); Palette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(128, 128, 128)); Palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(128, 128, 128)); QApplication::setPalette(Palette); QFile StylesheetFile(QLatin1String(":/stylesheet/stylesheet.qss")); if (StylesheetFile.open(QIODevice::ReadOnly)) { QString Stylesheet = QString::fromLatin1(StylesheetFile.readAll()); qApp->setStyleSheet(Stylesheet); } } else { QApplication::setStyle(mDefaultStyle); QApplication::setPalette(qApp->style()->standardPalette()); qApp->setStyleSheet(QString()); } } void lcApplication::SaveTabLayout() const { if (!mProject || mProject->GetFileName().isEmpty()) return; QSettings Settings; QByteArray TabLayout = gMainWindow->GetTabLayout(); Settings.setValue(GetTabLayoutKey(), TabLayout); } QString lcApplication::GetTabLayoutKey() const { if (mProject) { QString FileName = mProject->GetFileName(); if (!FileName.isEmpty()) { FileName.replace('\\', '?'); FileName.replace('/', '?'); return QString("TabLayouts/%1").arg(FileName); } } return QString(); } void lcApplication::SetProject(Project* Project) { SaveTabLayout(); if (gMainWindow) { gMainWindow->RemoveAllModelTabs(); if (gMainWindow->GetPreviewWidget()) gMainWindow->GetPreviewWidget()->ClearPreview(); } delete mProject; mProject = Project; Project->SetActiveModel(0); lcGetPiecesLibrary()->RemoveTemporaryPieces(); if (mProject && !mProject->GetFileName().isEmpty() && mPreferences.mRestoreTabLayout) { QSettings Settings; QByteArray TabLayout = Settings.value(GetTabLayoutKey()).toByteArray(); if (gMainWindow) gMainWindow->RestoreTabLayout(TabLayout); } } void lcApplication::SetClipboard(const QByteArray& Clipboard) { mClipboard = Clipboard; gMainWindow->UpdatePaste(!mClipboard.isEmpty()); } void lcApplication::ExportClipboard(const QByteArray& Clipboard) { QMimeData* MimeData = new QMimeData(); MimeData->setData("application/vnd.leocad-clipboard", Clipboard); QApplication::clipboard()->setMimeData(MimeData); SetClipboard(Clipboard); } bool lcApplication::LoadPartsLibrary(const QList>& LibraryPaths, bool OnlyUsePaths, bool ShowProgress) { if (mLibrary == nullptr) mLibrary = new lcPiecesLibrary(); if (!OnlyUsePaths) { char* EnvPath = getenv("LEOCAD_LIB"); if (EnvPath && EnvPath[0]) return mLibrary->Load(EnvPath, ShowProgress); QString CustomPath = lcGetProfileString(LC_PROFILE_PARTS_LIBRARY); if (!CustomPath.isEmpty()) return mLibrary->Load(CustomPath, ShowProgress); } for (const QPair& LibraryPathEntry : LibraryPaths) { if (mLibrary->Load(LibraryPathEntry.first, ShowProgress)) { if (LibraryPathEntry.second) mLibrary->SetOfficialPieces(); return true; } } return false; } lcStartupMode lcApplication::Initialize(QList>& LibraryPaths) { bool OnlyUseLibraryPaths = false; bool SaveImage = false; bool SaveWavefront = false; bool Save3DS = false; bool SaveCOLLADA = false; bool SaveHTML = false; bool SetCameraAngles = false; bool SetCameraPosition = false; bool Orthographic = false; bool SetFoV = false; bool SetZPlanes = false; bool SetFadeStepsColor = false; bool SetHighlightColor = false; bool FadeSteps = mPreferences.mFadeSteps; bool ImageHighlight = mPreferences.mHighlightNewParts; int ImageWidth = lcGetProfileInt(LC_PROFILE_IMAGE_WIDTH); int ImageHeight = lcGetProfileInt(LC_PROFILE_IMAGE_HEIGHT); int AASamples = lcGetProfileInt(LC_PROFILE_ANTIALIASING_SAMPLES); int StudLogo = lcGetProfileInt(LC_PROFILE_STUD_LOGO); int ImageStart = 0; int ImageEnd = 0; lcVector3 CameraPosition[3] = {}; float CameraLatLon[2] = {0.0f, 0.0f}; float FoV = 0.0f; float ZPlanes[2] = {0.0f, 0.0f}; lcViewpoint Viewpoint = lcViewpoint::Count; quint32 FadeStepsColor = mPreferences.mFadeStepsColor; quint32 HighlightColor = mPreferences.mHighlightNewPartsColor; QString ImageName; QString ModelName; QString CameraName; QString ProjectName; QString SaveWavefrontName; QString Save3DSName; QString SaveCOLLADAName; QString SaveHTMLName; QStringList Arguments = arguments(); const int NumArguments = Arguments.size(); bool ParseOK = true; for (int ArgIdx = 1; ArgIdx < NumArguments; ArgIdx++) { const QString& Param = Arguments[ArgIdx]; if (Param.isEmpty()) continue; if (Param[0] != '-') { ProjectName = Param; // TODO: check file is readable continue; } auto ParseString = [&ArgIdx, &Arguments, NumArguments, &ParseOK](QString& Value, bool Required) { if (ArgIdx < NumArguments - 1 && Arguments[ArgIdx + 1][0] != '-') { ArgIdx++; Value = Arguments[ArgIdx]; } else if (Required) { printf("Not enough parameters for the '%s' option.\n", Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; return false; } return true; }; auto ParseInteger = [&ArgIdx, &Arguments, NumArguments, &ParseOK](int& Value) { if (ArgIdx < NumArguments - 1 && Arguments[ArgIdx + 1][0] != '-') { bool Ok = false; ArgIdx++; int NewValue = Arguments[ArgIdx].toInt(&Ok); if (Ok) { Value = NewValue; return true; } else printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Arguments[ArgIdx - 1].toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); } else printf("Not enough parameters for the '%s' option.\n", Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; return false; }; auto ParseFloat = [&ArgIdx, &Arguments, NumArguments, &ParseOK](float& Value) { if (ArgIdx < NumArguments - 1 && Arguments[ArgIdx + 1][0] != '-') { bool Ok = false; ArgIdx++; float NewValue = Arguments[ArgIdx].toFloat(&Ok); if (Ok) { Value = NewValue; return true; } else printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Arguments[ArgIdx - 1].toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); } else printf("Not enough parameters for the '%s' option.\n", Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; return false; }; auto ParseFloatArray = [&ArgIdx, &Arguments, NumArguments, &ParseOK](int Count, float* ValueArray, bool NegativesValid) { if (ArgIdx + Count >= NumArguments) { printf("Not enough parameters for the '%s' option.\n", Arguments[ArgIdx].toLatin1().constData()); ArgIdx += Count; ParseOK = false; return false; } for (int ParseIndex = 0; ParseIndex < Count; ParseIndex++) { if (NegativesValid || Arguments[ArgIdx+ParseIndex+1][0] != '-') { bool Ok = false; float NewValue = Arguments[ArgIdx+ParseIndex+1].toFloat(&Ok); if (Ok) { *(ValueArray++) = NewValue; continue; } printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Arguments[ArgIdx].toLatin1().constData(), Arguments[ArgIdx+ParseIndex+1].toLatin1().constData()); } else printf("Not enough parameters for the '%s' option.\n", Arguments[ArgIdx].toLatin1().constData()); ArgIdx += ParseIndex; ParseOK = false; return false; } ArgIdx += Count; return true; }; auto ParseColor = [&ArgIdx, &Arguments, NumArguments, &ParseOK](quint32& Color) { if (ArgIdx < NumArguments - 1 && Arguments[ArgIdx + 1][0] != '-') { ArgIdx++; QColor ParsedColor = QColor(Arguments[ArgIdx]); if (ParsedColor.isValid()) { Color = LC_RGBA(ParsedColor.red(), ParsedColor.green(), ParsedColor.blue(), ParsedColor.alpha()); return true; } else printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Arguments[ArgIdx - 1].toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); } else printf("Not enough parameters for the '%s' option.\n", Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; return false; }; if (Param == QLatin1String("-l") || Param == QLatin1String("--libpath")) { QString LibPath; ParseString(LibPath, true); if (!LibPath.isEmpty()) { LibraryPaths.clear(); LibraryPaths += qMakePair(LibPath, false); OnlyUseLibraryPaths = true; } } else if (Param == QLatin1String("-i") || Param == QLatin1String("--image")) { SaveImage = true; ParseString(ImageName, false); } else if (Param == QLatin1String("-w") || Param == QLatin1String("--width")) { if (ParseInteger(ImageWidth) && ImageWidth <= 0) { printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Param.toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; } } else if (Param == QLatin1String("-h") || Param == QLatin1String("--height")) { if (ParseInteger(ImageHeight) && ImageWidth <= 0) { printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Param.toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; } } else if (Param == QLatin1String("-f") || Param == QLatin1String("--from")) { if (ParseInteger(ImageStart) && ImageStart <= 0) { printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Param.toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; } } else if (Param == QLatin1String("-t") || Param == QLatin1String("--to")) { if (ParseInteger(ImageEnd) && (ImageEnd <= 0 || (ImageStart && ImageStart > ImageEnd))) { printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Param.toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; } } else if (Param == QLatin1String("-s") || Param == QLatin1String("--submodel")) ParseString(ModelName, true); else if (Param == QLatin1String("-c") || Param == QLatin1String("--camera")) ParseString(CameraName, true); else if (Param == QLatin1String("--viewpoint")) { QString ViewpointName; if (ParseString(ViewpointName, true)) { Viewpoint = lcCamera::GetViewpoint(ViewpointName); if (Viewpoint == lcViewpoint::Count) { printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Param.toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; } } } else if (Param == QLatin1String("--camera-angles")) { if ((SetCameraAngles = ParseFloatArray(2, CameraLatLon, true)) && (fabsf(CameraLatLon[0]) > 360.0f || fabsf(CameraLatLon[1]) > 360.0f)) { printf("Invalid parameter value(s) specified for the '%s' option: limits are +/- 360\n", Param.toLatin1().constData()); ParseOK = false; } } else if (Param == QLatin1String("--camera-position") || Param == QLatin1String("--camera-position-ldraw")) { if((SetCameraPosition = ParseFloatArray(9, CameraPosition[0], true))) { lcVector3 Front = CameraPosition[1] - CameraPosition[0]; if (Front.LengthSquared() < 1.0f || CameraPosition[2].LengthSquared() < 1.0f || fabsf(lcDot(lcNormalize(Front), lcNormalize(CameraPosition[2]))) > 0.99f) { printf("Invalid parameter value(s) specified for the '%s' option.\n", Param.toLatin1().constData()); ParseOK = false; } else if (Param == QLatin1String("--camera-position-ldraw")) { CameraPosition[0] = lcVector3LDrawToLeoCAD(CameraPosition[0]); CameraPosition[1] = lcVector3LDrawToLeoCAD(CameraPosition[1]); CameraPosition[2] = lcVector3LDrawToLeoCAD(CameraPosition[2]); } } } else if (Param == QLatin1String("--orthographic")) Orthographic = true; else if (Param == QLatin1String("--fov")) { if ((SetFoV = ParseFloat(FoV)) && fabsf(FoV) >= 180) { printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Param.toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; } } else if (Param == QLatin1String("--zplanes")) { if ((SetZPlanes = ParseFloatArray(2, ZPlanes, false)) && (ZPlanes[0] < 1.0 || ZPlanes[0] >= ZPlanes[1])) { printf("Invalid parameter value(s) specified for the '%s' option: requirements are: 1 <= < \n", Param.toLatin1().constData()); ParseOK = false; } } else if (Param == QLatin1String("--fade-steps")) FadeSteps = true; else if (Param == QLatin1String("--no-fade-steps")) FadeSteps = false; else if (Param == QLatin1String("--fade-steps-color")) { if (ParseColor(FadeStepsColor)) { SetFadeStepsColor = true; FadeSteps = true; } } else if (Param == QLatin1String("--highlight")) ImageHighlight = true; else if (Param == QLatin1String("--no-highlight")) ImageHighlight = false; else if (Param == QLatin1String("--highlight-color")) { if (ParseColor(HighlightColor)) { SetHighlightColor = true; ImageHighlight = true; } } else if (Param == QLatin1String("--shading")) { QString ShadingString; if (ParseString(ShadingString, true)) { if (ShadingString == QLatin1String("wireframe")) mPreferences.mShadingMode = lcShadingMode::Wireframe; else if (ShadingString == QLatin1String("flat")) mPreferences.mShadingMode = lcShadingMode::Flat; else if (ShadingString == QLatin1String("default")) mPreferences.mShadingMode = lcShadingMode::DefaultLights; else if (ShadingString == QLatin1String("full")) mPreferences.mShadingMode = lcShadingMode::Full; else { printf("Invalid parameter value specified for the '%s' option: '%s'\n", Param.toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; } } } else if (Param == QLatin1String("--line-width")) ParseFloat(mPreferences.mLineWidth); else if (Param == QLatin1String("--aa-samples")) { if (ParseInteger(AASamples) && AASamples != 1 && AASamples != 2 && AASamples != 4 && AASamples != 8) { printf("Invalid parameter value specified for the '%s' option: '%s'.\n", Param.toLatin1().constData(), Arguments[ArgIdx].toLatin1().constData()); ParseOK = false; } } else if (Param == QLatin1String("-sl") || Param == QLatin1String("--stud-logo")) { ParseInteger(StudLogo); if (StudLogo != lcGetProfileInt(LC_PROFILE_STUD_LOGO)) { lcGetPiecesLibrary()->SetStudLogo(StudLogo, false); } } else if (Param == QLatin1String("-obj") || Param == QLatin1String("--export-wavefront")) { SaveWavefront = true; ParseString(SaveWavefrontName, false); } else if (Param == QLatin1String("-3ds") || Param == QLatin1String("--export-3ds")) { Save3DS = true; ParseString(Save3DSName, false); } else if (Param == QLatin1String("-dae") || Param == QLatin1String("--export-collada")) { SaveCOLLADA = true; ParseString(SaveCOLLADAName, false); } else if (Param == QLatin1String("-html") || Param == QLatin1String("--export-html")) { SaveHTML = true; ParseString(SaveHTMLName, false); } else if (Param == QLatin1String("-v") || Param == QLatin1String("--version")) { #ifdef LC_CONTINUOUS_BUILD printf("LeoCAD Continuous Build " QT_STRINGIFY(LC_CONTINUOUS_BUILD) "\n"); #else printf("LeoCAD Version " LC_VERSION_TEXT "\n"); #endif printf("Compiled " __DATE__ "\n"); return lcStartupMode::Success; } else if (Param == QLatin1String("-?") || Param == QLatin1String("--help")) { printf("Usage: leocad [options] [file]\n"); printf(" [options] can be:\n"); printf(" -l, --libpath : Set the Parts Library location to path.\n"); printf(" -i, --image : Save a picture in the format specified by ext and exit.\n"); printf(" -w, --width : Set the picture width.\n"); printf(" -h, --height : Set the picture height.\n"); printf(" -f, --from : Set the first step to save pictures.\n"); printf(" -t, --to : Set the last step to save pictures.\n"); printf(" -s, --submodel : Set the active submodel.\n"); printf(" -c, --camera : Set the active camera.\n"); printf(" -sl, --stud-logo : Set the stud logo type 0 - 5, 0 is no logo.\n"); printf(" --viewpoint : Set the viewpoint.\n"); printf(" --camera-angles : Set the camera angles in degrees around the model.\n"); printf(" --camera-position : Set the camera position, target and up vector.\n"); printf(" --camera-position-ldraw : Set the camera position, target and up vector using LDraw coordinates.\n"); printf(" --orthographic: Render images using an orthographic projection.\n"); printf(" --fov : Set the vertical field of view used to render images (< 180).\n"); printf(" --zplanes : Set the near and far clipping planes used to render images (1 <= < ).\n"); printf(" --fade-steps: Render parts from prior steps faded.\n"); printf(" --no-fade-steps: Do not render parts from prior steps faded.\n"); printf(" --fade-steps-color : Renderinng color for prior step parts (#AARRGGBB).\n"); printf(" --highlight: Highlight parts in the steps they appear.\n"); printf(" --no-highlight: Do not highlight parts in the steps they appear.\n"); printf(" --highlight-color: Renderinng color for highlighted parts (#AARRGGBB).\n"); printf(" --shading : Select shading mode for rendering.\n"); printf(" --line-width : Set the with of the edge lines.\n"); printf(" --aa-samples : AntiAliasing sample size (1, 2, 4, or 8).\n"); printf(" -obj, --export-wavefront : Export the model to Wavefront OBJ format.\n"); printf(" -3ds, --export-3ds : Export the model to 3D Studio 3DS format.\n"); printf(" -dae, --export-collada : Export the model to COLLADA DAE format.\n"); printf(" -html, --export-html : Create an HTML page for the model.\n"); printf(" -v, --version: Output version information and exit.\n"); printf(" -?, --help: Display this help message and exit.\n"); printf(" \n"); return lcStartupMode::Success; } else { printf("Unknown option: '%s'\n", Param.toLatin1().constData()); ParseOK = false; } } if (!ParseOK) return lcStartupMode::Error; const bool SaveAndExit = (SaveImage || SaveWavefront || Save3DS || SaveCOLLADA || SaveHTML); if (SaveAndExit && ProjectName.isEmpty()) { printf("No file name specified.\n"); return lcStartupMode::Error; } if (!InitializeRenderer(AASamples)) return lcStartupMode::Error; if (!SaveAndExit) { gMainWindow = new lcMainWindow(); lcLoadDefaultKeyboardShortcuts(); lcLoadDefaultMouseShortcuts(); } if (!LoadPartsLibrary(LibraryPaths, OnlyUseLibraryPaths, !SaveAndExit)) { QString Message; if (mLibrary->LoadBuiltinPieces()) Message = tr("LeoCAD could not find a compatible Parts Library so only a small number of parts will be available.\n\nPlease visit https://www.leocad.org for information on how to download and install a library."); else Message = tr("LeoCAD could not load Parts Library.\n\nPlease visit https://www.leocad.org for information on how to download and install a library."); if (!SaveAndExit) QMessageBox::information(gMainWindow, tr("LeoCAD"), Message); else fprintf(stderr, "%s", Message.toLatin1().constData()); } if (!SaveAndExit) gMainWindow->CreateWidgets(); Project* NewProject = new Project(); SetProject(NewProject); if (!SaveAndExit && ProjectName.isEmpty() && lcGetProfileInt(LC_PROFILE_AUTOLOAD_MOSTRECENT)) ProjectName = lcGetProfileString(LC_PROFILE_RECENT_FILE1); bool ProjectLoaded = false; if (!ProjectName.isEmpty()) { if (gMainWindow) gMainWindow->OpenProject(ProjectName); else { Project* LoadedProject = new Project(); if (LoadedProject->Load(ProjectName)) { SetProject(LoadedProject); ProjectLoaded = true; } else { delete LoadedProject; } } } if (ProjectLoaded) { if (!ModelName.isEmpty()) mProject->SetActiveModel(ModelName); std::unique_ptr ActiveView; if (SaveImage) { lcModel* Model; if (!ModelName.isEmpty()) { Model = mProject->GetModel(ModelName); if (!Model) { printf("Error: model '%s' does not exist.\n", ModelName.toLatin1().constData()); return lcStartupMode::Error; } } else Model = mProject->GetMainModel(); ActiveView = std::unique_ptr(new lcView(lcViewType::View, Model)); ActiveView->SetOffscreenContext(); ActiveView->MakeCurrent(); } if (SaveImage) ActiveView->SetSize(ImageWidth, ImageHeight); if (ActiveView) { if (!CameraName.isEmpty()) { ActiveView->SetCamera(CameraName); if (Viewpoint != lcViewpoint::Count) printf("Warning: --viewpoint is ignored when --camera is set.\n"); if (Orthographic) printf("Warning: --orthographic is ignored when --camera is set.\n"); if (SetCameraAngles) printf("Warning: --camera-angles is ignored when --camera is set.\n"); if (SetCameraPosition) printf("Warning: --camera-position is ignored when --camera is set.\n"); } else { ActiveView->SetProjection(Orthographic); if (SetFoV) ActiveView->GetCamera()->m_fovy = FoV; if (SetZPlanes) { lcCamera* Camera = ActiveView->GetCamera(); Camera->m_zNear = ZPlanes[0]; Camera->m_zFar = ZPlanes[1]; } if (Viewpoint != lcViewpoint::Count) { ActiveView->SetViewpoint(Viewpoint); if (SetCameraAngles) printf("Warning: --camera-angles is ignored when --viewpoint is set.\n"); if (SetCameraPosition) printf("Warning: --camera-position is ignored when --viewpoint is set.\n"); } else if (SetCameraAngles) { ActiveView->SetCameraAngles(CameraLatLon[0], CameraLatLon[1]); if (SetCameraPosition) printf("Warning: --camera-position is ignored when --camera-angles is set.\n"); } else if (SetCameraPosition) { ActiveView->SetViewpoint(CameraPosition[0], CameraPosition[1], CameraPosition[2]); } } } if (SaveImage) { lcModel* ActiveModel = ActiveView->GetModel(); if (ImageName.isEmpty()) ImageName = mProject->GetImageFileName(true); if (ImageEnd < ImageStart) ImageEnd = ImageStart; else if (ImageStart > ImageEnd) ImageStart = ImageEnd; if ((ImageStart == 0) && (ImageEnd == 0)) ImageStart = ImageEnd = ActiveModel->GetCurrentStep(); else if ((ImageStart == 0) && (ImageEnd != 0)) ImageStart = ImageEnd; else if ((ImageStart != 0) && (ImageEnd == 0)) ImageEnd = ImageStart; if (ImageStart > 255) ImageStart = 255; if (ImageEnd > 255) ImageEnd = 255; QString Frame; if (ImageStart != ImageEnd) { QString Extension = QFileInfo(ImageName).suffix(); Frame = ImageName.left(ImageName.length() - Extension.length() - 1) + QLatin1String("%1.") + Extension; } else Frame = ImageName; mPreferences.mFadeSteps = FadeSteps; if (SetFadeStepsColor) mPreferences.mFadeStepsColor = FadeStepsColor; mPreferences.mHighlightNewParts = ImageHighlight; if (SetHighlightColor) mPreferences.mHighlightNewPartsColor = HighlightColor; if (CameraName.isEmpty() && !SetCameraPosition) ActiveView->ZoomExtents(); ActiveView->SaveStepImages(Frame, ImageStart != ImageEnd, ImageStart, ImageEnd); } if (SaveWavefront) { QString FileName; if (!SaveWavefrontName.isEmpty()) FileName = SaveWavefrontName; else FileName = ProjectName; QString Extension = QFileInfo(FileName).suffix().toLower(); if (Extension.isEmpty()) { FileName += ".obj"; } else if (Extension != "obj") { FileName = FileName.left(FileName.length() - Extension.length() - 1); FileName += ".obj"; } mProject->ExportWavefront(FileName); } if (Save3DS) { QString FileName; if (!Save3DSName.isEmpty()) FileName = Save3DSName; else FileName = ProjectName; QString Extension = QFileInfo(FileName).suffix().toLower(); if (Extension.isEmpty()) { FileName += ".3ds"; } else if (Extension != "3ds") { FileName = FileName.left(FileName.length() - Extension.length() - 1); FileName += ".3ds"; } mProject->Export3DStudio(FileName); } if (SaveCOLLADA) { QString FileName; if (!SaveCOLLADAName.isEmpty()) FileName = SaveCOLLADAName; else FileName = ProjectName; QString Extension = QFileInfo(FileName).suffix().toLower(); if (Extension.isEmpty()) { FileName += ".dae"; } else if (Extension != "dae") { FileName = FileName.left(FileName.length() - Extension.length() - 1); FileName += ".dae"; } mProject->ExportCOLLADA(FileName); } if (SaveHTML) { lcHTMLExportOptions Options(mProject); if (!SaveHTMLName.isEmpty()) Options.PathName = SaveHTMLName; mProject->ExportHTML(Options); } } if (!SaveAndExit) { gMainWindow->SetColorIndex(lcGetColorIndex(7)); gMainWindow->GetPartSelectionWidget()->SetDefaultPart(); gMainWindow->UpdateRecentFiles(); gMainWindow->show(); } return SaveAndExit ? lcStartupMode::Success : lcStartupMode::ShowWindow; } void lcApplication::Shutdown() { delete gMainWindow; gMainWindow = nullptr; delete mProject; mProject = nullptr; delete mLibrary; mLibrary = nullptr; ShutdownRenderer(); } bool lcApplication::InitializeRenderer(int AASamples) { if (AASamples > 1) { QSurfaceFormat Format = QSurfaceFormat::defaultFormat(); Format.setSamples(AASamples); QSurfaceFormat::setDefaultFormat(Format); } if (!lcContext::CreateOffscreenContext()) return false; return true; } void lcApplication::ShutdownRenderer() { lcContext::DestroyOffscreenContext(); } void lcApplication::ShowPreferencesDialog() { lcPreferencesDialogOptions Options; int CurrentAASamples = lcGetProfileInt(LC_PROFILE_ANTIALIASING_SAMPLES); int CurrentStudLogo = lcGetProfileInt(LC_PROFILE_STUD_LOGO); Options.Preferences = mPreferences; Options.LibraryPath = lcGetProfileString(LC_PROFILE_PARTS_LIBRARY); Options.MinifigSettingsPath = lcGetProfileString(LC_PROFILE_MINIFIG_SETTINGS); Options.ColorConfigPath = lcGetProfileString(LC_PROFILE_COLOR_CONFIG); Options.POVRayPath = lcGetProfileString(LC_PROFILE_POVRAY_PATH); Options.LGEOPath = lcGetProfileString(LC_PROFILE_POVRAY_LGEO_PATH); Options.DefaultAuthor = lcGetProfileString(LC_PROFILE_DEFAULT_AUTHOR_NAME); Options.Language = lcGetProfileString(LC_PROFILE_LANGUAGE); Options.CheckForUpdates = lcGetProfileInt(LC_PROFILE_CHECK_UPDATES); Options.AASamples = CurrentAASamples; Options.StudLogo = CurrentStudLogo; Options.Categories = gCategories; Options.CategoriesModified = false; Options.CategoriesDefault = false; Options.KeyboardShortcuts = gKeyboardShortcuts; Options.KeyboardShortcutsModified = false; Options.KeyboardShortcutsDefault = false; Options.MouseShortcuts = gMouseShortcuts; Options.MouseShortcutsModified = false; Options.MouseShortcutsDefault = false; lcQPreferencesDialog Dialog(gMainWindow, &Options); if (Dialog.exec() != QDialog::Accepted) return; bool LanguageChanged = Options.Language != lcGetProfileString(LC_PROFILE_LANGUAGE); bool LibraryChanged = Options.LibraryPath != lcGetProfileString(LC_PROFILE_PARTS_LIBRARY); bool ColorsChanged = Options.ColorConfigPath != lcGetProfileString(LC_PROFILE_COLOR_CONFIG); bool AAChanged = CurrentAASamples != Options.AASamples; bool StudLogoChanged = CurrentStudLogo != Options.StudLogo; mPreferences = Options.Preferences; mPreferences.SaveDefaults(); UpdateStyle(); lcSetProfileString(LC_PROFILE_DEFAULT_AUTHOR_NAME, Options.DefaultAuthor); lcSetProfileString(LC_PROFILE_PARTS_LIBRARY, Options.LibraryPath); lcSetProfileString(LC_PROFILE_COLOR_CONFIG, Options.ColorConfigPath); lcSetProfileString(LC_PROFILE_MINIFIG_SETTINGS, Options.MinifigSettingsPath); lcSetProfileString(LC_PROFILE_POVRAY_PATH, Options.POVRayPath); lcSetProfileString(LC_PROFILE_POVRAY_LGEO_PATH, Options.LGEOPath); lcSetProfileString(LC_PROFILE_LANGUAGE, Options.Language); lcSetProfileInt(LC_PROFILE_CHECK_UPDATES, Options.CheckForUpdates); lcSetProfileInt(LC_PROFILE_ANTIALIASING_SAMPLES, Options.AASamples); lcSetProfileInt(LC_PROFILE_STUD_LOGO, Options.StudLogo); if (LanguageChanged || LibraryChanged || ColorsChanged || AAChanged) QMessageBox::information(gMainWindow, tr("LeoCAD"), tr("Some changes will only take effect the next time you start LeoCAD.")); if (Options.CategoriesModified) { if (Options.CategoriesDefault) lcResetDefaultCategories(); else { gCategories = Options.Categories; lcSaveDefaultCategories(); } gMainWindow->UpdateCategories(); } if (Options.KeyboardShortcutsModified) { if (Options.KeyboardShortcutsDefault) lcResetDefaultKeyboardShortcuts(); else { gKeyboardShortcuts = Options.KeyboardShortcuts; lcSaveDefaultKeyboardShortcuts(); } gMainWindow->UpdateShortcuts(); } if (Options.MouseShortcutsModified) { if (Options.MouseShortcutsDefault) lcResetDefaultMouseShortcuts(); else { gMouseShortcuts = Options.MouseShortcuts; lcSaveDefaultMouseShortcuts(); } } if (StudLogoChanged) { lcSetProfileInt(LC_PROFILE_STUD_LOGO, Options.StudLogo); lcGetPiecesLibrary()->SetStudLogo(Options.StudLogo, true); } // TODO: printing preferences /* strcpy(opts.strFooter, m_strFooter); strcpy(opts.strHeader, m_strHeader); */ gMainWindow->SetShadingMode(Options.Preferences.mShadingMode); lcView::UpdateAllViews(); }