TerrainRangeArc::TerrainRangeArc(const Arc2d & data, AxisType width)
{
	const Point2d vec = data.start - data.center;

	const AxisType radii = vec.GetLength();
	const AxisType radiiMax = radii + width / 2;
	const AxisType radiiMin = (width / 2 < radii) ? radii - width / 2 : 0;

	const int iMin = utils::FloatFloorToInt(-radiiMax);
	const int iMax = utils::FloatCeilToInt(radiiMax);
	const int iRadiiMin = utils::FloatFloorToInt(radiiMin);

	for (int i = iMin; i <= iMax; ++i)
	{
		const AxisType xMax = GetXShift(i, iMax);

		if (std::abs(i) >= iRadiiMin)
		{
			ProcessRange(vec, data.angle, i, AxisPairType(-xMax, xMax), data.center);
		}
		else
		{
			const AxisType xMin = GetXShift(i, iRadiiMin);

			const int lxMin = utils::FloatCeilToInt(-xMin);
			const int rxMin = utils::FloatFloorToInt(xMin);

			if (lxMin == rxMin)
			{
				ProcessRange(vec, data.angle, i, AxisPairType(-xMax, xMax), data.center);
			}
			else
			{
				ProcessRange(vec, data.angle, i, AxisPairType(-xMax, (float)lxMin), data.center);
				ProcessRange(vec, data.angle, i, AxisPairType((float)rxMin, xMax), data.center);
			}
		}
	}
}
void 
RailRoadSplitter::Visit(RailRoadArc & rra)
{
	RailRoadParametersTaker rrpt;
	rra.Accept(rrpt);

	const Point3d start = rrpt.GetStart();
	const Point2d start2d = Point2d::Cast(start);
	const Point3d & end = rrpt.GetEnd();
	const Point2d & center = rra.GetCenter();
	const float zShift = rra.GetZShift();
	const Rotation rotation = utils::GetAngleRotation(rra.GetAngle());
	const Angle absAngle = utils::GetAngleAbs(rra.GetAngle());
	const Point2d shiftedStart = start2d - center;
	const float radiiFromStart = shiftedStart.GetLength();

	using SortedPointsMap = std::map<Angle, Point3d>;
	SortedPointsMap sortedPoints;

	boost::for_each(splitPoints_,
		[&](Point3d p)
	{
		if (start == p || end == p)
		{
			return;
		}

		const Point2d shiftedSplit = Point2d::Cast(p) - center;
		const float radiiFromSplit = shiftedSplit.GetLength();
	
		// ignore split point not on the arc
		if (!utils::CheckNear(radiiFromSplit, radiiFromStart, 0.0001f))
		{
			return;
		}

		const Angle angleToSplit = utils::GetRotationAngle360(shiftedStart, shiftedSplit, rotation);

		if (angleToSplit > absAngle)
		{
			return;
		}

		p.z() = start.z() + zShift * angleToSplit / absAngle;
		sortedPoints.emplace(angleToSplit, p);
	});

	// if no points did a split, then nothing to return
	// ignore points not on the arc and points outside of the arc
	if (sortedPoints.empty())
	{
		return;
	}

	sortedPoints.emplace(absAngle, end);

	Angle prevAngle;
	Point3d startPoint = start;

	boost::for_each(sortedPoints,
		[&](const SortedPointsMap::value_type & v)
	{
		spitResult_.push_back(RailRoadFactory::Arc(startPoint, utils::GetAdjustedAngleByRotation(v.first - prevAngle, rotation), rra.GetCenter(), v.second.z() - startPoint.z()));
		startPoint = v.second;
		prevAngle = v.first;
	});
}