bool ObjectSelector::selectAt(MapCoordF position, double tolerance, bool toggle) { bool selection_changed; bool single_object_selected = map->getNumSelectedObjects() == 1; Object* single_selected_object = nullptr; if (single_object_selected) single_selected_object = *map->selectedObjectsBegin(); // Clicked - get objects below cursor SelectionInfoVector objects; map->findObjectsAt(position, 0.001f * tolerance, false, false, false, false, objects); if (objects.empty()) map->findObjectsAt(position, 0.001f * 1.5f * tolerance, false, true, false, false, objects); // Selection logic, trying to select the most relevant object(s) if (!toggle || map->getNumSelectedObjects() == 0) { if (objects.empty()) { // Clicked on empty space, deselect everything selection_changed = map->getNumSelectedObjects() > 0; last_results.clear(); map->clearObjectSelection(true); } else if (!last_results.empty() && selectionInfosEqual(objects, last_results)) { // If this result is the same as last time, select next object next_object_to_select = next_object_to_select % last_results_ordered.size(); map->clearObjectSelection(false); map->addObjectToSelection(last_results_ordered[next_object_to_select].second, true); selection_changed = true; ++next_object_to_select; } else { // Results different - select object with highest priority, if it is not the same as before last_results = objects; std::sort(objects.begin(), objects.end(), sortObjects); last_results_ordered = std::move(objects); next_object_to_select = 1; map->clearObjectSelection(false); if (single_selected_object == last_results_ordered.begin()->second) { next_object_to_select = next_object_to_select % last_results_ordered.size(); map->addObjectToSelection(last_results_ordered[next_object_to_select].second, true); ++next_object_to_select; } else map->addObjectToSelection(last_results_ordered.begin()->second, true); selection_changed = true; } } else { // Shift held and something is already selected if (objects.empty()) { // do nothing selection_changed = false; } else if (!last_results.empty() && selectionInfosEqual(objects, last_results)) { // Toggle last selected object - must work the same way, regardless if other objects are selected or not next_object_to_select = next_object_to_select % last_results_ordered.size(); if (map->toggleObjectSelection(last_results_ordered[next_object_to_select].second, true) == false) ++next_object_to_select; // only advance if object has been deselected selection_changed = true; } else { // Toggle selection of highest priority object last_results = objects; std::sort(objects.begin(), objects.end(), sortObjects); last_results_ordered = std::move(objects); map->toggleObjectSelection(last_results_ordered.begin()->second, true); selection_changed = true; } } return selection_changed; }
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; }
bool ObjectSelector::selectionInfosEqual(const SelectionInfoVector& a, const SelectionInfoVector& b) { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); }