diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..67929f0b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Version (please complete the following information):** + - OS: [e.g. Windows 10, Ubuntu, macOS] + - LeoCAD Version [e.g. 19.07 or continuous] + +**Crash information:** +Some versions of LeoCAD will show a message that they saved a minidump file when they crash, if you see this message please attach the file. + +**Additional context** +Add any other context about the problem here. If you're seeing a graphical issue, adding a screenshot of the About Dialog may help. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..bbcbbe7d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index 0e8cf2fd..fcee8ad4 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ *.dae *.3ds *.obj +.vs build debug release diff --git a/.travis.yml b/.travis.yml index c6696937..5279eb16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,9 @@ language: cpp -matrix: +jobs: include: - os: linux - dist: trusty - addons: - apt: - sources: - - sourceline: "ppa:beineri/opt-qt487-trusty" - packages: - - opt-qt4-qmake opt-qt4-dev-tools opt-libqt4-dev opt-libqt4-dev-bin opt-libqt4-opengl-dev - sudo: required - compiler: gcc - env: - - QT_BASE=48 - - os: linux + name: Linux amd64 dist: trusty addons: apt: @@ -22,76 +11,90 @@ matrix: - sourceline: "ppa:beineri/opt-qt-5.10.1-trusty" packages: - qt510base - sudo: required compiler: gcc - env: - - QT_BASE=510 + script: + - source /opt/qt*/bin/qt*-env.sh + - qmake PREFIX=/usr -v + - qmake PREFIX=/usr + - make -j$(nproc) + after_success: + - make install INSTALL_ROOT=AppDir + - wget https://github.com/leozide/leocad/releases/download/v19.07.1/Library-20.03.zip -O library.zip + - unzip library.zip + - mkdir -p AppDir/usr/share/leocad + - mv library.bin AppDir/usr/share/leocad/library.bin + - wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" + - chmod a+x linuxdeployqt*.AppImage + - unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH + - export VERSION=$(git rev-parse --short HEAD) + - ./linuxdeployqt*.AppImage ./AppDir/usr/share/applications/*.desktop -bundle-non-qt-libs + - ./linuxdeployqt*.AppImage --appimage-extract + - export PATH=$(readlink -f ./squashfs-root/usr/bin/):$PATH + - ./squashfs-root/usr/bin/appimagetool AppDir/ + - mv ./LeoCAD-$VERSION-x86_64.AppImage ./LeoCAD-Linux-$VERSION-x86_64.AppImage + - 'curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/leozide/leocad/commits/master -o repo.txt' + - 'export REMOTE=$(grep -Po ''(?<=: \")(([a-z0-9])\w+)(?=\")'' -m 1 repo.txt)' + - export LOCAL=$(git rev-parse HEAD) + - if [[ "$REMOTE" != "$LOCAL" ]]; then echo "Build no longer current. $REMOTE vs $LOCAL - aborting upload."; exit 0; fi; + - wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh + - bash upload.sh LeoCAD*.AppImage* + + - os: linux + name: Linux arm64 + arch: arm64 + dist: focal + addons: + apt: + packages: + - qt5-default + - qtbase5-dev + - qttools5-dev-tools + - libqt5opengl5-dev + - zip + compiler: gcc + script: + - qmake -v + - qmake PREFIX=/usr + - make -j$(nproc) + after_success: + - cp build/release/leocad . + - export VERSION=$(git rev-parse --short HEAD) + - zip LeoCAD-Linux-$VERSION-arm64.zip leocad + - 'curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/leozide/leocad/commits/master -o repo.txt' + - 'export REMOTE=$(grep -Po ''(?<=: \")(([a-z0-9])\w+)(?=\")'' -m 1 repo.txt)' + - export LOCAL=$(git rev-parse HEAD) + - if [[ "$REMOTE" != "$LOCAL" ]]; then echo "Build no longer current. $REMOTE vs $LOCAL - aborting upload."; travis_terminate 0; fi; + - wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh + - bash upload.sh LeoCAD*.zip + - os: osx + name: macOS + osx_image: xcode11 compiler: clang - env: - - QT_BASE=57 - -before_install: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - brew update; - fi - -install: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - brew install qt5 grep; - brew link --force qt5; - wget https://github.com/leozide/leocad/releases/download/v18.02/Library-Linux-11494.zip -O library.zip; - unzip library.zip; - fi - -script: - - | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - source /opt/qt*/bin/qt*-env.sh - qmake PREFIX=/usr -v - qmake PREFIX=/usr -r - make -j$(nproc); export COMPILE_RESULT=$? - if [[ "$QT_BASE" != "510" ]]; then exit $COMPILE_RESULT; fi - make install INSTALL_ROOT=AppDir - if [[ "$TRAVIS_TAG" != "" ]]; then - wget https://github.com/leozide/leocad/releases/download/v18.02/Library-Linux-11494.zip -O library.zip; - unzip library.zip; - mkdir -p AppDir/usr/share/leocad; - mv library.bin AppDir/usr/share/leocad/library.bin; - fi - wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" - chmod a+x linuxdeployqt*.AppImage - unset QTDIR; unset QT_PLUGIN_PATH ; unset LD_LIBRARY_PATH - export VERSION=$(git rev-parse --short HEAD) - ./linuxdeployqt*.AppImage ./AppDir/usr/share/applications/*.desktop -bundle-non-qt-libs - ./linuxdeployqt*.AppImage --appimage-extract - export PATH=$(readlink -f ./squashfs-root/usr/bin/):$PATH - ./squashfs-root/usr/bin/appimagetool AppDir/ - mv ./LeoCAD-$VERSION-x86_64.AppImage ./LeoCAD-Linux-$VERSION-x86_64.AppImage - elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - qmake PREFIX=/usr -v - qmake PREFIX=/usr -r - make -j$(sysctl -n hw.ncpu) - cd build/release - macdeployqt LeoCAD.app -dmg - mv LeoCAD.dmg LeoCAD-macOS-$(git rev-parse --short HEAD).dmg - fi - -after_success: - - | - export GREP_PATH=grep; - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export GREP_PATH=ggrep; fi - curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/leozide/leocad/commits/master -o repo.txt; - export REMOTE=$($GREP_PATH -Po '(?<=: \")(([a-z0-9])\w+)(?=\")' -m 1 repo.txt); - export LOCAL=$(git rev-parse HEAD); - if [[ "$REMOTE" != "$LOCAL" ]]; then echo "Build no longer current. $REMOTE vs $LOCAL - aborting upload."; exit 0; fi; - if [[ "$QT_BASE" = "510" && "$TRAVIS_OS_NAME" = "linux" ]]; then - wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh - bash upload.sh LeoCAD*.AppImage* - elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh - bash upload.sh LeoCAD*.dmg* - fi + addons: + homebrew: + packages: + - grep + - qt5 + install: + - brew link --force qt5 + - wget https://github.com/leozide/povray/releases/download/continuous/povray + - wget https://github.com/leozide/leocad/releases/download/v19.07.1/Library-20.03.zip -O library.zip + - unzip library.zip + script: + - qmake -v + - qmake + - make -j$(sysctl -n hw.ncpu) + after_success: + - cd build/release + - macdeployqt LeoCAD.app -dmg + - mv LeoCAD.dmg LeoCAD-macOS-$(git rev-parse --short HEAD).dmg + - 'curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/repos/leozide/leocad/commits/master -o repo.txt' + - 'export REMOTE=$(ggrep -Po ''(?<=: \")(([a-z0-9])\w+)(?=\")'' -m 1 repo.txt)' + - export LOCAL=$(git rev-parse HEAD) + - if [[ "$REMOTE" != "$LOCAL" ]]; then echo "Build no longer current. $REMOTE vs $LOCAL - aborting upload."; exit 0; fi; + - wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh + - bash upload.sh LeoCAD*.dmg* branches: except: diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..cc5d66c2 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,48 @@ +version: '{branch}.{build}' +skip_tags: true +image: +- Visual Studio 2019 +configuration: Release +platform: x64 + +environment: + GITHUB_TOKEN: + secure: +EZPzYX4wUEc6MYg4kBLx9TogAqeeeWUPgtEW3VtAJATrBtxwpuOQIHrrR4hbc7a + +before_build: +- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" +- set PATH=C:\Qt\5.15\msvc2019_64\bin;%PATH% +- git rev-parse --short HEAD > version.txt +- set /p VERSION= < version.txt +- qmake -v +- qmake -tp vc + +build: + project: leocad.vcxproj + verbosity: minimal + +after_build: +- 7z a symbols.zip build/release/leocad.exe build/release/leocad.pdb +- mkdir appdir +- copy build\release\leocad.exe appdir +- copy docs\readme.txt appdir +- appveyor DownloadFile https://github.com/leozide/leocad/releases/download/v19.07.1/Library-20.03.zip -FileName library.zip +- 7z e library.zip +- copy library.bin appdir\\library.bin +- appveyor DownloadFile https://github.com/leozide/povray/releases/download/continuous/povconsole32-sse2.exe -FileName appdir\povconsole32-sse2.exe +- windeployqt appdir\leocad.exe +- copy tools\setup\leocad.nsi . +- copy tools\setup\setup.ico . +- 'copy "%VCToolsRedistDir%\vcredist_x64.exe" appdir' +- '"C:\Program Files (x86)\NSIS\makensis.exe" /V4 /DX64 "/XOutFile LeoCAD-Windows-%VERSION%.exe" leocad.nsi' +- set TRAVIS_TAG=%APPVEYOR_REPO_TAG_NAME% +- set TRAVIS_REPO_SLUG=%APPVEYOR_REPO_NAME% +- set TRAVIS_COMMIT=%APPVEYOR_REPO_COMMIT% +- appveyor DownloadFile https://github.com/probonopd/uploadtool/raw/master/upload.sh +- if not defined APPVEYOR_PULL_REQUEST_NUMBER (bash upload.sh LeoCAD*.exe) + +artifacts: +- path: symbols.zip + name: symbols +- path: LeoCAD-Windows-$(VERSION).exe + name: leocad diff --git a/common/camera.cpp b/common/camera.cpp index c2f408bb..e8d64ae3 100644 --- a/common/camera.cpp +++ b/common/camera.cpp @@ -28,9 +28,9 @@ lcCamera::lcCamera(bool Simple) mTargetPosition = lcVector3(0.0f, 0.0f, 0.0f); mUpVector = lcVector3(-0.2357f, -0.2357f, 0.94281f); - ChangeKey(mPositionKeys, mPosition, 1, true); - ChangeKey(mTargetPositionKeys, mTargetPosition, 1, true); - ChangeKey(mUpVectorKeys, mUpVector, 1, true); + mPositionKeys.ChangeKey(mPosition, 1, true); + mTargetPositionKeys.ChangeKey(mTargetPosition, 1, true); + mUpVectorKeys.ChangeKey(mUpVector, 1, true); UpdatePosition(1); } @@ -51,9 +51,9 @@ lcCamera::lcCamera(float ex, float ey, float ez, float tx, float ty, float tz) Initialize(); - ChangeKey(mPositionKeys, lcVector3(ex, ey, ez), 1, true); - ChangeKey(mTargetPositionKeys, lcVector3(tx, ty, tz), 1, true); - ChangeKey(mUpVectorKeys, UpVector, 1, true); + mPositionKeys.ChangeKey(lcVector3(ex, ey, ez), 1, true); + mTargetPositionKeys.ChangeKey(lcVector3(tx, ty, tz), 1, true); + mUpVectorKeys.ChangeKey(UpVector, 1, true); UpdatePosition(1); } @@ -62,23 +62,50 @@ lcCamera::~lcCamera() { } +lcViewpoint lcCamera::GetViewpoint(const QString& ViewpointName) +{ + const QLatin1String ViewpointNames[] = + { + QLatin1String("front"), + QLatin1String("back"), + QLatin1String("top"), + QLatin1String("bottom"), + QLatin1String("left"), + QLatin1String("right"), + QLatin1String("home") + }; + + LC_ARRAY_SIZE_CHECK(ViewpointNames, lcViewpoint::Count); + + for (int ViewpointIndex = 0; ViewpointIndex < static_cast(lcViewpoint::Count); ViewpointIndex++) + if (ViewpointNames[ViewpointIndex] == ViewpointName) + return static_cast(ViewpointIndex); + + return lcViewpoint::Count; +} + void lcCamera::Initialize() { m_fovy = 30.0f; m_zNear = 25.0f; m_zFar = 50000.0f; mState = 0; - memset(m_strName, 0, sizeof(m_strName)); +} + +void lcCamera::SetName(const QString& Name) +{ + mName = Name; } void lcCamera::CreateName(const lcArray& Cameras) { - if (m_strName[0]) + if (!mName.isEmpty()) { bool Found = false; - for (int CameraIdx = 0; CameraIdx < Cameras.GetSize(); CameraIdx++) + + for (const lcCamera* Camera : Cameras) { - if (!strcmp(Cameras[CameraIdx]->m_strName, m_strName)) + if (Camera->GetName() == mName) { Found = true; break; @@ -89,16 +116,24 @@ void lcCamera::CreateName(const lcArray& Cameras) return; } - int i, max = 0; - const char* Prefix = "Camera "; + int MaxCameraNumber = 0; + const QLatin1String Prefix("Camera "); - for (int CameraIdx = 0; CameraIdx < Cameras.GetSize(); CameraIdx++) - if (strncmp(Cameras[CameraIdx]->m_strName, Prefix, strlen(Prefix)) == 0) - if (sscanf(Cameras[CameraIdx]->m_strName + strlen(Prefix), " %d", &i) == 1) - if (i > max) - max = i; + for (const lcCamera* Camera : Cameras) + { + QString CameraName = Camera->GetName(); - sprintf(m_strName, "%s %d", Prefix, max+1); + if (CameraName.startsWith(Prefix)) + { + bool Ok = false; + int CameraNumber = CameraName.midRef(Prefix.size()).toInt(&Ok); + + if (Ok && CameraNumber > MaxCameraNumber) + MaxCameraNumber = CameraNumber; + } + } + + mName = Prefix + QString::number(MaxCameraNumber + 1); } void lcCamera::SaveLDraw(QTextStream& Stream) const @@ -108,17 +143,17 @@ void lcCamera::SaveLDraw(QTextStream& Stream) const Stream << QLatin1String("0 !LEOCAD CAMERA FOV ") << m_fovy << QLatin1String(" ZNEAR ") << m_zNear << QLatin1String(" ZFAR ") << m_zFar << LineEnding; if (mPositionKeys.GetSize() > 1) - SaveKeysLDraw(Stream, mPositionKeys, "CAMERA POSITION_KEY "); + mPositionKeys.SaveKeysLDraw(Stream, "CAMERA POSITION_KEY "); else Stream << QLatin1String("0 !LEOCAD CAMERA POSITION ") << mPosition[0] << ' ' << mPosition[1] << ' ' << mPosition[2] << LineEnding; if (mTargetPositionKeys.GetSize() > 1) - SaveKeysLDraw(Stream, mTargetPositionKeys, "CAMERA TARGET_POSITION_KEY "); + mTargetPositionKeys.SaveKeysLDraw(Stream, "CAMERA TARGET_POSITION_KEY "); else Stream << QLatin1String("0 !LEOCAD CAMERA TARGET_POSITION ") << mTargetPosition[0] << ' ' << mTargetPosition[1] << ' ' << mTargetPosition[2] << LineEnding; if (mUpVectorKeys.GetSize() > 1) - SaveKeysLDraw(Stream, mUpVectorKeys, "CAMERA UP_VECTOR_KEY "); + mUpVectorKeys.SaveKeysLDraw(Stream, "CAMERA UP_VECTOR_KEY "); else Stream << QLatin1String("0 !LEOCAD CAMERA UP_VECTOR ") << mUpVector[0] << ' ' << mUpVector[1] << ' ' << mUpVector[2] << LineEnding; @@ -130,7 +165,7 @@ void lcCamera::SaveLDraw(QTextStream& Stream) const if (IsOrtho()) Stream << QLatin1String("ORTHOGRAPHIC "); - Stream << QLatin1String("NAME ") << m_strName << LineEnding; + Stream << QLatin1String("NAME ") << mName << LineEnding; } bool lcCamera::ParseLDrawLine(QTextStream& Stream) @@ -153,30 +188,27 @@ bool lcCamera::ParseLDrawLine(QTextStream& Stream) else if (Token == QLatin1String("POSITION")) { Stream >> mPosition[0] >> mPosition[1] >> mPosition[2]; - ChangeKey(mPositionKeys, mPosition, 1, true); + mPositionKeys.ChangeKey(mPosition, 1, true); } else if (Token == QLatin1String("TARGET_POSITION")) { Stream >> mTargetPosition[0] >> mTargetPosition[1] >> mTargetPosition[2]; - ChangeKey(mTargetPositionKeys, mTargetPosition, 1, true); + mTargetPositionKeys.ChangeKey(mTargetPosition, 1, true); } else if (Token == QLatin1String("UP_VECTOR")) { Stream >> mUpVector[0] >> mUpVector[1] >> mUpVector[2]; - ChangeKey(mUpVectorKeys, mUpVector, 1, true); + mUpVectorKeys.ChangeKey(mUpVector, 1, true); } else if (Token == QLatin1String("POSITION_KEY")) - LoadKeysLDraw(Stream, mPositionKeys); + mPositionKeys.LoadKeysLDraw(Stream); else if (Token == QLatin1String("TARGET_POSITION_KEY")) - LoadKeysLDraw(Stream, mTargetPositionKeys); + mTargetPositionKeys.LoadKeysLDraw(Stream); else if (Token == QLatin1String("UP_VECTOR_KEY")) - LoadKeysLDraw(Stream, mUpVectorKeys); + mUpVectorKeys.LoadKeysLDraw(Stream); else if (Token == QLatin1String("NAME")) { - QString Name = Stream.readAll().trimmed(); - QByteArray NameUtf = Name.toUtf8(); // todo: replace with qstring - strncpy(m_strName, NameUtf.constData(), sizeof(m_strName)); - m_strName[sizeof(m_strName) - 1] = 0; + mName = Stream.readAll().trimmed(); return true; } } @@ -212,13 +244,6 @@ bool lcCamera::FileLoad(lcFile& file) file.ReadU16(&time, 1); file.ReadFloats(param, 4); file.ReadU8(&type, 1); - - if (type == 0) - ChangeKey(mPositionKeys, lcVector3(param[0], param[1], param[2]), time, true); - else if (type == 1) - ChangeKey(mTargetPositionKeys, lcVector3(param[0], param[1], param[2]), time, true); - else if (type == 2) - ChangeKey(mUpVectorKeys, lcVector3(param[0], param[1], param[2]), time, true); } file.ReadU32(&n, 1); @@ -232,40 +257,25 @@ bool lcCamera::FileLoad(lcFile& file) if (version == 4) { - file.ReadBuffer(m_strName, 80); - m_strName[80] = 0; + char Name[81]; + file.ReadBuffer(Name, 80); } else { ch = file.ReadU8(); if (ch == 0xFF) - return false; // don't read CString - file.ReadBuffer(m_strName, ch); - m_strName[ch] = 0; + return false; + char Name[81]; + file.ReadBuffer(Name, ch); } if (version < 3) { double d[3]; - float f[3]; file.ReadDoubles(d, 3); - f[0] = (float)d[0]; - f[1] = (float)d[1]; - f[2] = (float)d[2]; - ChangeKey(mPositionKeys, lcVector3(f[0], f[1], f[2]), 1, true); - file.ReadDoubles(d, 3); - f[0] = (float)d[0]; - f[1] = (float)d[1]; - f[2] = (float)d[2]; - ChangeKey(mTargetPositionKeys, lcVector3(f[0], f[1], f[2]), 1, true); - file.ReadDoubles(d, 3); - f[0] = (float)d[0]; - f[1] = (float)d[1]; - f[2] = (float)d[2]; - ChangeKey(mUpVectorKeys, lcVector3(f[0], f[1], f[2]), 1, true); } if (version == 3) @@ -276,31 +286,12 @@ bool lcCamera::FileLoad(lcFile& file) { quint8 step; double eye[3], target[3], up[3]; - float f[3]; file.ReadDoubles(eye, 3); file.ReadDoubles(target, 3); file.ReadDoubles(up, 3); file.ReadU8(&step, 1); - if (up[0] == 0 && up[1] == 0 && up[2] == 0) - up[2] = 1; - - f[0] = (float)eye[0]; - f[1] = (float)eye[1]; - f[2] = (float)eye[2]; - ChangeKey(mPositionKeys, lcVector3(f[0], f[1], f[2]), step, true); - - f[0] = (float)target[0]; - f[1] = (float)target[1]; - f[2] = (float)target[2]; - ChangeKey(mTargetPositionKeys, lcVector3(f[0], f[1], f[2]), step, true); - - f[0] = (float)up[0]; - f[1] = (float)up[1]; - f[2] = (float)up[2]; - ChangeKey(mUpVectorKeys, lcVector3(f[0], f[1], f[2]), step, true); - file.ReadS32(); // snapshot file.ReadS32(); // cam } @@ -308,9 +299,9 @@ bool lcCamera::FileLoad(lcFile& file) if (version < 4) { - m_fovy = (float)file.ReadDouble(); - m_zFar = (float)file.ReadDouble(); - m_zNear= (float)file.ReadDouble(); + file.ReadDouble(); // m_fovy + file.ReadDouble(); // m_zFar + file.ReadDouble(); // m_zNear } else { @@ -328,13 +319,6 @@ bool lcCamera::FileLoad(lcFile& file) file.ReadU16(&time, 1); file.ReadFloats(param, 3); file.ReadU8(&type, 1); - - if (type == 0) - ChangeKey(mPositionKeys, lcVector3(param[0], param[1], param[2]), time, true); - else if (type == 1) - ChangeKey(mTargetPositionKeys, lcVector3(param[0], param[1], param[2]), time, true); - else if (type == 2) - ChangeKey(mUpVectorKeys, lcVector3(param[0], param[1], param[2]), time, true); } n = file.ReadS32(); @@ -346,21 +330,18 @@ bool lcCamera::FileLoad(lcFile& file) } } - file.ReadFloats(&m_fovy, 1); - file.ReadFloats(&m_zFar, 1); - file.ReadFloats(&m_zNear, 1); + float f; + file.ReadFloats(&f, 1); // m_fovy + file.ReadFloats(&f, 1); // m_zFar + file.ReadFloats(&f, 1); // m_zNear if (version < 5) { n = file.ReadS32(); - if (n != 0) - mState |= LC_CAMERA_HIDDEN; } else { ch = file.ReadU8(); - if (ch & 1) - mState |= LC_CAMERA_HIDDEN; file.ReadU8(); } } @@ -373,20 +354,6 @@ bool lcCamera::FileLoad(lcFile& file) file.ReadU32(&show, 1); // if (version > 2) file.ReadS32(&user, 1); - if (show == 0) - mState |= LC_CAMERA_HIDDEN; - } - - if (version < 7) - { - m_zFar *= 25.0f; - m_zNear *= 25.0f; - - for (int KeyIdx = 0; KeyIdx < mPositionKeys.GetSize(); KeyIdx++) - mPositionKeys[KeyIdx].Value *= 25.0f; - - for (int KeyIdx = 0; KeyIdx < mTargetPositionKeys.GetSize(); KeyIdx++) - mTargetPositionKeys[KeyIdx].Value *= 25.0f; } return true; @@ -418,19 +385,19 @@ void lcCamera::MoveSelected(lcStep Step, bool AddKey, const lcVector3& Distance) if (IsSelected(LC_CAMERA_SECTION_POSITION)) { mPosition += Distance; - ChangeKey(mPositionKeys, mPosition, Step, AddKey); + mPositionKeys.ChangeKey(mPosition, Step, AddKey); } if (IsSelected(LC_CAMERA_SECTION_TARGET)) { mTargetPosition += Distance; - ChangeKey(mTargetPositionKeys, mTargetPosition, Step, AddKey); + mTargetPositionKeys.ChangeKey(mTargetPosition, Step, AddKey); } else if (IsSelected(LC_CAMERA_SECTION_UPVECTOR)) { mUpVector += Distance; mUpVector.Normalize(); - ChangeKey(mUpVectorKeys, mUpVector, Step, AddKey); + mUpVectorKeys.ChangeKey(mUpVector, Step, AddKey); } lcVector3 FrontVector(mTargetPosition - mPosition); @@ -451,10 +418,10 @@ void lcCamera::MoveRelative(const lcVector3& Distance, lcStep Step, bool AddKey) lcVector3 Relative = lcMul30(Distance, lcMatrix44Transpose(mWorldView)) * 5.0f; mPosition += Relative; - ChangeKey(mPositionKeys, mPosition, Step, AddKey); + mPositionKeys.ChangeKey(mPosition, Step, AddKey); mTargetPosition += Relative; - ChangeKey(mTargetPositionKeys, mTargetPosition, Step, AddKey); + mTargetPositionKeys.ChangeKey(mTargetPosition, Step, AddKey); UpdatePosition(Step); } @@ -463,9 +430,9 @@ void lcCamera::UpdatePosition(lcStep Step) { if (!IsSimple()) { - mPosition = CalculateKey(mPositionKeys, Step); - mTargetPosition = CalculateKey(mTargetPositionKeys, Step); - mUpVector = CalculateKey(mUpVectorKeys, Step); + mPosition = mPositionKeys.CalculateKey(Step); + mTargetPosition = mTargetPositionKeys.CalculateKey(Step); + mUpVector = mUpVectorKeys.CalculateKey(Step); } lcVector3 FrontVector(mPosition - mTargetPosition); @@ -647,13 +614,13 @@ void lcCamera::DrawInterface(lcContext* Context, const lcScene& Scene) const void lcCamera::RemoveKeyFrames() { mPositionKeys.RemoveAll(); - ChangeKey(mPositionKeys, mPosition, 1, true); + mPositionKeys.ChangeKey(mPosition, 1, true); mTargetPositionKeys.RemoveAll(); - ChangeKey(mTargetPositionKeys, mTargetPosition, 1, true); + mTargetPositionKeys.ChangeKey(mTargetPosition, 1, true); mUpVectorKeys.RemoveAll(); - ChangeKey(mUpVectorKeys, mUpVector, 1, true); + mUpVectorKeys.ChangeKey(mUpVector, 1, true); } void lcCamera::RayTest(lcObjectRayTest& ObjectRayTest) const @@ -763,27 +730,27 @@ void lcCamera::BoxTest(lcObjectBoxTest& ObjectBoxTest) const void lcCamera::InsertTime(lcStep Start, lcStep Time) { - lcObject::InsertTime(mPositionKeys, Start, Time); - lcObject::InsertTime(mTargetPositionKeys, Start, Time); - lcObject::InsertTime(mUpVectorKeys, Start, Time); + mPositionKeys.InsertTime(Start, Time); + mTargetPositionKeys.InsertTime(Start, Time); + mUpVectorKeys.InsertTime(Start, Time); } void lcCamera::RemoveTime(lcStep Start, lcStep Time) { - lcObject::RemoveTime(mPositionKeys, Start, Time); - lcObject::RemoveTime(mTargetPositionKeys, Start, Time); - lcObject::RemoveTime(mUpVectorKeys, Start, Time); + mPositionKeys.RemoveTime(Start, Time); + mTargetPositionKeys.RemoveTime(Start, Time); + mUpVectorKeys.RemoveTime(Start, Time); } -void lcCamera::ZoomExtents(float AspectRatio, const lcVector3& Center, const lcVector3* Points, int NumPoints, lcStep Step, bool AddKey) +void lcCamera::ZoomExtents(float AspectRatio, const lcVector3& Center, const std::vector& Points, lcStep Step, bool AddKey) { if (IsOrtho()) { float MinX = FLT_MAX, MaxX = -FLT_MAX, MinY = FLT_MAX, MaxY = -FLT_MAX; - for (int PointIdx = 0; PointIdx < NumPoints; PointIdx++) + for (lcVector3 Point : Points) { - lcVector3 Point = lcMul30(Points[PointIdx], mWorldView); + Point = lcMul30(Point, mWorldView); MinX = lcMin(MinX, Point.x); MinY = lcMin(MinY, Point.y); @@ -791,8 +758,9 @@ void lcCamera::ZoomExtents(float AspectRatio, const lcVector3& Center, const lcV MaxY = lcMax(MaxY, Point.y); } - float Width = MaxX - MinX; - float Height = MaxY - MinY; + lcVector3 ViewCenter = lcMul30(Center, mWorldView); + float Width = qMax(fabsf(MaxX - ViewCenter.x), fabsf(ViewCenter.x - MinX)) * 2; + float Height = qMax(fabsf(MaxY - ViewCenter.y), fabsf(ViewCenter.y - MinY)) * 2; if (Width > Height * AspectRatio) Height = Width / AspectRatio; @@ -808,15 +776,15 @@ void lcCamera::ZoomExtents(float AspectRatio, const lcVector3& Center, const lcV lcVector3 Position(mPosition + Center - mTargetPosition); lcMatrix44 ProjectionMatrix = lcMatrix44Perspective(m_fovy, AspectRatio, m_zNear, m_zFar); - std::tie(mPosition, std::ignore) = lcZoomExtents(Position, mWorldView, ProjectionMatrix, Points, NumPoints); + std::tie(mPosition, std::ignore) = lcZoomExtents(Position, mWorldView, ProjectionMatrix, Points.data(), Points.size()); mTargetPosition = Center; } if (IsSimple()) AddKey = false; - ChangeKey(mPositionKeys, mPosition, Step, AddKey); - ChangeKey(mTargetPositionKeys, mTargetPosition, Step, AddKey); + mPositionKeys.ChangeKey(mPosition, Step, AddKey); + mTargetPositionKeys.ChangeKey(mTargetPosition, Step, AddKey); UpdatePosition(Step); } @@ -861,8 +829,8 @@ void lcCamera::ZoomRegion(float AspectRatio, const lcVector3& Position, const lc if (IsSimple()) AddKey = false; - ChangeKey(mPositionKeys, mPosition, Step, AddKey); - ChangeKey(mTargetPositionKeys, mTargetPosition, Step, AddKey); + mPositionKeys.ChangeKey(mPosition, Step, AddKey); + mTargetPositionKeys.ChangeKey(mTargetPosition, Step, AddKey); UpdatePosition(Step); } @@ -890,8 +858,8 @@ void lcCamera::Zoom(float Distance, lcStep Step, bool AddKey) if (IsSimple()) AddKey = false; - ChangeKey(mPositionKeys, mPosition, Step, AddKey); - ChangeKey(mTargetPositionKeys, mTargetPosition, Step, AddKey); + mPositionKeys.ChangeKey(mPosition, Step, AddKey); + mTargetPositionKeys.ChangeKey(mTargetPosition, Step, AddKey); UpdatePosition(Step); } @@ -904,8 +872,8 @@ void lcCamera::Pan(const lcVector3& Distance, lcStep Step, bool AddKey) if (IsSimple()) AddKey = false; - ChangeKey(mPositionKeys, mPosition, Step, AddKey); - ChangeKey(mTargetPositionKeys, mTargetPosition, Step, AddKey); + mPositionKeys.ChangeKey(mPosition, Step, AddKey); + mTargetPositionKeys.ChangeKey(mTargetPosition, Step, AddKey); UpdatePosition(Step); } @@ -935,9 +903,9 @@ void lcCamera::Orbit(float DistanceX, float DistanceY, const lcVector3& CenterPo if (IsSimple()) AddKey = false; - ChangeKey(mPositionKeys, mPosition, Step, AddKey); - ChangeKey(mTargetPositionKeys, mTargetPosition, Step, AddKey); - ChangeKey(mUpVectorKeys, mUpVector, Step, AddKey); + mPositionKeys.ChangeKey(mPosition, Step, AddKey); + mTargetPositionKeys.ChangeKey(mTargetPosition, Step, AddKey); + mUpVectorKeys.ChangeKey(mUpVector, Step, AddKey); UpdatePosition(Step); } @@ -952,7 +920,7 @@ void lcCamera::Roll(float Distance, lcStep Step, bool AddKey) if (IsSimple()) AddKey = false; - ChangeKey(mUpVectorKeys, mUpVector, Step, AddKey); + mUpVectorKeys.ChangeKey(mUpVector, Step, AddKey); UpdatePosition(Step); } @@ -962,18 +930,19 @@ void lcCamera::Center(const lcVector3& NewCenter, lcStep Step, bool AddKey) const lcMatrix44 Inverse = lcMatrix44AffineInverse(mWorldView); const lcVector3 Direction = -lcVector3(Inverse[2]); - float Yaw, Pitch, Roll; +// float Yaw, Pitch, Roll; + float Roll; if (fabsf(Direction.z) < 0.9999f) { - Yaw = atan2f(Direction.y, Direction.x); - Pitch = asinf(Direction.z); +// Yaw = atan2f(Direction.y, Direction.x); +// Pitch = asinf(Direction.z); Roll = atan2f(Inverse[0][2], Inverse[1][2]); } else { - Yaw = 0.0f; - Pitch = asinf(Direction.z); +// Yaw = 0.0f; +// Pitch = asinf(Direction.z); Roll = atan2f(Inverse[0][1], Inverse[1][1]); } @@ -995,8 +964,8 @@ void lcCamera::Center(const lcVector3& NewCenter, lcStep Step, bool AddKey) if (IsSimple()) AddKey = false; - ChangeKey(mTargetPositionKeys, mTargetPosition, Step, AddKey); - ChangeKey(mUpVectorKeys, mUpVector, Step, AddKey); + mTargetPositionKeys.ChangeKey(mTargetPosition, Step, AddKey); + mUpVectorKeys.ChangeKey(mUpVector, Step, AddKey); UpdatePosition(Step); } @@ -1005,13 +974,13 @@ void lcCamera::SetViewpoint(lcViewpoint Viewpoint) { lcVector3 Positions[] = { - lcVector3( 0.0f, -1250.0f, 0.0f), // LC_VIEWPOINT_FRONT - lcVector3( 0.0f, 1250.0f, 0.0f), // LC_VIEWPOINT_BACK - lcVector3( 0.0f, 0.0f, 1250.0f), // LC_VIEWPOINT_TOP - lcVector3( 0.0f, 0.0f, -1250.0f), // LC_VIEWPOINT_BOTTOM - lcVector3( 1250.0f, 0.0f, 0.0f), // LC_VIEWPOINT_LEFT - lcVector3(-1250.0f, 0.0f, 0.0f), // LC_VIEWPOINT_RIGHT - lcVector3( 375.0f, -375.0f, 187.5f) // LC_VIEWPOINT_HOME + lcVector3( 0.0f, -1250.0f, 0.0f), // lcViewpoint::Front + lcVector3( 0.0f, 1250.0f, 0.0f), // lcViewpoint::Back + lcVector3( 0.0f, 0.0f, 1250.0f), // lcViewpoint::Top + lcVector3( 0.0f, 0.0f, -1250.0f), // lcViewpoint::Bottom + lcVector3( 1250.0f, 0.0f, 0.0f), // lcViewpoint::Left + lcVector3(-1250.0f, 0.0f, 0.0f), // lcViewpoint::Right + lcVector3( 375.0f, -375.0f, 187.5f) // lcViewpoint::Home }; lcVector3 Ups[] = @@ -1025,13 +994,13 @@ void lcCamera::SetViewpoint(lcViewpoint Viewpoint) lcVector3(0.2357f, -0.2357f, 0.94281f) }; - mPosition = Positions[Viewpoint]; + mPosition = Positions[static_cast(Viewpoint)]; mTargetPosition = lcVector3(0, 0, 0); - mUpVector = Ups[Viewpoint]; + mUpVector = Ups[static_cast(Viewpoint)]; - ChangeKey(mPositionKeys, mPosition, 1, false); - ChangeKey(mTargetPositionKeys, mTargetPosition, 1, false); - ChangeKey(mUpVectorKeys, mUpVector, 1, false); + mPositionKeys.ChangeKey(mPosition, 1, false); + mTargetPositionKeys.ChangeKey(mTargetPosition, 1, false); + mUpVectorKeys.ChangeKey(mUpVector, 1, false); UpdatePosition(1); } @@ -1051,9 +1020,28 @@ void lcCamera::SetViewpoint(const lcVector3& Position) UpVector.Normalize(); mUpVector = UpVector; - ChangeKey(mPositionKeys, mPosition, 1, false); - ChangeKey(mTargetPositionKeys, mTargetPosition, 1, false); - ChangeKey(mUpVectorKeys, mUpVector, 1, false); + mPositionKeys.ChangeKey(mPosition, 1, false); + mTargetPositionKeys.ChangeKey(mTargetPosition, 1, false); + mUpVectorKeys.ChangeKey(mUpVector, 1, false); + + UpdatePosition(1); +} + +void lcCamera::SetViewpoint(const lcVector3& Position, const lcVector3& Target, const lcVector3& Up) +{ + mPosition = Position; + mTargetPosition = Target; + + lcVector3 Direction = Target - Position; + lcVector3 UpVector, SideVector; + SideVector = lcCross(Direction, Up); + UpVector = lcCross(SideVector, Direction); + UpVector.Normalize(); + mUpVector = UpVector; + + mPositionKeys.ChangeKey(mPosition, 1, false); + mTargetPositionKeys.ChangeKey(mTargetPosition, 1, false); + mUpVectorKeys.ChangeKey(mUpVector, 1, false); UpdatePosition(1); } @@ -1072,9 +1060,9 @@ void lcCamera::SetAngles(float Latitude, float Longitude, float Distance) mPosition = lcMul(mPosition, LatitudeMatrix) * Distance; mUpVector = lcMul(mUpVector, LatitudeMatrix); - ChangeKey(mPositionKeys, mPosition, 1, false); - ChangeKey(mTargetPositionKeys, mTargetPosition, 1, false); - ChangeKey(mUpVectorKeys, mUpVector, 1, false); + mPositionKeys.ChangeKey(mPosition, 1, false); + mTargetPositionKeys.ChangeKey(mTargetPosition, 1, false); + mUpVectorKeys.ChangeKey(mUpVector, 1, false); UpdatePosition(1); } diff --git a/common/camera.h b/common/camera.h index 3e793534..61fb7284 100644 --- a/common/camera.h +++ b/common/camera.h @@ -17,15 +17,16 @@ #define LC_CAMERA_SELECTION_MASK (LC_CAMERA_POSITION_SELECTED | LC_CAMERA_TARGET_SELECTED | LC_CAMERA_UPVECTOR_SELECTED) #define LC_CAMERA_FOCUS_MASK (LC_CAMERA_POSITION_FOCUSED | LC_CAMERA_TARGET_FOCUSED | LC_CAMERA_UPVECTOR_FOCUSED) -enum lcViewpoint +enum class lcViewpoint { - LC_VIEWPOINT_FRONT, - LC_VIEWPOINT_BACK, - LC_VIEWPOINT_TOP, - LC_VIEWPOINT_BOTTOM, - LC_VIEWPOINT_LEFT, - LC_VIEWPOINT_RIGHT, - LC_VIEWPOINT_HOME + Front, + Back, + Top, + Bottom, + Left, + Right, + Home, + Count }; enum lcCameraSection @@ -43,15 +44,16 @@ public: ~lcCamera(); lcCamera(const lcCamera&) = delete; - lcCamera(lcCamera&&) = delete; lcCamera& operator=(const lcCamera&) = delete; - lcCamera& operator=(lcCamera&&) = delete; - const char* GetName() const override + static lcViewpoint GetViewpoint(const QString& ViewpointName); + + QString GetName() const override { - return m_strName; + return mName; } + void SetName(const QString& Name); void CreateName(const lcArray& Cameras); bool IsSimple() const @@ -240,17 +242,17 @@ public: void SetPosition(const lcVector3& Position, lcStep Step, bool AddKey) { - ChangeKey(mPositionKeys, Position, Step, AddKey); + mPositionKeys.ChangeKey(Position, Step, AddKey); } void SetTargetPosition(const lcVector3& TargetPosition, lcStep Step, bool AddKey) { - ChangeKey(mTargetPositionKeys, TargetPosition, Step, AddKey); + mTargetPositionKeys.ChangeKey(TargetPosition, Step, AddKey); } void SetUpVector(const lcVector3& UpVector, lcStep Step, bool AddKey) { - ChangeKey(mPositionKeys, UpVector, Step, AddKey); + mPositionKeys.ChangeKey(UpVector, Step, AddKey); } float GetOrthoHeight() const @@ -272,15 +274,14 @@ public: void InsertTime(lcStep Start, lcStep Time); void RemoveTime(lcStep Start, lcStep Time); - bool FileLoad(lcFile& file); - void Select(bool bSelecting, bool bFocus, bool bMultiple); + static bool FileLoad(lcFile& file); void CompareBoundingBox(lcVector3& Min, lcVector3& Max); void UpdatePosition(lcStep Step); void CopyPosition(const lcCamera* Camera); void CopySettings(const lcCamera* Camera); - void ZoomExtents(float AspectRatio, const lcVector3& Center, const lcVector3* Points, int NumPoints, lcStep Step, bool AddKey); + void ZoomExtents(float AspectRatio, const lcVector3& Center, const std::vector& Points, lcStep Step, bool AddKey); void ZoomRegion(float AspectRatio, const lcVector3& Position, const lcVector3& TargetPosition, const lcVector3* Corners, lcStep Step, bool AddKey); void Zoom(float Distance, lcStep Step, bool AddKey); void Pan(const lcVector3& Distance, lcStep Step, bool AddKey); @@ -291,11 +292,10 @@ public: void MoveRelative(const lcVector3& Distance, lcStep Step, bool AddKey); void SetViewpoint(lcViewpoint Viewpoint); void SetViewpoint(const lcVector3& Position); + void SetViewpoint(const lcVector3& Position, const lcVector3& Target, const lcVector3& Up); void GetAngles(float& Latitude, float& Longitude, float& Distance) const; void SetAngles(float Latitude, float Longitude, float Distance); - char m_strName[81]; - float m_fovy; float m_zNear; float m_zFar; @@ -306,12 +306,12 @@ public: lcVector3 mUpVector; protected: - lcArray> mPositionKeys; - lcArray> mTargetPositionKeys; - lcArray> mUpVectorKeys; + lcObjectKeyArray mPositionKeys; + lcObjectKeyArray mTargetPositionKeys; + lcObjectKeyArray mUpVectorKeys; void Initialize(); + QString mName; quint32 mState; }; - diff --git a/common/lc_application.cpp b/common/lc_application.cpp index 7ff7a92d..99febb66 100644 --- a/common/lc_application.cpp +++ b/common/lc_application.cpp @@ -8,7 +8,9 @@ #include "lc_qpreferencesdialog.h" #include "lc_partselectionwidget.h" #include "lc_shortcuts.h" -#include "view.h" +#include "lc_view.h" +#include "camera.h" +#include "lc_previewwidget.h" lcApplication* gApplication; @@ -17,10 +19,22 @@ 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); @@ -30,6 +44,7 @@ void lcPreferences::LoadDefaults() mDrawGridLines = lcGetProfileInt(LC_PROFILE_GRID_LINES); mGridLineSpacing = lcGetProfileInt(LC_PROFILE_GRID_LINE_SPACING); mGridLineColor = lcGetProfileInt(LC_PROFILE_GRID_LINE_COLOR); + mDrawGridOrigin = lcGetProfileInt(LC_PROFILE_GRID_ORIGIN); mViewSphereEnabled = lcGetProfileInt(LC_PROFILE_VIEW_SPHERE_ENABLED); mViewSphereLocation = static_cast(lcGetProfileInt(LC_PROFILE_VIEW_SPHERE_LOCATION)); mViewSphereSize = lcGetProfileInt(LC_PROFILE_VIEW_SPHERE_SIZE); @@ -38,6 +53,18 @@ void lcPreferences::LoadDefaults() 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); + mStudCylinderColor = lcGetProfileInt(LC_PROFILE_STUD_CYLINDER_COLOR); + mPartEdgeColor = lcGetProfileInt(LC_PROFILE_PART_EDGE_COLOR); + mBlackEdgeColor = lcGetProfileInt(LC_PROFILE_BLACK_EDGE_COLOR); + mDarkEdgeColor = lcGetProfileInt(LC_PROFILE_DARK_EDGE_COLOR); + mPartEdgeContrast = lcGetProfileFloat(LC_PROFILE_PART_EDGE_CONTRAST); + mPartColorValueLDIndex = lcGetProfileFloat(LC_PROFILE_PART_COLOR_VALUE_LD_INDEX); + mAutomateEdgeColor = lcGetProfileInt(LC_PROFILE_AUTOMATE_EDGE_COLOR); } void lcPreferences::SaveDefaults() @@ -46,9 +73,21 @@ void lcPreferences::SaveDefaults() 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); @@ -58,42 +97,131 @@ void lcPreferences::SaveDefaults() lcSetProfileInt(LC_PROFILE_GRID_LINES, mDrawGridLines); lcSetProfileInt(LC_PROFILE_GRID_LINE_SPACING, mGridLineSpacing); lcSetProfileInt(LC_PROFILE_GRID_LINE_COLOR, mGridLineColor); + lcSetProfileInt(LC_PROFILE_GRID_ORIGIN, mDrawGridOrigin); lcSetProfileInt(LC_PROFILE_VIEW_SPHERE_ENABLED, mViewSphereSize ? 1 : 0); - lcSetProfileInt(LC_PROFILE_VIEW_SPHERE_LOCATION, (int)mViewSphereLocation); + 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); + lcSetProfileInt(LC_PROFILE_STUD_CYLINDER_COLOR, mStudCylinderColor); + lcSetProfileInt(LC_PROFILE_PART_EDGE_COLOR, mPartEdgeColor); + lcSetProfileInt(LC_PROFILE_BLACK_EDGE_COLOR, mBlackEdgeColor); + lcSetProfileInt(LC_PROFILE_DARK_EDGE_COLOR, mDarkEdgeColor); + lcSetProfileFloat(LC_PROFILE_PART_EDGE_CONTRAST, mPartEdgeContrast); + lcSetProfileFloat(LC_PROFILE_PART_COLOR_VALUE_LD_INDEX, mPartColorValueLDIndex); + lcSetProfileInt(LC_PROFILE_AUTOMATE_EDGE_COLOR, mAutomateEdgeColor); +} + +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) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) - setApplicationDisplayName("LeoCAD"); -#endif - - setOrganizationDomain("leocad.org"); - setOrganizationName("LeoCAD Software"); - setApplicationName("LeoCAD"); - setApplicationVersion(LC_VERSION_TEXT); + setApplicationDisplayName(QLatin1String("LeoCAD")); gApplication = this; - mProject = nullptr; - mLibrary = nullptr; + mDefaultStyle = style()->objectName(); mPreferences.LoadDefaults(); } 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()) @@ -125,7 +253,13 @@ void lcApplication::SetProject(Project* Project) { SaveTabLayout(); - gMainWindow->RemoveAllModelTabs(); + if (gMainWindow) + { + gMainWindow->RemoveAllModelTabs(); + + if (gMainWindow->GetPreviewWidget()) + gMainWindow->GetPreviewWidget()->ClearPreview(); + } delete mProject; mProject = Project; @@ -138,7 +272,8 @@ void lcApplication::SetProject(Project* Project) QSettings Settings; QByteArray TabLayout = Settings.value(GetTabLayoutKey()).toByteArray(); - gMainWindow->RestoreTabLayout(TabLayout); + if (gMainWindow) + gMainWindow->RestoreTabLayout(TabLayout); } } @@ -158,11 +293,13 @@ void lcApplication::ExportClipboard(const QByteArray& Clipboard) SetClipboard(Clipboard); } -bool lcApplication::LoadPartsLibrary(const QList>& LibraryPaths, bool OnlyUsePaths, bool ShowProgress) +bool lcApplication::LoadPartsLibrary(const QList>& LibraryPaths, bool OnlyUsePaths) { if (mLibrary == nullptr) mLibrary = new lcPiecesLibrary(); + const bool ShowProgress = gMainWindow != nullptr; + if (!OnlyUsePaths) { char* EnvPath = getenv("LEOCAD_LIB"); @@ -190,254 +327,601 @@ bool lcApplication::LoadPartsLibrary(const QList>& LibraryP return false; } -bool lcApplication::Initialize(QList>& LibraryPaths, bool& ShowWindow) +lcCommandLineOptions lcApplication::ParseCommandLineOptions() { - bool OnlyUseLibraryPaths = false; - bool SaveImage = false; - bool SaveWavefront = false; - bool Save3DS = false; - bool SaveCOLLADA = false; - bool SaveHTML = false; - bool SetCameraAngles = false; - bool Orthographic = false; - bool ImageHighlight = mPreferences.mHighlightNewParts; - int ImageWidth = lcGetProfileInt(LC_PROFILE_IMAGE_WIDTH); - int ImageHeight = lcGetProfileInt(LC_PROFILE_IMAGE_HEIGHT); - int StudLogo = lcGetProfileInt(LC_PROFILE_STUD_LOGO); - int ImageStart = 0; - int ImageEnd = 0; - float CameraLatitude = 0.0f, CameraLongitude = 0.0f; - QString ImageName; - QString ModelName; - QString CameraName; - QString ViewpointName; - QString ProjectName; - QString SaveWavefrontName; - QString Save3DSName; - QString SaveCOLLADAName; - QString SaveHTMLName; + lcPreferences Preferences; + Preferences.LoadDefaults(); + + lcCommandLineOptions Options; + + Options.ParseOK = true; + Options.Exit = false; + Options.SaveImage = false; + Options.SaveWavefront = false; + Options.Save3DS = false; + Options.SaveCOLLADA = false; + Options.SaveHTML = false; + Options.SetCameraAngles = false; + Options.SetCameraPosition = false; + Options.Orthographic = false; + Options.SetFoV = false; + Options.SetZPlanes = false; + Options.SetFadeStepsColor = false; + Options.SetHighlightColor = false; + Options.FadeSteps = Preferences.mFadeSteps; + Options.ImageHighlight = Preferences.mHighlightNewParts; + Options.ImageWidth = lcGetProfileInt(LC_PROFILE_IMAGE_WIDTH); + Options.ImageHeight = lcGetProfileInt(LC_PROFILE_IMAGE_HEIGHT); + Options.ShadingMode = Preferences.mShadingMode; + Options.LineWidth = Preferences.mLineWidth; + Options.AASamples = lcGetProfileInt(LC_PROFILE_ANTIALIASING_SAMPLES); + Options.StudStyle = static_cast(lcGetProfileInt(LC_PROFILE_STUD_STYLE)); + Options.ImageStart = 0; + Options.ImageEnd = 0; + Options.CameraPosition[0] = lcVector3(0.0f, 0.0f, 0.0f); + Options.CameraPosition[1] = lcVector3(0.0f, 0.0f, 0.0f); + Options.CameraPosition[2] = lcVector3(0.0f, 0.0f, 0.0f); + Options.CameraLatLon = lcVector2(0.0f, 0.0f); + Options.FoV = 0.0f; + Options.ZPlanes = lcVector2(0.0f, 0.0f); + Options.Viewpoint = lcViewpoint::Count; + Options.FadeStepsColor = Preferences.mFadeStepsColor; + Options.HighlightColor = Preferences.mHighlightNewPartsColor; + Options.StudCylinderColor = Preferences.mStudCylinderColor; + Options.PartEdgeColor = Preferences.mPartEdgeColor; + Options.BlackEdgeColor = Preferences.mBlackEdgeColor; + Options.DarkEdgeColor = Preferences.mDarkEdgeColor; + Options.PartEdgeContrast = Preferences.mPartEdgeContrast; + Options.PartColorValueLDIndex = Preferences.mPartColorValueLDIndex; + Options.AutomateEdgeColor = Preferences.mAutomateEdgeColor; QStringList Arguments = arguments(); - const int NumArguments = Arguments.size(); - for (int ArgIdx = 1; ArgIdx < NumArguments; ArgIdx++) + if (Arguments.isEmpty()) + return Options; + + Arguments.removeFirst(); + + while (!Arguments.isEmpty()) { - const QString& Param = Arguments[ArgIdx]; + QString Option = Arguments.takeFirst(); - if (Param.isEmpty()) + if (Option.isEmpty()) continue; - if (Param[0] != '-') + auto ParseString = [&Option, &Arguments, &Options](QString& Value, bool Required) { - ProjectName = Param; - continue; - } - - auto ParseString = [&ArgIdx, &Arguments, NumArguments](QString& Value, bool Required) - { - if (ArgIdx < NumArguments - 1 && Arguments[ArgIdx + 1][0] != '-') + if (!Arguments.isEmpty() && Arguments.front()[0] != '-') { - ArgIdx++; - Value = Arguments[ArgIdx]; + QString Parameter = Arguments.takeFirst(); + Value = Parameter; } else if (Required) - printf("Not enough parameters for the '%s' argument.\n", Arguments[ArgIdx].toLatin1().constData()); + { + Options.StdErr += tr("Not enough parameters for the '%1' option.\n").arg(Option); + Options.ParseOK = false; + return false; + } + + return true; }; - auto ParseInteger = [&ArgIdx, &Arguments, NumArguments](int& Value) + auto ParseInteger = [&Option, &Arguments, &Options](int& Value, int Min, int Max) { - if (ArgIdx < NumArguments - 1 && Arguments[ArgIdx + 1][0] != '-') + if (!Arguments.isEmpty() && Arguments.front()[0] != '-') { bool Ok = false; - ArgIdx++; - int NewValue = Arguments[ArgIdx].toInt(&Ok); + QString Parameter = Arguments.takeFirst(); + int NewValue = Parameter.toInt(&Ok); - if (Ok) - Value = NewValue; - else - printf("Invalid value specified for the '%s' argument.\n", Arguments[ArgIdx - 1].toLatin1().constData()); - } - else - printf("Not enough parameters for the '%s' argument.\n", Arguments[ArgIdx].toLatin1().constData()); - }; - - auto ParseFloat = [&ArgIdx, &Arguments, NumArguments](float& Value) - { - if (ArgIdx < NumArguments - 1 && Arguments[ArgIdx + 1][0] != '-') - { - bool Ok = false; - ArgIdx++; - int NewValue = Arguments[ArgIdx].toFloat(&Ok); - - if (Ok) - Value = NewValue; - else - printf("Invalid value specified for the '%s' argument.\n", Arguments[ArgIdx - 1].toLatin1().constData()); - } - else - printf("Not enough parameters for the '%s' argument.\n", Arguments[ArgIdx].toLatin1().constData()); - }; - - auto ParseVector2 = [&ArgIdx, &Arguments, NumArguments](float& Value1, float& Value2) - { - if (ArgIdx < NumArguments - 2 && Arguments[ArgIdx + 1][0] != '-' && Arguments[ArgIdx + 2][0] != '-') - { - bool Ok1 = false, Ok2 = false; - - ArgIdx++; - float NewValue1 = Arguments[ArgIdx].toFloat(&Ok1); - ArgIdx++; - float NewValue2 = Arguments[ArgIdx].toFloat(&Ok2); - - if (Ok1 && Ok2) + if (Ok && NewValue >= Min && NewValue <= Max) { - Value1 = NewValue1; - Value2 = NewValue2; + Value = NewValue; return true; } else - printf("Invalid value specified for the '%s' argument.\n", Arguments[ArgIdx - 2].toLatin1().constData()); + Options.StdErr += tr("Invalid parameter value specified for the '%1' option: '%2'.\n").arg(Option, Parameter); } else - printf("Not enough parameters for the '%s' argument.\n", Arguments[ArgIdx].toLatin1().constData()); + Options.StdErr += tr("Not enough parameters for the '%1' option.\n").arg(Option); + Options.ParseOK = false; return false; }; - if (Param == QLatin1String("-l") || Param == QLatin1String("--libpath")) + auto ParseUnsigned = [&Option, &Arguments, &Options](uint& Value, uint Min, uint Max) + { + if (!Arguments.isEmpty() && Arguments.front()[0] != '-') + { + bool Ok = false; + QString Parameter = Arguments.takeFirst(); + uint NewValue = Parameter.toUInt(&Ok); + + if (Ok && NewValue >= Min && NewValue <= Max) + { + Value = NewValue; + return true; + } + else + Options.StdErr += tr("Invalid parameter value specified for the '%1' option: '%2'.\n").arg(Option, Parameter); + } + else + Options.StdErr += tr("Not enough parameters for the '%1' option.\n").arg(Option); + + Options.ParseOK = false; + return false; + }; + + auto ParseFloat = [&Option, &Arguments, &Options](float& Value, float Min, float Max) + { + if (!Arguments.isEmpty() && Arguments.front()[0] != '-') + { + bool Ok = false; + QString Parameter = Arguments.takeFirst(); + float NewValue = Parameter.toFloat(&Ok); + + if (Ok && NewValue >= Min && NewValue <= Max) + { + Value = NewValue; + return true; + } + else + Options.StdErr += tr("Invalid parameter value specified for the '%1' option: '%2'.\n").arg(Option, Parameter); + } + else + Options.StdErr += tr("Not enough parameters for the '%1' option.\n").arg(Option); + + Options.ParseOK = false; + return false; + }; + + auto ParseFloatArray = [&Option, &Arguments, &Options](int Count, float* ValueArray, bool NegativesValid) + { + if (Arguments.size() < Count) + { + Options.StdErr += tr("Not enough parameters for the '%1' option.\n").arg(Option); + Arguments.clear(); + Options.ParseOK = false; + return false; + } + + for (int ParseIndex = 0; ParseIndex < Count; ParseIndex++) + { + if (NegativesValid || Arguments.front()[0] != '-') + { + bool Ok = false; + QString Parameter = Arguments.takeFirst(); + float NewValue = Parameter.toFloat(&Ok); + + if (Ok) + { + *(ValueArray++) = NewValue; + continue; + } + + Options.StdErr += tr("Invalid parameter value specified for the '%1' option: '%2'.\n").arg(Option, Parameter); + } + else + Options.StdErr += tr("Not enough parameters for the '%1' option.\n").arg(Option); + + Options.ParseOK = false; + return false; + } + + return true; + }; + + auto ParseColor = [&Option, &Arguments, &Options](quint32& Color) + { + if (!Arguments.isEmpty() && Arguments.front()[0] != '-') + { + QString Parameter = Arguments.takeFirst(); + QColor ParsedColor = QColor(Parameter); + + if (ParsedColor.isValid()) + { + Color = LC_RGBA(ParsedColor.red(), ParsedColor.green(), ParsedColor.blue(), ParsedColor.alpha()); + return true; + } + else + Options.StdErr += tr("Invalid parameter value specified for the '%1' option: '%2'.\n").arg(Option, Parameter); + } + else + Options.StdErr += tr("Not enough parameters for the '%1' option.\n").arg(Option); + + Options.ParseOK = false; + return false; + }; + + if (Option[0] != '-') + { + if (QFileInfo(Option).isReadable()) + Options.ProjectName = Option; + else + Options.StdErr += tr("The file '%1' is not readable.\n").arg(Option); + + continue; + } + + if (Option == QLatin1String("-l") || Option == QLatin1String("--libpath")) { QString LibPath; - ParseString(LibPath, true); - if (!LibPath.isEmpty()) + + if (ParseString(LibPath, true)) + Options.LibraryPaths += qMakePair(LibPath, false); + } + else if (Option == QLatin1String("-i") || Option == QLatin1String("--image")) + { + Options.SaveImage = true; + ParseString(Options.ImageName, false); + } + else if (Option == QLatin1String("-w") || Option == QLatin1String("--width")) + ParseInteger(Options.ImageWidth, 1, INT_MAX); + else if (Option == QLatin1String("-h") || Option == QLatin1String("--height")) + ParseInteger(Options.ImageHeight, 1, INT_MAX); + else if (Option == QLatin1String("-f") || Option == QLatin1String("--from")) + ParseUnsigned(Options.ImageStart, 1, LC_STEP_MAX); + else if (Option == QLatin1String("-t") || Option == QLatin1String("--to")) + ParseUnsigned(Options.ImageEnd, 1, LC_STEP_MAX); + else if (Option == QLatin1String("-s") || Option == QLatin1String("--submodel")) + ParseString(Options.ModelName, true); + else if (Option == QLatin1String("-c") || Option == QLatin1String("--camera")) + ParseString(Options.CameraName, true); + else if (Option == QLatin1String("--viewpoint")) + { + QString ViewpointName; + + if (ParseString(ViewpointName, true)) { - LibraryPaths.clear(); - LibraryPaths += qMakePair(LibPath, false); - OnlyUseLibraryPaths = true; + Options.Viewpoint = lcCamera::GetViewpoint(ViewpointName); + + if (Options.Viewpoint == lcViewpoint::Count) + { + Options.StdErr += tr("Invalid parameter value specified for the '%1' option: '%2'.\n").arg(Option, ViewpointName); + Options.ParseOK = false; + } } } - else if (Param == QLatin1String("-i") || Param == QLatin1String("--image")) + else if (Option == QLatin1String("--camera-angles")) { - SaveImage = true; - ParseString(ImageName, false); + if ((Options.SetCameraAngles = ParseFloatArray(2, Options.CameraLatLon, true)) && (fabsf(Options.CameraLatLon[0]) > 360.0f || fabsf(Options.CameraLatLon[1]) > 360.0f)) + { + Options.StdErr += tr("Invalid parameter value(s) specified for the '%1' option: limits are +/- 360.\n").arg(Option); + Options.ParseOK = false; + } } - else if (Param == QLatin1String("-w") || Param == QLatin1String("--width")) - ParseInteger(ImageWidth); - else if (Param == QLatin1String("-h") || Param == QLatin1String("--height")) - ParseInteger(ImageHeight); - else if (Param == QLatin1String("-f") || Param == QLatin1String("--from")) - ParseInteger(ImageStart); - else if (Param == QLatin1String("-t") || Param == QLatin1String("--to")) - ParseInteger(ImageEnd); - 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")) - ParseString(ViewpointName, true); - else if (Param == QLatin1String("--camera-angles")) - SetCameraAngles = ParseVector2(CameraLatitude, CameraLongitude); - else if (Param == QLatin1String("--orthographic")) - Orthographic = true; - else if (Param == QLatin1String("--highlight")) - ImageHighlight = true; - else if (Param == QLatin1String("--shading")) + else if (Option == QLatin1String("--camera-position") || Option == QLatin1String("--camera-position-ldraw")) + { + if ((Options.SetCameraPosition = ParseFloatArray(9, Options.CameraPosition[0], true))) + { + lcVector3 Front = Options.CameraPosition[1] - Options.CameraPosition[0]; + + if (Front.LengthSquared() < 1.0f || Options.CameraPosition[2].LengthSquared() < 1.0f || fabsf(lcDot(lcNormalize(Front), lcNormalize(Options.CameraPosition[2]))) > 0.99f) + { + Options.StdErr += tr("Invalid parameter value(s) specified for the '%1' option.\n").arg(Option); + Options.ParseOK = false; + } + else if (Option == QLatin1String("--camera-position-ldraw")) + { + Options.CameraPosition[0] = lcVector3LDrawToLeoCAD(Options.CameraPosition[0]); + Options.CameraPosition[1] = lcVector3LDrawToLeoCAD(Options.CameraPosition[1]); + Options.CameraPosition[2] = lcVector3LDrawToLeoCAD(Options.CameraPosition[2]); + } + } + } + else if (Option == QLatin1String("--orthographic")) + Options.Orthographic = true; + else if (Option == QLatin1String("--fov")) + Options.SetFoV = ParseFloat(Options.FoV, 1.0f, 180.0f); + else if (Option == QLatin1String("--zplanes")) + { + if ((Options.SetZPlanes = ParseFloatArray(2, Options.ZPlanes, false)) && (Options.ZPlanes[0] < 1.0 || Options.ZPlanes[0] >= Options.ZPlanes[1])) + { + Options.StdErr += tr("Invalid parameter value(s) specified for the '%1' option: requirements are: 1 <= < .\n").arg(Option); + Options.ParseOK = false; + } + } + else if (Option == QLatin1String("-scc") || Option == QLatin1String("--stud-cylinder-color")) + { + if (ParseColor(Options.StudCylinderColor)) + { + if (!lcIsHighContrast(Options.StudStyle)) + { + Options.StdErr += tr("High contrast stud style is required for the '%1' option but is not enabled.\n").arg(Option); + Options.ParseOK = false; + } + } + } + else if (Option == QLatin1String("-ec") || Option == QLatin1String("--edge-color")) + { + if (ParseColor(Options.PartEdgeColor)) + { + if (!lcIsHighContrast(Options.StudStyle)) + { + Options.StdErr += tr("High contrast stud style is required for the '%1' option but is not enabled.\n").arg(Option); + Options.ParseOK = false; + } + } + } + else if (Option == QLatin1String("-bec") || Option == QLatin1String("--black-edge-color")) + { + if (ParseColor(Options.BlackEdgeColor)) + { + if (!lcIsHighContrast(Options.StudStyle)) + { + Options.StdErr += tr("High contrast stud style is required for the '%1' option but is not enabled.\n").arg(Option); + Options.ParseOK = false; + } + } + } + else if (Option == QLatin1String("-dec") || Option == QLatin1String("--dark-edge-color")) + { + if (ParseColor(Options.DarkEdgeColor)) + { + if (!lcIsHighContrast(Options.StudStyle)) + { + Options.StdErr += tr("High contrast stud style is required for the '%1' option but is not enabled.\n").arg(Option); + Options.ParseOK = false; + } + } + } + else if (Option == QLatin1String("-aec") || Option == QLatin1String("--automate-edge-color")) + { + Options.AutomateEdgeColor = true; + } + else if (Option == QLatin1String("-cc") || Option == QLatin1String("--color-contrast")) + { + if (ParseFloat(Options.PartEdgeContrast, 0.0f, 1.0f)) + { + if (!Options.AutomateEdgeColor) + { + Options.StdErr += tr("Automate edge color is required for the '%1' option but is not enabled.\n").arg(Option); + Options.ParseOK = false; + } + } + } + else if (Option == QLatin1String("-ldv") || Option == QLatin1String("--light-dark-value")) + { + if (ParseFloat(Options.PartColorValueLDIndex, 0.0f, 1.0f)) + { + if (!Options.AutomateEdgeColor) + { + Options.StdErr += tr("Automate edge color is required for the '%1' option but is not enabled.\n").arg(Option); + Options.ParseOK = false; + } + } + } + else if (Option == QLatin1String("--fade-steps")) + Options.FadeSteps = true; + else if (Option == QLatin1String("--no-fade-steps")) + Options.FadeSteps = false; + else if (Option == QLatin1String("--fade-steps-color")) + { + if (ParseColor(Options.FadeStepsColor)) + { + Options.SetFadeStepsColor = true; + Options.FadeSteps = true; + } + } + else if (Option == QLatin1String("--highlight")) + Options.ImageHighlight = true; + else if (Option == QLatin1String("--no-highlight")) + Options.ImageHighlight = false; + else if (Option == QLatin1String("--highlight-color")) + { + if (ParseColor(Options.HighlightColor)) + { + Options.SetHighlightColor = true; + Options.ImageHighlight = true; + } + } + else if (Option == QLatin1String("--shading")) { QString ShadingString; - 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 if (Param == QLatin1String("--line-width")) - ParseFloat(mPreferences.mLineWidth); - else if (Param == QLatin1String("-sl") || Param == QLatin1String("--stud-logo")) - { - ParseInteger(StudLogo); - if (StudLogo != lcGetProfileInt(LC_PROFILE_STUD_LOGO)) + if (ParseString(ShadingString, true)) { - lcGetPiecesLibrary()->SetStudLogo(StudLogo, false); + if (ShadingString == QLatin1String("wireframe")) + Options.ShadingMode = lcShadingMode::Wireframe; + else if (ShadingString == QLatin1String("flat")) + Options.ShadingMode = lcShadingMode::Flat; + else if (ShadingString == QLatin1String("default")) + Options.ShadingMode = lcShadingMode::DefaultLights; + else if (ShadingString == QLatin1String("full")) + Options.ShadingMode = lcShadingMode::Full; + else + { + Options.StdErr += tr("Invalid parameter value specified for the '%1' option: '%2'.\n").arg(Option, ShadingString); + Options.ParseOK = false; + } } } - else if (Param == QLatin1String("-obj") || Param == QLatin1String("--export-wavefront")) + else if (Option == QLatin1String("--line-width")) + ParseFloat(Options.LineWidth, 0.0f, 10.0f); + else if (Option == QLatin1String("--aa-samples")) { - SaveWavefront = true; - ParseString(SaveWavefrontName, false); + if (ParseInteger(Options.AASamples, 1, 8) && Options.AASamples != 1 && Options.AASamples != 2 && Options.AASamples != 4 && Options.AASamples != 8) + { + Options.StdErr += tr("Invalid parameter value specified for the '%1' option: '%2'.\n").arg(Option, QString::number(Options.AASamples)); + Options.ParseOK = false; + } } - else if (Param == QLatin1String("-3ds") || Param == QLatin1String("--export-3ds")) + else if (Option == QLatin1String("-ss") || Option == QLatin1String("--stud-style")) { - Save3DS = true; - ParseString(Save3DSName, false); + int StudStyle; + + if (ParseInteger(StudStyle, 0, static_cast(lcStudStyle::Count) - 1)) + Options.StudStyle = static_cast(StudStyle); } - else if (Param == QLatin1String("-dae") || Param == QLatin1String("--export-collada")) + else if (Option == QLatin1String("-obj") || Option == QLatin1String("--export-wavefront")) { - SaveCOLLADA = true; - ParseString(SaveCOLLADAName, false); + Options.SaveWavefront = true; + ParseString(Options.SaveWavefrontName, false); } - else if (Param == QLatin1String("-html") || Param == QLatin1String("--export-html")) + else if (Option == QLatin1String("-3ds") || Option == QLatin1String("--export-3ds")) { - SaveHTML = true; - ParseString(SaveHTMLName, false); + Options.Save3DS = true; + ParseString(Options.Save3DSName, false); } - else if (Param == QLatin1String("-v") || Param == QLatin1String("--version")) + else if (Option == QLatin1String("-dae") || Option == QLatin1String("--export-collada")) + { + Options.SaveCOLLADA = true; + ParseString(Options.SaveCOLLADAName, false); + } + else if (Option == QLatin1String("-html") || Option == QLatin1String("--export-html")) + { + Options.SaveHTML = true; + ParseString(Options.SaveHTMLName, false); + } + else if (Option == QLatin1String("-v") || Option == QLatin1String("--version")) { #ifdef LC_CONTINUOUS_BUILD - printf("LeoCAD Continuous Build " QT_STRINGIFY(LC_CONTINUOUS_BUILD) "\n"); + Options.StdOut += tr("LeoCAD Continuous Build %1\n").arg(QT_STRINGIFY(LC_CONTINUOUS_BUILD)); #else - printf("LeoCAD Version " LC_VERSION_TEXT "\n"); + Options.StdOut += tr("LeoCAD Version %1\n").arg(LC_VERSION_TEXT); #endif - printf("LeoCAD Version " LC_VERSION_TEXT "\n"); - printf("Compiled " __DATE__ "\n"); - - ShowWindow = false; - return true; + Options.StdOut += tr("Compiled %1\n").arg(__DATE__); + Options.Exit = true; } - else if (Param == QLatin1String("-?") || Param == QLatin1String("--help")) + else if (Option == QLatin1String("-?") || Option == 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.\n"); - printf(" -w, --width : Set the picture width.\n"); - printf(" -h, --height : Set the picture height.\n"); - printf(" -f, --from