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 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(); }
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; }