void MuseScore::oscAction() { PathObject* pathObject = qobject_cast<PathObject*>(sender()); QString path = pathObject->path().mid(9); QAction* a = getAction(path.toLocal8Bit().data()); a->trigger(); }
void ToolsTest::editTool() { // Initialization TestMap map; TestMapEditor editor(map.map); EditTool* tool = new EditPointTool(editor.editor, nullptr); // TODO: Refactor EditTool: MapEditorController and SymbolWidget pointers could be unnecessary editor.editor->setTool(tool); // Move the first coordinate of the line object MapWidget* map_widget = editor.map_widget; PathObject* object = map.line_object; const MapCoord& coord = object->getCoordinate(0); QPointF drag_start_pos = map_widget->mapToViewport(coord); QPointF drag_end_pos = drag_start_pos + QPointF(0, -50); // Clear selection. map.map->clearObjectSelection(false); QVERIFY(map.map->selectedObjects().empty()); // Click to select the object editor.simulateClick(drag_start_pos); QCOMPARE(map.map->getFirstSelectedObject(), object); // Drag the coordinate to the new position editor.simulateDrag(drag_start_pos, drag_end_pos); // Check position deviation QPointF difference = map_widget->mapToViewport(coord) - drag_end_pos; QCOMPARE(qMax(qAbs(difference.x()), 0.1), 0.1); QCOMPARE(qMax(qAbs(difference.y()), 0.1), 0.1); // Cleanup editor.editor->setTool(nullptr); }
bool CutTool::findEditPoint(PathCoord& out_edit_point, PathObject*& out_edit_object, MapCoordF cursor_pos_map, int with_type, int without_type, MapWidget* widget) { Map* map = this->map(); out_edit_object = nullptr; if (hover_state == HoverFlag::OverObjectNode && hover_object->getSymbol()->getContainedTypes() & with_type && !(hover_object->getSymbol()->getContainedTypes() & without_type)) { // Hovering over a point of a line if (hover_object->getType() != Object::Path) { Q_ASSERT(!"TODO: make this work for non-path objects"); return false; } out_edit_object = reinterpret_cast<PathObject*>(hover_object); out_edit_point = out_edit_object->findPathCoordForIndex(hover_point); } else { // Check if a line segment was clicked float min_distance_sq = 999999; for (const auto object : map->selectedObjects()) { if (!(object->getSymbol()->getContainedTypes() & with_type) || object->getSymbol()->getContainedTypes() & without_type) { continue; } if (object->getType() != Object::Path) { Q_ASSERT(!"TODO: make this work for non-path objects"); continue; } PathObject* path = reinterpret_cast<PathObject*>(object); float distance_sq; PathCoord path_coord; path->calcClosestPointOnPath(cursor_pos_map, distance_sq, path_coord); float click_tolerance_map = 0.001f * widget->getMapView()->pixelToLength(clickTolerance()); if (distance_sq < min_distance_sq && distance_sq <= click_tolerance_map*click_tolerance_map) { min_distance_sq = distance_sq; out_edit_object = path; out_edit_point = path_coord; } } } return out_edit_object != nullptr; }
void TextSymbol::createBaselineRenderables( const TextObject* text_object, const VirtualCoordVector& coords, ObjectRenderables& output) const { const MapColor* dominant_color = guessDominantColor(); if (dominant_color && text_object->getNumLines() > 0) { // Insert text boundary LineSymbol line_symbol; line_symbol.setColor(dominant_color); line_symbol.setLineWidth(0); const TextObjectLineInfo* line = text_object->getLineInfo(0); QRectF text_bbox(line->line_x, line->line_y - line->ascent, line->width, line->ascent + line->descent); for (int i = 1; i < text_object->getNumLines(); ++i) { const TextObjectLineInfo* line = text_object->getLineInfo(i); rectInclude(text_bbox, QRectF(line->line_x, line->line_y - line->ascent, line->width, line->ascent + line->descent)); } Q_UNUSED(coords); // coords should be used for calcTextToMapTransform() QTransform text_to_map = text_object->calcTextToMapTransform(); PathObject path; path.addCoordinate(MapCoord(text_to_map.map(text_bbox.topLeft()))); path.addCoordinate(MapCoord(text_to_map.map(text_bbox.topRight()))); path.addCoordinate(MapCoord(text_to_map.map(text_bbox.bottomRight()))); path.addCoordinate(MapCoord(text_to_map.map(text_bbox.bottomLeft()))); path.parts().front().setClosed(true, true); path.updatePathCoords(); LineRenderable* line_renderable = new LineRenderable(&line_symbol, path.parts().front(), false); output.insertRenderable(line_renderable); } }
void MuseScore::oscMuteChannel(double val) { if(!cs) return; PathObject* po = (PathObject*) sender(); int i = po->path().mid(5).toInt() - 1; QList<MidiMapping>* mms = cs->midiMapping(); if( i >= 0 && i < mms->size()) { MidiMapping mm = mms->at(i); Channel* channel = mm.articulation; channel->mute = (val==0.0f ? false : true); if (mixer) mixer->partEdit(i)->mute->setCheckState(val==0.0f ? Qt::Unchecked:Qt::Checked); } }
void MuseScore::oscVolChannel(double val) { if(!cs) return; PathObject* po = (PathObject*) sender(); int i = po->path().mid(4).toInt() - 1; QList<MidiMapping>* mms = cs->midiMapping(); if( i >= 0 && i < mms->size()) { MidiMapping mm = mms->at(i); Channel* channel = mm.articulation; int iv = lrint(val*127); seq->setController(channel->channel, CTRL_VOLUME, iv); channel->volume = iv; if (iledit) iledit->partEdit(i)->volume->setValue(iv); } }
void MuseScore::oscPanChannel(double val) { if(!cs) return; PathObject* po = (PathObject*) sender(); int i = po->path().mid(4).toInt() - 1; QList<MidiMapping>* mms = cs->midiMapping(); if( i >= 0 && i < mms->size()) { MidiMapping mm = mms->at(i); Channel* channel = mm.articulation; int iv = lrint((val + 1) * 64); seq->setController(channel->channel, CTRL_PANPOT, iv); channel->volume = iv; if (mixer) mixer->partEdit(i)->pan->setValue(iv); } }
UndoStep* SwitchDashesUndoStep::undo() { int const part_index = getPartIndex(); SwitchDashesUndoStep* undo_step = new SwitchDashesUndoStep(map); undo_step->setPartIndex(part_index); MapPart* part = map->getPart(part_index); for (ObjectList::iterator it = modified_objects.begin(), end = modified_objects.end(); it != end; ++it) { PathObject* object = reinterpret_cast<PathObject*>(part->getObject(*it)); object->reverse(); object->update(); undo_step->addObject(*it); } return undo_step; }
bool DrawPathTool::removeLastPointFromSelectedPath() { if (editingInProgress() || map()->getNumSelectedObjects() != 1) { return false; } Object* object = map()->getFirstSelectedObject(); if (object->getType() != Object::Path) { return false; } PathObject* path = object->asPath(); if (path->parts().size() != 1) { return false; } int points_on_path = 0; int num_coords = path->getCoordinateCount(); for (int i = 0; i < num_coords && points_on_path < 3; ++i) { ++points_on_path; if (path->getCoordinate(i).isCurveStart()) { i += 2; // Skip the control points. } } if (points_on_path < 3) { // Too few points after deleting the last: delete the whole object. map()->deleteSelectedObjects(); return true; } ReplaceObjectsUndoStep* undo_step = new ReplaceObjectsUndoStep(map()); Object* undo_duplicate = object->duplicate(); undo_duplicate->setMap(map()); undo_step->addObject(object, undo_duplicate); map()->push(undo_step); updateDirtyRect(); path->parts().front().setClosed(false); path->deleteCoordinate(num_coords - 1, false); path->update(); map()->setObjectsDirty(); map()->emitSelectionEdited(); return true; }
void BooleanTool::rebuildTwoIndexSegment( ClipperLib::Path::size_type start_index, ClipperLib::Path::size_type end_index, bool sequence_increasing, const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object) { Q_UNUSED(sequence_increasing); // only used in Q_ASSERT. PathCoordInfo start_info = polymap.value(polygon.at(start_index)); PathCoordInfo end_info = polymap.value(polygon.at(end_index)); PathObject* original = end_info.first->path; bool coords_increasing; bool is_curve; int coord_index; if (start_info.second->index == end_info.second->index) { coord_index = end_info.second->index; bool found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve); if (!found) { object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false); rebuildCoordinate(end_index, polygon, polymap, object); return; } Q_ASSERT(coords_increasing == sequence_increasing); } else { coord_index = end_info.second->index; bool found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve); if (!found) { coord_index = start_info.second->index; found = checkSegmentMatch(original, coord_index, polygon, start_index, end_index, coords_increasing, is_curve); if (!found) { object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false); rebuildCoordinate(end_index, polygon, polymap, object); return; } } } if (!is_curve) object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(false); if (coords_increasing) { object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 1))); if (is_curve) { object->addCoordinate(original->getCoordinate(coord_index + 2)); object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 3))); } } else { if (is_curve) { object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 2))); object->addCoordinate(original->getCoordinate(coord_index + 1)); } object->addCoordinate(resetCoordinate(original->getCoordinate(coord_index + 0))); } }
void CutTool::pathFinished(PathObject* split_path) { Map* map = this->map(); // Get path endpoint and check if it is on the area boundary const MapCoordVector& path_coords = split_path->getRawCoordinateVector(); MapCoord path_end = path_coords.at(path_coords.size() - 1); PathObject* edited_path = reinterpret_cast<PathObject*>(edit_object); PathCoord end_path_coord; float distance_sq; edited_path->calcClosestPointOnPath(MapCoordF(path_end), distance_sq, end_path_coord); float click_tolerance_map = 0.001 * edit_widget->getMapView()->pixelToLength(clickTolerance()); if (distance_sq > click_tolerance_map*click_tolerance_map) { QMessageBox::warning(window(), tr("Error"), tr("The split line must end on the area boundary!")); pathAborted(); return; } else if (drag_part_index != edited_path->findPartIndexForIndex(end_path_coord.index)) { QMessageBox::warning(window(), tr("Error"), tr("Start and end of the split line are at different parts of the object!")); pathAborted(); return; } else if (drag_start_len == end_path_coord.clen) { QMessageBox::warning(window(), tr("Error"), tr("Start and end of the split line are at the same position!")); pathAborted(); return; } Q_ASSERT(split_path->parts().size() == 1); split_path->parts().front().setClosed(false); split_path->setCoordinate(split_path->getCoordinateCount() - 1, MapCoord(end_path_coord.pos)); // Do the splitting const double split_threshold = 0.01; MapPart* part = map->getCurrentPart(); AddObjectsUndoStep* add_step = new AddObjectsUndoStep(map); add_step->addObject(part->findObjectIndex(edited_path), edited_path); map->removeObjectFromSelection(edited_path, false); map->deleteObject(edited_path, true); map->setObjectsDirty(); DeleteObjectsUndoStep* delete_step = new DeleteObjectsUndoStep(map); PathObject* holes = nullptr; // if the edited path contains holes, they are saved in this temporary object if (edited_path->parts().size() > 1) { holes = edited_path->duplicate()->asPath(); holes->deletePart(0); } bool ok; Q_UNUSED(ok); // "ok" is only used in Q_ASSERT. PathObject* parts[2] = { new PathObject { edited_path->parts().front() }, nullptr }; const PathPart& drag_part = edited_path->parts()[drag_part_index]; if (drag_part.isClosed()) { parts[1] = new PathObject { *parts[0] }; parts[0]->changePathBounds(drag_part_index, drag_start_len, end_path_coord.clen); ok = parts[0]->connectIfClose(split_path, split_threshold); Q_ASSERT(ok); parts[1]->changePathBounds(drag_part_index, end_path_coord.clen, drag_start_len); ok = parts[1]->connectIfClose(split_path, split_threshold); Q_ASSERT(ok); } else { float min_cut_pos = qMin(drag_start_len, end_path_coord.clen); float max_cut_pos = qMax(drag_start_len, end_path_coord.clen); float path_len = drag_part.path_coords.back().clen; if (min_cut_pos <= 0 && max_cut_pos >= path_len) { ok = parts[0]->connectIfClose(split_path, split_threshold); Q_ASSERT(ok); parts[1] = new PathObject { *split_path }; parts[1]->setSymbol(edited_path->getSymbol(), false); } else if (min_cut_pos <= 0 || max_cut_pos >= path_len) { float cut_pos = (min_cut_pos <= 0) ? max_cut_pos : min_cut_pos; parts[1] = new PathObject { *parts[0] }; parts[0]->changePathBounds(drag_part_index, 0, cut_pos); ok = parts[0]->connectIfClose(split_path, split_threshold); Q_ASSERT(ok); parts[1]->changePathBounds(drag_part_index, cut_pos, path_len); ok = parts[1]->connectIfClose(split_path, split_threshold); Q_ASSERT(ok); } else { parts[1] = new PathObject { *parts[0] }; PathObject* temp_path = new PathObject { *parts[0] }; parts[0]->changePathBounds(drag_part_index, min_cut_pos, max_cut_pos); ok = parts[0]->connectIfClose(split_path, split_threshold); Q_ASSERT(ok); parts[1]->changePathBounds(drag_part_index, 0, min_cut_pos); ok = parts[1]->connectIfClose(split_path, split_threshold); Q_ASSERT(ok); temp_path->changePathBounds(drag_part_index, max_cut_pos, path_len); ok = parts[1]->connectIfClose(temp_path, split_threshold); Q_ASSERT(ok); delete temp_path; } } // If the object had holes, check into which parts they go if (holes) { for (const auto& hole : holes->parts()) { PathPartVector::size_type part_index = (parts[0]->isPointOnPath(MapCoordF(holes->getCoordinate(hole.first_index)), 0, false, false) != Symbol::NoSymbol) ? 0 : 1; parts[part_index]->getCoordinate(parts[part_index]->getCoordinateCount() - 1).setHolePoint(true); parts[part_index]->appendPathPart(hole); } } for (auto& object : parts) { map->addObject(object); delete_step->addObject(part->findObjectIndex(object)); map->addObjectToSelection(object, false); } CombinedUndoStep* undo_step = new CombinedUndoStep(map); undo_step->push(add_step); undo_step->push(delete_step); map->push(undo_step); map->setObjectsDirty(); pathAborted(); }
void EditPointTool::updateHoverState(MapCoordF cursor_pos) { HoverState new_hover_state = OverNothing; const Object* new_hover_object = nullptr; MapCoordVector::size_type new_hover_point = no_point; if (text_editor) { handle_offset = MapCoordF(0, 0); } else if (!map()->selectedObjects().empty()) { if (map()->selectedObjects().size() <= max_objects_for_handle_display) { // Try to find object node. auto best_distance_sq = std::numeric_limits<double>::max(); for (const auto object : map()->selectedObjects()) { MapCoordF handle_pos; auto hover_point = findHoverPoint(cur_map_widget->mapToViewport(cursor_pos), cur_map_widget, object, true, &handle_pos); if (hover_point == no_point) continue; auto distance_sq = cursor_pos.distanceSquaredTo(handle_pos); if (distance_sq < best_distance_sq) { new_hover_state |= OverObjectNode; new_hover_object = object; new_hover_point = hover_point; best_distance_sq = distance_sq; handle_offset = handle_pos - cursor_pos; } } if (!new_hover_state.testFlag(OverObjectNode)) { // No object node found. Try to find path object edge. /// \todo De-duplicate: Copied from EditLineTool auto click_tolerance_sq = qPow(0.001 * cur_map_widget->getMapView()->pixelToLength(clickTolerance()), 2); for (auto object : map()->selectedObjects()) { if (object->getType() == Object::Path) { PathObject* path = object->asPath(); float distance_sq; PathCoord path_coord; path->calcClosestPointOnPath(cursor_pos, distance_sq, path_coord); if (distance_sq >= +0.0 && distance_sq < best_distance_sq && distance_sq < qMax(click_tolerance_sq, qPow(path->getSymbol()->calculateLargestLineExtent(map()), 2))) { new_hover_state |= OverPathEdge; new_hover_object = path; new_hover_point = path_coord.index; best_distance_sq = distance_sq; handle_offset = path_coord.pos - cursor_pos; } } } } } if (!new_hover_state.testFlag(OverObjectNode) && selection_extent.isValid()) { QRectF selection_extent_viewport = cur_map_widget->mapToViewport(selection_extent); if (pointOverRectangle(cur_map_widget->mapToViewport(cursor_pos), selection_extent_viewport)) { new_hover_state |= OverFrame; handle_offset = closestPointOnRect(cursor_pos, selection_extent) - cursor_pos; } } } if (new_hover_state != hover_state || new_hover_object != hover_object || new_hover_point != hover_point) { hover_state = new_hover_state; // We have got a Map*, so we may get an non-const Object*. hover_object = const_cast<Object*>(new_hover_object); hover_point = new_hover_point; if (hover_state != OverNothing) start_drag_distance = 0; else start_drag_distance = Settings::getInstance().getStartDragDistancePx(); updateDirtyRect(); } Q_ASSERT((hover_state.testFlag(OverObjectNode) || hover_state.testFlag(OverPathEdge)) == (hover_object != nullptr)); }
void EditPointTool::clickPress() { Q_ASSERT(!hover_state.testFlag(OverObjectNode) || !hover_state.testFlag(OverPathEdge) || hover_object); if (hover_state.testFlag(OverPathEdge) && active_modifiers & Qt::ControlModifier) { // Add new point to path PathObject* path = hover_object->asPath(); float distance_sq; PathCoord path_coord; path->calcClosestPointOnPath(cur_pos_map, distance_sq, path_coord); float click_tolerance_map_sq = cur_map_widget->getMapView()->pixelToLength(clickTolerance()); click_tolerance_map_sq = click_tolerance_map_sq * click_tolerance_map_sq; if (distance_sq <= click_tolerance_map_sq) { startEditing(); QScopedValueRollback<bool> no_effect_rollback(no_more_effect_on_click); no_more_effect_on_click = true; startDragging(); hover_state = OverObjectNode; hover_point = path->subdivide(path_coord); if (addDashPointDefault() ^ space_pressed) { MapCoord point = path->getCoordinate(hover_point); point.setDashPoint(true); path->setCoordinate(hover_point, point); map()->emitSelectionEdited(); } startEditingSetup(); updatePreviewObjects(); } } else if (hover_state.testFlag(OverObjectNode) && hover_object->getType() == Object::Path) { PathObject* hover_object = this->hover_object->asPath(); Q_ASSERT(hover_point < hover_object->getCoordinateCount()); if (space_pressed && !hover_object->isCurveHandle(hover_point)) { // Switch point between dash / normal point createReplaceUndoStep(hover_object); MapCoord& hover_coord = hover_object->getCoordinate(hover_point); hover_coord.setDashPoint(!hover_coord.isDashPoint()); hover_object->update(); updateDirtyRect(); no_more_effect_on_click = true; } else if (active_modifiers & Qt::ControlModifier) { auto hover_point_part_index = hover_object->findPartIndexForIndex(hover_point); PathPart& hover_point_part = hover_object->parts()[hover_point_part_index]; if (hover_object->isCurveHandle(hover_point)) { // Convert the curve into a straight line createReplaceUndoStep(hover_object); hover_object->deleteCoordinate(hover_point, false); hover_object->update(); map()->emitSelectionEdited(); updateHoverState(cur_pos_map); updateDirtyRect(); no_more_effect_on_click = true; } else { // Delete the point if (hover_point_part.countRegularNodes() <= 2 || ( !(hover_object->getSymbol()->getContainedTypes() & Symbol::Line) && hover_point_part.size() <= 3 ) ) { // Not enough remaining points -> delete the part and maybe object if (hover_object->parts().size() == 1) { map()->removeObjectFromSelection(hover_object, false); auto undo_step = new AddObjectsUndoStep(map()); auto part = map()->getCurrentPart(); int index = part->findObjectIndex(hover_object); Q_ASSERT(index >= 0); undo_step->addObject(index, hover_object); map()->deleteObject(hover_object, true); map()->push(undo_step); map()->setObjectsDirty(); map()->emitSelectionEdited(); updateHoverState(cur_pos_map); } else { createReplaceUndoStep(hover_object); hover_object->deletePart(hover_point_part_index); hover_object->update(); map()->emitSelectionEdited(); updateHoverState(cur_pos_map); updateDirtyRect(); } no_more_effect_on_click = true; } else { // Delete the point only createReplaceUndoStep(hover_object); int delete_bezier_spline_point_setting; if (active_modifiers & Qt::ShiftModifier) delete_bezier_spline_point_setting = Settings::EditTool_DeleteBezierPointActionAlternative; else delete_bezier_spline_point_setting = Settings::EditTool_DeleteBezierPointAction; hover_object->deleteCoordinate(hover_point, true, Settings::getInstance().getSettingCached((Settings::SettingsEnum)delete_bezier_spline_point_setting).toInt()); hover_object->update(); map()->emitSelectionEdited(); updateHoverState(cur_pos_map); updateDirtyRect(); no_more_effect_on_click = true; } } } } else if (hoveringOverSingleText()) { TextObject* hover_object = map()->getFirstSelectedObject()->asText(); startEditing(); // Don't show the original text while editing map()->removeRenderablesOfObject(hover_object, true); // Make sure that the TextObjectEditorHelper remembers the correct standard cursor cur_map_widget->setCursor(getCursor()); old_text = hover_object->getText(); old_horz_alignment = (int)hover_object->getHorizontalAlignment(); old_vert_alignment = (int)hover_object->getVerticalAlignment(); text_editor = new TextObjectEditorHelper(hover_object, editor); connect(text_editor, SIGNAL(selectionChanged(bool)), this, SLOT(textSelectionChanged(bool))); // Select clicked position int pos = hover_object->calcTextPositionAt(cur_pos_map, false); text_editor->setSelection(pos, pos); updatePreviewObjects(); } click_timer.restart(); }
PathObject* TemplateTrack::importPathStart() { PathObject* path = new PathObject(); path->setSymbol(map->getUndefinedLine(), true); return path; }
bool TemplateTrack::import(QWidget* dialog_parent) { if (track.getNumWaypoints() == 0 && track.getNumSegments() == 0) { QMessageBox::critical(dialog_parent, tr("Error"), tr("The path is empty, there is nothing to import!")); return false; } const Track::ElementTags& tags = track.tags(); DeleteObjectsUndoStep* undo_step = new DeleteObjectsUndoStep(map); MapPart* part = map->getCurrentPart(); std::vector< Object* > result; map->clearObjectSelection(false); if (track.getNumWaypoints() > 0) { int res = QMessageBox::question(dialog_parent, tr("Question"), tr("Should the waypoints be imported as a line going through all points?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (res == QMessageBox::No) { for (int i = 0; i < track.getNumWaypoints(); i++) result.push_back(importWaypoint(templateToMap(track.getWaypoint(i).map_coord), track.getWaypointName(i))); } else { PathObject* path = importPathStart(); for (int i = 0; i < track.getNumWaypoints(); i++) path->addCoordinate(MapCoord(templateToMap(track.getWaypoint(i).map_coord))); importPathEnd(path); path->setTag(QStringLiteral("name"), QString{}); result.push_back(path); } } int skipped_paths = 0; for (int i = 0; i < track.getNumSegments(); i++) { const int segment_size = track.getSegmentPointCount(i); if (segment_size == 0) { ++skipped_paths; continue; // Don't create path without objects. } PathObject* path = importPathStart(); QString name = track.getSegmentName(i); if (!tags[name].isEmpty()) { path->setTags(tags[name]); } else { path->setTag(QStringLiteral("name"), name); } for (int j = 0; j < segment_size; j++) { const TrackPoint& track_point = track.getSegmentPoint(i, j); auto coord = MapCoord { templateToMap(track_point.map_coord) }; if (track_point.is_curve_start && j < segment_size - 3) coord.setCurveStart(true); path->addCoordinate(coord); } if (track.getSegmentPoint(i, 0).gps_coord == track.getSegmentPoint(i, segment_size-1).gps_coord) { path->closeAllParts(); } importPathEnd(path); result.push_back(path); } for (int i = 0; i < (int)result.size(); ++i) // keep as separate loop to get the correct (final) indices undo_step->addObject(part->findObjectIndex(result[i])); map->setObjectsDirty(); map->push(undo_step); map->emitSelectionChanged(); map->emitSelectionEdited(); // TODO: is this necessary here? if (skipped_paths) { QMessageBox::information( dialog_parent, tr("Import problems"), tr("%n path object(s) could not be imported (reason: missing coordinates).", "", skipped_paths) ); } return true; }
void BooleanTool::executeForLine(const PathObject* area, const PathObject* line, BooleanTool::PathObjects& out_objects) { if (op != BooleanTool::Intersection && op != BooleanTool::Difference) { Q_ASSERT_X(false, "BooleanTool::executeForLine", "Only intersection and difference are supported."); return; // no-op in release build } if (line->parts().size() != 1) { Q_ASSERT_X(false, "BooleanTool::executeForLine", "Only single-part lines are supported."); return; // no-op in release build } // Calculate all intersection points of line with the area path PathObject::Intersections intersections; line->calcAllIntersectionsWith(area, intersections); intersections.normalize(); PathObject* first_segment = NULL; PathObject* last_segment = NULL; const auto& part = line->parts().front(); const auto& path_coords = part.path_coords; // Only one segment? if (intersections.empty()) { auto middle_length = part.length(); auto middle = SplitPathCoord::at(path_coords, middle_length); if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) out_objects.push_back(new PathObject(*line)); return; } // First segment auto middle_length = intersections[0].length / 2; auto middle = SplitPathCoord::at(path_coords, middle_length); if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) { PathObject* segment = new PathObject(*line); segment->changePathBounds(0, 0.0, intersections[0].length); first_segment = segment; } // Middle segments for (std::size_t i = 0; i < intersections.size() - 1; ++i) { middle_length = (intersections[i].length + intersections[i+1].length) / 2; auto middle = SplitPathCoord::at(path_coords, middle_length); if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) { PathObject* segment = new PathObject(*line); segment->changePathBounds(0, intersections[i].length, intersections[i+1].length); out_objects.push_back(segment); } } // Last segment middle_length = (part.length() + intersections.back().length) / 2; middle = SplitPathCoord::at(path_coords, middle_length); if (area->isPointInsideArea(middle.pos) == (op == BooleanTool::Intersection)) { PathObject* segment = new PathObject(*line); segment->changePathBounds(0, intersections.back().length, part.length()); last_segment = segment; } // If the line was closed at the beginning, merge the first and last segments if they were kept both. if (part.isClosed() && first_segment && last_segment) { last_segment->connectPathParts(0, first_segment, 0, false); delete first_segment; out_objects.push_back(last_segment); } else { if (first_segment) out_objects.push_back(first_segment); if (last_segment) out_objects.push_back(last_segment); } }
MapCoord SnappingToolHelper::snapToObject(MapCoordF position, MapWidget* widget, SnappingToolHelperSnapInfo* info, Object* exclude_object, float snap_distance) { if (snap_distance < 0) snap_distance = 0.001f * widget->getMapView()->pixelToLength(Settings::getInstance().getMapEditorSnapDistancePx()); float closest_distance_sq = snap_distance * snap_distance; auto result_position = MapCoord { position }; SnappingToolHelperSnapInfo result_info; result_info.type = NoSnapping; result_info.object = NULL; result_info.coord_index = -1; result_info.path_coord.pos = MapCoordF(0, 0); result_info.path_coord.index = -1; result_info.path_coord.clen = -1; result_info.path_coord.param = -1; if (filter & (ObjectCorners | ObjectPaths)) { // Find map objects at the given position SelectionInfoVector objects; map->findAllObjectsAt(position, snap_distance, true, false, false, true, objects); // Find closest snap spot from map objects for (SelectionInfoVector::const_iterator it = objects.begin(), end = objects.end(); it != end; ++it) { Object* object = it->second; if (object == exclude_object) continue; float distance_sq; if (object->getType() == Object::Point && filter & ObjectCorners) { PointObject* point = object->asPoint(); distance_sq = point->getCoordF().distanceSquaredTo(position); if (distance_sq < closest_distance_sq) { closest_distance_sq = distance_sq; result_position = point->getCoord(); result_info.type = ObjectCorners; result_info.object = object; result_info.coord_index = 0; } } else if (object->getType() == Object::Path) { PathObject* path = object->asPath(); if (filter & ObjectPaths) { PathCoord path_coord; path->calcClosestPointOnPath(position, distance_sq, path_coord); if (distance_sq < closest_distance_sq) { closest_distance_sq = distance_sq; result_position = MapCoord(path_coord.pos); result_info.object = object; if (path_coord.param == 0.0) { result_info.type = ObjectCorners; result_info.coord_index = path_coord.index; } else { result_info.type = ObjectPaths; result_info.coord_index = -1; result_info.path_coord = path_coord; } } } else { MapCoordVector::size_type index; path->calcClosestCoordinate(position, distance_sq, index); if (distance_sq < closest_distance_sq) { closest_distance_sq = distance_sq; result_position = path->getCoordinate(index); result_info.type = ObjectCorners; result_info.object = object; result_info.coord_index = index; } } } else if (object->getType() == Object::Text) { // No snapping to texts continue; } } } // Find closest grid snap position if ((filter & GridCorners) && widget->getMapView()->isGridVisible() && map->getGrid().isSnappingEnabled() && map->getGrid().getDisplayMode() == MapGrid::AllLines) { MapCoordF closest_grid_point = map->getGrid().getClosestPointOnGrid(position, map); float distance_sq = closest_grid_point.distanceSquaredTo(position); if (distance_sq < closest_distance_sq) { closest_distance_sq = distance_sq; result_position = MapCoord(closest_grid_point); result_info.type = GridCorners; result_info.object = NULL; result_info.coord_index = -1; } } // Return if (snap_mark != result_position || snapped_type != result_info.type) { snap_mark = result_position; snapped_type = result_info.type; emit displayChanged(); } if (info != NULL) *info = result_info; return result_position; }
void Importer::doImport(bool load_symbols_only, const QString& map_path) { import(load_symbols_only); // Object post processing: // - make sure that there is no object without symbol // - make sure that all area-only path objects are closed // - make sure that there are no special points in wrong places (e.g. curve starts inside curves) for (int p = 0; p < map->getNumParts(); ++p) { MapPart* part = map->getPart(p); for (int o = 0; o < part->getNumObjects(); ++o) { Object* object = part->getObject(o); if (object->getSymbol() == NULL) { addWarning(Importer::tr("Found an object without symbol.")); if (object->getType() == Object::Point) object->setSymbol(map->getUndefinedPoint(), true); else if (object->getType() == Object::Path) object->setSymbol(map->getUndefinedLine(), true); else { // There is no undefined symbol for this type of object, delete the object part->deleteObject(o, false); --o; continue; } } if (object->getType() == Object::Path) { PathObject* path = object->asPath(); Symbol::Type contained_types = path->getSymbol()->getContainedTypes(); if (contained_types & Symbol::Area && !(contained_types & Symbol::Line)) path->closeAllParts(); for (MapCoordVector::size_type i = 0; i < path->getCoordinateCount(); ++i) { if (path->getCoordinate(i).isCurveStart()) { if (i+3 >= path->getCoordinateCount()) { path->getCoordinate(i).setCurveStart(false); continue; } if (path->getCoordinate(i + 1).isClosePoint() || path->getCoordinate(i + 1).isHolePoint() || path->getCoordinate(i + 2).isClosePoint() || path->getCoordinate(i + 2).isHolePoint()) { path->getCoordinate(i).setCurveStart(false); continue; } path->getCoordinate(i + 1).setCurveStart(false); path->getCoordinate(i + 1).setDashPoint(false); path->getCoordinate(i + 2).setCurveStart(false); path->getCoordinate(i + 2).setDashPoint(false); i += 2; } if (i > 0 && path->getCoordinate(i).isHolePoint()) { if (path->getCoordinate(i-1).isHolePoint()) path->deleteCoordinate(i, false); } } } } } // Symbol post processing for (int i = 0; i < map->getNumSymbols(); ++i) { if (!map->getSymbol(i)->loadFinished(map)) throw FileFormatException(Importer::tr("Error during symbol post-processing.")); } // Template loading: try to find all template files bool have_lost_template = false; for (int i = 0; i < map->getNumTemplates(); ++i) { Template* temp = map->getTemplate(i); bool loaded_from_template_dir = false; temp->tryToFindAndReloadTemplateFile(map_path, &loaded_from_template_dir); if (loaded_from_template_dir) addWarning(Importer::tr("Template \"%1\" has been loaded from the map's directory instead of the relative location to the map file where it was previously.").arg(temp->getTemplateFilename())); if (temp->getTemplateState() != Template::Loaded) have_lost_template = true; } if (have_lost_template) { #if defined(Q_OS_ANDROID) addWarning(tr("At least one template file could not be found.")); #else addWarning(tr("At least one template file could not be found.") + " " + tr("Click the red template name(s) in the Templates -> Template setup window to locate the template file name(s).")); #endif } }