diff --git a/bouncer/src/repo/core/model/bson/repo_bson_calibration.h b/bouncer/src/repo/core/model/bson/repo_bson_calibration.h new file mode 100644 index 000000000..5adbf09a2 --- /dev/null +++ b/bouncer/src/repo/core/model/bson/repo_bson_calibration.h @@ -0,0 +1,42 @@ +/** +* Copyright (C) 2024 3D Repo Ltd +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +/** +* Assets BSON +*/ + +#pragma once + +#include "repo_bson.h" + +namespace repo { + namespace core { + namespace model { + + class REPO_API_EXPORT RepoCalibration : public RepoBSON + { + public: + + RepoCalibration() : RepoBSON() {} + + RepoCalibration(RepoBSON bson) : RepoBSON(bson) {} + + ~RepoCalibration() {} + }; + }// end namespace model + } // end namespace core +} // end namespace repo diff --git a/bouncer/src/repo/core/model/bson/repo_bson_factory.cpp b/bouncer/src/repo/core/model/bson/repo_bson_factory.cpp index aeb6528c2..a84a67f5a 100644 --- a/bouncer/src/repo/core/model/bson/repo_bson_factory.cpp +++ b/bouncer/src/repo/core/model/bson/repo_bson_factory.cpp @@ -742,6 +742,74 @@ RepoAssets RepoBSONFactory::makeRepoBundleAssets( return RepoAssets(builder.obj()); } +RepoCalibration repo::core::model::RepoBSONFactory::makeRepoCalibration( + const repo::lib::RepoUUID& projectId, + const repo::lib::RepoUUID& drawingId, + const repo::lib::RepoUUID& revisionId, + const std::vector& horizontal3d, + const std::vector& horizontal2d, + const std::vector& verticalRange, + const std::string& units) +{ + RepoBSONBuilder bsonBuilder; + bsonBuilder.append(REPO_LABEL_ID, repo::lib::RepoUUID::createUUID()); + bsonBuilder.append(REPO_LABEL_PROJECT, projectId); + bsonBuilder.append(REPO_LABEL_DRAWING, drawingId); + bsonBuilder.append(REPO_LABEL_REVISION, revisionId); + bsonBuilder.appendTimeStamp(REPO_LABEL_CREATEDAT); + + RepoBSONBuilder horizontalBuilder; + if (horizontal3d.size() == 2) { + std::vector>arrays; + std::vector arr1; + arr1.push_back(horizontal3d[0].x); + arr1.push_back(horizontal3d[0].y); + arr1.push_back(horizontal3d[0].z); + arrays.push_back(arr1); + + std::vector arr2; + arr2.push_back(horizontal3d[1].x); + arr2.push_back(horizontal3d[1].y); + arr2.push_back(horizontal3d[1].z); + arrays.push_back(arr2); + + horizontalBuilder.appendArray(REPO_LABEL_MODEL, arrays); + } + else { + repoError << "Incorrect amount of horizontal 3D vectors supplied to makeRepoCalibration" << std::endl; + } + + if (horizontal2d.size() == 2) { + std::vector>arrays; + std::vector arr1; + arr1.push_back(horizontal2d[0].x); + arr1.push_back(horizontal2d[0].y); + arrays.push_back(arr1); + + std::vector arr2; + arr2.push_back(horizontal2d[1].x); + arr2.push_back(horizontal2d[1].y); + arrays.push_back(arr2); + + horizontalBuilder.appendArray(REPO_LABEL_DRAWING, arrays); + } + else { + repoError << "Incorrect amount of horizontal 2D vectors supplied to makeRepoCalibration" << std::endl; + } + bsonBuilder.append(REPO_LABEL_HORIZONTAL, horizontalBuilder.obj()); + + if (verticalRange.size() == 2) { + bsonBuilder.appendArray(REPO_LABEL_VERTICALRANGE, verticalRange); + } + else { + repoError << "Incorrect amount of values for vertical range supplied to makeRepoCalibration" << std::endl; + } + + bsonBuilder.append(REPO_LABEL_UNITS, units); + + return RepoCalibration(bsonBuilder.obj()); +} + ReferenceNode RepoBSONFactory::makeReferenceNode( const std::string &database, const std::string &project, diff --git a/bouncer/src/repo/core/model/bson/repo_bson_factory.h b/bouncer/src/repo/core/model/bson/repo_bson_factory.h index ac4dfdf4b..85b68aa84 100644 --- a/bouncer/src/repo/core/model/bson/repo_bson_factory.h +++ b/bouncer/src/repo/core/model/bson/repo_bson_factory.h @@ -29,6 +29,7 @@ #include "repo_bson_task.h" #include "repo_bson_user.h" #include "repo_bson_assets.h" +#include "repo_bson_calibration.h" #include "repo_node.h" #include "repo_node_camera.h" #include "repo_node_metadata.h" @@ -162,6 +163,27 @@ namespace repo { const std::vector& repoJsonFiles, const std::vector metadata); + /** + * Create a Drawing Calibration BSON + * @param projectId uuid of the project + * @param drawingId uuid of the drawing + * @param revisionId uuid of the revision + * @param horizontal3d two reference points in the 3d space + * @param horizontal2d two reference points in the 2d space + * @param verticalRange two values marking the vertical range + * @param units the units used for the values. + * @return returns a RepoCalibration + */ + static RepoCalibration makeRepoCalibration( + const repo::lib::RepoUUID& projectId, + const repo::lib::RepoUUID& drawingId, + const repo::lib::RepoUUID& revisionId, + const std::vector& horizontal3d, + const std::vector& horizontal2d, + const std::vector& verticalRange, + const std::string& units + ); + /* * -------------------- REPO NODES ------------------------ */ diff --git a/bouncer/src/repo/core/model/repo_model_global.h b/bouncer/src/repo/core/model/repo_model_global.h index 24b523a38..c50652439 100644 --- a/bouncer/src/repo/core/model/repo_model_global.h +++ b/bouncer/src/repo/core/model/repo_model_global.h @@ -81,6 +81,15 @@ // Vertex/triangle map propeties #define REPO_LABEL_MERGED_NODES "merged_nodes" +// Drawing calibration properties +#define REPO_LABEL_DRAWING "drawing" +#define REPO_LABEL_REVISION "revision" +#define REPO_LABEL_CREATEDAT "createdAt" +#define REPO_LABEL_CREATEDBY "createdBy" +#define REPO_LABEL_HORIZONTAL "horizontal" +#define REPO_LABEL_VERTICALRANGE "verticalRange" +#define REPO_LABEL_UNITS "units" + #define REPO_COMMAND_UPDATE "update" #define REPO_COMMAND_UPDATES "updates" #define REPO_COMMAND_DELETE "delete" @@ -105,6 +114,7 @@ #define REPO_COLLECTION_SEQUENCE "sequences" #define REPO_COLLECTION_TASK "activities" #define REPO_COLLECTION_DRAWINGS "drawings.history" +#define REPO_COLLECTION_CALIBRATIONS "drawings.revisions.calibrations" #define REPO_COLLECTION_SETTINGS "settings" diff --git a/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dgn.cpp b/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dgn.cpp index 303c360e5..cd447ba0a 100644 --- a/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dgn.cpp +++ b/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dgn.cpp @@ -358,10 +358,50 @@ void FileProcessorDgn::importDrawing(OdDgDatabasePtr pDb, const ODCOLORREF* pPal // to the 3D coordinates above. Add these to the appropriate schema when // it is ready... - std::vector points; - points.push_back(worldToDeviceMatrix * a); - points.push_back(worldToDeviceMatrix * b); - points.push_back(worldToDeviceMatrix * c); + //std::vector points; + //points.push_back(worldToDeviceMatrix * a); + //points.push_back(worldToDeviceMatrix * b); + //points.push_back(worldToDeviceMatrix * c); + + // Calculate points in SVG space + OdGePoint3d aS = worldToDeviceMatrix * a; + OdGePoint3d bS = worldToDeviceMatrix * b; + + // Convert to 2D by dropping z component (note: have not thought about it. Just conceptually). + repo::lib::RepoVector2D aS2d = repo::lib::RepoVector2D(aS.x, aS.y); + repo::lib::RepoVector2D bS2d = repo::lib::RepoVector2D(bS.x, bS.y); + + // Convert 3d vectors from ODA format to 3d repo format + repo::lib::RepoVector3D a3d = repo::lib::RepoVector3D(a.x, a.y, a.z); + repo::lib::RepoVector3D b3d = repo::lib::RepoVector3D(b.x, b.y, b.z); + + // Assemble calibration outcome + std::vector horizontal3d; + horizontal3d.push_back(a3d); + horizontal3d.push_back(b3d); + + std::vector horizontal2d; + horizontal2d.push_back(aS2d); + horizontal2d.push_back(bS2d); + + repo::manipulator::modelutility::DrawingCalibration calibration; + calibration.horizontalCalibration3d = horizontal3d; + calibration.horizontalCalibration2d = horizontal2d; + + calibration.verticalRange = { 0, 10 }; // TODO: how do I calculate that? + + OdDgElementId elementActId = pDb->getActiveModelId(); + OdDgModelPtr pModel = elementActId.safeOpenObject(); + + repo::manipulator::modelconvertor::ModelUnits units = determineModelUnits(pModel->getMasterUnit()); + + calibration.units = repo::manipulator::modelconvertor::toUnitsString(units); + + + + // Pass calibration outcome to collector + drawingCollector->drawingCalibration = calibration; + // The call to update is what will create the svg in the memory stream diff --git a/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dwg.cpp b/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dwg.cpp index 5940a6486..275c546ab 100644 --- a/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dwg.cpp +++ b/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dwg.cpp @@ -35,6 +35,9 @@ #include "data_processor_dwg.h" +#include +#include + using namespace repo::manipulator::modelconvertor::odaHelper; class VectoriseDeviceDwg : public OdGsBaseVectorizeDevice @@ -84,7 +87,7 @@ class DeviceModuleDwg : public OdGsBaseModule }; ODRX_DEFINE_PSEUDO_STATIC_MODULE(DeviceModuleDwg); -void importModel(OdDbDatabasePtr pDb, GeometryCollector* collector) +void FileProcessorDwg::importModel(OdDbDatabasePtr pDb) { // Create the vectorizer device that will render the DWG database. This will // use the GeometryCollector underneath. @@ -108,7 +111,7 @@ void importModel(OdDbDatabasePtr pDb, GeometryCollector* collector) pGsModule.release(); } -void importDrawing(OdDbDatabasePtr pDb, repo::manipulator::modelutility::DrawingImageInfo* collector) +void FileProcessorDwg::importDrawing(OdDbDatabasePtr pDb) { OdGsModulePtr pModule = ::odrxDynamicLinker()->loadModule(OdSvgExportModuleName, false); OdGsDevicePtr dev = pModule->createDevice(); @@ -142,8 +145,67 @@ void importDrawing(OdDbDatabasePtr pDb, repo::manipulator::modelutility::Drawing pHelperDevice->onSize(OdGsDCRect(0, 1024, 768, 0)); - // Here we can extract the calibration in the same way as file_processor_dgn when ready... + // This section extracts the view information which can be used to map + // betweeen the SVG and world coordinate systems. + // The graphics system (Gs) view https://docs.opendesign.com/tv/gs_OdGsView.html + // is used to derive points that map between the WCS of the drawing and + // the svg file. + + const OdGsView* pGsView = pDeviceSvg->viewAt(0); + + auto worldToDeviceMatrix = pGsView->worldToDeviceMatrix(); + auto objectToDeviceMatrix = pGsView->objectToDeviceMatrix(); + + // Pick three points (two vectors) to describe the map. The transform + // can be computed from these each time from then on. + + OdGePoint3d a(0, 0, 0); + OdGePoint3d b(1, 0, 0); + OdGePoint3d c(0, 1, 0); + + // The following vector contains the points in the SVG file corresponding + // to the 3D coordinates above. Add these to the appropriate schema when + // it is ready... + + //std::vector points; + //points.push_back(worldToDeviceMatrix * a); + //points.push_back(worldToDeviceMatrix * b); + //points.push_back(worldToDeviceMatrix * c); + + // Calculate points in SVG space + OdGePoint3d aS = worldToDeviceMatrix * a; + OdGePoint3d bS = worldToDeviceMatrix * b; + + // Convert to 2D by dropping z component (note: have not thought about it. Just conceptually). + repo::lib::RepoVector2D aS2d = repo::lib::RepoVector2D(aS.x, aS.y); + repo::lib::RepoVector2D bS2d = repo::lib::RepoVector2D(bS.x, bS.y); + + // Convert 3d vectors from ODA format to 3d repo format + repo::lib::RepoVector3D a3d = repo::lib::RepoVector3D(a.x, a.y, a.z); + repo::lib::RepoVector3D b3d = repo::lib::RepoVector3D(b.x, b.y, b.z); + // Assemble calibration outcome + std::vector horizontal3d; + horizontal3d.push_back(a3d); + horizontal3d.push_back(b3d); + + std::vector horizontal2d; + horizontal2d.push_back(aS2d); + horizontal2d.push_back(bS2d); + + repo::manipulator::modelutility::DrawingCalibration calibration; + calibration.horizontalCalibration3d = horizontal3d; + calibration.horizontalCalibration2d = horizontal2d; + + calibration.verticalRange = { 0, 10 }; // TODO: how do I calculate that? + + repo::manipulator::modelconvertor::ModelUnits units = determineModelUnits(pDb->getINSUNITS()); + calibration.units = repo::manipulator::modelconvertor::toUnitsString(units); + + // Pass calibration outcome to collector + drawingCollector->drawingCalibration = calibration; + + pHelperDevice->update(); // Copy the SVG contents into a string @@ -161,7 +223,21 @@ void importDrawing(OdDbDatabasePtr pDb, repo::manipulator::modelutility::Drawing // Provide the string to the collector as a vector - std::copy(svg.c_str(), svg.c_str() + svg.length(), std::back_inserter(collector->data)); + std::copy(svg.c_str(), svg.c_str() + svg.length(), std::back_inserter(drawingCollector->data)); + } +} + +repo::manipulator::modelconvertor::ModelUnits FileProcessorDwg::determineModelUnits(const OdDb::UnitsValue units){ + switch (units) { + case OdDb::kUnitsMeters: return ModelUnits::METRES; + case OdDb::kUnitsDecimeters: return ModelUnits::DECIMETRES; + case OdDb::kUnitsCentimeters: return ModelUnits::CENTIMETRES; + case OdDb::kUnitsMillimeters: return ModelUnits::MILLIMETRES; + case OdDb::kUnitsFeet: return ModelUnits::FEET; + case OdDb::kUnitsInches: return ModelUnits::INCHES; + default: + repoWarning << "Unrecognised unit measure: " << (int)units; + return ModelUnits::UNKNOWN; } } @@ -187,12 +263,12 @@ uint8_t FileProcessorDwg::readFile() if (collector) { - importModel(pDb, collector); + importModel(pDb); } if (drawingCollector) { - importDrawing(pDb, drawingCollector); + importDrawing(pDb); } pDb.release(); diff --git a/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dwg.h b/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dwg.h index 4a6d95bb9..b4d5e6f8e 100644 --- a/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dwg.h +++ b/bouncer/src/repo/manipulator/modelconvertor/import/odaHelper/file_processor_dwg.h @@ -47,6 +47,11 @@ namespace repo { protected: OdStaticRxObject svcs; + + private: + void importModel(OdDbDatabasePtr pDb); + void importDrawing(OdDbDatabasePtr pDb); + ModelUnits determineModelUnits(const OdDb::UnitsValue units); }; } } diff --git a/bouncer/src/repo/manipulator/modelutility/repo_drawing.h b/bouncer/src/repo/manipulator/modelutility/repo_drawing.h index 3715e0dda..e9dc029a1 100644 --- a/bouncer/src/repo/manipulator/modelutility/repo_drawing.h +++ b/bouncer/src/repo/manipulator/modelutility/repo_drawing.h @@ -19,11 +19,22 @@ #include #include +#include "repo/lib/datastructure/repo_vector.h" namespace repo { namespace manipulator { namespace modelutility { + /** + * Holds outcome of the autocalibration + */ + struct DrawingCalibration { + std::vector horizontalCalibration3d; + std::vector horizontalCalibration2d; + std::vector verticalRange; + std::string units; + }; + /** * Holds complete information about an imported drawing at runtime */ @@ -31,6 +42,7 @@ namespace repo { { std::string name; // The name of the original file (e.g. "Floor1.DWG") std::vector data; // The drawing in svg format + DrawingCalibration drawingCalibration; }; } } diff --git a/bouncer/src/repo/manipulator/modelutility/repo_drawing_manager.cpp b/bouncer/src/repo/manipulator/modelutility/repo_drawing_manager.cpp index d836e0b7c..413d4b03b 100644 --- a/bouncer/src/repo/manipulator/modelutility/repo_drawing_manager.cpp +++ b/bouncer/src/repo/manipulator/modelutility/repo_drawing_manager.cpp @@ -18,6 +18,7 @@ #include "../../core/model/bson/repo_bson_builder.h" #include "../../core/model/bson/repo_bson_ref.h" +#include "../../core/model/bson/repo_bson_factory.h" #include "../../error_codes.h" using namespace repo::manipulator::modelutility; @@ -45,11 +46,12 @@ uint8_t DrawingManager::commitImage( auto name = drawing.name.substr(0, drawing.name.size() - 3) + "svg"; // The name should be the drawing's original name with an updated extension repo::core::model::RepoBSONBuilder metadata; + auto revId = revision.getUniqueID(); metadata.append(REPO_NODE_LABEL_NAME, name); metadata.append(REPO_LABEL_MEDIA_TYPE, REPO_MEDIA_TYPE_SVG); metadata.append(REPO_LABEL_PROJECT, revision.getProject()); metadata.append(REPO_LABEL_MODEL, revision.getModel()); - metadata.append(REPO_NODE_REVISION_ID, revision.getUniqueID()); + metadata.append(REPO_NODE_REVISION_ID, revId); fileManager->uploadFileAndCommit( teamspace, @@ -58,7 +60,7 @@ uint8_t DrawingManager::commitImage( drawing.data, metadata.obj() ); - + auto updated = revision.cloneAndAddImage(drawingRefNodeId); std::string error; @@ -70,5 +72,27 @@ uint8_t DrawingManager::commitImage( return REPOERR_UPLOAD_FAILED; } + // Retreive and process calibration + auto calibration = drawing.drawingCalibration; + + auto calibrationBSON = repo::core::model::RepoBSONFactory::makeRepoCalibration( + revision.getProject(), + revision.getModel(), + revId, + calibration.horizontalCalibration3d, + calibration.horizontalCalibration2d, + calibration.verticalRange, + calibration.units + ); + + handler->insertDocument(teamspace, REPO_COLLECTION_CALIBRATIONS, calibrationBSON, error); + + if (error.size()) + { + repoError << "Error committing calibration: " << error; + return REPOERR_UPLOAD_FAILED; + } + + return REPOERR_OK; } \ No newline at end of file