예제 #1
0
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;
}
예제 #2
0
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));
}
예제 #3
0
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();
}
예제 #4
0
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;
}