Beispiel #1
0
void DrawPathTool::updateDirtyRect()
{
	QRectF rect;
	
	if (dragging)
	{
		rectIncludeSafe(rect, click_pos_map);
		rectInclude(rect, cur_pos_map);
	}
	if (editingInProgress() && previous_point_is_curve_point)
	{
		rectIncludeSafe(rect, previous_pos_map);
		rectInclude(rect, previous_drag_map);
	}
	if ((editingInProgress() && !dragging) ||
		(!editingInProgress() && !shift_pressed && ctrl_pressed) ||
		(!editingInProgress() && (picking_angle || picked_angle)))
	{
		angle_helper->includeDirtyRect(rect);
	}
	if (shift_pressed || (!editingInProgress() && ctrl_pressed))
		snap_helper->includeDirtyRect(rect);
	includePreviewRects(rect);
	
	if (is_helper_tool)
		emit(dirtyRectChanged(rect));
	else
	{
		if (rect.isValid())
			map()->setDrawingBoundingBox(rect, qMax(qMax(dragging ? 1 : 0, angle_helper->getDisplayRadius()), snap_helper->getDisplayRadius()), true);
		else
			map()->clearDrawingBoundingBox();
	}
}
Beispiel #2
0
QRectF Template::calculateTemplateBoundingBox() const
{
	// Create bounding box by calculating the positions of all corners of the transformed extent rect
	QRectF extent = getTemplateExtent();
	QRectF bbox;
	rectIncludeSafe(bbox, templateToMap(extent.topLeft()));
	rectInclude(bbox, templateToMap(extent.topRight()));
	rectInclude(bbox, templateToMap(extent.bottomRight()));
	rectInclude(bbox, templateToMap(extent.bottomLeft()));
	return bbox;
}
Beispiel #3
0
QRectF MapView::calculateViewBoundingBox(QRectF rect) const
{
	auto top_left     = mapToView(static_cast<MapCoordF>(rect.topLeft()));
	auto top_right    = mapToView(static_cast<MapCoordF>(rect.topRight()));
	auto bottom_right = mapToView(static_cast<MapCoordF>(rect.bottomRight()));
	auto bottom_left  = mapToView(static_cast<MapCoordF>(rect.bottomLeft()));
	
	rect = QRectF{ top_left, bottom_right }.normalized();
	rectInclude(rect, top_right);
	rectInclude(rect, bottom_left);
	rect.adjust(-1.0, -1.0, +1.0, +1.0);
	return rect;
}
Beispiel #4
0
QRectF MapView::calculateViewedRect(QRectF rect) const
{
	auto top_left     = viewToMapF(rect.topLeft());
	auto top_right    = viewToMapF(rect.topRight());
	auto bottom_right = viewToMapF(rect.bottomRight());
	auto bottom_left  = viewToMapF(rect.bottomLeft());
	
	rect = QRectF{ top_left, bottom_right }.normalized();
	rectInclude(rect, top_right);
	rectInclude(rect, bottom_left);
	rect.adjust(-0.001, -0.001, +0.001, +0.001);
	return rect;
}
int DrawPointTool::updateDirtyRectImpl(QRectF& rect)
{
	Q_ASSERT(preview_object);
	
	int result = 0;
	rectIncludeSafe(rect, preview_object->getExtent());
	if (isDragging() && preview_object->getSymbol()->asPoint()->isRotatable())
	{
		rectInclude(rect, constrained_click_pos_map);
		rectInclude(rect, constrained_pos_map);
		result = qMax(3, angle_helper->getDisplayRadius());
	}
	
	return result;
}
Beispiel #6
0
QRectF MapPart::calculateExtent(bool include_helper_symbols) const
{
	QRectF rect;
	
	int i = 0;
	int size = objects.size();
	while (size > i && !rect.isValid())
	{
		if ((include_helper_symbols || !objects[i]->getSymbol()->isHelperSymbol()) && !objects[i]->getSymbol()->isHidden())
		{
			objects[i]->update();
			rect = objects[i]->getExtent();
		}
		++i;
	}
	
	for (; i < size; ++i)
	{
		if ((include_helper_symbols || !objects[i]->getSymbol()->isHelperSymbol()) && !objects[i]->getSymbol()->isHidden())
		{
			objects[i]->update();
			rectInclude(rect, objects[i]->getExtent());
		}
	}
	
	return rect;
}
Beispiel #7
0
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);
	}
}
Beispiel #8
0
int EditPointTool::updateDirtyRectImpl(QRectF& rect)
{
	bool show_object_points = map()->selectedObjects().size() <= max_objects_for_handle_display;
	
	selection_extent = QRectF();
	map()->includeSelectionRect(selection_extent);
	
	rectInclude(rect, selection_extent);
	int pixel_border = show_object_points ? (scaleFactor() * 6) : 1;
	
	// Control points
	if (show_object_points)
	{
		for (Map::ObjectSelection::const_iterator it = map()->selectedObjectsBegin(), end = map()->selectedObjectsEnd(); it != end; ++it)
			(*it)->includeControlPointsRect(rect);
	}
	
	// Text selection
	if (text_editor)
		text_editor->includeDirtyRect(rect);
	
	// Box selection
	if (isDragging() && box_selection)
	{
		rectIncludeSafe(rect, click_pos_map);
		rectIncludeSafe(rect, cur_pos_map);
	}
	
	return pixel_border;
}
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);
}
Beispiel #10
0
int DrawPointTool::updateDirtyRectImpl(QRectF& rect)
{
	if (isDragging())
	{
		rectIncludeSafe(rect, click_pos_map);
		rectInclude(rect, constrained_pos_map);
		if (preview_object)
			rectInclude(rect, preview_object->getExtent());
		return qMax(1, angle_helper->getDisplayRadius());
	}
	else
	{
		if (preview_object)
		{
			rectIncludeSafe(rect, preview_object->getExtent());
			return 0;
		}
		else
			return -1;
	}
}
void LineRenderable::extentIncludeJoin(quint32 i, float half_line_width, const LineSymbol* symbol, const VirtualPath& path)
{
	auto coords = path.coords;
	if (symbol->getJoinStyle() == LineSymbol::RoundJoin)
	{
		rectInclude(extent, QPointF(coords[i].x() - half_line_width, coords[i].y() - half_line_width));
		rectInclude(extent, QPointF(coords[i].x() + half_line_width, coords[i].y() + half_line_width));
		return;
	}
	
	auto offset_length = half_line_width;
	auto params = path.calculateTangentScaling(i);
	auto& offset = params.first;
	if (symbol->getJoinStyle() == LineSymbol::MiterJoin)
	{
		offset_length *= qMin(params.second, 2.0 * LineSymbol::miterLimit());
	}
	offset.setLength(offset_length);
	rectInclude(extent, coords[i] + offset);
	rectInclude(extent, coords[i] - offset);
}
Beispiel #12
0
int CutoutTool::updateDirtyRectImpl(QRectF& rect)
{
	rectInclude(rect, cutout_object->getExtent());
	
	map()->includeSelectionRect(rect);
	
	// Box selection
	if (isDragging())
	{
		rectIncludeSafe(rect, click_pos_map);
		rectIncludeSafe(rect, cur_pos_map);
	}
	
	return 0;
}
Beispiel #13
0
void Template::drawOntoTemplate(MapCoordF* coords, int num_coords, QColor color, float width, QRectF map_bbox)
{
	Q_ASSERT(canBeDrawnOnto());
	Q_ASSERT(num_coords > 1);
	
	if (!map_bbox.isValid())
	{
		map_bbox = QRectF(coords[0].x(), coords[0].y(), 0, 0);
		for (int i = 1; i < num_coords; ++i)
			rectInclude(map_bbox, coords[i]);
	}
	float radius = qMin(getTemplateScaleX(), getTemplateScaleY()) * qMax((width+1) / 2, 1.0f);
	QRectF radius_bbox = QRectF(map_bbox.left() - radius, map_bbox.top() - radius,
								map_bbox.width() + 2*radius, map_bbox.height() + 2*radius);
	
	drawOntoTemplateImpl(coords, num_coords, color, width);
	map->setTemplateAreaDirty(this, radius_bbox, 0);
	
	setHasUnsavedChanges(true);
}
AreaRenderable::AreaRenderable(const AreaSymbol* symbol, const PathPartVector& path_parts)
 : Renderable(symbol->getColor())
{
	if (!path_parts.empty())
	{
		auto part = begin(path_parts);
		if (part->size() > 2)
		{
			extent = part->path_coords.calculateExtent();
			addSubpath(*part);
			
			auto last = end(path_parts);
			for (++part; part != last; ++part)
			{
				rectInclude(extent, part->path_coords.calculateExtent());
				addSubpath(*part);
			}
		}
	}
	Q_ASSERT(extent.right() < 999999);	// assert if bogus values are returned
}
void LineRenderable::extentIncludeCap(quint32 i, float half_line_width, bool end_cap, const LineSymbol* symbol, const VirtualPath& path)
{
	auto coords = path.coords;
	if (symbol->getCapStyle() == LineSymbol::RoundCap)
	{
		rectInclude(extent, QPointF(coords[i].x() - half_line_width, coords[i].y() - half_line_width));
		rectInclude(extent, QPointF(coords[i].x() + half_line_width, coords[i].y() + half_line_width));
		return;
	}
	
	auto right = path.calculateTangent(i).perpRight();
	right.normalize();
	rectInclude(extent, coords[i] + half_line_width * right);
	rectInclude(extent, coords[i] - half_line_width * right);
	
	if (symbol->getCapStyle() == LineSymbol::SquareCap)
	{
		auto back = right.perpRight();
		
		float sign = end_cap ? -1 : 1;
		rectInclude(extent, coords[i] + half_line_width * (sign*back - right));
		rectInclude(extent, coords[i] + half_line_width * (sign*back + right));
	}
}
LineRenderable::LineRenderable(const LineSymbol* symbol, const VirtualPath& virtual_path, bool closed)
 : Renderable(symbol->getColor())
 , line_width(0.001f * symbol->getLineWidth())
{
	Q_ASSERT(virtual_path.size() >= 2);
	
	float half_line_width = (color_priority < 0) ? 0.0f : 0.5f * line_width;
	
	switch (symbol->getCapStyle())
	{
		case LineSymbol::FlatCap:		cap_style = Qt::FlatCap;	break;
		case LineSymbol::RoundCap:		cap_style = Qt::RoundCap;	break;
		case LineSymbol::SquareCap:		cap_style = Qt::SquareCap;	break;
		case LineSymbol::PointedCap:	cap_style = Qt::FlatCap;	break;
	}
	switch (symbol->getJoinStyle())
	{
		case LineSymbol::BevelJoin:		join_style = Qt::BevelJoin;	break;
		case LineSymbol::MiterJoin:		join_style = Qt::MiterJoin;	break;
		case LineSymbol::RoundJoin:		join_style = Qt::RoundJoin;	break;
	}
	
	auto& flags  = virtual_path.coords.flags;
	auto& coords = virtual_path.coords;
	
	bool has_curve = false;
	bool hole = false;
	bool gap = false;
	QPainterPath first_subpath;
	
	auto i = virtual_path.first_index;
	path.moveTo(coords[i]);
	extent = QRectF(coords[i].x(), coords[i].y(), 0.0001f, 0.0001f);
	extentIncludeCap(i, half_line_width, false, symbol, virtual_path);
	
	for (++i; i <= virtual_path.last_index; ++i)
	{
		if (gap)
		{
			if (flags[i].isHolePoint())
			{
				gap = false;
				hole = true;
			}
			else if (flags[i].isGapPoint())
			{
				gap = false;
				if (first_subpath.isEmpty() && closed)
				{
					first_subpath = path;
					path = QPainterPath();
				}
				path.moveTo(coords[i]);
				extentIncludeCap(i, half_line_width, false, symbol, virtual_path);
			}
			continue;
		}
		
		if (hole)
		{
			Q_ASSERT(!flags[i].isHolePoint() && "Two hole points in a row!");
			if (first_subpath.isEmpty() && closed)
			{
				first_subpath = path;
				path = QPainterPath();
			}
			path.moveTo(coords[i]);
			extentIncludeCap(i, half_line_width, false, symbol, virtual_path);
			hole = false;
			continue;
		}
		
		if (flags[i-1].isCurveStart())
		{
			Q_ASSERT(i < virtual_path.last_index-1);
			has_curve = true;
			path.cubicTo(coords[i], coords[i+1], coords[i+2]);
			i += 2;
		}
		else
			path.lineTo(coords[i]);
		
		if (flags[i].isHolePoint())
			hole = true;
		else if (flags[i].isGapPoint())
			gap = true;
		
		if ((i < virtual_path.last_index && !hole && !gap) || (i == virtual_path.last_index && closed))
			extentIncludeJoin(i, half_line_width, symbol, virtual_path);
		else
			extentIncludeCap(i, half_line_width, true, symbol, virtual_path);
	}
	
	if (closed)
	{
		if (first_subpath.isEmpty())
			path.closeSubpath();
		else
			path.connectPath(first_subpath);
	}
	
	// If we do not have the path coords, but there was a curve, calculate path coords.
	if (has_curve)
	{
		//  This happens for point symbols with curved lines in them.
		const auto& path_coords = virtual_path.path_coords;
		Q_ASSERT(path_coords.front().param == 0.0);
		Q_ASSERT(path_coords.back().param == 0.0);
		for (auto i = path_coords.size()-1; i > 0; --i)
		{
			if (path_coords[i].param != 0.0)
			{
				const auto& pos = path_coords[i].pos;
				auto to_coord   = pos - path_coords[i-1].pos;
				auto to_next    = path_coords[i+1].pos - pos;
				to_coord.normalize();
				to_next.normalize();
				auto right = (to_coord + to_next).perpRight();
				right.setLength(half_line_width);
				
				rectInclude(extent, pos + right);
				rectInclude(extent, pos - right);
			}
		}
	}
	Q_ASSERT(extent.right() < 999999);	// assert if bogus values are returned
}
TextRenderable::TextRenderable(const TextSymbol* symbol, const TextObject* text_object, const MapColor* color, double anchor_x, double anchor_y, bool framing_line)
 : Renderable(color)
{
	const QFont& font(symbol->getQFont());
	const QFontMetricsF& metrics(symbol->getFontMetrics());
	this->anchor_x = anchor_x;
	this->anchor_y = anchor_y;
	this->rotation = text_object->getRotation();
	scale_factor = symbol->getFontSize() / TextSymbol::internal_point_size;
	this->framing_line = framing_line;
	framing_line_width = framing_line ? (2 * 0.001 * symbol->getFramingLineHalfWidth() / scale_factor) : 0;
	
	path.setFillRule(Qt::WindingFill);	// Otherwise, when text and an underline intersect, holes appear
	
	int num_lines = text_object->getNumLines();
	for (int i=0; i < num_lines; i++)
	{
		const TextObjectLineInfo* line_info = text_object->getLineInfo(i);
		
		double line_y = line_info->line_y;
		
		double underline_x0 = 0.0;
		double underline_y0 = line_info->line_y + metrics.underlinePos();
		double underline_y1 = underline_y0 + metrics.lineWidth();
		
		int num_parts = line_info->part_infos.size();
		for (int j=0; j < num_parts; j++)
		{
			const TextObjectPartInfo& part(line_info->part_infos.at(j));
			if (font.underline())
			{
				if (j > 0)
				{
					// draw underline for gap between parts as rectangle
					// TODO: watch out for inconsistency between text and gap underline
					path.moveTo(underline_x0, underline_y0);
					path.lineTo(part.part_x,  underline_y0);
					path.lineTo(part.part_x,  underline_y1);
					path.lineTo(underline_x0, underline_y1);
					path.closeSubpath();
				}
				underline_x0 = part.part_x;
			}
			path.addText(part.part_x, line_y, font, part.part_text);
		}
	}
	
	extent = path.controlPointRect();
	extent = QRectF(scale_factor * (extent.left() - 0.5f * framing_line_width),
					scale_factor * (extent.top() - 0.5f * framing_line_width),
					scale_factor * (extent.width() + framing_line_width),
					scale_factor * (extent.height() + framing_line_width));
	if (rotation != 0)
	{
		float rcos = cos(-rotation);
		float rsin = sin(-rotation);
		
		std::vector<QPointF> extent_corners;
		extent_corners.push_back(extent.topLeft());
		extent_corners.push_back(extent.topRight());
		extent_corners.push_back(extent.bottomRight());
		extent_corners.push_back(extent.bottomLeft());
		
		for (int i = 0; i < 4; ++i)
		{
			auto x = extent_corners[i].x() * rcos - extent_corners[i].y() * rsin;
			auto y = extent_corners[i].y() * rcos + extent_corners[i].x() * rsin;
			
			if (i == 0)
				extent = QRectF(x, y, 0, 0);
			else
				rectInclude(extent, QPointF(x, y));
		}
	}
	extent = QRectF(extent.left() + anchor_x, extent.top() + anchor_y, extent.width(), extent.height());
	
	Q_ASSERT(extent.right() < 999999);	// assert if bogus values are returned
}