Layer* Sprite::indexToLayer(LayerIndex index) const { if (index < LayerIndex(0)) return NULL; int index_count = -1; return index2layer(folder(), index, &index_count); }
static LayerIndex layer2index(const Layer* layer, const Layer* find_layer, int* index_count) { if (layer == find_layer) return LayerIndex(*index_count); else { (*index_count)++; if (layer->isFolder()) { int found; LayerConstIterator it = static_cast<const LayerFolder*>(layer)->getLayerBegin(); LayerConstIterator end = static_cast<const LayerFolder*>(layer)->getLayerEnd(); for (; it != end; ++it) { if ((found = layer2index(*it, find_layer, index_count)) >= 0) return LayerIndex(found); } } return LayerIndex(-1); } }
void RemoveLayerCommand::onExecute(Context* context) { std::string layer_name; ContextWriter writer(context); Document* document(writer.document()); Layer* layer(writer.layer()); { UndoTransaction undoTransaction(writer.context(), "Remove Layer"); // TODO the range of selected layer should be in the DocumentLocation. Timeline::Range range = App::instance()->getMainWindow()->getTimeline()->range(); if (range.enabled()) { Sprite* sprite = writer.sprite(); // TODO indexes in timeline are inverted!! fix that for a future release for (LayerIndex layer = sprite->countLayers() - LayerIndex(range.layerBegin()+1), end = sprite->countLayers() - LayerIndex(range.layerEnd()+2); layer != end; --layer) { document->getApi().removeLayer(sprite->indexToLayer(layer)); } } else { layer_name = layer->getName(); document->getApi().removeLayer(layer); } undoTransaction.commit(); } update_screen_for_document(document); StatusBar::instance()->invalidate(); if (!layer_name.empty()) StatusBar::instance()->showTip(1000, "Layer `%s' removed", layer_name.c_str()); else StatusBar::instance()->showTip(1000, "Layers removed"); }
void UIContext::onGetActiveSite(Site* site) const { DocumentView* view = activeView(); if (view) { view->getSite(site); } // Default/dummy site (maybe for batch/command line mode) else if (!isUIAvailable()) { if (Document* doc = m_lastSelectedDoc) { site->document(doc); site->sprite(doc->sprite()); site->layer(doc->sprite()->indexToLayer(LayerIndex(0))); site->frame(0); } } }
void GotoPreviousLayerCommand::onExecute(Context* context) { const ContextReader reader(context); const Sprite* sprite = reader.sprite(); Site site = *reader.site(); if (site.layerIndex() > 0) site.layerIndex(site.layerIndex().previous()); else site.layerIndex(LayerIndex(sprite->countLayers()-1)); // Flash the current layer ASSERT(current_editor != NULL && "Current editor cannot be null when we have a current sprite"); current_editor->setLayer(site.layer()); current_editor->flashCurrentLayer(); updateStatusBar(site); }
static DocumentRange drop_range_op( Document* doc, Op op, const DocumentRange& from, DocumentRangePlace place, const DocumentRange& to) { if (place != kDocumentRangeBefore && place != kDocumentRangeAfter) { ASSERT(false); throw std::invalid_argument("Invalid 'place' argument"); } Sprite* sprite = doc->sprite(); // Check noop/trivial/do nothing cases, i.e., move a range to the same place. // Also check invalid cases, like moving a Background layer. switch (from.type()) { case DocumentRange::kCels: if (from == to) return from; break; case DocumentRange::kFrames: if (op == Move) { if ((to.frameBegin() >= from.frameBegin() && to.frameEnd() <= from.frameEnd()) || (place == kDocumentRangeBefore && to.frameBegin() == from.frameEnd()+1) || (place == kDocumentRangeAfter && to.frameEnd() == from.frameBegin()-1)) return from; } break; case DocumentRange::kLayers: if (op == Move) { if ((to.layerBegin() >= from.layerBegin() && to.layerEnd() <= from.layerEnd()) || (place == kDocumentRangeBefore && to.layerBegin() == from.layerEnd()+1) || (place == kDocumentRangeAfter && to.layerEnd() == from.layerBegin()-1)) return from; // We cannot move the background for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) if (sprite->indexToLayer(i)->isBackground()) throw std::runtime_error("The background layer cannot be moved"); // Before background if (place == kDocumentRangeBefore) { Layer* background = sprite->indexToLayer(to.layerBegin()); if (background && background->isBackground()) throw std::runtime_error("You cannot move something below the background layer"); } } break; } const char* undoLabel = NULL; switch (op) { case Move: undoLabel = "Move Range"; break; case Copy: undoLabel = "Copy Range"; break; default: ASSERT(false); throw std::invalid_argument("Invalid 'op' argument"); } DocumentRange resultRange; { const app::Context* context = static_cast<app::Context*>(doc->context()); const ContextReader reader(context); ContextWriter writer(reader); Transaction transaction(writer.context(), undoLabel, ModifyDocument); DocumentApi api = doc->getApi(transaction); // TODO Try to add the range with just one call to DocumentApi // methods, to avoid generating a lot of SetCelFrame undoers (see // DocumentApi::setCelFramePosition). switch (from.type()) { case DocumentRange::kCels: { std::vector<Layer*> layers; sprite->getLayersList(layers); int srcLayerBegin, srcLayerStep, srcLayerEnd; int dstLayerBegin, dstLayerStep; frame_t srcFrameBegin, srcFrameStep, srcFrameEnd; frame_t dstFrameBegin, dstFrameStep; if (to.layerBegin() <= from.layerBegin()) { srcLayerBegin = from.layerBegin(); srcLayerStep = 1; srcLayerEnd = from.layerEnd()+1; dstLayerBegin = to.layerBegin(); dstLayerStep = 1; } else { srcLayerBegin = from.layerEnd(); srcLayerStep = -1; srcLayerEnd = from.layerBegin()-1; dstLayerBegin = to.layerEnd(); dstLayerStep = -1; } if (to.frameBegin() <= from.frameBegin()) { srcFrameBegin = from.frameBegin(); srcFrameStep = frame_t(1); srcFrameEnd = from.frameEnd()+1; dstFrameBegin = to.frameBegin(); dstFrameStep = frame_t(1); } else { srcFrameBegin = from.frameEnd(); srcFrameStep = frame_t(-1); srcFrameEnd = from.frameBegin()-1; dstFrameBegin = to.frameEnd(); dstFrameStep = frame_t(-1); } for (int srcLayerIdx = srcLayerBegin, dstLayerIdx = dstLayerBegin; srcLayerIdx != srcLayerEnd; ) { for (frame_t srcFrame = srcFrameBegin, dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) { LayerImage* srcLayer = static_cast<LayerImage*>(layers[srcLayerIdx]); LayerImage* dstLayer = static_cast<LayerImage*>(layers[dstLayerIdx]); switch (op) { case Move: api.moveCel(srcLayer, srcFrame, dstLayer, dstFrame); break; case Copy: api.copyCel(srcLayer, srcFrame, dstLayer, dstFrame); break; } srcFrame += srcFrameStep; dstFrame += dstFrameStep; } srcLayerIdx += srcLayerStep; dstLayerIdx += dstLayerStep; } resultRange = to; } break; case DocumentRange::kFrames: { frame_t srcFrameBegin = 0, srcFrameStep, srcFrameEnd = 0; frame_t dstFrameBegin = 0, dstFrameStep; switch (op) { case Move: if (place == kDocumentRangeBefore) { if (to.frameBegin() <= from.frameBegin()) { srcFrameBegin = from.frameBegin(); srcFrameStep = frame_t(1); srcFrameEnd = from.frameEnd()+1; dstFrameBegin = to.frameBegin(); dstFrameStep = frame_t(1); } else { srcFrameBegin = from.frameEnd(); srcFrameStep = frame_t(-1); srcFrameEnd = from.frameBegin()-1; dstFrameBegin = to.frameBegin(); dstFrameStep = frame_t(-1); } } else if (place == kDocumentRangeAfter) { if (to.frameEnd() <= from.frameBegin()) { srcFrameBegin = from.frameBegin(); srcFrameStep = frame_t(1); srcFrameEnd = from.frameEnd()+1; dstFrameBegin = to.frameEnd()+1; dstFrameStep = frame_t(1); } else { srcFrameBegin = from.frameEnd(); srcFrameStep = frame_t(-1); srcFrameEnd = from.frameBegin()-1; dstFrameBegin = to.frameEnd()+1; dstFrameStep = frame_t(-1); } } break; case Copy: if (place == kDocumentRangeBefore) { if (to.frameBegin() <= from.frameBegin()) { srcFrameBegin = from.frameBegin(); srcFrameStep = frame_t(2); srcFrameEnd = from.frameBegin() + 2*from.frames(); dstFrameBegin = to.frameBegin(); dstFrameStep = frame_t(1); } else { srcFrameBegin = from.frameEnd(); srcFrameStep = frame_t(-1); srcFrameEnd = from.frameBegin()-1; dstFrameBegin = to.frameBegin(); dstFrameStep = frame_t(0); } } else if (place == kDocumentRangeAfter) { if (to.frameEnd() <= from.frameBegin()) { srcFrameBegin = from.frameBegin(); srcFrameStep = frame_t(2); srcFrameEnd = from.frameBegin() + 2*from.frames(); dstFrameBegin = to.frameEnd()+1; dstFrameStep = frame_t(1); } else { srcFrameBegin = from.frameEnd(); srcFrameStep = frame_t(-1); srcFrameEnd = from.frameBegin()-1; dstFrameBegin = to.frameEnd()+1; dstFrameStep = frame_t(0); } } break; } for (frame_t srcFrame = srcFrameBegin, dstFrame = dstFrameBegin; srcFrame != srcFrameEnd; ) { switch (op) { case Move: api.moveFrame(sprite, srcFrame, dstFrame); break; case Copy: api.copyFrame(sprite, srcFrame, dstFrame); break; } srcFrame += srcFrameStep; dstFrame += dstFrameStep; } if (place == kDocumentRangeBefore) { resultRange.startRange(LayerIndex::NoLayer, frame_t(to.frameBegin()), from.type()); resultRange.endRange(LayerIndex::NoLayer, frame_t(to.frameBegin()+from.frames()-1)); } else if (place == kDocumentRangeAfter) { resultRange.startRange(LayerIndex::NoLayer, frame_t(to.frameEnd()+1), from.type()); resultRange.endRange(LayerIndex::NoLayer, frame_t(to.frameEnd()+1+from.frames()-1)); } if (op == Move && from.frameBegin() < to.frameBegin()) resultRange.displace(0, -from.frames()); } break; case DocumentRange::kLayers: { std::vector<Layer*> layers; sprite->getLayersList(layers); if (layers.empty()) break; switch (op) { case Move: if (place == kDocumentRangeBefore) { for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) { api.restackLayerBefore( layers[i], layers[to.layerBegin()]); } } else if (place == kDocumentRangeAfter) { for (LayerIndex i = from.layerEnd(); i >= from.layerBegin(); --i) { api.restackLayerAfter( layers[i], layers[to.layerEnd()]); } } break; case Copy: if (place == kDocumentRangeBefore) { for (LayerIndex i = from.layerBegin(); i <= from.layerEnd(); ++i) { api.duplicateLayerBefore( layers[i], layers[to.layerBegin()]); } } else if (place == kDocumentRangeAfter) { for (LayerIndex i = from.layerEnd(); i >= from.layerBegin(); --i) { api.duplicateLayerAfter( layers[i], layers[to.layerEnd()]); } } break; } if (place == kDocumentRangeBefore) { resultRange.startRange(LayerIndex(to.layerBegin()), frame_t(-1), from.type()); resultRange.endRange(LayerIndex(to.layerBegin()+from.layers()-1), frame_t(-1)); } else if (place == kDocumentRangeAfter) { resultRange.startRange(LayerIndex(to.layerEnd()+1), frame_t(-1), from.type()); resultRange.endRange(LayerIndex(to.layerEnd()+1+from.layers()-1), frame_t(-1)); } if (op == Move && from.layerBegin() < to.layerBegin()) resultRange.displace(-from.layers(), 0); } break; } transaction.commit(); } return resultRange; }
LayerIndex Site::layerIndex() const { return (m_sprite && m_layer ? m_sprite->layerToIndex(m_layer): LayerIndex()); }
void ObjectBase::onSimulation(float timeStep) { Point2 delta = m_velocityRelativeToParent * timeStep; if (! m_collidesWithCells || (delta.squaredLength() < 1e-6f)) { // Case without collisions m_frameRelativeToJoint.translation += delta; } else { if (! isRoot()) { report(format("ObjectBase::collidesWithCells must be false for non-root objects. (%d)", m_id.c_str()), ReportLevel::ERROR); m_frameRelativeToJoint.translation += delta; return; } // Collision case const float speed = m_velocityRelativeToParent.length(); // Algorithm: Find all tiles within the maximum extent of the object's // movement (i.e., ignoring direction) plus its radius. Reduce to // the edges (line segments) of non-traversable tiles. // A static object with default radius can hit four tiles; // one that is moving might typically hit nine, and there is no // upper bound. Map::SmallEdgeArray edgesInDisk; const Frame2D& frame = this->frame(); m_world.map->getEdgesInDisk(frame.translation, delta.length() + m_collisionRadius, m_elevation, LayerIndex(1), edgesInDisk); // Transform the edges into the parent's object space const Frame2D& parentFrame = (isRoot() ? Frame2D() : m_world.objectTable.valueFromKey(m_parent)->frame()); for (int e = 0; e < edgesInDisk.size(); ++e) { LineSegment2D& edge = edgesInDisk[e]; debugDrawEdgeArray.append(edge); // TODO: Remove edge = parentFrame.bringFromParentSpace(edge); } const int maxIterations = 20; // Check for collisions with the map edges, adjusting velocity into the unblocked direction each time int iterations; for (iterations = 0; (iterations < maxIterations) && (timeStep > 1e-6); ++iterations) { // Find the first collision float firstCollisionTime = finf(); Point2 firstCollisionLocation; for (int e = 0; e < edgesInDisk.size(); ++e) { const LineSegment2D& edge = edgesInDisk[e]; // Recall that everything is in body space, now Point2 collisionLocation; const float collisionTime = movingDiskFixedLineSegmentCollisionTime(m_frameRelativeToJoint.translation, m_collisionRadius, m_velocityRelativeToParent, edge, collisionLocation); if (collisionTime < inf()) { debugDrawPointArray.append(collisionLocation); } if (collisionTime < firstCollisionTime) { firstCollisionTime = collisionTime; firstCollisionLocation = collisionLocation; } } // Resolve the collision if it happens before the end of the time step if (firstCollisionTime < timeStep) { // Advance to just before the collision firstCollisionTime = max(firstCollisionTime - 1e-5f, 0.0f); m_frameRelativeToJoint.translation += m_velocityRelativeToParent * firstCollisionTime; timeStep -= firstCollisionTime; const Vector2 normal = (m_frameRelativeToJoint.translation - firstCollisionLocation).directionOrZero(); // Alter velocity at the collision by removing the component of the velocity along the collision normal m_velocityRelativeToParent -= normal * min(normal.dot(m_velocityRelativeToParent), 0.0f); // Restore full speed, deflecting movement m_velocityRelativeToParent = m_velocityRelativeToParent.directionOrZero() * speed; if (m_velocityRelativeToParent.squaredLength() < 1e-6f) { // Unable to proceed with movement because there is no velocity left timeStep = 0; } } else { // Go to the end of the time step firstCollisionTime = timeStep; m_frameRelativeToJoint.translation += m_velocityRelativeToParent * timeStep; timeStep = 0; } } if (iterations == maxIterations) { report("Hit maximum number of iterations in ObjectBase::onSimulation collision resolution.", ReportLevel::WARNING); } } }
void Weaver::weave(MeshGroup* meshgroup) { wireFrame.meshgroup = meshgroup; const coord_t maxz = meshgroup->max().z; const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; const coord_t initial_layer_thickness = mesh_group_settings.get<coord_t>("layer_height_0"); const coord_t connection_height = mesh_group_settings.get<coord_t>("wireframe_height"); const size_t layer_count = (maxz - initial_layer_thickness) / connection_height + 1; std::vector<AdaptiveLayer> layer_thicknesses; log("Layer count: %i\n", layer_count); std::vector<cura::Slicer*> slicerList; for(Mesh& mesh : meshgroup->meshes) { constexpr bool variable_layer_heights = false; cura::Slicer* slicer = new cura::Slicer(&mesh, connection_height, layer_count, variable_layer_heights, &layer_thicknesses); slicerList.push_back(slicer); } LayerIndex starting_layer_idx; { // find first non-empty layer for (starting_layer_idx = 0; starting_layer_idx < LayerIndex(layer_count); starting_layer_idx++) { Polygons parts; for (cura::Slicer* slicer : slicerList) parts.add(slicer->layers[starting_layer_idx].polygons); if (parts.size() > 0) break; } if (starting_layer_idx > 0) { logWarning("First %i layers are empty!\n", starting_layer_idx); } } log("Chainifying layers...\n"); { int starting_z = -1; for (cura::Slicer* slicer : slicerList) wireFrame.bottom_outline.add(slicer->layers[starting_layer_idx].polygons); Application::getInstance().communication->sendPolygons(PrintFeatureType::OuterWall, wireFrame.bottom_outline, 1, 1, 1); if (slicerList.empty()) //Wait, there is nothing to slice. { wireFrame.z_bottom = 0; } else { wireFrame.z_bottom = slicerList[0]->layers[starting_layer_idx].z; } Point starting_point_in_layer; if (wireFrame.bottom_outline.size() > 0) { starting_point_in_layer = (wireFrame.bottom_outline.max() + wireFrame.bottom_outline.min()) / 2; } else { starting_point_in_layer = (Point(0,0) + meshgroup->max() + meshgroup->min()) / 2; } Progress::messageProgressStage(Progress::Stage::INSET_SKIN, nullptr); for (LayerIndex layer_idx = starting_layer_idx + 1; layer_idx < LayerIndex(layer_count); layer_idx++) { Progress::messageProgress(Progress::Stage::INSET_SKIN, layer_idx+1, layer_count); // abuse the progress system of the normal mode of CuraEngine Polygons parts1; for (cura::Slicer* slicer : slicerList) parts1.add(slicer->layers[layer_idx].polygons); Polygons chainified; chainify_polygons(parts1, starting_point_in_layer, chainified); Application::getInstance().communication->sendPolygons(PrintFeatureType::OuterWall, chainified, 1, 1, 1); if (chainified.size() > 0) { if (starting_z == -1) starting_z = slicerList[0]->layers[layer_idx-1].z; wireFrame.layers.emplace_back(); WeaveLayer& layer = wireFrame.layers.back(); layer.z0 = slicerList[0]->layers[layer_idx-1].z - starting_z; layer.z1 = slicerList[0]->layers[layer_idx].z - starting_z; layer.supported = chainified; starting_point_in_layer = layer.supported.back().back(); } } } log("Finding horizontal parts...\n"); { Progress::messageProgressStage(Progress::Stage::SUPPORT, nullptr); for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) { Progress::messageProgress(Progress::Stage::SUPPORT, layer_idx+1, wireFrame.layers.size()); // abuse the progress system of the normal mode of CuraEngine WeaveLayer& layer = wireFrame.layers[layer_idx]; Polygons empty; Polygons& layer_above = (layer_idx+1 < wireFrame.layers.size())? wireFrame.layers[layer_idx+1].supported : empty; createHorizontalFill(layer, layer_above); } } // at this point layer.supported still only contains the polygons to be connected // when connecting layers, we further add the supporting polygons created by the roofs log("Connecting layers...\n"); { Polygons* lower_top_parts = &wireFrame.bottom_outline; int last_z = wireFrame.z_bottom; for (unsigned int layer_idx = 0; layer_idx < wireFrame.layers.size(); layer_idx++) // use top of every layer but the last { WeaveLayer& layer = wireFrame.layers[layer_idx]; connect_polygons(*lower_top_parts, last_z, layer.supported, layer.z1, layer); layer.supported.add(layer.roofs.roof_outlines); lower_top_parts = &layer.supported; last_z = layer.z1; } } { // roofs: if (!wireFrame.layers.empty()) //If there are no layers, create no roof. { WeaveLayer& top_layer = wireFrame.layers.back(); Polygons to_be_supported; // empty for the top layer fillRoofs(top_layer.supported, to_be_supported, -1, top_layer.z1, top_layer.roofs); } } { // bottom: if (!wireFrame.layers.empty()) //If there are no layers, create no bottom. { Polygons to_be_supported; // is empty for the bottom layer, cause the order of insets doesn't really matter (in a sense everything is to be supported) fillRoofs(wireFrame.bottom_outline, to_be_supported, -1, wireFrame.layers.front().z0, wireFrame.bottom_infill); } } }
LayerIndex Sprite::countLayers() const { return LayerIndex(getFolder()->getLayersCount()); }
Layer* Sprite::layer(int layerIndex) const { return indexToLayer(LayerIndex(layerIndex)); }
LayerIndex Sprite::lastLayer() const { return LayerIndex(folder()->getLayersCount()-1); }
LayerIndex Sprite::firstLayer() const { return LayerIndex(0); }
TEST_F(SettingsTest, AddSettingLayerIndex) { settings.add("test_setting", "4"); EXPECT_EQ(LayerIndex(3), settings.get<LayerIndex>("test_setting")) << "LayerIndex settings start counting from 0, so subtract one."; }
TEST_F(SettingsTest, AddSettingLayerIndexNegative) { settings.add("test_setting", "-10"); EXPECT_EQ(LayerIndex(-11), settings.get<LayerIndex>("test_setting")) << "LayerIndex settings still subtract 1 even in negative layers."; }