TransformationNode RepoBSONFactory::makeTransformationNode( const std::vector<std::vector<float>> &transMatrix, const std::string &name, const std::vector<repoUUID> &parents, const int &apiLevel) { RepoBSONBuilder builder; auto defaults = appendDefaults(REPO_NODE_TYPE_TRANSFORMATION, apiLevel, generateUUID(), name, parents); builder.appendElements(defaults); //-------------------------------------------------------------------------- // Store matrix as array of arrays uint32_t matrixSize = 4; RepoBSONBuilder rows; for (uint32_t i = 0; i < transMatrix.size(); ++i) { RepoBSONBuilder columns; for (uint32_t j = 0; j < transMatrix[i].size(); ++j){ columns << std::to_string(j) << transMatrix[i][j]; } rows.appendArray(std::to_string(i), columns.obj()); } builder.appendArray(REPO_NODE_LABEL_MATRIX, rows.obj()); return TransformationNode(builder.obj()); }
RepoRole RepoBSONFactory::_makeRepoRole( const std::string &roleName, const std::string &database, const std::vector<RepoPrivilege> &privileges, const std::vector<std::pair<std::string, std::string> > &inheritedRoles ) { RepoBSONBuilder builder; builder << REPO_LABEL_ID << database + "." + roleName; builder << REPO_ROLE_LABEL_ROLE << roleName; builder << REPO_ROLE_LABEL_DATABASE << database; //====== Add Privileges ======== if (privileges.size() > 0) { RepoBSONBuilder privilegesBuilder; for (size_t i = 0; i < privileges.size(); ++i) { const auto &p = privileges[i]; RepoBSONBuilder innerBsonBuilder, actionBuilder; RepoBSON resource = BSON(REPO_ROLE_LABEL_DATABASE << p.database << REPO_ROLE_LABEL_COLLECTION << p.collection); innerBsonBuilder << REPO_ROLE_LABEL_RESOURCE << resource; for (size_t aCount = 0; aCount < p.actions.size(); ++aCount) { actionBuilder << std::to_string(aCount) << RepoRole::dbActionToString(p.actions[aCount]); } innerBsonBuilder.appendArray(REPO_ROLE_LABEL_ACTIONS, actionBuilder.obj()); privilegesBuilder << std::to_string(i) << innerBsonBuilder.obj(); } builder.appendArray(REPO_ROLE_LABEL_PRIVILEGES, privilegesBuilder.obj()); } else { repoDebug << "Creating a role with no privileges!"; } //====== Add Inherited Roles ======== if (inheritedRoles.size() > 0) { RepoBSONBuilder inheritedRolesBuilder; for (size_t i = 0; i < inheritedRoles.size(); ++i) { RepoBSON parentRole = BSON( REPO_ROLE_LABEL_ROLE << inheritedRoles[i].second << REPO_ROLE_LABEL_DATABASE << inheritedRoles[i].first ); inheritedRolesBuilder << std::to_string(i) << parentRole; } builder.appendArray(REPO_ROLE_LABEL_INHERITED_ROLES, inheritedRolesBuilder.obj()); } return RepoRole(builder.obj()); }
RepoRoleSettings RepoBSONFactory::makeRepoRoleSettings( const std::string &uniqueRoleName, const std::string &color, const std::string &description, const std::vector<std::string> &modules) { RepoBSONBuilder builder; //-------------------------------------------------------------------------- // Project name if (!uniqueRoleName.empty()) builder << REPO_LABEL_ID << uniqueRoleName; // Color if (!color.empty()) builder << REPO_LABEL_COLOR << color; // Description if (!description.empty()) builder << REPO_LABEL_DESCRIPTION << description; // Modules if (modules.size() > 0) builder.appendArray(REPO_LABEL_MODULES, modules); return RepoRoleSettings(builder.obj()); }
RepoNode RepoNode::cloneAndRemoveParent( const repoUUID &parentID, const bool &newUniqueID) const { RepoBSONBuilder builder; RepoBSONBuilder arrayBuilder; std::vector<repoUUID> currentParents = getParentIDs(); auto parentIdx = std::find(currentParents.begin(), currentParents.end(), parentID); if (parentIdx != currentParents.end()) { currentParents.erase(parentIdx); if (newUniqueID) { builder.append(REPO_NODE_LABEL_ID, generateUUID()); } } else { repoWarning << "Trying to remove a parent that isn't really a parent!"; } if (currentParents.size() > 0) { builder.appendArray(REPO_NODE_LABEL_PARENTS, currentParents); builder.appendElementsUnique(*this); } else { builder.appendElementsUnique(removeField(REPO_NODE_LABEL_PARENTS)); } return RepoNode(builder.obj(), bigFiles); }
RepoNode RepoNode::cloneAndAddParent( const repoUUID &parentID, const bool &newUniqueID, const bool &newSharedID, const bool &overwrite) const { RepoBSONBuilder builder; RepoBSONBuilder arrayBuilder; std::vector<repoUUID> currentParents; if (!overwrite) { currentParents = getParentIDs(); } if (std::find(currentParents.begin(), currentParents.end(), parentID) == currentParents.end()) currentParents.push_back(parentID); builder.appendArray(REPO_NODE_LABEL_PARENTS, currentParents); if (newUniqueID) builder.append(REPO_NODE_LABEL_ID, generateUUID()); if (newSharedID) builder.append(REPO_NODE_LABEL_SHARED_ID, generateUUID()); builder.appendElementsUnique(*this); return RepoNode(builder.obj(), bigFiles); }
MaterialNode RepoBSONFactory::makeMaterialNode( const repo_material_t &material, const std::string &name, const int &apiLevel) { RepoBSONBuilder builder; // Compulsory fields such as _id, type, api as well as path // and optional name auto defaults = appendDefaults(REPO_NODE_TYPE_MATERIAL, apiLevel, generateUUID(), name); builder.appendElements(defaults); if (material.ambient.size() > 0) builder.appendArray(REPO_NODE_MATERIAL_LABEL_AMBIENT, material.ambient); if (material.diffuse.size() > 0) builder.appendArray(REPO_NODE_MATERIAL_LABEL_DIFFUSE, material.diffuse); if (material.specular.size() > 0) builder.appendArray(REPO_NODE_MATERIAL_LABEL_SPECULAR, material.specular); if (material.emissive.size() > 0) builder.appendArray(REPO_NODE_MATERIAL_LABEL_EMISSIVE, material.emissive); if (material.isWireframe) builder << REPO_NODE_MATERIAL_LABEL_WIREFRAME << material.isWireframe; if (material.isTwoSided) builder << REPO_NODE_MATERIAL_LABEL_TWO_SIDED << material.isTwoSided; if (material.opacity == material.opacity) builder << REPO_NODE_MATERIAL_LABEL_OPACITY << material.opacity; if (material.shininess == material.shininess) builder << REPO_NODE_MATERIAL_LABEL_SHININESS << material.shininess; if (material.shininessStrength == material.shininessStrength) builder << REPO_NODE_MATERIAL_LABEL_SHININESS_STRENGTH << material.shininessStrength; return MaterialNode(builder.obj()); }
RepoBSON RepoBSONFactory::appendDefaults( const std::string &type, const unsigned int api, const repoUUID &sharedId, const std::string &name, const std::vector<repoUUID> &parents, const repoUUID &uniqueID) { RepoBSONBuilder builder; uint64_t bytesize = 0; //-------------------------------------------------------------------------- // ID field (UUID) builder.append(REPO_NODE_LABEL_ID, uniqueID); //-------------------------------------------------------------------------- // Shared ID (UUID) builder.append(REPO_NODE_LABEL_SHARED_ID, sharedId); bytesize += 2 * sizeof(repoUUID); //-------------------------------------------------------------------------- // Type if (!type.empty()) { builder << REPO_NODE_LABEL_TYPE << type; } //-------------------------------------------------------------------------- // API level builder << REPO_NODE_LABEL_API << api; //-------------------------------------------------------------------------- // Parents if (parents.size() > 0) { builder.appendArray(REPO_NODE_LABEL_PARENTS, parents); } //-------------------------------------------------------------------------- // Name if (!name.empty()) { builder << REPO_NODE_LABEL_NAME << name; } return builder.obj(); }
RepoBSON MeshNode::meshMappingAsBSON(const repo_mesh_mapping_t &mapping) { RepoBSONBuilder builder; builder.append(REPO_NODE_MESH_LABEL_MAP_ID, mapping.mesh_id); builder.append(REPO_NODE_MESH_LABEL_MATERIAL_ID, mapping.material_id); builder << REPO_NODE_MESH_LABEL_VERTEX_FROM << mapping.vertFrom; builder << REPO_NODE_MESH_LABEL_VERTEX_TO << mapping.vertTo; builder << REPO_NODE_MESH_LABEL_TRIANGLE_FROM << mapping.triFrom; builder << REPO_NODE_MESH_LABEL_TRIANGLE_TO << mapping.triTo; RepoBSONBuilder bbBuilder; bbBuilder.append("0", mapping.min); bbBuilder.append("1", mapping.max); builder.appendArray(REPO_NODE_MESH_LABEL_BOUNDING_BOX, bbBuilder.obj()); return builder.obj(); }
RepoNode RepoNode::cloneAndAddParent( const std::vector<repoUUID> &parentIDs) const { RepoBSONBuilder builder; RepoBSONBuilder arrayBuilder; std::vector<repoUUID> currentParents = getParentIDs(); currentParents.insert(currentParents.end(), parentIDs.begin(), parentIDs.end()); std::sort(currentParents.begin(), currentParents.end()); auto last = std::unique(currentParents.begin(), currentParents.end()); if (last != currentParents.end()) currentParents.erase(last, currentParents.end()); builder.appendArray(REPO_NODE_LABEL_PARENTS, currentParents); builder.appendElementsUnique(*this); return RepoNode(builder.obj(), bigFiles); }
RevisionNode RepoBSONFactory::makeRevisionNode( const std::string &user, const repoUUID &branch, const std::vector<repoUUID> ¤tNodes, //const std::vector<repoUUID> &added, //const std::vector<repoUUID> &removed, //const std::vector<repoUUID> &modified, const std::vector<std::string> &files, const std::vector<repoUUID> &parent, const std::vector<double> &worldOffset, const std::string &message, const std::string &tag, const int &apiLevel ) { RepoBSONBuilder builder; repoUUID uniqueID = generateUUID(); //-------------------------------------------------------------------------- // Compulsory fields such as _id, type, api as well as path auto defaults = appendDefaults(REPO_NODE_TYPE_REVISION, apiLevel, branch, "", parent, uniqueID); builder.appendElements(defaults); //-------------------------------------------------------------------------- // Author if (!user.empty()) builder << REPO_NODE_REVISION_LABEL_AUTHOR << user; //-------------------------------------------------------------------------- // Message if (!message.empty()) builder << REPO_NODE_REVISION_LABEL_MESSAGE << message; //-------------------------------------------------------------------------- // Tag if (!tag.empty()) builder << REPO_NODE_REVISION_LABEL_TAG << tag; //-------------------------------------------------------------------------- // Timestamp builder.appendTimeStamp(REPO_NODE_REVISION_LABEL_TIMESTAMP); //-------------------------------------------------------------------------- // Current Unique IDs if (currentNodes.size() > 0) builder.appendArray(REPO_NODE_REVISION_LABEL_CURRENT_UNIQUE_IDS, currentNodes); //-------------------------------------------------------------------------- // Shift for world coordinates if (worldOffset.size() > 0) builder.appendArray(REPO_NODE_REVISION_LABEL_WORLD_COORD_SHIFT, worldOffset); ////-------------------------------------------------------------------------- //// Added Shared IDs //if (added.size() > 0) // builder.appendArray(REPO_NODE_REVISION_LABEL_ADDED_SHARED_IDS, builder.createArrayBSON(added)); ////-------------------------------------------------------------------------- //// Deleted Shared IDs //if (removed.size() > 0) // builder.appendArray(REPO_NODE_REVISION_LABEL_DELETED_SHARED_IDS, builder.createArrayBSON(removed)); ////-------------------------------------------------------------------------- //// Modified Shared IDs //if (modified.size() > 0) // builder.appendArray(REPO_NODE_REVISION_LABEL_MODIFIED_SHARED_IDS, builder.createArrayBSON(modified)); //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- // original files references if (files.size() > 0) { std::string uniqueIDStr = UUIDtoString(uniqueID); mongo::BSONObjBuilder arrbuilder; for (int i = 0; i < files.size(); ++i) { arrbuilder << std::to_string(i) << uniqueIDStr + files[i]; } builder.appendArray(REPO_NODE_REVISION_LABEL_REF_FILE, arrbuilder.obj()); } return RevisionNode(builder.obj()); }
MeshNode RepoBSONFactory::makeMeshNode( const std::vector<repo_vector_t> &vertices, const std::vector<repo_face_t> &faces, const std::vector<repo_vector_t> &normals, const std::vector<std::vector<float>> &boundingBox, const std::vector<std::vector<repo_vector2d_t>> &uvChannels, const std::vector<repo_color4d_t> &colors, const std::vector<std::vector<float>> &outline, const std::string &name, const int &apiLevel) { RepoBSONBuilder builder; uint64_t bytesize = 0; //track the (approximate) size to know when we need to offload to gridFS repoUUID uniqueID = generateUUID(); auto defaults = appendDefaults(REPO_NODE_TYPE_MESH, apiLevel, generateUUID(), name, std::vector<repoUUID>(), uniqueID); bytesize += defaults.objsize(); builder.appendElements(defaults); if (!vertices.size() || !faces.size()) { repoWarning << "Creating a mesh (" << defaults.getUUIDField(REPO_NODE_LABEL_ID) << ") with no vertices/faces!"; } std::unordered_map<std::string, std::pair<std::string, std::vector<uint8_t>>> binMapping; if (boundingBox.size() > 0) { RepoBSONBuilder arrayBuilder; for (int i = 0; i < boundingBox.size(); i++) { arrayBuilder.appendArray(std::to_string(i), boundingBox[i]); bytesize += boundingBox[i].size() * sizeof(boundingBox[i][0]); } builder.appendArray(REPO_NODE_MESH_LABEL_BOUNDING_BOX, arrayBuilder.obj()); } if (outline.size() > 0) { RepoBSONBuilder arrayBuilder; for (int i = 0; i < outline.size(); i++) { arrayBuilder.appendArray(boost::lexical_cast<std::string>(i), outline[i]); bytesize += outline[i].size() * sizeof(outline[i][0]); } builder.appendArray(REPO_NODE_MESH_LABEL_OUTLINE, arrayBuilder.obj()); } /* * TODO: because mongo has a stupid internal limit of 64MB, we can't store everything in a BSON * There are 2 options * 1. store binaries in memory outside of the bson and put it into GRIDFS at the point of commit * 2. leave mongo's bson, use our own/exteral library that doesn't have this limit and database handle this at the point of commit * below uses option 1, but ideally we should be doing option 2. */ if (vertices.size() > 0) { uint64_t verticesByteCount = vertices.size() * sizeof(vertices[0]); if (verticesByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE) { std::string bName = UUIDtoString(uniqueID) + "_vertices"; //inclusion of this binary exceeds the maximum, store separately binMapping[REPO_NODE_MESH_LABEL_VERTICES] = std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>()); binMapping[REPO_NODE_MESH_LABEL_VERTICES].second.resize(verticesByteCount); //uint8_t will ensure it is a byte addrressing memcpy(binMapping[REPO_NODE_MESH_LABEL_VERTICES].second.data(), &vertices[0], verticesByteCount); bytesize += sizeof(bName); } else { builder.appendBinary( REPO_NODE_MESH_LABEL_VERTICES, &vertices[0], vertices.size() * sizeof(vertices[0]) ); bytesize += verticesByteCount; } } if (faces.size() > 0) { builder << REPO_NODE_MESH_LABEL_FACES_COUNT << (uint32_t)(faces.size()); // In API LEVEL 1, faces are stored as // [n1, v1, v2, ..., n2, v1, v2...] std::vector<repo_face_t>::iterator faceIt; std::vector<uint32_t> facesLevel1; for (auto &face : faces){ auto nIndices = face.size(); if (!nIndices) { repoWarning << "number of indices in this face is 0!"; } facesLevel1.push_back(nIndices); for (uint32_t ind = 0; ind < nIndices; ind++) { facesLevel1.push_back(face[ind]); } } uint64_t facesByteCount = facesLevel1.size() * sizeof(facesLevel1[0]); if (facesByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE) { std::string bName = UUIDtoString(uniqueID) + "_faces"; //inclusion of this binary exceeds the maximum, store separately binMapping[REPO_NODE_MESH_LABEL_FACES] = std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>()); binMapping[REPO_NODE_MESH_LABEL_FACES].second.resize(facesByteCount); //uint8_t will ensure it is a byte addrressing memcpy(binMapping[REPO_NODE_MESH_LABEL_FACES].second.data(), &facesLevel1[0], facesByteCount); bytesize += sizeof(bName); } else { builder.appendBinary( REPO_NODE_MESH_LABEL_FACES, &facesLevel1[0], facesLevel1.size() * sizeof(facesLevel1[0]) ); bytesize += facesByteCount; } } if (normals.size() > 0) { uint64_t normalsByteCount = normals.size() * sizeof(normals[0]); if (normalsByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE) { std::string bName = UUIDtoString(uniqueID) + "_normals"; //inclusion of this binary exceeds the maximum, store separately binMapping[REPO_NODE_MESH_LABEL_NORMALS] = std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>()); binMapping[REPO_NODE_MESH_LABEL_NORMALS].second.resize(normalsByteCount); //uint8_t will ensure it is a byte addrressing memcpy(binMapping[REPO_NODE_MESH_LABEL_NORMALS].second.data(), &normals[0], normalsByteCount); bytesize += sizeof(bName); } else { builder.appendBinary( REPO_NODE_MESH_LABEL_NORMALS, &normals[0], normals.size() * sizeof(normals[0])); bytesize += normalsByteCount; } } //if (!vertexHash.empty()) //{ // // TODO: Fix this call - needs to be fixed as int conversion is overloaded // //builder << REPO_NODE_LABEL_SHA256 << (long unsigned int)(vertexHash); //} //-------------------------------------------------------------------------- // Vertex colors if (colors.size()) { uint64_t colorsByteCount = colors.size() * sizeof(colors[0]); if (colorsByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE) { std::string bName = UUIDtoString(uniqueID) + "_colors"; //inclusion of this binary exceeds the maximum, store separately binMapping[REPO_NODE_MESH_LABEL_COLORS] = std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>()); binMapping[REPO_NODE_MESH_LABEL_COLORS].second.resize(colorsByteCount); //uint8_t will ensure it is a byte addrressing memcpy(binMapping[REPO_NODE_MESH_LABEL_COLORS].second.data(), &colors[0], colorsByteCount); bytesize += sizeof(bName); } else { builder.appendBinary( REPO_NODE_MESH_LABEL_COLORS, &colors[0], colors.size() * sizeof(colors[0])); bytesize += colorsByteCount; } } //-------------------------------------------------------------------------- // UV channels if (uvChannels.size() > 0) { // Could be unsigned __int64 if BSON had such construct (the closest is only __int64) builder << REPO_NODE_MESH_LABEL_UV_CHANNELS_COUNT << (uint32_t)(uvChannels.size()); std::vector<repo_vector2d_t> concatenated; for (auto it = uvChannels.begin(); it != uvChannels.end(); ++it) { std::vector<repo_vector2d_t> channel = *it; std::vector<repo_vector2d_t>::iterator cit; for (cit = channel.begin(); cit != channel.end(); ++cit) { concatenated.push_back(*cit); } } uint64_t uvByteCount = concatenated.size() * sizeof(concatenated[0]); if (uvByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE) { std::string bName = UUIDtoString(uniqueID) + "_uv"; //inclusion of this binary exceeds the maximum, store separately binMapping[REPO_NODE_MESH_LABEL_UV_CHANNELS] = std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>()); binMapping[REPO_NODE_MESH_LABEL_UV_CHANNELS].second.resize(uvByteCount); //uint8_t will ensure it is a byte addrressing memcpy(binMapping[REPO_NODE_MESH_LABEL_UV_CHANNELS].second.data(), &concatenated[0], uvByteCount); bytesize += sizeof(bName); } else { builder.appendBinary( REPO_NODE_MESH_LABEL_UV_CHANNELS, &concatenated[0], concatenated.size() * sizeof(concatenated[0])); bytesize += uvByteCount; } } return MeshNode(builder.obj(), binMapping); }
RepoNode MeshNode::cloneAndApplyTransformation( const std::vector<float> &matrix) const { std::vector<repo_vector_t> vertices = getVertices(); std::vector<repo_vector_t> normals = getNormals(); auto newBigFiles = bigFiles; RepoBSONBuilder builder; std::vector<repo_vector_t> resultVertice; std::vector<repo_vector_t> newBbox; if (vertices.size()) { resultVertice.reserve(vertices.size()); for (const repo_vector_t &v : vertices) { resultVertice.push_back(multiplyMatVec(matrix, v)); if (newBbox.size()) { if (resultVertice.back().x < newBbox[0].x) newBbox[0].x = resultVertice.back().x; if (resultVertice.back().y < newBbox[0].y) newBbox[0].y = resultVertice.back().y; if (resultVertice.back().z < newBbox[0].z) newBbox[0].z = resultVertice.back().z; if (resultVertice.back().x > newBbox[1].x) newBbox[1].x = resultVertice.back().x; if (resultVertice.back().y > newBbox[1].y) newBbox[1].y = resultVertice.back().y; if (resultVertice.back().z > newBbox[1].z) newBbox[1].z = resultVertice.back().z; } else { newBbox.push_back(resultVertice.back()); newBbox.push_back(resultVertice.back()); } } if (newBigFiles.find(REPO_NODE_MESH_LABEL_VERTICES) != newBigFiles.end()) { const uint64_t verticesByteCount = resultVertice.size() * sizeof(repo_vector_t); newBigFiles[REPO_NODE_MESH_LABEL_VERTICES].second.resize(verticesByteCount); memcpy(newBigFiles[REPO_NODE_MESH_LABEL_VERTICES].second.data(), resultVertice.data(), verticesByteCount); } else builder.appendBinary(REPO_NODE_MESH_LABEL_VERTICES, resultVertice.data(), resultVertice.size() * sizeof(repo_vector_t)); if (normals.size()) { auto matInverse = invertMat(matrix); auto worldMat = transposeMat(matInverse); std::vector<repo_vector_t> resultNormals; resultNormals.reserve(normals.size()); for (const repo_vector_t &v : normals) { auto transformedNormal = multiplyMatVecFake3x3(worldMat, v); normalize(transformedNormal); resultNormals.push_back(transformedNormal); } if (newBigFiles.find(REPO_NODE_MESH_LABEL_NORMALS) != newBigFiles.end()) { const uint64_t byteCount = resultNormals.size() * sizeof(repo_vector_t); newBigFiles[REPO_NODE_MESH_LABEL_NORMALS].second.resize(byteCount); memcpy(newBigFiles[REPO_NODE_MESH_LABEL_NORMALS].second.data(), resultNormals.data(), byteCount); } else builder.appendBinary(REPO_NODE_MESH_LABEL_NORMALS, resultNormals.data(), resultNormals.size() * sizeof(repo_vector_t)); } RepoBSONBuilder arrayBuilder, outlineBuilder; for (size_t i = 0; i < newBbox.size(); ++i) { std::vector<float> boundVec = { newBbox[i].x, newBbox[i].y, newBbox[i].z }; arrayBuilder.appendArray(std::to_string(i), boundVec); } if (newBbox[0].x > newBbox[1].x || newBbox[0].z > newBbox[1].z || newBbox[0].y > newBbox[1].y) { repoError << "New bounding box is incorrect!!!"; } builder.appendArray(REPO_NODE_MESH_LABEL_BOUNDING_BOX, arrayBuilder.obj()); std::vector<float> outline0 = { newBbox[0].x, newBbox[0].y }; std::vector<float> outline1 = { newBbox[1].x, newBbox[0].y }; std::vector<float> outline2 = { newBbox[1].x, newBbox[1].y }; std::vector<float> outline3 = { newBbox[0].x, newBbox[1].y }; outlineBuilder.appendArray("0", outline0); outlineBuilder.appendArray("1", outline1); outlineBuilder.appendArray("2", outline2); outlineBuilder.appendArray("3", outline3); builder.appendArray(REPO_NODE_MESH_LABEL_OUTLINE, outlineBuilder.obj()); return MeshNode(builder.appendElementsUnique(*this), newBigFiles); } else { repoError << "Unable to apply transformation: Cannot find vertices within a mesh!"; return RepoNode(this->copy(), bigFiles); } }