/
Team8
/
kittycad
Обзор
Документация
Войти
/
Team8
/
kittycad
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/main.cpp
944 строки
23 KB
levovix
add: drawing linear dimensions for parameters
11 янв 2026, 03:08
11 янв 2026, 03:08
c09faff
Код
Авторство
О чём код?
#define LA_IMPLEMENTATION #include "la.h" #include "main.h" #include <filesystem> #include <fstream> #include <iostream> // #include <print> #include <QTranslator> #include <QVBoxLayout> #include <QHBoxLayout> #include <QMenuBar> #include <QStatusBar> #include <QFileDialog> #include <QMessageBox> #include <QSplitter> #include <QWindow> #include <QShortcut> #include <QScrollArea> #include <QJsonDocument> #include <QJsonObject> #include <tool_enabler.h> #include <vsn_application.h> #include "data/config.h" #include "data/document.h" #include "data/serialization.h" #include "gui/document_view.h" #include "gui/license_dialog.h" #include "gui/tools/assembly/transmission_3d_constraint.h" #include "gui/utils.h" #include "gui/welcome.h" #include "gui/model_tree.h" #if DEBUG #include "gui/tools/debug.h" #endif #include "gui/tools/sketch/sketch.h" #include "gui/tools/sketch/line.h" #include "gui/tools/sketch/arc.h" #include "gui/tools/sketch/point_coinsidence.h" #include "gui/tools/sketch/linear_dim.h" #include "gui/tools/sketch/circle.h" #include "gui/tools/sketch/chamfer.h" #include "gui/tools/sketch/fillet.h" #include "gui/tools/solids/extrude.h" #include "gui/tools/solids/revolve.h" #include "gui/tools/solids/loft.h" #include "gui/tools/solids/boolean.h" #include "gui/tools/solids/radial_array.h" #include "gui/tools/solids/thread.h" #include "gui/tools/assembly/assembly.h" #include "gui/tools/assembly/solid_clone.h" #include "gui/tools/assembly/fixed_3d_constraint.h" #include "gui/tools/assembly/coinsidence_3d_constraint.h" #include "gui/tools/templates/flange_coupling.h" AppWindow::AppWindow() : QMainWindow{} { initAllRtti(m_rttiDb); loadConfig(); initUi(); } ref<data::Document> AppWindow::currentDocument() const { if (m_curDocView == nullptr) { return nullptr; } return m_curDocView->document(); } void AppWindow::setCurrentDocument(ref<data::Document> doc, QString const& filePath) { if (doc == currentDocument()) { return; } if (m_welcome_page != nullptr) { delete m_welcome_page; m_welcome_page = nullptr; } // нужно убрать все лишние ссылки на предыдущий документ перед удалением DocumentView m_modelTree->setDocument(doc); if (m_curDocView != nullptr) { assert(m_curDocView->m_document.use_count() == 1); delete m_curDocView; m_curDocView = nullptr; } if (doc != nullptr) { m_curDocView = new DocumentView(doc, filePath); m_page->layout()->addWidget(m_curDocView); } if (doc != nullptr) { doc->modelTree = m_modelTree; doc->activeToolPalette = m_activeToolPalette; doc->toolForSelectedObjectPalette = m_toolForSelectedObjectPalette; } currentDocumentChanged(currentDocument()); } void AppWindow::on_doc_new() { auto doc = ref(new data::Document()); setCurrentDocument(doc, ""); } void AppWindow::on_doc_open() { QString filename = QFileDialog::getOpenFileName(this, tr("Open document"), "", "*.kcd"); openDocument(filename); } void AppWindow::on_doc_save() { if (m_curDocView != nullptr && !m_curDocView->filePath().isEmpty()) { saveDocument(m_curDocView->document(), m_curDocView->filePath()); } else { on_doc_saveAs(); } } void AppWindow::on_doc_saveAs() { if (m_curDocView == nullptr) { return; } QString filename = QFileDialog::getSaveFileName(this, tr("Save document as..."), "", "*.kcd"); if (filename.isEmpty()) { return; } saveDocument(m_curDocView->document(), filename); m_curDocView->setFilePath(filename); if (!std::any_of(m_config->recent_files.begin(), m_config->recent_files.end(), [filename](std::string const& s) { return s == filename.toStdString(); })) m_config->recent_files.push_back(filename.toStdString()); } void AppWindow::on_doc_close() { setCurrentDocument(nullptr, ""); makeWelcomePage(); } void AppWindow::on_help_about() { QMessageBox mb(this); mb.setWindowTitle(tr("About")); mb.setText(tr("KittiCAD is a CAD created for КИТТИ 2025")); mb.exec(); } void AppWindow::on_exit() { this->close(); } void AppWindow::loadConfig() { { // поиск конфига const char* xdg_config_home = std::getenv("XDG_CONFIG_HOME"); const char* home = std::getenv("HOME"); if (std::filesystem::exists("./config.json")) config_path = "./config.json"; else if (xdg_config_home) { if (std::filesystem::exists(std::string(xdg_config_home) + "/kittycad/config.json")) { config_path = std::string(xdg_config_home) + "/kittycad/config.json"; } } else if (home) { if (std::filesystem::exists(std::string(home) + "/.config/kittycad/config.json")) { config_path = std::string(home) + "/.config/kittycad/config.json"; } } if (config_path == "") { config_path = "./config.json"; } // std::print("reading {}\n", config_path); } ref<ISerializable> cfg_raw{nullptr}; std::fstream f(config_path, std::ios::in); if (f) { Serializator s(f); s.rttiDb = &m_rttiDb; try { readRoot(s, cfg_raw); } catch (ParseError e) {} } f.close(); m_config = std::dynamic_pointer_cast<data::Config>(cfg_raw); if (m_config == nullptr) { m_config = ref(new data::Config()); } } void AppWindow::saveConfig() { std::fstream f(config_path, std::ios::out | std::ios::trunc); Serializator s(f); s.rttiDb = &m_rttiDb; writeRoot(s, m_config); f.close(); } bool AppWindow::fetchLicence() { ::EnableMathModules( m_config->licence_name.data(), m_config->licence_name.size(), m_config->licence_key.data(), m_config->licence_key.size() ); while (!::IsMathVisionEnable()) { LicenseDialog dlg{m_config}; if (dlg.exec() == QDialog::Accepted) { ::EnableMathModules( m_config->licence_name.data(), m_config->licence_name.size(), m_config->licence_key.data(), m_config->licence_key.size() ); saveConfig(); } else { return false; } } return true; } void AppWindow::openDocument(QString const& filename) { if (filename.isEmpty()) { return; } ref<ISerializable> doc_raw; std::fstream f(filename.toStdString(), std::ios::in); Serializator s(f); s.rttiDb = &m_rttiDb; try { readRoot(s, doc_raw); } catch (ParseError e) { // todo: показать окно с ошибкой throwWithLineInfo(s, std::move(e), filename.toStdString()); } f.close(); auto doc = std::dynamic_pointer_cast<data::Document>(doc_raw); if (doc == nullptr) { // todo: показать окно с ошибкой throw ParseError("expected a data::Document as root object"/*, std::stacktrace::current()*/); } setCurrentDocument(doc, filename); if (!std::any_of(m_config->recent_files.begin(), m_config->recent_files.end(), [filename](std::string const& s) { return s == filename.toStdString(); })) m_config->recent_files.push_back(filename.toStdString()); } void AppWindow::saveDocument(ref<data::Document> doc, QString const& filename) { std::fstream f(filename.toStdString(), std::ios::out | std::ios::trunc); Serializator s(f); s.rttiDb = &m_rttiDb; assignMissingIds(s, doc.get()); writeRoot(s, doc); // todo: обработка ошибок f.close(); } void AppWindow::addAction( QIcon const& icon, QString const& text, void(AppWindow::*callback)(), QWidget* parentMenu, QWidget* parentToolbar, QKeySequence shortcut = {} ) { QAction* act = parentMenu->addAction(icon, text); if (!shortcut.isEmpty()) { act->setShortcut(shortcut); } connect(act, &QAction::triggered, this, callback); if (parentToolbar != nullptr) { parentToolbar->addAction(act); } }; void AppWindow::addAction( QIcon const& icon, QString const& text, std::function<void()> callback, QWidget* parentMenu, QWidget* parentToolbar, QKeySequence shortcut ) { QAction* act = parentMenu->addAction(icon, text); if (shortcut != QKeySequence{}) act->setShortcut(shortcut); connect(act, &QAction::triggered, callback); if (parentToolbar != nullptr) { parentToolbar->addAction(act); } } void AppWindow::initUi() { setWindowTitle(tr("KittyCAD")); resize(m_config->windowWidth, m_config->windowHeight); if (m_config->isMaximized) { showMaximized(); } m_page = new QWidget(); m_page->setLayout(new QVBoxLayout()); m_page->layout()->setContentsMargins(0, 0, 0, 0); m_toolBar = new QToolBar(this); makeWelcomePage(); m_page->setMinimumSize(100, 100); { auto main_w = new QSplitter(this); setCentralWidget(main_w); { auto paleteWidget = new QSplitter(main_w); paleteWidget->setOrientation(Qt::Vertical); main_w->addWidget(paleteWidget); m_activeToolPalette = new QWidget(this); m_activeToolPalette->setMinimumWidth(100); m_activeToolPalette->setLayout(new QVBoxLayout()); m_activeToolPalette->layout()->setContentsMargins(0, 0, 0, 0); auto scrollArea1 = new QScrollArea(this); scrollArea1->setWidget(m_activeToolPalette); scrollArea1->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea1->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea1->setWidgetResizable(true); scrollArea1->setWidget(m_activeToolPalette); paleteWidget->addWidget(scrollArea1); m_toolForSelectedObjectPalette = new QWidget(this); m_toolForSelectedObjectPalette->setMinimumWidth(100); m_toolForSelectedObjectPalette->setLayout(new QVBoxLayout()); m_toolForSelectedObjectPalette->layout()->setContentsMargins(0, 0, 0, 0); auto scrollArea2 = new QScrollArea(this); scrollArea2->setWidget(m_toolForSelectedObjectPalette); scrollArea2->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea2->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea2->setWidgetResizable(true); scrollArea2->setWidget(m_toolForSelectedObjectPalette); paleteWidget->addWidget(scrollArea2); paleteWidget->setSizes({ (int)(height() * ((float)1 / 2)), (int)(height() * ((float)1 / 2)), }); } main_w->addWidget(m_page); { auto modelTreeWidget = new QWidget(this); auto modelTreeWidget_layout = new QVBoxLayout(modelTreeWidget); modelTreeWidget_layout->setContentsMargins(0, 0, 0, 0); main_w->addWidget(modelTreeWidget); m_modelTree = new ModelTree(this); m_modelTree->setMinimumWidth(100); modelTreeWidget_layout->addWidget(m_modelTree); } main_w->setSizes({ (int)(width() * ((float)1 / 4)), (int)(width() * ((float)2 / 4)), (int)(width() * ((float)1 / 4)), }); } initToolbar(); } void AppWindow::initToolbar() { m_toolBar = new QToolBar(this); #if 0 auto addAction = overloaded{ [this]( QIcon const& icon, QString const& text, void(AppWindow::*callback)(), QWidget* parentMenu, QWidget* parentToolbar ) { QAction* act = parentMenu->addAction(icon, text); connect(act, &QAction::triggered, this, callback); if (parentToolbar != nullptr) { parentToolbar->addAction(act); } }, [this]( QIcon const& icon, QString const& text, std::function<void()> callback, QWidget* parentMenu, QWidget* parentToolbar ) { QAction* act = parentMenu->addAction(icon, text); connect(act, &QAction::triggered, callback); if (parentToolbar != nullptr) { parentToolbar->addAction(act); } }, }; #endif auto menuBar = new QMenuBar(nullptr); auto menu_file = menuBar->addMenu(tr("File")); auto menu_sketch = menuBar->addMenu(tr("Sketch")); auto menu_body = menuBar->addMenu(tr("Body")); auto menu_assembly = menuBar->addMenu(tr("Assembly")); auto menu_templates = menuBar->addMenu(tr("Templates")); auto menu_help = menuBar->addMenu(tr("Help")); addAction( qIconFromResources("document-new"), tr("New file"), &AppWindow::on_doc_new, menu_file, m_toolBar, Qt::CTRL + Qt::Key_N ); addAction( qIconFromResources("document-open"), tr("Open..."), &AppWindow::on_doc_open, menu_file, m_toolBar, Qt::CTRL + Qt::Key_O ); menu_file->addSeparator(); m_toolBar->addSeparator(); addAction( qIconFromResources("document-save"), tr("Save"), &AppWindow::on_doc_save, menu_file, m_toolBar, Qt::CTRL + Qt::Key_S ); addAction( qIconFromResources("document-save-as"), tr("Save as..."), &AppWindow::on_doc_saveAs, menu_file, m_toolBar, (Qt::CTRL | Qt::SHIFT) + Qt::Key_S ); menu_file->addSeparator(); m_toolBar->addSeparator(); addAction( qIconFromResources("document-close"), tr("Close current document"), &AppWindow::on_doc_close, menu_file, m_toolBar, Qt::CTRL + Qt::Key_W ); addAction( qIconFromResources("help-about"), tr("About"), &AppWindow::on_help_about, menu_help, nullptr ); menu_file->addSeparator(); addAction( qIconFromResources("exit"), tr("Exit"), &AppWindow::on_exit, menu_file, nullptr, Qt::CTRL + Qt::Key_Q ); m_toolBar->addSeparator(); addAction( qIconFromResources("tool_sketch"), tr("New sketch"), [this]() { if (m_curDocView != nullptr) { auto tool = new SketchTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_sketch, m_toolBar ); menu_sketch->addSeparator(); m_toolBar->addSeparator(); addAction( qIconFromResources("tool_line_segment"), tr("Line segment"), [this]() { if (m_curDocView != nullptr) { auto tool = new LineTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_sketch, m_toolBar, Qt::Key_S ); addAction( qIconFromResources("tool_arc"), tr("Circle arc"), [this]() { if (m_curDocView != nullptr) { // todo: при переключении инструмента с отрезка на дугу (и наоборот), сохранять между инструментами начальную точку auto tool = new ArcTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_sketch, m_toolBar, Qt::Key_A ); addAction( qIconFromResources("tool_circle"), tr("Circle"), [this]() { if (m_curDocView != nullptr) { auto tool = new SketchCircleTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_sketch, m_toolBar, Qt::Key_C ); m_toolBar->addSeparator(); menu_sketch->addSeparator(); addAction( qIconFromResources("tool_linear_dim"), tr("Measure"), [this]() { if (m_curDocView != nullptr) { auto tool = new LinearDimTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_sketch, m_toolBar, Qt::Key_D ); addAction( qIconFromResources("tool_coinsidence"), tr("Points tool"), [this]() -> void { if (m_curDocView != nullptr) { auto tool = new CoinsidenceTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_sketch, m_toolBar, Qt::Key_C ); m_toolBar->addSeparator(); menu_sketch->addSeparator(); addAction( qIconFromResources("tool_chamfer"), tr("Chamfer"), [this]() { if (m_curDocView != nullptr) { auto tool = new ChamferTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_sketch, m_toolBar, Qt::SHIFT + Qt::Key_C ); addAction( qIconFromResources("tool_fillet"), tr("Fillet"), [this]() { if (m_curDocView != nullptr) { auto tool = new FilletTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_sketch, m_toolBar, Qt::SHIFT + Qt::Key_F ); m_toolBar->addSeparator(); addAction( qIconFromResources("tool_extrude"), tr("Extrude"), [this]() { if (m_curDocView != nullptr) { auto tool = new ExtrudeTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_body, m_toolBar, Qt::Key_E ); addAction( qIconFromResources("tool_revolve"), tr("Revolve"), [this]() { if (m_curDocView != nullptr) { auto tool = new RevolveTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_body, m_toolBar, Qt::Key_R ); addAction( qIconFromResources("tool_loft"), tr("Loft"), [this]() { if (m_curDocView != nullptr) { auto tool = new LoftTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_body, m_toolBar, Qt::Key_L ); m_toolBar->addSeparator(); menu_body->addSeparator(); addAction( qIconFromResources("tool_boolean"), tr("Boolean"), [this]() { if (m_curDocView != nullptr) { auto tool = new BooleanTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_body, m_toolBar, Qt::SHIFT + Qt::Key_B ); addAction( qIconFromResources("tool_radial_array"), tr("Radial array"), [this]() { if (m_curDocView != nullptr) { auto tool = new SolidRadialArrayTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_body, m_toolBar, Qt::SHIFT + Qt::Key_R ); addAction( qIconFromResources("tool_thread"), tr("Thread"), [this]() { if (m_curDocView != nullptr) { auto tool = new ThreadTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_body, m_toolBar, Qt::SHIFT + Qt::Key_T ); m_toolBar->addSeparator(); addAction( qIconFromResources("tool_assembly"), tr("New assembly"), [this]() { if (m_curDocView != nullptr) { auto tool = new AssemblyTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_assembly, m_toolBar ); m_toolBar->addSeparator(); menu_assembly->addSeparator(); addAction( qIconFromResources("tool_solid_clone"), tr("Solid body clone"), [this]() { if (m_curDocView != nullptr) { auto tool = new SolidCloneTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_assembly, m_toolBar, Qt::SHIFT + Qt::Key_A ); m_toolBar->addSeparator(); menu_assembly->addSeparator(); addAction( qIconFromResources("tool_fixed"), tr("Fixed part constraint"), [this]() { if (m_curDocView != nullptr) { auto tool = new Fixed3dConstraintTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_assembly, m_toolBar ); addAction( qIconFromResources("tool_coinsidence_3d"), tr("Coinsidence 3d constraint"), [this]() { if (m_curDocView != nullptr) { auto tool = new Coinsidence3dConstraintTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_assembly, m_toolBar ); addAction( qIconFromResources("tool_transmission"), tr("Transmission 3d constraint"), [this]() { if (m_curDocView != nullptr) { auto tool = new Transmission3dConstraintTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_assembly, m_toolBar ); m_toolBar->addSeparator(); addAction( qIconFromResources("template_flange_coupling"), tr("Flange coupling"), [this]() { if (m_curDocView != nullptr) { auto tool = new FlangeCouplingTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_templates, m_toolBar ); #if DEBUG m_toolBar->addSeparator(); menu_file->addSeparator(); addAction( qIconFromResources("tool_debug"), tr("Debug tool"), [this]() { if (m_curDocView != nullptr) { auto tool = new DebugTool(m_activeToolPalette); tool->m_activatedFromModelTree = false; m_curDocView->setActiveTool(tool); } }, menu_file, m_toolBar ); #endif setMenuBar(menuBar); addToolBar(m_toolBar); } void AppWindow::makeWelcomePage() { if (!m_welcome_page) { m_welcome_page = new Welcome(m_config, m_page); m_page->layout()->addWidget(m_welcome_page); QObject::connect(m_welcome_page, &Welcome::new_document_button_clicked, [this](){on_doc_new();}); QObject::connect(m_welcome_page, &Welcome::open_file_button_clicked, [this](){on_doc_open();}); QObject::connect(m_welcome_page, &Welcome::recent_document_button_clicked, [this](std::string filename){ openDocument(QString::fromStdString(filename)); }); } } void AppWindow::closeEvent(QCloseEvent *event) { m_config->windowWidth = this->width(); m_config->windowHeight = this->height(); m_config->isMaximized = this->isMaximized(); saveConfig(); QMainWindow::closeEvent(event); } void AppWindow::showEvent(QShowEvent *event) { windowHandle()->setSurfaceType(QWindow::OpenGLSurface); } int main(int argc, char *argv[]) { // todo: УБРАТЬ if (QDateTime::currentDateTime().date().year() != 2025) { printf("invalid year, faking time...\n"); execlp("faketime", "faketime", "2025-12-24", argv[0]); printf("faking time failed!\n"); } // todo: УБРАТЬ #ifdef __linux__ setenv("QT_QPA_PLATFORM", "xcb", 1); #endif if (!qEnvironmentVariable("ICON_DIR").isEmpty()) { qDebug() << "searching icons in " << qEnvironmentVariable("ICON_DIR") << "\n"; QIcon::setThemeSearchPaths({qEnvironmentVariable("ICON_DIR")}); } if (!qEnvironmentVariable("ICON_THEME").isEmpty()) { qDebug() << "using icons from theme " << qEnvironmentVariable("ICON_THEME") << "\n"; QIcon::setThemeName(qEnvironmentVariable("ICON_THEME")); } QApplication application{argc, argv}; VSN::Application vapp; qRegisterMetaType<std::weak_ptr<data::C3dObject>>("std::weak_ptr<data::C3dObject>"); QTranslator myappTranslator; if (myappTranslator.load(QLocale::system(), QString(u"maints"), QString(u"_"), QString(u":/i18n"), QString(u".qm"))) { application.installTranslator(&myappTranslator); } else { std::cout << "translation not found" << std::endl; } AppWindow win{}; if (!win.fetchLicence()) { return 1; } win.show(); return application.exec(); }