示例#1
0
void DrawPathTool::updateDrawHover()
{
	if (!shift_pressed)
		angle_helper->getConstrainedCursorPosMap(cur_pos_map, constrained_pos_map);
	if (!previous_point_is_curve_point && !left_mouse_down && editingInProgress())
	{
		// Show a line to the cursor position as preview
		hidePreviewPoints();
		
		if (!path_has_preview_point)
		{
			preview_path->addCoordinate(MapCoord(constrained_pos_map));
			path_has_preview_point = true;
		}
		preview_path->setCoordinate(preview_path->getCoordinateCount() - 1, MapCoord(constrained_pos_map));
		
		updatePreviewPath();
		updateDirtyRect();	// TODO: Possible optimization: mark only the last segment as dirty
	}
	else if (previous_point_is_curve_point && !left_mouse_down && editingInProgress())
	{
		setPreviewPointsPosition(constrained_pos_map, 1);
		updateDirtyRect();
	}
}
示例#2
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);
	}
}
示例#3
0
void DrawPathTool::createPreviewCurve(MapCoord position, float direction)
{
	if (!path_has_preview_point)
	{
		int last = preview_path->getCoordinateCount() - 1;
		(preview_path->getCoordinate(last)).setCurveStart(true);
		
		preview_path->addCoordinate(MapCoord(0, 0));
		preview_path->addCoordinate(MapCoord(0, 0));
		if (draw_dash_points)
			position.setDashPoint(true);
		position.setCurveStart(false);
		preview_path->addCoordinate(position);
		
		path_has_preview_point = true;
	}
	
	// Adjust the preview curve
	int last = preview_path->getCoordinateCount() - 1;
	MapCoord previous_point = preview_path->getCoordinate(last - 3);
	MapCoord last_point = preview_path->getCoordinate(last);
	
	double bezier_handle_distance = BEZIER_HANDLE_DISTANCE * previous_point.distanceTo(last_point);
	
	preview_path->setCoordinate(last - 2, MapCoord(previous_point.x() - bezier_handle_distance * sin(previous_point_direction),
	                                               previous_point.y() - bezier_handle_distance * cos(previous_point_direction)));
	preview_path->setCoordinate(last - 1, MapCoord(last_point.x() + bezier_handle_distance * sin(direction),
	                                               last_point.y() + bezier_handle_distance * cos(direction)));
	updatePreviewPath();	
}
示例#4
0
TestMap::TestMap()
{
	MapCoord coord;
	
	map = new Map();
	
	MapColor* black = new MapColor();
	black->setCmyk(MapColorCmyk(0.0f, 0.0f, 0.0f, 1.0f));
	black->setOpacity(1.0f);
	black->setName("black");
	map->addColor(black, 0);
	
	line_symbol = new LineSymbol();
	line_symbol->setLineWidth(1);
	line_symbol->setColor(black);
	map->addSymbol(line_symbol, 0);
	
	line_object = new PathObject(line_symbol);
	line_object->addCoordinate(MapCoord(10, 10));
	coord = MapCoord(20, 10);
	coord.setCurveStart(true);
	line_object->addCoordinate(coord);
	line_object->addCoordinate(MapCoord(20, 20));
	line_object->addCoordinate(MapCoord(30, 20));
	line_object->addCoordinate(MapCoord(30, 10));
	map->addObject(line_object);
	
	// TODO: fill map with more content as needed
}
示例#5
0
void FileFormatTest::mapCoordtoString()
{
	QCOMPARE(MapCoord().toString(), QString("0 0;"));
	
	// Verify toString for native coordinates at the numeric limits.
	auto native_x = MapCoord().nativeX();
	using bounds = std::numeric_limits<decltype(native_x)>;
	static_assert(sizeof(decltype(native_x)) == sizeof(qint32), "This test assumes qint32 native coordinates");
	QCOMPARE(MapCoord::fromNative(bounds::max(), bounds::max(), 8).toString(), QString("2147483647 2147483647 8;"));
	QCOMPARE(MapCoord::fromNative(bounds::min(), bounds::min(), 1).toString(), QString("-2147483648 -2147483648 1;"));
}
示例#6
0
TimedRestGather::TimedRestGather(uint16 x, uint16 y)
    : TimedPartyMove(50)
{
    MapCoord center = MapCoord(x, y);
    init(&center, 0, 0); // set dest to campfire location
    Game::get_game()->get_map_window()->updateAmbience();
    check_campfire();
}
示例#7
0
void TextObject::setBox(qint32 mid_x, qint32 mid_y, double width, double height)
{
	coords.resize(2);
	coords[0].setNativeX(mid_x);
	coords[0].setNativeY(mid_y);
	coords[1] = MapCoord(width, height);
	setOutputDirty();
}
void PassPoint::save(QXmlStreamWriter& xml) const
{
	XmlElementWriter passpoint{xml, QLatin1String("passpoint")};
	passpoint.writeAttribute(QLatin1String("error"), error);
	
	{
		XmlElementWriter element{xml, QLatin1String("source")};
		MapCoord(src_coords).save(xml);
	}
	{
		XmlElementWriter element{xml, QLatin1String("destination")};
		MapCoord(dest_coords).save(xml);
	}
	{
		XmlElementWriter element{xml, QLatin1String("calculated")};
		MapCoord(calculated_coords).save(xml);
	}
}
示例#9
0
TextObject::TextObject(const Symbol* symbol)
 : Object(Object::Text, symbol)
 , h_align(AlignHCenter)
 , v_align(AlignVCenter)
 , rotation(0.0f)
{
	Q_ASSERT(!symbol || (symbol->getType() == Symbol::Text));
	coords.reserve(2);
	coords.push_back(MapCoord(0, 0));
}
示例#10
0
void DrawFreehandTool::updatePath()
{
	float length_threshold_sq = 0.06f*0.06f; // minimum point distance in mm
	
	if (!dragging)
	{
		preview_path->clearCoordinates();
		preview_path->addCoordinate(MapCoord(cur_pos_map));
	}
	else
	{
		if (last_pos_map.distanceSquaredTo(cur_pos_map) < length_threshold_sq)
			return;
		preview_path->addCoordinate(MapCoord(cur_pos_map));
	}
	last_pos_map = cur_pos_map;
	
	updatePreviewPath();
	setDirtyRect();
}
示例#11
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);
}
示例#12
0
MapCoord GameWorldBase::CalcDistanceAroundBorderY(const MapCoord y1, const MapCoord y2) const
{
	int diff = int(y2) - int(y1);
	
	if(diff >= 0)
		// Differenz positiv --> nicht über den Rand, d.h. normale Distanz
		return MapCoord(diff);
	else
	{
		// Ansonten Stück bis zum Rand und das Stück vom Rand bis zu Punkt 2
		return (width-y1) + y2;
	}
}
示例#13
0
bool GeoreferencingTool::mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget*)
{
	bool handled = false;
	switch (event->button())
	{
	case Qt::LeftButton:
		dialog->setMapRefPoint(MapCoord(map_coord));
		// fall through
	case Qt::RightButton:
		QTimer::singleShot(0, dialog, SIGNAL(exec()));
		handled = true;
		break;
	default:
		; // nothing
	}
	
	return handled;
}
示例#14
0
void RotatePatternTool::dragMove()
{
	const auto rotation = -M_PI / 2 - (constrained_pos_map - click_pos_map).angle();
	
	for (auto object : editedObjects())
	{
		/// \todo Refactor, provide a unified interface for rotation in Object
		if (object->getType() == Object::Point)
		{
			if (object->getSymbol()->asPoint()->isRotatable())
				object->asPoint()->setRotation(rotation);
		}
		else if (object->getType() == Object::Path)
		{
			object->asPath()->setPatternOrigin(MapCoord(click_pos_map));
			object->asPath()->setPatternRotation(rotation);
		}
	}
	
	updatePreviewObjects();
	updateStatusText();
}
示例#15
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();
}
示例#16
0
bool TemplateTrack::import(QWidget* dialog_parent)
{
	if (track.getNumWaypoints() == 0 && track.getNumSegments() == 0)
	{
		QMessageBox::critical(dialog_parent, tr("Error"), tr("The path is empty, there is nothing to import!"));
		return false;
	}
	
	const Track::ElementTags& tags = track.tags();
	DeleteObjectsUndoStep* undo_step = new DeleteObjectsUndoStep(map);
	MapPart* part = map->getCurrentPart();
	std::vector< Object* > result;
	
	map->clearObjectSelection(false);
	
	if (track.getNumWaypoints() > 0)
	{
		int res = QMessageBox::question(dialog_parent, tr("Question"), tr("Should the waypoints be imported as a line going through all points?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
		if (res == QMessageBox::No)
		{
			for (int i = 0; i < track.getNumWaypoints(); i++)
				result.push_back(importWaypoint(templateToMap(track.getWaypoint(i).map_coord), track.getWaypointName(i)));
		}
		else
		{
			PathObject* path = importPathStart();
			for (int i = 0; i < track.getNumWaypoints(); i++)
				path->addCoordinate(MapCoord(templateToMap(track.getWaypoint(i).map_coord)));
			importPathEnd(path);
			path->setTag(QStringLiteral("name"), QString{});
			result.push_back(path);
		}
	}
	
	int skipped_paths = 0;
	for (int i = 0; i < track.getNumSegments(); i++)
	{
		const int segment_size = track.getSegmentPointCount(i);
		if (segment_size == 0)
		{
			++skipped_paths;
			continue; // Don't create path without objects.
		}
		
		PathObject* path = importPathStart();
		QString name = track.getSegmentName(i);
		if (!tags[name].isEmpty())
		{
			path->setTags(tags[name]);
		}
		else
		{
			path->setTag(QStringLiteral("name"), name);
		}
		
		for (int j = 0; j < segment_size; j++)
		{
			const TrackPoint& track_point = track.getSegmentPoint(i, j);
			auto coord = MapCoord { templateToMap(track_point.map_coord) };
			if (track_point.is_curve_start && j < segment_size - 3)
				coord.setCurveStart(true);
			path->addCoordinate(coord);
		}
		if (track.getSegmentPoint(i, 0).gps_coord == track.getSegmentPoint(i, segment_size-1).gps_coord)
		{
			path->closeAllParts();
		}
		importPathEnd(path);
		result.push_back(path);
	}
	
	for (int i = 0; i < (int)result.size(); ++i) // keep as separate loop to get the correct (final) indices
		undo_step->addObject(part->findObjectIndex(result[i]));
	
	map->setObjectsDirty();
	map->push(undo_step);
	
	map->emitSelectionChanged();
	map->emitSelectionEdited();		// TODO: is this necessary here?
	
	if (skipped_paths)
	{
		QMessageBox::information(
		  dialog_parent,
		  tr("Import problems"),
		  tr("%n path object(s) could not be imported (reason: missing coordinates).", "", skipped_paths) );
	}
	
	return true;
}
示例#17
0
bool DrawPathTool::mouseReleaseEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget)
{
	if (!isDrawingButton(event->button()))
		return false;

	left_mouse_down = false;
	
	if (picking_angle)
	{
		picking_angle = false;
		picked_angle = pickAngle(map_coord, widget);
		return true;
	}
	else if (!editingInProgress())
	{
		return false;
	}
	
	if (following)
	{
		finishFollowing();
		if ((event->button() == Qt::RightButton) && drawOnRightClickEnabled())
			finishDrawing();
		return true;
	}
	
	if (!create_segment)
		return true;
	
	if (previous_point_is_curve_point && !dragging && !create_spline_corner)
	{
		// The new point has not been added yet
		MapCoord coord;
		if (shift_pressed)
		{
			coord = snap_helper->snapToObject(map_coord, widget);
		}
		else if (angle_helper->isActive())
		{
			QPointF constrained_pos;
			angle_helper->getConstrainedCursorPositions(map_coord, constrained_pos_map, constrained_pos, widget);
			coord = MapCoord(constrained_pos_map);
		}
		else
		{
			coord = MapCoord(map_coord);
		}
		
		if (draw_dash_points)
			coord.setDashPoint(true);
		preview_path->addCoordinate(coord);
		updatePreviewPath();
		updateDirtyRect();
	}
	
	previous_point_is_curve_point = dragging;
	if (previous_point_is_curve_point)
	{
		QPointF constrained_pos;
		angle_helper->getConstrainedCursorPositions(map_coord, constrained_pos_map, constrained_pos, widget);
		
		previous_pos_map = click_pos_map;
		previous_drag_map = constrained_pos_map;
		previous_point_direction = calculateRotation(constrained_pos.toPoint(), constrained_pos_map);
	}
	
	updateAngleHelper();
	
	create_spline_corner = false;
	path_has_preview_point = false;
	dragging = false;
	
	if ((event->button() == Qt::RightButton) && drawOnRightClickEnabled())
		finishDrawing();
	return true;
}
示例#18
0
bool DrawPathTool::mouseMoveEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget)
{
	cur_pos = event->pos();
	cur_pos_map = map_coord;
	
	if (!containsDrawingButtons(event->buttons()))
	{
		updateHover();
	}
	else if (!editingInProgress())
	{
		left_mouse_down = true;
		if (picking_angle)
			pickAngle(map_coord, widget);
		else
			return false;
	}
	else if (following)
	{
		updateFollowing();
	}
	else
	{
		bool drag_distance_reached = (event->pos() - click_pos).manhattanLength() >= Settings::getInstance().getStartDragDistancePx();
		if (dragging && !drag_distance_reached)
		{
			if (create_spline_corner)
			{
				create_spline_corner = false;
			}
			else if (path_has_preview_point)
			{
				undoLastPoint();
			}
		}
		else if (drag_distance_reached)
		{
			// Giving a direction by dragging
			dragging = true;
			create_spline_corner = false;
			create_segment = true;
			
			if (previous_point_is_curve_point)
				angle_helper->setCenter(click_pos_map);
			
			QPointF constrained_pos;
			angle_helper->getConstrainedCursorPositions(map_coord, constrained_pos_map, constrained_pos, widget);
			
			if (previous_point_is_curve_point)
			{
				hidePreviewPoints();
				float drag_direction = calculateRotation(constrained_pos.toPoint(), constrained_pos_map);
				
				// Add a new node or convert the last node into a corner?
				if ((widget->mapToViewport(previous_pos_map) - click_pos).manhattanLength() >= Settings::getInstance().getStartDragDistancePx())
					createPreviewCurve(MapCoord(click_pos_map), drag_direction);
				else
				{
					create_spline_corner = true;
					// This hides the old direction indicator
					previous_drag_map = previous_pos_map;
				}
			}
			
			updateDirtyRect();
		}
	}
	
	return true;
}
示例#19
0
文件: map_view.cpp 项目: FEK10/mapper
MapCoord MapView::viewToMap(double x, double y) const
{
	return MapCoord(view_to_map.m11() * x + view_to_map.m12() * y + view_to_map.m13(),
	                view_to_map.m21() * x + view_to_map.m22() * y + view_to_map.m23());
}
示例#20
0
void DrawRectangleTool::updateRectangle()
{
	double angle = angle_helper->getConstrainedCursorPosMap(constrained_pos_map, constrained_pos_map);
	
	if (angles.size() == 1)
	{
		// Update vectors and angles
		forward_vector = constrained_pos_map - MapCoordF(preview_path->getCoordinate(0));
		forward_vector.normalize();
		
		angles.back() = -forward_vector.angle();
		
		// Update rectangle
		MapCoord coord = MapCoord(constrained_pos_map);
		coord.setDashPoint(draw_dash_points);
		preview_path->setCoordinate(1, coord);
	}
	else
	{
		// Update vectors and angles
		forward_vector = MapCoordF::fromPolar(1.0, -angle);
		
		angles.back() = angle;
		
		// Update rectangle
		auto cur_point_index = angles.size();
		deleteClosePoint();
		
		float forward_dist = MapCoordF::dotProduct(forward_vector, constrained_pos_map - MapCoordF(preview_path->getCoordinate(cur_point_index - 1)));
		MapCoord coord = preview_path->getCoordinate(cur_point_index - 1) + MapCoord(forward_dist * forward_vector);
		
		snapped_to_line = false;
		float best_snap_distance_sq = std::numeric_limits<float>::max();
		if (ctrl_pressed && angles.size() > 2)
		{
			// Try to snap to existing lines
			MapCoord original_coord = coord;
			for (int i = 0; i < (int)angles.size() - 1; ++i)
			{
				MapCoordF direction(100, 0);
				direction.rotate(-angles[i]);
				
				int num_steps;
				double angle_step, angle_offset = 0;
				if (i == 0 || qAbs(qAbs(fmod_pos(angles[i], M_PI) - fmod_pos(angles[i-1], M_PI)) - (M_PI / 2)) < 0.1)
				{
					num_steps = 2;
					angle_step = M_PI/2;
				}
				else
				{
					num_steps = 4;
					angle_step = M_PI/4;
				}
				
				for (int k = 0; k < num_steps; ++k)
				{
					if (qAbs(fmod_pos(angle, M_PI) - fmod_pos(angles[i] - (angle_offset + k * angle_step), M_PI)) < 0.1)
						continue;
					
					MapCoordF rotated_direction = direction;
					rotated_direction.rotate(angle_offset + k * angle_step);
					
					QLineF a(QPointF(preview_path->getCoordinate(cur_point_index - 1)), MapCoordF(preview_path->getCoordinate(cur_point_index - 1)) + forward_vector);
					QLineF b(QPointF(preview_path->getCoordinate(i)), MapCoordF(preview_path->getCoordinate(i)) + rotated_direction);
					QPointF intersection_point;
					QLineF::IntersectType intersection_type = a.intersect(b, &intersection_point);
					if (intersection_type == QLineF::NoIntersection)
						continue;
					
					float snap_distance_sq = original_coord.distanceSquaredTo(MapCoord(intersection_point));
					if (snap_distance_sq > best_snap_distance_sq)
						continue;
					
					best_snap_distance_sq = snap_distance_sq;
					coord = MapCoord(intersection_point);
					snapped_to_line_a = coord;
					snapped_to_line_b = coord + MapCoord(rotated_direction);
					snapped_to_line = true;
				}
			}
		}
		
		coord.setDashPoint(draw_dash_points);
		preview_path->setCoordinate(cur_point_index, coord);
		
		auto close_vector = calculateClosingVector();
		float close_dist = MapCoordF::dotProduct(close_vector, MapCoordF(preview_path->getCoordinate(0) - preview_path->getCoordinate(cur_point_index)));
		coord = preview_path->getCoordinate(cur_point_index) + MapCoord(close_dist * close_vector);
		coord.setDashPoint(draw_dash_points);
		preview_path->setCoordinate(cur_point_index + 1, coord);
		
		preview_path->parts().front().setClosed(true, true);
	}
	
	updatePreviewPath();
	updateDirtyRect();
}
示例#21
0
void NativeFileImport::import(bool load_symbols_only)
{
    addWarning(Importer::tr("This file uses an obsolete format. "
                            "Support for this format is to be removed from this program soon. "
                            "To be able to open the file in the future, save it again."));

    MapCoord::boundsOffset().reset(true);

    char buffer[4];
    stream->read(buffer, 4); // read the magic

    int version;
    stream->read((char*)&version, sizeof(int));
    if (version < 0)
    {
        addWarning(Importer::tr("Invalid file format version."));
    }
    else if (version < NativeFileFormat::least_supported_file_format_version)
    {
        throw FileFormatException(Importer::tr("Unsupported old file format version. Please use an older program version to load and update the file."));
    }
    else if (version > NativeFileFormat::current_file_format_version)
    {
        throw FileFormatException(Importer::tr("Unsupported new file format version. Some map features will not be loaded or saved by this version of the program. Consider updating."));
    }

    if (version <= 16)
	{
		Georeferencing georef;
		stream->read((char*)&georef.scale_denominator, sizeof(int));
		
		if (version >= 15)
			loadString(stream, map->map_notes);
		
		bool gps_projection_params_set; // obsolete
		stream->read((char*)&gps_projection_params_set, sizeof(bool));
		GPSProjectionParameters gps_projection_parameters; // obsolete
		stream->read((char*)&gps_projection_parameters, sizeof(GPSProjectionParameters));
		if (gps_projection_params_set)
		{
			LatLon ref_point = LatLon::fromRadiant(gps_projection_parameters.center_latitude, gps_projection_parameters.center_longitude);
			georef.setGeographicRefPoint(ref_point);
		}
		*map->georeferencing = georef;
	}
	else if (version >= 17)
	{
		loadString(stream, map->map_notes);
		
		Georeferencing georef;
		stream->read((char*)&georef.scale_denominator, sizeof(int));
		double value;
		if (version >= 18)
		{
			stream->read((char*)&value, sizeof(double));
			georef.declination = Georeferencing::roundDeclination(value);
		}
		stream->read((char*)&value, sizeof(double));
		georef.grivation = Georeferencing::roundDeclination(value);
		georef.grivation_error = value - georef.grivation;
		
		double x,y;
		stream->read((char*)&x, sizeof(double));
		stream->read((char*)&y, sizeof(double));
		georef.map_ref_point = MapCoord(x,y);
		stream->read((char*)&x, sizeof(double));
		stream->read((char*)&y, sizeof(double));
		georef.projected_ref_point = QPointF(x,y);
		loadString(stream, georef.projected_crs_id);
		loadString(stream, georef.projected_crs_spec);
		stream->read((char*)&y, sizeof(double));
		stream->read((char*)&x, sizeof(double));
		georef.geographic_ref_point = LatLon::fromRadiant(y, x); 
		QString geographic_crs_id, geographic_crs_spec;
		loadString(stream, geographic_crs_id);   // reserved for geographic crs id
		loadString(stream, geographic_crs_spec); // reserved for full geographic crs specification
		if (geographic_crs_spec != Georeferencing::geographic_crs_spec)
		{
			addWarning(
			  Importer::tr("The geographic coordinate reference system of the map was \"%1\". This CRS is not supported. Using \"%2\".").
			  arg(geographic_crs_spec).
			  arg(Georeferencing::geographic_crs_spec)
			);
		}
		if (version <= 17)
			georef.initDeclination();
		// Correctly set georeferencing state
		georef.setProjectedCRS(georef.projected_crs_id, georef.projected_crs_spec);
		*map->georeferencing = georef;
	}
	
	if (version >= 24)
		map->setGrid(MapGrid().load(stream, version));
	
	map->renderable_options = Symbol::RenderNormal;
	if (version >= 25)
	{
		bool area_hatching_enabled, baseline_view_enabled;
		stream->read((char*)&area_hatching_enabled, sizeof(bool));
		stream->read((char*)&baseline_view_enabled, sizeof(bool));
		if (area_hatching_enabled)
			map->renderable_options |= Symbol::RenderAreasHatched;
		if (baseline_view_enabled)
			map->renderable_options |= Symbol::RenderBaselines;
	}
	
	if (version >= 6)
	{
		bool print_params_set;
		stream->read((char*)&print_params_set, sizeof(bool));
		if (print_params_set)
		{
			MapPrinterConfig printer_config(*map);
			stream->read((char*)&printer_config.page_format.orientation, sizeof(int));
			stream->read((char*)&printer_config.page_format.paper_size, sizeof(int));
			
			float resolution;
			stream->read((char*)&resolution, sizeof(float));
			printer_config.options.resolution = qRound(resolution);
			stream->read((char*)&printer_config.options.show_templates, sizeof(bool));
			if (version >= 24)
				stream->read((char*)&printer_config.options.show_grid, sizeof(bool));
			else
				printer_config.options.show_grid = false;
			
			stream->read((char*)&printer_config.center_print_area, sizeof(bool));
			
			float print_area_left, print_area_top, print_area_width, print_area_height;
			stream->read((char*)&print_area_left, sizeof(float));
			stream->read((char*)&print_area_top, sizeof(float));
			stream->read((char*)&print_area_width, sizeof(float));
			stream->read((char*)&print_area_height, sizeof(float));
			printer_config.print_area = QRectF(print_area_left, print_area_top, print_area_width, print_area_height);
			
			if (version >= 26)
			{
				bool print_different_scale_enabled;
				stream->read((char*)&print_different_scale_enabled, sizeof(bool));
				stream->read((char*)&printer_config.options.scale, sizeof(int));
				if (!print_different_scale_enabled)
					printer_config.options.scale = map->getScaleDenominator();
			}
			map->setPrinterConfig(printer_config);
		}
	}
	
    if (version >= 16)
	{
		stream->read((char*)&map->image_template_use_meters_per_pixel, sizeof(bool));
		stream->read((char*)&map->image_template_meters_per_pixel, sizeof(double));
		stream->read((char*)&map->image_template_dpi, sizeof(double));
		stream->read((char*)&map->image_template_scale, sizeof(double));
	}

    // Load colors
    int num_colors;
    stream->read((char*)&num_colors, sizeof(int));
    map->color_set->colors.resize(num_colors);

    for (int i = 0; i < num_colors; ++i)
    {
        int priority;
        stream->read((char*)&priority, sizeof(int));
        MapColor* color = new MapColor(priority);

        MapColorCmyk cmyk;
        stream->read((char*)&cmyk.c, sizeof(float));
        stream->read((char*)&cmyk.m, sizeof(float));
        stream->read((char*)&cmyk.y, sizeof(float));
        stream->read((char*)&cmyk.k, sizeof(float));
        color->setCmyk(cmyk);
        float opacity;
        stream->read((char*)&opacity, sizeof(float));
        color->setOpacity(opacity);

        QString name;
        loadString(stream, name);
        color->setName(name);

        map->color_set->colors[i] = color;
    }

    // Load symbols
    int num_symbols;
    stream->read((char*)&num_symbols, sizeof(int));
    map->symbols.resize(num_symbols);

    for (int i = 0; i < num_symbols; ++i)
    {
        QScopedValueRollback<MapCoord::BoundsOffset> offset { MapCoord::boundsOffset() };
        MapCoord::boundsOffset().reset(false);

        int symbol_type;
        stream->read((char*)&symbol_type, sizeof(int));

        Symbol* symbol = Symbol::getSymbolForType(static_cast<Symbol::Type>(symbol_type));
        if (!symbol)
        {
            throw FileFormatException(Importer::tr("Error while loading a symbol with type %2.").arg(symbol_type));
        }

        if (!symbol->load(stream, version, map))
        {
            throw FileFormatException(Importer::tr("Error while loading a symbol."));
        }
        map->symbols[i] = symbol;
    }

    if (!load_symbols_only)
	{
		// Load templates
		stream->read((char*)&map->first_front_template, sizeof(int));

		int num_templates;
		stream->read((char*)&num_templates, sizeof(int));
		map->templates.resize(num_templates);

		for (int i = 0; i < num_templates; ++i)
		{
			QString path;
			loadString(stream, path);
			auto temp = Template::templateForFile(path, map);
			if (!temp)
				temp.reset(new TemplateImage(path, map)); // fallback
			
			if (version >= 27)
			{
				loadString(stream, path);
				temp->setTemplateRelativePath(path);
			}
			
			temp->loadTemplateConfiguration(stream, version);

			map->templates[i] = temp.release();
		}
		
		if (version >= 28)
		{
			int num_closed_templates;
			stream->read((char*)&num_closed_templates, sizeof(int));
			map->closed_templates.resize(num_closed_templates);
			
			for (int i = 0; i < num_closed_templates; ++i)
			{
				QString path;
				loadString(stream, path);
				auto temp = Template::templateForFile(path, map);
				if (!temp)
					temp.reset(new TemplateImage(path, map)); // fallback
				
				loadString(stream, path);
				temp->setTemplateRelativePath(path);
				
				temp->loadTemplateConfiguration(stream, version);
				
				map->closed_templates[i] = temp.release();
			}
		}

		// Restore widgets and views
		if (view)
		{
			view->load(stream, version);
		}
		else
		{
			MapView tmp{ map };
			tmp.load(stream, version);
		}

		// Load undo steps
		if (version >= 7)
		{
			if (!map->undoManager().load(stream, version))
			{
				throw FileFormatException(Importer::tr("Error while loading undo steps."));
			}
		}

		// Load parts
		stream->read((char*)&map->current_part_index, sizeof(int));

		int num_parts;
		if (stream->read((char*)&num_parts, sizeof(int)) < (int)sizeof(int))
		{
			throw FileFormatException(Importer::tr("Error while reading map part count."));
		}
		delete map->parts[0];
		map->parts.resize(num_parts);

		for (int i = 0; i < num_parts; ++i)
		{
			MapPart* part = new MapPart({}, map);
			if (!part->load(stream, version, map))
			{
				throw FileFormatException(Importer::tr("Error while loading map part %2.").arg(i+1));
			}
			map->parts[i] = part;
		}
	}
	
	emit map->currentMapPartIndexChanged(map->current_part_index);
	emit map->currentMapPartChanged(map->getPart(map->current_part_index));
}
示例#22
0
void BooleanTool::rebuildSegment(
        ClipperLib::Path::size_type start_index,
        ClipperLib::Path::size_type end_index,
        bool sequence_increasing,
        const ClipperLib::Path& polygon,
        const PolyMap& polymap,
        PathObject* object)
{
	auto num_points = polygon.size();
	
	object->getCoordinate(object->getCoordinateCount() - 1).setCurveStart(true);
	
	if ((start_index + 1) % num_points == end_index)
	{
		// This could happen for a straight line or a very flat curve - take coords directly from original
		rebuildTwoIndexSegment(start_index, end_index, sequence_increasing, polygon, polymap, object);
		return;
	}

	// Get polygon point coordinates
	const auto& start_point       = polygon.at(start_index);
	const auto& second_point      = polygon.at((start_index + 1) % num_points);
	const auto& second_last_point = polygon.at((end_index - 1) % num_points);
	const auto& end_point         = polygon.at(end_index);
	
	// Try to find the middle coordinates in the same part
	bool found = false;
	PathCoordInfo second_info{ nullptr, nullptr };
	PathCoordInfo second_last_info{ nullptr, nullptr };
	for (auto second_it = polymap.find(second_point); second_it != polymap.end(); ++second_it)
	{
		for (auto second_last_it = polymap.find(second_last_point);
		     second_last_it != polymap.end() && second_last_it.key() == second_last_point;
		     ++second_last_it)
		{
			if (second_it->first == second_last_it->first &&
			    second_it->second->index == second_last_it->second->index)
			{
				// Same part
				found = true;
				second_info = *second_it;
				second_last_info = *second_last_it;
				break;
			}
		}
		if (found)
			break;
	}
	
	if (!found)
	{
		// Need unambiguous path part information to find the original object with high probability
		qDebug() << "BooleanTool::rebuildSegment: cannot identify original object!";
		rebuildSegmentFromPathOnly(start_point, second_point, second_last_point, end_point, object);
		return;
	}
	
	const PathPart* original_path = second_info.first;
	
	// Try to find the outer coordinates in the same part
	PathCoordInfo start_info{ nullptr, nullptr };
	for (auto start_it = polymap.find(start_point);
	     start_it != polymap.end() && start_it.key() == start_point;
	     ++start_it)
	{
		if (start_it->first == original_path)
		{
			start_info = *start_it;
			break;
		}
	}
	Q_ASSERT(!start_info.first || start_info.first == second_info.first);
	
	PathCoordInfo end_info{ nullptr, nullptr };
	for (auto end_it = polymap.find(end_point);
	     end_it != polymap.end() && end_it.key() == end_point;
	     ++end_it)
	{
		if (end_it->first == original_path)
		{
			end_info = *end_it;
			break;
		}
	}
	Q_ASSERT(!end_info.first || end_info.first == second_info.first);
	
	const PathObject* original = original_path->path;
	auto edge_start = second_info.second->index;
	if (edge_start == second_info.first->last_index)
		edge_start = second_info.first->first_index;
	
	// Find out start tangent
	auto start_param = 0.0;
	MapCoord start_coord = MapCoord(0.001 * start_point.X, 0.001 * start_point.Y);
	MapCoord start_tangent;
	MapCoord end_tangent;
	MapCoord end_coord;
	
	double start_error_sq, end_error_sq;
	// Maximum difference in mm from reconstructed start and end coords to the
	// intersection points returned by Clipper
	const double error_bound = 0.4;
	
	if (sequence_increasing)
	{
		if ( second_info.second->param == 0.0 ||
		     ( start_info.first &&
		       start_info.second->param == 0.0 &&
		       ( start_info.second->index == edge_start ||
		         (start_info.second->index == start_info.first->last_index && start_info.first->first_index == edge_start) ) ) )
		{
			// Take coordinates directly
			start_tangent = original->getCoordinate(edge_start + 1);
			end_tangent = original->getCoordinate(edge_start + 2);
			
			start_error_sq = start_coord.distanceSquaredTo(original->getCoordinate(edge_start + 0));
			if (start_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: start error too high in increasing direct case: " << sqrt(start_error_sq);
		}
		else
		{
			// Approximate coords
			const PathCoord* prev_coord = second_info.second - 1;
			
			auto dx = second_point.X - start_point.X;
			auto dy = second_point.Y - start_point.Y;
			auto point_dist = 0.001 * sqrt(dx*dx + dy*dy);
			
			auto delta_start_param = (second_info.second->param - prev_coord->param) * point_dist / qMax(1e-7f, (second_info.second->clen - prev_coord->clen));
			start_param = qBound(0.0, second_info.second->param - delta_start_param, 1.0);
			
			MapCoordF unused, o2, o3, o4;
			PathCoord::splitBezierCurve(MapCoordF(original->getCoordinate(edge_start + 0)), MapCoordF(original->getCoordinate(edge_start + 1)),
			                            MapCoordF(original->getCoordinate(edge_start + 2)), MapCoordF(original->getCoordinate(edge_start + 3)),
			                            start_param,
			                            unused, unused, o2, o3, o4);
			start_tangent = MapCoord(o3);
			end_tangent = MapCoord(o4);
			
			start_error_sq = start_coord.distanceSquaredTo(MapCoord(o2));
			if (start_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: start error too high in increasing general case: " << sqrt(start_error_sq);
		}
		
		// Find better end point approximation and its tangent
		if ( second_last_info.second->param == 0.0 ||
		     (end_info.first &&
		      end_info.second->param == 0.0 &&
		      ( end_info.second->index == edge_start+3 ||
		        (end_info.second->index == end_info.first->first_index && end_info.first->last_index == edge_start+3) ) ) )
		{
			// Take coordinates directly
			end_coord = original->getCoordinate(edge_start + 3);
			
			auto test_x = end_point.X - end_coord.nativeX();
			auto test_y = end_point.Y - end_coord.nativeY();
			end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y);
			if (end_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: end error too high in increasing direct case: " << sqrt(end_error_sq);
		}
		else
		{
			// Approximate coords
			const PathCoord* next_coord = second_last_info.second + 1;
			auto next_coord_param = next_coord->param;
			if (next_coord_param == 0.0)
				next_coord_param = 1.0;
			
			auto dx = end_point.X - second_last_point.X;
			auto dy = end_point.Y - second_last_point.Y;
			auto point_dist = 0.001 * sqrt(dx*dx + dy*dy);
			
			auto delta_end_param = (next_coord_param - second_last_info.second->param) * point_dist / qMax(1e-7f, (next_coord->clen - second_last_info.second->clen));
			auto end_param = (second_last_info.second->param + delta_end_param - start_param) / (1.0 - start_param);
			
			MapCoordF o0, o1, o2, unused;
			PathCoord::splitBezierCurve(MapCoordF(start_coord), MapCoordF(start_tangent),
			                            MapCoordF(end_tangent), MapCoordF(original->getCoordinate(edge_start + 3)),
			                            end_param,
			                            o0, o1, o2, unused, unused);
			start_tangent = MapCoord(o0);
			end_tangent = MapCoord(o1);
			end_coord = MapCoord(o2);
			
			auto test_x = end_point.X - end_coord.nativeX();
			auto test_y = end_point.Y - end_coord.nativeY();
			end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y);
			if (end_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: end error too high in increasing general case: " << sqrt(end_error_sq);
		}
	}
	else // if (!sequence_increasing)
	{
		if ( second_info.second->param == 0.0 ||
		     ( start_info.first &&
		       start_info.second->param == 0.0 &&
		       ( start_info.second->index == edge_start+3 ||
			     (start_info.second->index == start_info.first->first_index && start_info.first->last_index == edge_start+3) ) ) )
		{
			// Take coordinates directly
			start_tangent = original->getCoordinate(edge_start + 2);
			end_tangent = original->getCoordinate(edge_start + 1);
			
			start_error_sq = start_coord.distanceSquaredTo(original->getCoordinate(edge_start + 3));
			if (start_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: start error too high in decreasing direct case: " << sqrt(start_error_sq);
		}
		else
		{
			// Approximate coords
			const PathCoord* next_coord = second_info.second + 1;
			auto next_coord_param = next_coord->param;
			if (next_coord_param == 0.0)
				next_coord_param = 1.0;
			
			auto dx = second_point.X - start_point.X;
			auto dy = second_point.Y - start_point.Y;
			auto point_dist = 0.001 * sqrt(dx*dx + dy*dy);
			
			auto delta_start_param = (next_coord_param - second_info.second->param) * point_dist / qMax(1e-7f, (next_coord->clen - second_info.second->clen));
			start_param = qBound(0.0, 1.0 - second_info.second->param + delta_start_param, 1.0);
			
			MapCoordF unused, o2, o3, o4;
			PathCoord::splitBezierCurve(MapCoordF(original->getCoordinate(edge_start + 3)), MapCoordF(original->getCoordinate(edge_start + 2)),
			                            MapCoordF(original->getCoordinate(edge_start + 1)), MapCoordF(original->getCoordinate(edge_start + 0)),
			                            start_param,
			                            unused, unused, o2, o3, o4);
			start_tangent = MapCoord(o3);
			end_tangent = MapCoord(o4);
			
			start_error_sq = start_coord.distanceSquaredTo(MapCoord(o2));
			if (start_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: start error too high in decreasing general case: " << sqrt(start_error_sq);
		}
		
		// Find better end point approximation and its tangent
		if ( second_last_info.second->param == 0.0 ||
		     ( end_info.first &&
		       end_info.second->param == 0.0 &&
		       ( end_info.second->index == edge_start ||
		         (end_info.second->index == end_info.first->last_index && end_info.first->first_index == edge_start) ) ) )
		{
			// Take coordinates directly
			end_coord = original->getCoordinate(edge_start + 0);
			
			auto test_x = end_point.X - end_coord.nativeX();
			auto test_y = end_point.Y - end_coord.nativeY();
			end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y);
			if (end_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: end error too high in decreasing direct case: " << sqrt(end_error_sq);
		}
		else
		{
			// Approximate coords
			const PathCoord* prev_coord = second_last_info.second - 1;
			
			auto dx = end_point.X - second_last_point.X;
			auto dy = end_point.Y - second_last_point.Y;
			auto point_dist = 0.001 * sqrt(dx*dx + dy*dy);
			
			auto delta_end_param = (second_last_info.second->param - prev_coord->param) * point_dist / qMax(1e-7f, (second_last_info.second->clen - prev_coord->clen));
			auto end_param = (1.0 - second_last_info.second->param + delta_end_param) / (1 - start_param);
			
			MapCoordF o0, o1, o2, unused;
			PathCoord::splitBezierCurve(MapCoordF(start_coord), MapCoordF(start_tangent),
			                            MapCoordF(end_tangent), MapCoordF(original->getCoordinate(edge_start + 0)),
			                            end_param,
			                            o0, o1, o2, unused, unused);
			start_tangent = MapCoord(o0);
			end_tangent = MapCoord(o1);
			end_coord = MapCoord(o2);
			
			auto test_x = end_point.X - end_coord.nativeX();
			auto test_y = end_point.Y - end_coord.nativeY();
			end_error_sq = 0.001 * sqrt(test_x*test_x + test_y*test_y);
			if (end_error_sq > error_bound)
				qDebug() << "BooleanTool::rebuildSegment: end error too high in decreasing general case: " << sqrt(end_error_sq);
		}
	}
	
	if (start_error_sq <= error_bound && end_error_sq <= error_bound)
	{
		// Rebuild bezier curve using information from original curve
		object->addCoordinate(start_tangent);
		object->addCoordinate(end_tangent);
		object->addCoordinate(resetCoordinate(end_coord));
	}
	else
	{
		// Rebuild bezier curve approximately using tangents derived from result polygon
		rebuildSegmentFromPathOnly(start_point, second_point, second_last_point, end_point, object);
	}
}
示例#23
0
void BooleanTool::polygonToPathPart(const ClipperLib::Path& polygon, const PolyMap& polymap, PathObject* object)
{
	auto num_points = polygon.size();
	if (num_points < 3)
		return;
	
	// Index of first used point in polygon
	auto part_start_index = 0u;
	auto cur_info = PathCoordInfo{ nullptr, nullptr };
	
	// Check if we can find either an unknown intersection point
	// or a path coord with parameter 0.
	// This gives a starting point to search for curves to rebuild
	// (because we cannot start in the middle of a curve)
	for (; part_start_index < num_points; ++part_start_index)
	{
		auto current_point = polygon.at(part_start_index);
		if (!polymap.contains(current_point))
			break;
		
		if (polymap.value(current_point).second->param == 0.0)
		{
			cur_info = polymap.value(current_point);
			break;
		}
	}
	
	if (part_start_index == num_points)
	{
		// Did not find a valid starting point. Return the part as a polygon.
		for (auto i = 0u; i < num_points; ++i)
			object->addCoordinate(MapCoord(0.001 * polygon.at(i).X, 0.001 * polygon.at(i).Y), (i == 0));
		object->parts().back().setClosed(true, true);
		return;
	}
	
	// Add the first point to the object
	rebuildCoordinate(part_start_index, polygon, polymap, object, true);
	
	
	// Index of first segment point in polygon
	auto segment_start_index = part_start_index;
	bool have_sequence = false;
	bool sequence_increasing = false;
	bool stop_before = false;
	
	// Advance along the boundary and rebuild the curve for every sequence
	// of path coord pointers with the same path and index.
	auto i = part_start_index;
	do
	{
		++i;
		if (i >= num_points)
			i = 0;
		
		PathCoordInfo new_info{ nullptr, nullptr };
		auto new_point = polygon.at(i);
		if (polymap.contains(new_point))
			new_info = polymap.value(new_point);
		
		if (cur_info.first && cur_info.first == new_info.first)
		{
			// Same original part
			auto cur_coord_index = cur_info.second->index;
			MapCoord& cur_coord = cur_info.first->path->getCoordinate(cur_coord_index);
			
			auto new_coord_index = new_info.second->index;
			MapCoord& new_coord = new_info.first->path->getCoordinate(new_coord_index);
			
			auto cur_coord_index_adjusted = cur_coord_index;
			if (cur_coord_index_adjusted == new_info.first->first_index)
				cur_coord_index_adjusted = new_info.first->last_index;
			
			auto new_coord_index_adjusted = new_coord_index;
			if (new_coord_index_adjusted == new_info.first->first_index)
				new_coord_index_adjusted = new_info.first->last_index;
			
			if (cur_coord_index == new_coord_index)
			{
				// Somewhere on a curve
				bool param_increasing = new_info.second->param > cur_info.second->param;
				if (!have_sequence)
				{
					have_sequence = true;
					sequence_increasing = param_increasing;
				}
				else if (have_sequence && sequence_increasing != param_increasing)
				{
					stop_before = true;
				}
			}
			else if (new_info.second->param == 0.0 &&
			         ( (cur_coord.isCurveStart() && new_coord_index_adjusted == cur_coord_index + 3) ||
					   (!cur_coord.isCurveStart() && new_coord_index_adjusted == cur_coord_index + 1) ) )
			{
				// Original curve is from cur_coord_index to new_coord_index_adjusted.
				if (!have_sequence)
				{
					have_sequence = true;
					sequence_increasing = true;
				}
				else
				{
					stop_before = !sequence_increasing;
				}
			}
			else if (cur_info.second->param == 0.0 &&
			         ( (new_coord.isCurveStart() && new_coord_index + 3 == cur_coord_index_adjusted) ||
					   (!new_coord.isCurveStart() && new_coord_index + 1 == cur_coord_index_adjusted) ) )
			{
				// Original curve is from new_coord_index to cur_coord_index_adjusted.
				if (!have_sequence)
				{
					have_sequence = true;
					sequence_increasing = false;
				}
				else
				{
					stop_before = sequence_increasing;
				}
			}
			else if ((segment_start_index + 1) % num_points != i)
			{
				// Not immediately after segment_start_index
				stop_before = true;
			}
		}
		
		if (i == part_start_index ||
		    stop_before ||
		    (new_info.second && new_info.second->param == 0.0) ||
			(cur_info.first && (cur_info.first != new_info.first || cur_info.second->index != new_info.second->index) && i != (segment_start_index + 1) % num_points) ||
			!new_info.first)
		{
			if (stop_before)
			{
				if (i == 0)
					i = num_points - 1;
				else
					--i;
			}
			
			if (have_sequence)
				// A sequence of at least two points belonging to the same curve
				rebuildSegment(segment_start_index, i, sequence_increasing, polygon, polymap, object);
			else
				// A single straight edge
				rebuildCoordinate(i, polygon, polymap, object);
			
			if (stop_before)
			{
				++i;
				if (i >= num_points)
					i = 0;
				rebuildCoordinate(i, polygon, polymap, object);
				stop_before = false;
			}
			
			segment_start_index = i;
			have_sequence = false;
		}
		
		cur_info = new_info;
	}
	while  (i != part_start_index);
	
	object->parts().back().connectEnds();
}
示例#24
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;
}
示例#25
0
bool DrawRectangleTool::mousePressEvent(QMouseEvent* event, MapCoordF map_coord, MapWidget* widget)
{
	// Adjust flags to have possibly more recent state
	int modifiers = (event->modifiers() | (key_button_bar ? key_button_bar->activeModifiers() : 0));
	ctrl_pressed = modifiers & Qt::ControlModifier;
	shift_pressed = modifiers & Qt::ShiftModifier;
	cur_map_widget = widget;
	if (isDrawingButton(event->button()))
	{
		dragging = false;
		click_pos = event->pos();
		click_pos_map = map_coord;
		cur_pos = event->pos();
		cur_pos_map = click_pos_map;
		if (shift_pressed)
			cur_pos_map = MapCoordF(snap_helper->snapToObject(cur_pos_map, widget));
		constrained_pos_map = cur_pos_map;
		
		if (!editingInProgress())
		{
			if (ctrl_pressed)
			{
				// Pick direction
				pickDirection(cur_pos_map, widget);
			}
			else
			{
				// Start drawing
				if (angle_helper->isActive())
					angle_helper->setCenter(click_pos_map);
				startDrawing();
				MapCoord coord = MapCoord(cur_pos_map);
				coord.setDashPoint(draw_dash_points);
				preview_path->addCoordinate(coord);
				preview_path->addCoordinate(coord);
				angles.push_back(0);
				updateStatusText();
			}
		}
		else
		{
			if (angles.size() >= 2 && drawingParallelTo(angles[angles.size() - 2]))
			{
				// Drawing parallel to last section, just move the last point
				undoLastPoint();
			}
			
			// Add new point
			int cur_point_index = angles.size();
			if (!preview_path->getCoordinate(cur_point_index).isPositionEqualTo(preview_path->getCoordinate(cur_point_index - 1)))
			{
				MapCoord coord = MapCoord(cur_pos_map);
				coord.setDashPoint(draw_dash_points);
				preview_path->addCoordinate(coord);
				if (angles.size() == 1)
				{
					// Bring to correct number of points: line becomes a rectangle
					preview_path->addCoordinate(coord);
				}
				angles.push_back(0);
				
				angle_helper->setActive(true, MapCoordF(preview_path->getCoordinate(cur_point_index)));
				angle_helper->clearAngles();
				angle_helper->addAngles(angles[0], M_PI/4);
			
				if (event->button() != Qt::RightButton || !drawOnRightClickEnabled())
				{
					updateHover(false);
					updateHover(false); // Call it again, really.
				}
			}
		}
	}
	else if (event->button() == Qt::RightButton && editingInProgress())
	{
		constrained_pos_map = MapCoordF(preview_path->getCoordinate(angles.size() - 1));
		undoLastPoint();
		if (editingInProgress()) // despite undoLastPoint()
			finishDrawing();
		no_more_effect_on_click = true;
	}
	else
	{
		return false;
	}
	
	return true;
}
示例#26
0
void DrawCircleTool::updateCircle()
{
	MapCoordF first_pos_map;
	if (start_from_center)
		first_pos_map = circle_start_pos_map + (circle_start_pos_map - opposite_pos_map);
	else
		first_pos_map = circle_start_pos_map;
	
	float radius = 0.5f * first_pos_map.distanceTo(opposite_pos_map);
	float kappa = BEZIER_KAPPA;
	float m_kappa = 1 - BEZIER_KAPPA;
	
	MapCoordF across = opposite_pos_map - first_pos_map;
	across.setLength(radius);
	MapCoordF right = across.perpRight();
	
	float right_radius = radius;
	if (second_point_set && dragging)
	{
		if (right.length() < 1e-8)
			right_radius = 0;
		else
		{
			MapCoordF to_cursor = cur_pos_map - first_pos_map;
			right_radius = MapCoordF::dotProduct(to_cursor, right) / right.length();
		}
	}
	right.setLength(right_radius);
	
	preview_path->clearCoordinates();
	preview_path->addCoordinate(MapCoord(first_pos_map, MapCoord::CurveStart));
	preview_path->addCoordinate(MapCoord(first_pos_map + kappa * right));
	preview_path->addCoordinate(MapCoord(first_pos_map + right + m_kappa * across));
	preview_path->addCoordinate(MapCoord(first_pos_map + right + across, MapCoord::CurveStart));
	preview_path->addCoordinate(MapCoord(first_pos_map + right + (1 + kappa) * across));
	preview_path->addCoordinate(MapCoord(first_pos_map + kappa * right + 2 * across));
	preview_path->addCoordinate(MapCoord(first_pos_map + 2 * across, MapCoord::CurveStart));
	preview_path->addCoordinate(MapCoord(first_pos_map - kappa * right + 2 * across));
	preview_path->addCoordinate(MapCoord(first_pos_map - right + (1 + kappa) * across));
	preview_path->addCoordinate(MapCoord(first_pos_map - right + across, MapCoord::CurveStart));
	preview_path->addCoordinate(MapCoord(first_pos_map - right + m_kappa * across));
	preview_path->addCoordinate(MapCoord(first_pos_map - kappa * right));
	preview_path->parts().front().setClosed(true, false);
	
	updatePreviewPath();
	setDirtyRect();
}
示例#27
0
void CutTool::pathFinished(PathObject* split_path)
{
	Map* map = this->map();
	
	// Get path endpoint and check if it is on the area boundary
	const MapCoordVector& path_coords = split_path->getRawCoordinateVector();
	MapCoord path_end = path_coords.at(path_coords.size() - 1);
	
	PathObject* edited_path = reinterpret_cast<PathObject*>(edit_object);
	PathCoord end_path_coord;
	float distance_sq;
	edited_path->calcClosestPointOnPath(MapCoordF(path_end), distance_sq, end_path_coord);
	
	float click_tolerance_map = 0.001 * edit_widget->getMapView()->pixelToLength(clickTolerance());
	if (distance_sq > click_tolerance_map*click_tolerance_map)
	{
		QMessageBox::warning(window(), tr("Error"), tr("The split line must end on the area boundary!"));
		pathAborted();
		return;
	}
	else if (drag_part_index != edited_path->findPartIndexForIndex(end_path_coord.index))
	{
		QMessageBox::warning(window(), tr("Error"), tr("Start and end of the split line are at different parts of the object!"));
		pathAborted();
		return;
	}
	else if (drag_start_len == end_path_coord.clen)
	{
		QMessageBox::warning(window(), tr("Error"), tr("Start and end of the split line are at the same position!"));
		pathAborted();
		return;
	}
	
	Q_ASSERT(split_path->parts().size() == 1);
	split_path->parts().front().setClosed(false);
	split_path->setCoordinate(split_path->getCoordinateCount() - 1, MapCoord(end_path_coord.pos));
	
	// Do the splitting
	const double split_threshold = 0.01;
	
	MapPart* part = map->getCurrentPart();
	AddObjectsUndoStep* add_step = new AddObjectsUndoStep(map);
	add_step->addObject(part->findObjectIndex(edited_path), edited_path);
	map->removeObjectFromSelection(edited_path, false);
	map->deleteObject(edited_path, true);
	map->setObjectsDirty();
	
	DeleteObjectsUndoStep* delete_step = new DeleteObjectsUndoStep(map);
	
	PathObject* holes = nullptr; // if the edited path contains holes, they are saved in this temporary object
	if (edited_path->parts().size() > 1)
	{
		holes = edited_path->duplicate()->asPath();
		holes->deletePart(0);
	}
	
	bool ok; Q_UNUSED(ok); // "ok" is only used in Q_ASSERT.
	PathObject* parts[2] = { new PathObject { edited_path->parts().front() }, nullptr };
	const PathPart& drag_part = edited_path->parts()[drag_part_index];
	if (drag_part.isClosed())
	{
		parts[1] = new PathObject { *parts[0] };
		
		parts[0]->changePathBounds(drag_part_index, drag_start_len, end_path_coord.clen);
		ok = parts[0]->connectIfClose(split_path, split_threshold);
		Q_ASSERT(ok);

		parts[1]->changePathBounds(drag_part_index, end_path_coord.clen, drag_start_len);
		ok = parts[1]->connectIfClose(split_path, split_threshold);
		Q_ASSERT(ok);
	}
	else
	{
		float min_cut_pos = qMin(drag_start_len, end_path_coord.clen);
		float max_cut_pos = qMax(drag_start_len, end_path_coord.clen);
		float path_len = drag_part.path_coords.back().clen;
		if (min_cut_pos <= 0 && max_cut_pos >= path_len)
		{
			ok = parts[0]->connectIfClose(split_path, split_threshold);
			Q_ASSERT(ok);
			
			parts[1] = new PathObject { *split_path };
			parts[1]->setSymbol(edited_path->getSymbol(), false);
		}
		else if (min_cut_pos <= 0 || max_cut_pos >= path_len)
		{
			float cut_pos = (min_cut_pos <= 0) ? max_cut_pos : min_cut_pos;
			parts[1] = new PathObject { *parts[0] };
			
			parts[0]->changePathBounds(drag_part_index, 0, cut_pos);
			ok = parts[0]->connectIfClose(split_path, split_threshold);
			Q_ASSERT(ok);
			
			parts[1]->changePathBounds(drag_part_index, cut_pos, path_len);
			ok = parts[1]->connectIfClose(split_path, split_threshold);
			Q_ASSERT(ok);
		}
		else
		{
			parts[1] = new PathObject { *parts[0] };
			PathObject* temp_path = new PathObject { *parts[0] };
			
			parts[0]->changePathBounds(drag_part_index, min_cut_pos, max_cut_pos);
			ok = parts[0]->connectIfClose(split_path, split_threshold);
			Q_ASSERT(ok);
			
			parts[1]->changePathBounds(drag_part_index, 0, min_cut_pos);
			ok = parts[1]->connectIfClose(split_path, split_threshold);
			Q_ASSERT(ok);
			
			temp_path->changePathBounds(drag_part_index, max_cut_pos, path_len);
			ok = parts[1]->connectIfClose(temp_path, split_threshold);
			Q_ASSERT(ok);
			
			delete temp_path;
		}
	}
	
	// If the object had holes, check into which parts they go
	if (holes)
	{
		for (const auto& hole : holes->parts())
		{
			PathPartVector::size_type part_index = (parts[0]->isPointOnPath(MapCoordF(holes->getCoordinate(hole.first_index)), 0, false, false) != Symbol::NoSymbol) ? 0 : 1;
			parts[part_index]->getCoordinate(parts[part_index]->getCoordinateCount() - 1).setHolePoint(true);
			parts[part_index]->appendPathPart(hole);
		}
	}
	
	for (auto& object : parts)
	{
		map->addObject(object);
		delete_step->addObject(part->findObjectIndex(object));
		map->addObjectToSelection(object, false);
	}
	
	CombinedUndoStep* undo_step = new CombinedUndoStep(map);
	undo_step->push(add_step);
	undo_step->push(delete_step);
	map->push(undo_step);
	map->setObjectsDirty();
	
	pathAborted();
}
示例#28
0
void PathObjectTest::calcIntersectionsTest_data()
{
	QTest::addColumn<void*>("v_path1");
	QTest::addColumn<void*>("v_path2");
	QTest::addColumn<void*>("v_predicted_intersections");
	
	PathObject::Intersection intersection;
	intersection.part_index = 0;
	intersection.other_part_index = 0;
	
	
	// Line-Line perpendicular intersection
	DummyPathObject* perpendicular1 = new DummyPathObject();
	perpendicular1->addCoordinate(MapCoord(10, 20));
	perpendicular1->addCoordinate(MapCoord(30, 20));
	
	DummyPathObject* perpendicular2 = new DummyPathObject();
	perpendicular2->addCoordinate(MapCoord(20, 10));
	perpendicular2->addCoordinate(MapCoord(20, 30));
	
	PathObject::Intersections* intersections_perpendicular = new PathObject::Intersections();
	intersection.coord = MapCoordF(20, 20);
	intersection.length = 10;
	intersection.other_length = 10;
	intersections_perpendicular->push_back(intersection);
	
	QTest::newRow("Line-Line perpendicular") << (void*)perpendicular1 << (void*)perpendicular2 << (void*)intersections_perpendicular;
	
	
	// Line-Line perpendicular intersection, test reversed
	QTest::newRow("Line-Line perpendicular, test reversed") << (void*)perpendicular2 << (void*)perpendicular1 << (void*)intersections_perpendicular;
	
	
	// Line-Line perpendicular intersection, line1 reversed
	PathObject* perpendicular3 = perpendicular1->duplicate()->asPath();
	perpendicular3->reverse();
	QTest::newRow("Line-Line perpendicular, line1 reversed") << (void*)perpendicular2 << (void*)perpendicular3 << (void*)intersections_perpendicular;
	
	
	// Line-Line parallel intersection
	DummyPathObject* parallel1 = new DummyPathObject();
	parallel1->addCoordinate(MapCoord(10, 0));
	parallel1->addCoordinate(MapCoord(30, 0));
	parallel1->addCoordinate(MapCoord(50, 0));
	
	DummyPathObject* parallel2 = new DummyPathObject();
	parallel2->addCoordinate(MapCoord(20, 0));
	parallel2->addCoordinate(MapCoord(40, 0));
	parallel2->addCoordinate(MapCoord(60, 0));
	
	PathObject::Intersections* intersections_parallel = new PathObject::Intersections();
	intersection.coord = MapCoordF(20, 0);
	intersection.length = 10;
	intersection.other_length = 0;
	intersections_parallel->push_back(intersection);
	intersection.coord = MapCoordF(50, 0);
	intersection.length = 40;
	intersection.other_length = 30;
	intersections_parallel->push_back(intersection);
	
	QTest::newRow("Line-Line parallel") << (void*)parallel1 << (void*)parallel2 << (void*)intersections_parallel;
	
	
	// Intersection at parallel start / end
	DummyPathObject* parallel3 = new DummyPathObject();
	parallel3->addCoordinate(MapCoord(20, 10));
	parallel3->addCoordinate(MapCoord(20, 0));
	parallel3->addCoordinate(MapCoord(30, 0));
	parallel3->addCoordinate(MapCoord(30, -10));
	
	PathObject::Intersections* intersections_parallel_se = new PathObject::Intersections();
	intersection.coord = MapCoordF(20, 0);
	intersection.length = 10;
	intersection.other_length = 10;
	intersections_parallel_se->push_back(intersection);
	intersection.coord = MapCoordF(30, 0);
	intersection.length = 20;
	intersection.other_length = 20;
	intersections_parallel_se->push_back(intersection);
	
	QTest::newRow("Line-Line parallel with intersection at start / end") << (void*)parallel1 << (void*)parallel3 << (void*)intersections_parallel_se;
	
	
	// Intersection at point
	DummyPathObject* point1 = new DummyPathObject();
	point1->addCoordinate(MapCoord(10, 30));
	point1->addCoordinate(MapCoord(30, 30));
	point1->addCoordinate(MapCoord(50, 30));
	
	DummyPathObject* point2 = new DummyPathObject();
	point2->addCoordinate(MapCoord(30, 10));
	point2->addCoordinate(MapCoord(30, 30));
	point2->addCoordinate(MapCoord(30, 50));
	
	PathObject::Intersections* intersections_point = new PathObject::Intersections();
	intersection.coord = MapCoordF(30, 30);
	intersection.length = 20;
	intersection.other_length = 20;
	intersections_point->push_back(intersection);
	
	QTest::newRow("Intersection at point") << (void*)point1 << (void*)point2 << (void*)intersections_point;
	
	
	// Duplicate of object intersection
	DummyPathObject* duplicate1 = new DummyPathObject();
	duplicate1->addCoordinate(MapCoord(10, 30));
	duplicate1->addCoordinate(MapCoord(30, 30));
	duplicate1->addCoordinate(MapCoord(50, 50));
	duplicate1->addCoordinate(MapCoord(50, 70));
	duplicate1->update(); // for duplicate1->parts().front().getLength();
	
	PathObject::Intersections* intersections_duplicate = new PathObject::Intersections();
	intersection.coord = MapCoordF(10, 30);
	intersection.length = 0;
	intersection.other_length = 0;
	intersections_duplicate->push_back(intersection);
	intersection.coord = MapCoordF(50, 70);
	intersection.length = duplicate1->parts().front().length();
	intersection.other_length = duplicate1->parts().front().length();
	intersections_duplicate->push_back(intersection);
	
	QTest::newRow("Start/end intersections for duplicate") << (void*)duplicate1 << (void*)duplicate1 << (void*)intersections_duplicate;
	
	
	// Reversed duplicate
	PathObject* duplicate2 = duplicate1->duplicate()->asPath();
	duplicate2->reverse();
	
	PathObject::Intersections* intersections_duplicate_reversed = new PathObject::Intersections();
	intersection.coord = MapCoordF(10, 30);
	intersection.length = 0;
	intersection.other_length = duplicate1->parts().front().length();
	intersections_duplicate_reversed->push_back(intersection);
	intersection.coord = MapCoordF(50, 70);
	intersection.length = duplicate1->parts().front().length();
	intersection.other_length = 0;
	intersections_duplicate_reversed->push_back(intersection);
	
	QTest::newRow("Start/end intersections for reversed duplicate") << (void*)duplicate1 << (void*)duplicate2 << (void*)intersections_duplicate_reversed;
	
	
	// Duplicate of closed object intersection
	PathObject* duplicate1_closed = duplicate1->duplicate()->asPath();
	duplicate1_closed->parts().front().setClosed(true);
	
	PathObject::Intersections* no_intersections = new PathObject::Intersections();
	
	QTest::newRow("No intersections for closed duplicate") << (void*)duplicate1_closed << (void*)duplicate1_closed << (void*)no_intersections;
	
	
	// Parallel at start intersection
	DummyPathObject* ps1 = new DummyPathObject();
	ps1->addCoordinate(MapCoord(10, 10));
	ps1->addCoordinate(MapCoord(30, 10));
	ps1->addCoordinate(MapCoord(30, 30));
	ps1->addCoordinate(MapCoord(10, 30));
	ps1->parts().front().setClosed(true);
	
	DummyPathObject* ps2 = new DummyPathObject();
	ps2->addCoordinate(MapCoord(10, 10));
	ps2->addCoordinate(MapCoord(30, 10));
	
	PathObject::Intersections* intersections_ps = new PathObject::Intersections();
	intersection.coord = MapCoordF(10, 10);
	intersection.length = 0;
	intersection.other_length = 0;
	intersections_ps->push_back(intersection);
	intersection.coord = MapCoordF(30, 10);
	intersection.length = 20;
	intersection.other_length = 20;
	intersections_ps->push_back(intersection);
	intersection.coord = MapCoordF(10, 10);
	intersection.length = 80;
	intersection.other_length = 0;
	intersections_ps->push_back(intersection);
	
	QTest::newRow("Parallel at start intersection") << (void*)ps1 << (void*)ps2 << (void*)intersections_ps;
	
	
	// Parallel at end intersection
	DummyPathObject* pe1 = new DummyPathObject();
	pe1->addCoordinate(MapCoord(10, 10));
	pe1->addCoordinate(MapCoord(30, 10));
	pe1->addCoordinate(MapCoord(30, 30));
	pe1->addCoordinate(MapCoord(10, 30));
	pe1->parts().front().setClosed(true);
	
	DummyPathObject* pe2 = new DummyPathObject();
	pe2->addCoordinate(MapCoord(10, 30));
	pe2->addCoordinate(MapCoord(10, 10));
	
	PathObject::Intersections* intersections_pe = new PathObject::Intersections();
	intersection.coord = MapCoordF(10, 10);
	intersection.length = 0;
	intersection.other_length = 20;
	intersections_pe->push_back(intersection);
	intersection.coord = MapCoordF(10, 30);
	intersection.length = 60;
	intersection.other_length = 0;
	intersections_pe->push_back(intersection);
	intersection.coord = MapCoordF(10, 10);
	intersection.length = 80;
	intersection.other_length = 20;
	intersections_pe->push_back(intersection);
	
	QTest::newRow("Parallel at end intersection") << (void*)pe1 << (void*)pe2 << (void*)intersections_pe;
	
	
	// a inside b
	DummyPathObject* aib1 = new DummyPathObject();
	aib1->addCoordinate(MapCoord(10, 0));
	aib1->addCoordinate(MapCoord(30, 0));
	
	DummyPathObject* aib2 = new DummyPathObject();
	aib2->addCoordinate(MapCoord(0, 0));
	aib2->addCoordinate(MapCoord(40, 0));
	
	PathObject::Intersections* intersections_aib = new PathObject::Intersections();
	intersection.coord = MapCoordF(10, 0);
	intersection.length = 0;
	intersection.other_length = 10;
	intersections_aib->push_back(intersection);
	intersection.coord = MapCoordF(30, 0);
	intersection.length = 20;
	intersection.other_length = 30;
	intersections_aib->push_back(intersection);
	
	QTest::newRow("a inside b") << (void*)aib1 << (void*)aib2 << (void*)intersections_aib;
	
	
	// b inside a
	PathObject::Intersections* intersections_bia = new PathObject::Intersections();
	intersection.coord = MapCoordF(10, 0);
	intersection.length = 10;
	intersection.other_length = 0;
	intersections_bia->push_back(intersection);
	intersection.coord = MapCoordF(30, 0);
	intersection.length = 30;
	intersection.other_length = 20;
	intersections_bia->push_back(intersection);
	
	QTest::newRow("b inside a") << (void*)aib2 << (void*)aib1 << (void*)intersections_bia;
}