示例#1
0
void MapGrid::calculateFinalParameters(double& final_horz_spacing, double& final_vert_spacing, double& final_horz_offset, double& final_vert_offset, double& final_rotation, Map* map) const
{
	double factor = ((unit == MetersInTerrain) ? (1000.0 / map->getScaleDenominator()) : 1);
	final_horz_spacing = factor * horz_spacing;
	final_vert_spacing = factor * vert_spacing;
	final_horz_offset = factor * horz_offset;
	final_vert_offset = factor * vert_offset;
	final_rotation = additional_rotation + M_PI / 2;
	
	const Georeferencing& georeferencing = map->getGeoreferencing();
	if (alignment == GridNorth)
	{
		final_rotation += georeferencing.getGrivation() * M_PI / 180;
		
		// Shift origin to projected coordinates origin
		double prev_horz_offset = MapCoordF::dotProduct(MapCoordF(0, -1).rotated(final_rotation), MapCoordF(georeferencing.getMapRefPoint().x(), -1 * georeferencing.getMapRefPoint().y()));
		double target_horz_offset = MapCoordF::dotProduct(MapCoordF(0, -1).rotated(additional_rotation + M_PI / 2), MapCoordF(georeferencing.getProjectedRefPoint().x(), georeferencing.getProjectedRefPoint().y()));
		final_horz_offset -= factor * target_horz_offset - prev_horz_offset;
		
		double prev_vert_offset = MapCoordF::dotProduct(MapCoordF(1, 0).rotated(final_rotation), MapCoordF(georeferencing.getMapRefPoint().x(), -1 * georeferencing.getMapRefPoint().y()));
		double target_vert_offset = MapCoordF::dotProduct(MapCoordF(1, 0).rotated(additional_rotation + M_PI / 2), MapCoordF(georeferencing.getProjectedRefPoint().x(), georeferencing.getProjectedRefPoint().y()));
		final_vert_offset += factor * target_vert_offset - prev_vert_offset;
	}
	else if (alignment == TrueNorth)
		final_rotation += georeferencing.getDeclination() * M_PI / 180;
}
示例#2
0
MapCoordF DrawRectangleTool::calculateClosingVector() const
{
	int cur_point_index = angles.size();
	auto close_vector = MapCoordF(1, 0);
	close_vector.rotate(-angles[0]);
	if (drawingParallelTo(angles[0]))
		close_vector = close_vector.perpRight();
	if (MapCoordF::dotProduct(close_vector, MapCoordF(preview_path->getCoordinate(0) - preview_path->getCoordinate(cur_point_index - 1))) < 0)
		close_vector = -close_vector;
	return close_vector;
}
示例#3
0
void DrawRectangleTool::updateHover(bool mouse_down)
{
	if (shift_pressed)
		constrained_pos_map = MapCoordF(snap_helper->snapToObject(cur_pos_map, cur_map_widget));
	else
		constrained_pos_map = cur_pos_map;
	
	if (!editingInProgress())
	{
		setPreviewPointsPosition(constrained_pos_map);
		updateDirtyRect();
		
		if (mouse_down && ctrl_pressed)
			pickDirection(constrained_pos_map, cur_map_widget);
		else if (!mouse_down)
			angle_helper->setCenter(constrained_pos_map);
	}
	else
	{
		hidePreviewPoints();
		if (mouse_down && !dragging && (cur_pos - click_pos).manhattanLength() >= Settings::getInstance().getStartDragDistancePx())
		{
			// Start dragging
			dragging = true;
		}
		if (!mouse_down || dragging)
			updateRectangle();
	}
}
示例#4
0
bool DrawRectangleTool::mouseDoubleClickEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget)
{
	Q_UNUSED(map_coord);
	Q_UNUSED(widget);
	
	if (event->button() != Qt::LeftButton)
		return false;
	
	if (editingInProgress())
	{
		// Finish drawing by double click.
		// As the double click is also reported as two single clicks first
		// (in total: press - release - press - double - release),
		// need to undo two steps in general.
		if (angles.size() > 2 && !ctrl_pressed)
		{
			undoLastPoint();
			updateHover(false);
		}
		
		if (angles.size() <= 1)
		{
			abortDrawing();
		}
		else
		{
			constrained_pos_map = MapCoordF(preview_path->getCoordinate(angles.size() - 1));
			undoLastPoint();
			finishDrawing();
		}
		no_more_effect_on_click = true;
	}
	return true;
}
示例#5
0
void TextSymbol::createLineBelowRenderables(const Object* object, ObjectRenderables& output) const
{
	const TextObject* text_object = reinterpret_cast<const TextObject*>(object);
	if (text_object->getNumLines())
	{
		double scale_factor = calculateInternalScaling();
		AreaSymbol area_symbol;
		area_symbol.setColor(line_below_color);
		
		MapCoordVector  line_flags(4);
		MapCoordVectorF line_coords(4);
		VirtualPath line_path = { line_flags, line_coords };
		line_flags.back().setHolePoint(true);
		
		QTransform transform = text_object->calcTextToMapTransform();
		
		for (int i = 0; i < text_object->getNumLines(); ++i)
		{
			const TextObjectLineInfo* line_info = text_object->getLineInfo(i);
			if (!line_info->paragraph_end)
				continue;
			
			double line_below_x0;
			double line_below_x1;
			if (text_object->hasSingleAnchor())
			{
				line_below_x0 = line_info->line_x;
				line_below_x1 = line_below_x0 + line_info->width;
			}
			else
			{
				double box_width = text_object->getBoxWidth() * scale_factor;
				line_below_x0 = -0.5 * box_width;
				line_below_x1 = line_below_x0 + box_width;
			}
			double line_below_y0 = line_info->line_y + getLineBelowDistance() * scale_factor;
			double line_below_y1 = line_below_y0 + getLineBelowWidth() * scale_factor;
			line_coords[0] = MapCoordF(transform.map(QPointF(line_below_x0, line_below_y0)));
			line_coords[1] = MapCoordF(transform.map(QPointF(line_below_x1, line_below_y0)));
			line_coords[2] = MapCoordF(transform.map(QPointF(line_below_x1, line_below_y1)));
			line_coords[3] = MapCoordF(transform.map(QPointF(line_below_x0, line_below_y1)));
			
			line_path.path_coords.update(0);
			output.insertRenderable(new AreaRenderable(&area_symbol, line_path));
		}
	}
}
示例#6
0
ConstrainAngleToolHelper::ConstrainAngleToolHelper()
 : active_angle(-1),
   center(MapCoordF(0, 0)),
   active(true),
   have_default_angles_only(false)
{
	connect(&Settings::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged()));
}
示例#7
0
void DrawRectangleTool::pickDirection(MapCoordF coord, MapWidget* widget)
{
	MapCoord snap_position;
	snap_helper->snapToDirection(coord, widget, angle_helper.data(), &snap_position);
	angle_helper->setActive(true, MapCoordF(snap_position));
	updateDirtyRect();
	picked_direction = true;
}
示例#8
0
	inline bool operator()(Object* object, MapPart* part, int object_index)
	{
		Q_UNUSED(part);
		Q_UNUSED(object_index);
		
		// If there is a selection, only clip selected objects
		if (map->getNumSelectedObjects() > 0 && !map->isObjectSelected(object))
			return true;
		// Don't clip object itself
		if (object == cutout_object)
			return true;
		
		// Early out
		if (!object->getExtent().intersects(cutout_object->getExtent()))
		{
			if (!cut_away)
				add_step->addObject(object, object);
			return true;
		}
		
		if (object->getType() == Object::Point ||
			object->getType() == Object::Text)
		{
			// Simple check if the (first) point is inside the area
			if (cutout_object->isPointInsideArea(MapCoordF(object->getRawCoordinateVector().at(0))) == cut_away)
				add_step->addObject(object, object);
		}
		else if (object->getType() == Object::Path)
		{
			if (object->getSymbol()->getContainedTypes() & Symbol::Area)
			{
				// Use the Clipper library to clip the area
				BooleanTool boolean_tool(cut_away ? BooleanTool::Difference : BooleanTool::Intersection, map);
				BooleanTool::PathObjects in_objects;
				in_objects.push_back(cutout_object);
				in_objects.push_back(object->asPath());
				BooleanTool::PathObjects out_objects;
				if (!boolean_tool.executeForObjects(object->asPath(), in_objects, out_objects))
					return true;
				
				add_step->addObject(object, object);
				new_objects.insert(new_objects.end(), out_objects.begin(), out_objects.end());
			}
			else
			{
				// Use some custom code to clip the line
				BooleanTool boolean_tool(cut_away ? BooleanTool::Difference : BooleanTool::Intersection, map);
				BooleanTool::PathObjects out_objects;
				boolean_tool.executeForLine(cutout_object, object->asPath(), out_objects);
				
				add_step->addObject(object, object);
				new_objects.insert(new_objects.end(), out_objects.begin(), out_objects.end());
			}
		}
		
		return true;
	}
示例#9
0
bool DrawRectangleTool::mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget)
{
	cur_pos = event->pos();
	cur_pos_map = map_coord;
	if (shift_pressed)
		cur_pos_map = MapCoordF(snap_helper->snapToObject(cur_pos_map, widget));
	constrained_pos_map = cur_pos_map;
	
	if (no_more_effect_on_click)
	{
		no_more_effect_on_click = false;
		return true;
	}
	
	if (ctrl_pressed && event->button() == Qt::LeftButton && !editingInProgress())
	{
		pickDirection(cur_pos_map, widget);
		return true;
	}
	
	bool result = false;
	if (editingInProgress())
	{
		if (isDrawingButton(event->button()) && dragging)
		{
			dragging = false;
			result = mousePressEvent(event, cur_pos_map, widget);
		}
		
		if (event->button() == Qt::RightButton && drawOnRightClickEnabled())
		{
			if (!dragging)
			{
				constrained_pos_map = MapCoordF(preview_path->getCoordinate(angles.size() - 1));
				undoLastPoint();
			}
			if (editingInProgress()) // despite undoLastPoint()
				finishDrawing();
			return true;
		}
	}
	return result;
}
示例#10
0
double ConstrainAngleToolHelper::getConstrainedCursorPosMap(const MapCoordF& in_pos, MapCoordF& out_pos)
{
	MapCoordF to_cursor = in_pos - center;
	double in_angle = -1 * to_cursor.angle();
	if (!active)
	{
		out_pos = in_pos;
		return in_angle;
	}
	
	double lower_angle = in_angle, lower_angle_delta = 999;
	double higher_angle = in_angle, higher_angle_delta = -999;
	for (std::set<double>::const_iterator it = angles.begin(), end = angles.end(); it != end; ++it)
	{
		double delta = in_angle - *it;
		if (delta < -M_PI)
			delta = in_angle - (*it - 2*M_PI);
		else if (delta > M_PI)
			delta = (in_angle - 2*M_PI) - *it;
		
		if (delta > 0)
		{
			if (delta < lower_angle_delta)
			{
				lower_angle_delta = delta;
				lower_angle = *it;
			}
		}
		else
		{
			if (delta > higher_angle_delta)
			{
				higher_angle_delta = delta;
				higher_angle = *it;
			}
		}
	}
	
	double new_active_angle;
	if (lower_angle_delta < -1 * higher_angle_delta)
		new_active_angle = lower_angle;
	else
		new_active_angle = higher_angle;
	if (new_active_angle != active_angle)
	{
		emitActiveAngleChanged();
		active_angle = new_active_angle;
	}
	
	MapCoordF unit_direction = MapCoordF(cos(active_angle), -sin(active_angle));
	to_cursor = MapCoordF::dotProduct(unit_direction, to_cursor) * unit_direction;
	out_pos = center + to_cursor;
	
	return active_angle;
}
示例#11
0
void RotateTool::initImpl()
{
	// Set initial rotation center to the bounding box center of the selected objects
	if (map()->getNumSelectedObjects() > 0)
	{
		QRectF rect;
		map()->includeSelectionRect(rect);
		rotation_center = MapCoordF(rect.center());
		rotation_center_set = true;
	}
}
示例#12
0
MapCoordF MapGrid::getClosestPointOnGrid(MapCoordF position, Map* map) const
{
	double final_horz_spacing, final_vert_spacing;
	double final_horz_offset, final_vert_offset;
	double final_rotation;
	calculateFinalParameters(final_horz_spacing, final_vert_spacing, final_horz_offset, final_vert_offset, final_rotation, map);
	
	position.rotate(final_rotation - M_PI / 2);
	return MapCoordF(qRound((position.x() - final_horz_offset) / final_horz_spacing) * final_horz_spacing + final_horz_offset,
					 qRound((position.y() - final_vert_offset) / final_vert_spacing) * final_vert_spacing + final_vert_offset).rotated(-1 * (final_rotation - M_PI / 2));
}
示例#13
0
void DrawPathTool::finishFollowing()
{
	following = false;
	
	int last = preview_path->getCoordinateCount() - 1;
	
	if (last >= 3 && preview_path->getCoordinate(last - 3).isCurveStart())
	{
		MapCoord first = preview_path->getCoordinate(last - 1);
		MapCoord second = preview_path->getCoordinate(last);
		
		previous_point_is_curve_point = true;
		previous_point_direction = -atan2(second.x() - first.x(), first.y() - second.y());
		previous_pos_map = MapCoordF(second);
		previous_drag_map = MapCoordF(second.x() + (second.x() - first.x()) / 2, second.y() + (second.y() - first.y()) / 2);
	}
	else
		previous_point_is_curve_point = false;
	
	updateAngleHelper();
}
示例#14
0
void DrawRectangleTool::undoLastPoint()
{
	if (angles.size() == 1)
	{
		abortDrawing();
		return;
	}
	
	deleteClosePoint();
	preview_path->deleteCoordinate(preview_path->getCoordinateCount() - 1, false);
	if (angles.size() == 2)
		preview_path->deleteCoordinate(preview_path->getCoordinateCount() - 1, false);
	
	angles.pop_back();
	forward_vector = MapCoordF(1, 0);
	forward_vector.rotate(-angles[angles.size() - 1]);
	
	angle_helper->setCenter(MapCoordF(preview_path->getCoordinate(angles.size() - 1)));
	
	updateRectangle();
}
示例#15
0
void DrawFreehandTool::checkLineSegment(int a, int b, std::vector< bool >& point_mask)
{
	if (b <= a + 1)
		return;
	
	const MapCoord& start_coord = preview_path->getRawCoordinateVector()[a];
	const MapCoord& end_coord = preview_path->getRawCoordinateVector()[b];
	
	// Find point between a and b with highest distance from line segment
	float max_distance_sq = -1;
	int best_index = a + 1;
	for (int i = a + 1; i < b; ++ i)
	{
		const MapCoord& coord = preview_path->getRawCoordinateVector()[i];
		
		MapCoordF to_coord = MapCoordF(coord.x() - start_coord.x(), coord.y() - start_coord.y());
		MapCoordF to_next = MapCoordF(end_coord.x() - start_coord.x(), end_coord.y() - start_coord.y());
		MapCoordF tangent = to_next;
		tangent.normalize();
		
		float distance_sq;
		
		float dist_along_line = MapCoordF::dotProduct(to_coord, tangent);
		if (dist_along_line <= 0)
		{
			distance_sq = to_coord.lengthSquared();
		}
		else
		{
			float line_length = MapCoordF(end_coord).distanceTo(MapCoordF(start_coord));
			if (dist_along_line >= line_length)
			{
				distance_sq = MapCoordF(coord).distanceSquaredTo(MapCoordF(end_coord));
			}
			else
			{
				auto right = tangent.perpRight();
				distance_sq = qAbs(MapCoordF::dotProduct(right, to_coord));
				distance_sq = distance_sq*distance_sq;
			}
		}
		
		if (distance_sq > max_distance_sq)
		{
			max_distance_sq = distance_sq;
			best_index = i;
		}
	}
	
	// Make new segment?
	const float split_distance_sq = 0.09f*0.09f;
	
	if (max_distance_sq > split_distance_sq)
	{
		point_mask[best_index] = true;
		checkLineSegment(a, best_index, point_mask);
		checkLineSegment(best_index, b, point_mask);
	}
}
示例#16
0
void BooleanTool::rebuildSegmentFromPathOnly(
        const ClipperLib::IntPoint& start_point,
        const ClipperLib::IntPoint& second_point,
        const ClipperLib::IntPoint& second_last_point,
        const ClipperLib::IntPoint& end_point,
        PathObject* object)
{
	MapCoord start_point_c = MapCoord::fromNative64(start_point.X, start_point.Y);
	MapCoord second_point_c = MapCoord::fromNative64(second_point.X, second_point.Y);
	MapCoord second_last_point_c = MapCoord::fromNative64(second_last_point.X, second_last_point.Y);
	MapCoord end_point_c = MapCoord::fromNative64(end_point.X, end_point.Y);
	
	MapCoordF polygon_start_tangent = MapCoordF(second_point_c - start_point_c);
	polygon_start_tangent.normalize();
	MapCoordF polygon_end_tangent = MapCoordF(second_last_point_c - end_point_c);
	polygon_end_tangent.normalize();
	
	double tangent_length = BEZIER_HANDLE_DISTANCE * start_point_c.distanceTo(end_point_c);
	object->addCoordinate(MapCoord(MapCoordF(start_point_c) + tangent_length * polygon_start_tangent));
	object->addCoordinate(MapCoord((MapCoordF(end_point_c) + tangent_length * polygon_end_tangent)));
	object->addCoordinate(end_point_c);
}
示例#17
0
PassPoint PassPoint::load(QXmlStreamReader& xml)
{
	Q_ASSERT(xml.name() == QLatin1String("passpoint"));
	
	XmlElementReader passpoint{xml};
	PassPoint p;
	p.error = passpoint.attribute<double>(QLatin1String("error"));
	while (xml.readNextStartElement())
	{
		QStringRef name = xml.name();
		while (xml.readNextStartElement())
		{
			if (xml.name() == QLatin1String("coord"))
			{
				try
				{
					if (name == QLatin1String("source"))
						p.src_coords = MapCoordF(MapCoord::load(xml));
					else if (name == QLatin1String("destination"))
						p.dest_coords = MapCoordF(MapCoord::load(xml));
					else if (name == QLatin1String("calculated"))
						p.calculated_coords = MapCoordF(MapCoord::load(xml));
					else
						xml.skipCurrentElement(); // unsupported
				}
				catch (std::range_error& e)
				{
					throw FileFormatException(MapCoord::tr(e.what()));
				}
			}
			else
				xml.skipCurrentElement(); // unsupported
		}
	}
	return p;
}
示例#18
0
void EditPointTool::dragMove()
{
	if (no_more_effect_on_click)
		return;
	
	if (editingInProgress())
	{
		if (snapped_to_pos && handle_offset != MapCoordF(0, 0))
		{
			// Snapped to a position. Correct the handle offset, so the
			// object moves to this position exactly.
			click_pos_map += handle_offset;
			object_mover->setStartPos(click_pos_map);
			handle_offset = MapCoordF(0, 0);
		}
		
		object_mover->move(constrained_pos_map, !(active_modifiers & Qt::ShiftModifier));
		updatePreviewObjectsAsynchronously();
	}
	else if (box_selection)
	{
		updateDirtyRect();
	}
}
示例#19
0
void RotateTool::initImpl()
{
	// Set initial rotation center to the bounding box center of the selected objects
	if (map()->getNumSelectedObjects() > 0)
	{
		QRectF rect;
		map()->includeSelectionRect(rect);
		rotation_center = rect.center();
	}
	else
	{
		rotation_center = MapCoordF(cur_map_widget->getMapView()->center());
	}
	angle_helper->setCenter(rotation_center);
}
示例#20
0
bool DrawPathTool::pickAngle(MapCoordF coord, MapWidget* widget)
{
	MapCoord snap_position;
	bool picked = snap_helper->snapToDirection(coord, widget, angle_helper.data(), &snap_position);
	if (picked)
		angle_helper->setCenter(MapCoordF(snap_position));
	else
	{
		updateAngleHelper();
		angle_helper->setCenter(constrained_pos_map);
	}
	hidePreviewPoints();
	updateDirtyRect();
	return picked;
}
示例#21
0
void ScaleTool::init()
{
	// Set initial scaling center to the center of the bounding box of the selected objects
	if (map()->getNumSelectedObjects() > 0)
	{
		QRectF rect;
		map()->includeSelectionRect(rect);
		scaling_center = MapCoordF(rect.center());
		scaling_center_set = true;
	}
	
	connect(map(), SIGNAL(objectSelectionChanged()), this, SLOT(objectSelectionChanged()));
	connect(map(), SIGNAL(selectedObjectEdited()), this, SLOT(updateDirtyRect()));
	updateDirtyRect();
	updateStatusText();
	
	MapEditorTool::init();
}
示例#22
0
void DrawPathTool::updateAngleHelper()
{
	if (picked_angle)
		return;
	
	if (!preview_path
	    || (updatePreviewPath(), preview_path->parts().empty()))
	{
		angle_helper->clearAngles();
		angle_helper->addDefaultAnglesDeg(0);
		return;
	}
	
	const auto& part = preview_path->parts().back();
	
	bool rectangular_stepping = true;
	double angle;
	if (part.size() >= 2)
	{
		bool ok = false;
		MapCoordF tangent = part.calculateTangent(part.size()-1, true, ok);
		if (!ok)
			tangent = MapCoordF(1, 0);
		angle = -tangent.angle();
	}
	else
	{
		if (previous_point_is_curve_point)
			angle = previous_point_direction;
		else
		{
			angle = 0;
			rectangular_stepping = false;
		}
	}
	
	if (!part.empty())
		angle_helper->setCenter(part.coords.back());
	angle_helper->clearAngles();
	if (rectangular_stepping)
		angle_helper->addAnglesDeg(angle * 180 / M_PI, 45);
	else
		angle_helper->addDefaultAnglesDeg(angle * 180 / M_PI);
}
示例#23
0
void DrawPathTool::updateHover()
{
	if (shift_pressed)
		constrained_pos_map = MapCoordF(snap_helper->snapToObject(cur_pos_map, cur_map_widget));
	else
		constrained_pos_map = cur_pos_map;
	
	if (!editingInProgress())
	{
		// Show preview objects at this position
		setPreviewPointsPosition(constrained_pos_map);
		if (picked_angle)
			angle_helper->setCenter(constrained_pos_map);
		updateDirtyRect();
	}
	else // if (draw_in_progress)
	{
		updateDrawHover();
	}
}
示例#24
0
LineRenderable::LineRenderable(const LineSymbol* symbol, QPointF first, QPointF second)
 : Renderable(symbol->getColor())
 , line_width(0.001f * symbol->getLineWidth())
 , cap_style(Qt::FlatCap)
 , join_style(Qt::MiterJoin)
{
	qreal half_line_width = (color_priority < 0) ? 0.0f : 0.5f * line_width;
	
	auto right = MapCoordF(second - first).perpRight();
	right.normalize();
	right *= half_line_width;
	
	extent.setTopLeft(first + right);
	rectInclude(extent, first - right);
	rectInclude(extent, second - right);
	rectInclude(extent, second + right);
	
	path.moveTo(first);
	path.lineTo(second);
}
示例#25
0
void TemplateImage::updatePosFromGeoreferencing()
{
	// Determine map coords of three image corner points
	// by transforming the points from one Georeferencing into the other
	bool ok;
	MapCoordF top_left = map->getGeoreferencing().toMapCoordF(georef.data(), MapCoordF(-0.5, -0.5), &ok);
	if (!ok)
	{
		qDebug() << "updatePosFromGeoreferencing() failed";
		return; // TODO: proper error message?
	}
	MapCoordF top_right = map->getGeoreferencing().toMapCoordF(georef.data(), MapCoordF(image.width() - 0.5, -0.5), &ok);
	if (!ok)
	{
		qDebug() << "updatePosFromGeoreferencing() failed";
		return; // TODO: proper error message?
	}
	MapCoordF bottom_left = map->getGeoreferencing().toMapCoordF(georef.data(), MapCoordF(-0.5, image.height() - 0.5), &ok);
	if (!ok)
	{
		qDebug() << "updatePosFromGeoreferencing() failed";
		return; // TODO: proper error message?
	}
	
	// Calculate template transform as similarity transform from pixels to map coordinates
	PassPointList pp_list;
	
	PassPoint pp;
	pp.src_coords = MapCoordF(-0.5 * image.width(), -0.5 * image.height());
	pp.dest_coords = top_left;
	pp_list.push_back(pp);
	pp.src_coords = MapCoordF(0.5 * image.width(), -0.5 * image.height());
	pp.dest_coords = top_right;
	pp_list.push_back(pp);
	pp.src_coords = MapCoordF(-0.5 * image.width(), 0.5 * image.height());
	pp.dest_coords = bottom_left;
	pp_list.push_back(pp);
	
	QTransform q_transform;
	if (!pp_list.estimateNonIsometricSimilarityTransform(&q_transform))
	{
		qDebug() << "updatePosFromGeoreferencing() failed";
		return; // TODO: proper error message?
	}
	qTransformToTemplateTransform(q_transform, &transform);
	updateTransformationMatrices();
}
示例#26
0
void MapEditorToolBase::calcConstrainedPositions(MapWidget* widget)
{
	if (snap_helper->getFilter() != SnappingToolHelper::NoSnapping)
	{
		SnappingToolHelperSnapInfo info;
		constrained_pos_map = MapCoordF(snap_helper->snapToObject(cur_pos_map, widget, &info, snap_exclude_object));
		constrained_pos = widget->mapToViewport(constrained_pos_map).toPoint();
		snapped_to_pos = info.type != SnappingToolHelper::NoSnapping;
	}
	else
	{
		constrained_pos_map = cur_pos_map;
		constrained_pos = cur_pos;
		snapped_to_pos = false;
	}
	if (angle_helper->isActive())
	{
		QPointF temp_pos;
		angle_helper->getConstrainedCursorPositions(constrained_pos_map, constrained_pos_map, temp_pos, widget);
		constrained_pos = temp_pos.toPoint();
	}
}
示例#27
0
bool PassPointList::estimateSimilarityTransformation(TemplateTransform* transform)
{
	auto num_pass_points = int(size());
	if (num_pass_points == 1)
	{
		PassPoint* point = &at(0);
		MapCoordF offset = point->dest_coords - point->src_coords;
		
		transform->template_x += qRound64(1000 * offset.x());
		transform->template_y += qRound64(1000 * offset.y());
		point->calculated_coords = point->dest_coords;
		point->error = 0;
	}
	else if (num_pass_points >= 2)
	{
		// Create linear equation system and solve using the pseuo inverse
		
		// Derivation:
		// (Attention: not by a mathematician. Please correct any errors.)
		//
		// Start by stating that the original coordinates (x, y) multiplied
		// with the transformation matrix should give the desired coordinates (X, Y):
		//
		// | a  b  c|   |x|   |X|
		// | d  e  f| * |y| = |Y|
		//              |1|
		//
		// The parametrization of the transformation matrix should be simplified because
		// we want to have isotropic scaling.
		// With s = scaling, r = rotation and (x, y) = offset, it looks like this:
		//
		// | s*cos(r) s*sin(r) x|    | a  b  c|
		// |-s*sin(r) s*cos(r) y| =: |-b  a  d|
		// 
		// With this, reordering the matrices to have the unknowns
		// in the second matrix results in:
		// 
		// | x  y  1  0|   |a|   |X|
		// | y -x  0  1| * |b| = |Y|
		//                 |c|
		//                 |d|
		//
		// For every pass point, two rows like this result. The complete, stacked
		// equation system is then "solved" as good as possible using the
		// pseudo inverse. Finally, s, r, x and y are recovered from a, b, c and d.
		
		Matrix mat(2*num_pass_points, 4);
		Matrix values(2*num_pass_points, 1);
		for (int i = 0; i < num_pass_points; ++i)
		{
			PassPoint* point = &at(i);
			mat.set(2*i, 0, point->src_coords.x());
			mat.set(2*i, 1, point->src_coords.y());
			mat.set(2*i, 2, 1);
			mat.set(2*i, 3, 0);
			mat.set(2*i+1, 0, point->src_coords.y());
			mat.set(2*i+1, 1, -point->src_coords.x());
			mat.set(2*i+1, 2, 0);
			mat.set(2*i+1, 3, 1);
			
			values.set(2*i, 0, point->dest_coords.x());
			values.set(2*i+1, 0, point->dest_coords.y());
		}
		
		Matrix transposed;
		mat.transpose(transposed);
		
		Matrix mat_temp, mat_temp2, pseudo_inverse;
		transposed.multiply(mat, mat_temp);
		if (!mat_temp.invert(mat_temp2))
			return false;
		mat_temp2.multiply(transposed, pseudo_inverse);
		
		// Calculate transformation parameters
		Matrix output;
		pseudo_inverse.multiply(values, output);
		
		double move_x = output.get(2, 0);
		double move_y = output.get(3, 0);
		double rotation = qAtan2((-1) * output.get(1, 0), output.get(0, 0));
		double scale = output.get(0, 0) / qCos(rotation);
		
		// Calculate transformation matrix
		double cosr = cos(rotation);
		double sinr = sin(rotation);
		
		Matrix trans_change(3, 3);
		trans_change.set(0, 0, scale * cosr);
		trans_change.set(0, 1, scale * (-sinr));
		trans_change.set(1, 0, scale * sinr);
		trans_change.set(1, 1, scale * cosr);
		trans_change.set(0, 2, move_x);
		trans_change.set(1, 2, move_y);
		
		// Transform the original transformation parameters to get the new transformation
		transform->template_scale_x *= scale;
		transform->template_scale_y *= scale;
		transform->template_rotation -= rotation;
		auto temp_x = qRound(1000.0 * (trans_change.get(0, 0) * (transform->template_x/1000.0) + trans_change.get(0, 1) * (transform->template_y/1000.0) + trans_change.get(0, 2)));
		transform->template_y = qRound(1000.0 * (trans_change.get(1, 0) * (transform->template_x/1000.0) + trans_change.get(1, 1) * (transform->template_y/1000.0) + trans_change.get(1, 2)));
		transform->template_x = temp_x;
		
		// Transform the pass points and calculate error
		for (auto& point : *this)
		{
			point.calculated_coords = MapCoordF(trans_change.get(0, 0) * point.src_coords.x() + trans_change.get(0, 1) * point.src_coords.y() + trans_change.get(0, 2),
			                                    trans_change.get(1, 0) * point.src_coords.x() + trans_change.get(1, 1) * point.src_coords.y() + trans_change.get(1, 2));
			point.error = point.calculated_coords.distanceTo(point.dest_coords);
		}
	}
	
	return true;
}
示例#28
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));
}
示例#29
0
void DrawPathTool::undoLastPoint()
{
	Q_ASSERT(editingInProgress());
	
	if (preview_path->getCoordinateCount() <= (preview_path->parts().front().isClosed() ? 3 : (path_has_preview_point ? 2 : 1)))
	{
		abortDrawing();
		return;
	}
	
	auto& part = preview_path->parts().back();
	auto last_index = part.last_index;
	auto prev_coord_index = part.prevCoordIndex(part.last_index);
	auto prev_coord = preview_path->getCoordinate(prev_coord_index);
	
	// Pre-undo preparation
	if (path_has_preview_point)
	{
		if (prev_coord.isCurveStart())
		{
			// Undo just the preview point
			path_has_preview_point = false;
		}
		else
		{
			// Remove the preview point from a straight edge, preparing for re-adding.
			Q_ASSERT(!previous_point_is_curve_point);
			
			preview_path->deleteCoordinate(last_index, false);
			last_index = prev_coord_index;
			prev_coord_index = part.prevCoordIndex(part.last_index);
			prev_coord = preview_path->getCoordinate(prev_coord_index);
			
			path_has_preview_point = !prev_coord.isCurveStart();
		}
	}
	
	if (prev_coord.isCurveStart())
	{
		// Removing last point of a curve, no re-adding of preview point.
		MapCoord prev_drag = preview_path->getCoordinate(prev_coord_index+1);
		previous_point_direction = -atan2(prev_drag.x() - prev_coord.x(), prev_coord.y() - prev_drag.y());
		previous_pos_map = MapCoordF(prev_coord);
		previous_drag_map = MapCoordF((prev_coord.x() + prev_drag.x()) / 2, (prev_coord.y() + prev_drag.y()) / 2);
		previous_point_is_curve_point = true;
		path_has_preview_point = false;
	}
	else if (!path_has_preview_point)
	{
		// Removing last point from a straight edge, no re-adding of preview point.
		previous_point_is_curve_point = false;
	}
	
	// Actually delete the last point of the edge.
	preview_path->deleteCoordinate(last_index, false);
	if (preview_path->getRawCoordinateVector().empty())
	{
		// Re-add first point.
		prev_coord.setCurveStart(false);
		preview_path->addCoordinate(prev_coord);
	}
	
	// Post-undo
	if (path_has_preview_point)
	{
		// Re-add preview point.
		preview_path->addCoordinate(MapCoord(cur_pos_map));
	}
	else if (previous_point_is_curve_point && dragging)
	{
		cur_pos = click_pos;
		cur_pos_map = click_pos_map;
	}
	
	dragging = false;
	
	updateHover();
	updatePreviewPath();
	updateAngleHelper();
	updateDirtyRect();
}
示例#30
0
bool DrawPathTool::mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget)
{
	cur_map_widget = widget;
	created_point_at_last_mouse_press = false;
	
	if (editingInProgress() &&
		((event->button() == Qt::RightButton) &&
		!drawOnRightClickEnabled()))
	{
		finishDrawing();
		return true;
	}
	else if (editingInProgress() &&
		((event->button() == Qt::RightButton && event->buttons() & Qt::LeftButton) ||
		 (event->button() == Qt::LeftButton && event->buttons() & Qt::RightButton)))
	{
		if (!previous_point_is_curve_point)
			undoLastPoint();
		if (editingInProgress())
			finishDrawing();
		return true;
	}
	else if (isDrawingButton(event->button()))
	{
		dragging = false;
		bool start_appending = false;
		if (shift_pressed)
		{
			SnappingToolHelperSnapInfo snap_info;
			MapCoord snap_coord = snap_helper->snapToObject(map_coord, widget, &snap_info);
			click_pos_map = MapCoordF(snap_coord);
			cur_pos_map = click_pos_map;
			click_pos = widget->mapToViewport(click_pos_map).toPoint();
			
			// Check for following and appending
			if (!is_helper_tool)
			{
				if (!editingInProgress())
				{
					if (snap_info.type == SnappingToolHelper::ObjectCorners &&
						(snap_info.coord_index == 0 || snap_info.coord_index == snap_info.object->asPath()->getCoordinateCount() - 1) &&
						snap_info.object->getSymbol() == editor->activeSymbol())
					{
						// Appending to another path
						start_appending = true;
						startAppending(snap_info);
					}
					
					// Setup angle helper
					if (snap_helper->snapToDirection(map_coord, widget, angle_helper.data()))
						picked_angle = true;
				}
				else if (editingInProgress() &&
						 (snap_info.type == SnappingToolHelper::ObjectCorners || snap_info.type == SnappingToolHelper::ObjectPaths) &&
						 snap_info.object->getType() == Object::Path)
				{
					// Start following another path
					startFollowing(snap_info, snap_coord);
					return true;
				}
			}
		}
		else if (!editingInProgress() && ctrl_pressed)
		{
			// Start picking direction of an existing object
			picking_angle = true;
			pickAngle(map_coord, widget);
			return true;
		}
		else
		{
			click_pos = event->pos();
			click_pos_map = map_coord;
			cur_pos_map = map_coord;
		}
		
		if (!editingInProgress())
		{
			// Start a new path
			startDrawing();
			angle_helper->setCenter(click_pos_map);
			updateSnapHelper();
			
			path_has_preview_point = false;
			previous_point_is_curve_point = false;
			appending = start_appending;
		}
		else
		{
			if (!shift_pressed)
				angle_helper->getConstrainedCursorPosMap(click_pos_map, click_pos_map);
			cur_pos_map = click_pos_map;
		}

		// Set path point
		auto coord = MapCoord { click_pos_map };
		if (draw_dash_points)
			coord.setDashPoint(true);
		
		if (preview_path->getCoordinateCount() > 0 && picked_angle)
			picked_angle = false;
		if (previous_point_is_curve_point)
		{
			// Do nothing yet, wait until the user drags or releases the mouse button
		}
		else if (path_has_preview_point)
		{
			preview_path->setCoordinate(preview_path->getCoordinateCount() - 1, coord);
			updateAngleHelper();
			created_point_at_last_mouse_press = true;
		}
		else
		{
			if (preview_path->getCoordinateCount() == 0 || !preview_path->getCoordinate(preview_path->getCoordinateCount() - 1).isPositionEqualTo(coord))
			{
				preview_path->addCoordinate(coord);
				updatePreviewPath();
				if (!start_appending)
					updateAngleHelper();
				created_point_at_last_mouse_press = true;
			}
		}
		
		path_has_preview_point = false;
		
		create_segment = true;
		updateDirtyRect();
		updateStatusText();
		return true;
	}
	
	return false;
}