/// <summary>
 /// Creates a copy of an inscription TitleBufferElement located along the object.
 /// </summary>
 public TitleBufferElement(FollowingTitle followingTitle, TitleStyle style, int number)
 {
     _isSimple = false;
     _style = style;
     _box = new Segment(followingTitle.GetBoundingBox().Min, followingTitle.GetBoundingBox().Max);
     _followingTitle = followingTitle;
     _titleCount = number;
 }
        private FollowingTitle getFollowingTitle(Graphics g, LinePath part, double length, string label, TitleStyle titleStyle, BoundingRectangle viewBox, double scaleFactor)
        {
            StringFormat format = StringFormat.GenericTypographic;
            SizeF sizeF;
            PointF zeroPoint = new PointF(0, 0);

            using (Font f = titleStyle.GetFont())
            {
                sizeF = g.MeasureString(label, f, zeroPoint, format);

                // label length must be less than the length of the line
                if (sizeF.Width / scaleFactor < length)
                {
                    LinePath tempPart = new LinePath();
                    foreach (ICoordinate p in part.Vertices)
                        tempPart.Vertices.Add(p);

                    int vertexNumber = 0;
                    ICoordinate centerPoint = getDistantPoint(tempPart.Vertices, length / 2, out vertexNumber);

                    // if the point of the proposed mid-label misses the display area of ​​the map, the inscription does not appear
                    if (!viewBox.ContainsPoint(centerPoint))
                        return null;

                    // simplify the line
                    tempPart.Weed(sizeF.Height / scaleFactor / 2);

                    //get the center point of the simplified line
                    centerPoint = getDistantPoint(tempPart.Vertices, length / 2, out vertexNumber);

                    List<double> leftPointsRotationDeltas = new List<double>();
                    List<double> rightPointsRotationDeltas = new List<double>();

                    // coordinates of points on the left of the middle of the inscription
                    IList<ICoordinate> leftPoints =
                        getLeftPoints(tempPart.Vertices,
                                      centerPoint,
                                      sizeF.Width / 2 / scaleFactor,
                                      vertexNumber,
                                      sizeF.Height / 2 / scaleFactor,
                                      leftPointsRotationDeltas);

                    // coordinates of the points to the right of the middle of the inscription
                    IList<ICoordinate> rightPoints =
                        getRightPoints(tempPart.Vertices,
                                       centerPoint,
                                       sizeF.Width / 2 / scaleFactor,
                                       vertexNumber,
                                       sizeF.Height / 2 / scaleFactor,
                                       rightPointsRotationDeltas);

                    //coordinates of the vertices of the broken line, which will be located along the inscription
                    List<ICoordinate> points = leftPoints.ToList();
                    points.AddRange(rightPoints);

                    // shifts of the inscriptions associated with break lines
                    List<double> rotationDeltas = leftPointsRotationDeltas;
                    rotationDeltas.AddRange(rightPointsRotationDeltas);

                    for (int i = 0; i < points.Count; i++)
                        points[i] = PlanimetryEnvironment.NewCoordinate((points[i].X - viewBox.MinX) * scaleFactor,
                                               (viewBox.MaxY - points[i].Y) * scaleFactor);

                    for (int i = 0; i < rotationDeltas.Count; i++)
                        rotationDeltas[i] = rotationDeltas[i] * scaleFactor;

                    //determine the direction of following labels (direct or reverse)
                    double forwardWeight = 0;
                    double backwardWeight = 0;

                    for (int i = 1; i < points.Count; i++)
                    {
                        Segment s = new Segment(PlanimetryEnvironment.NewCoordinate(points[i].X, points[i].Y),
                                                PlanimetryEnvironment.NewCoordinate(points[i - 1].X, points[i - 1].Y));
                        int quadNumber = pointQuadrantNumber(PlanimetryEnvironment.NewCoordinate(s.V1.X - s.V2.X, s.V1.Y - s.V2.Y));
                        if (quadNumber == 1 || quadNumber == 4)
                            forwardWeight += s.Length();
                        else
                            backwardWeight += s.Length();
                    }

                    if (backwardWeight > forwardWeight)
                    {
                        points.Reverse();
                        rotationDeltas.Reverse();
                    }

                    // inscriptions along the route should not be a large number of points
                    if (label.Length > points.Count - 2)
                    {
                        List<int> subStringLengths = new List<int>();
                        List<double> deltas = new List<double>();

                        LinePath p1 = new LinePath(points.ToArray());
                        double l = p1.Length();

                        // partition of the inscription on the straight parts, the calculation of displacement
                        int startIndex = 0;
                        for (int i = 1; i < points.Count; i++)
                        {
                            double currentDistance = PlanimetryAlgorithms.Distance(points[i - 1], points[i]);

                            if (deltas.Count > 0)
                                if (deltas[deltas.Count - 1] < currentDistance)
                                    currentDistance -= deltas[deltas.Count - 1];

                            //subtract the offset associated with line breaks
                            currentDistance -= rotationDeltas[i - 1];
                            if (i < rotationDeltas.Count)
                                currentDistance -= rotationDeltas[i];

                            int currentLength = (int)(currentDistance / l * label.Length);

                            if (startIndex + currentLength > label.Length)
                                currentLength = label.Length - startIndex;

                            subStringLengths.Add(currentLength > 0 ? currentLength : 0);

                            string subString;
                            if (subStringLengths[i - 1] > 0)
                                subString = label.Substring(startIndex, subStringLengths[i - 1]);
                            else
                                subString = string.Empty;

                            float width1, width2, width3;
                            width1 = width2 = width3 = g.MeasureString(subString, f, zeroPoint, format).Width;

                            if (!string.IsNullOrEmpty(subString))
                            {
                                if (subStringLengths[i - 1] > 1)
                                {
                                    width2 = g.MeasureString(label.Substring(startIndex, subStringLengths[i - 1] - 1), f, zeroPoint, format).Width;
                                    if (Math.Abs(width2 - currentDistance) < Math.Abs(width1 - currentDistance))
                                    {
                                        subStringLengths[i - 1] = subStringLengths[i - 1] - 1;
                                        width1 = width2;
                                    }
                                }

                                if (label.Length > subStringLengths[i - 1] + startIndex)
                                {
                                    width3 = g.MeasureString(label.Substring(startIndex, subStringLengths[i - 1] + 1), f, zeroPoint, format).Width;
                                    if (Math.Abs(width3 - currentDistance) < Math.Abs(width1 - currentDistance) &&
                                        Math.Abs(width3 - currentDistance) < sizeF.Width / label.Length / 6)
                                    {
                                        subStringLengths[i - 1] = subStringLengths[i - 1] + 1;
                                        width1 = width3;
                                    }
                                }
                            }

                            deltas.Add(0.5 * (width1 - currentDistance));
                            if (currentLength > 0)
                                startIndex += currentLength;
                        }

                        int sum = 0;
                        int maxZeroLengths = 0;
                        int zeroLengthsCount = 0;
                        foreach (int k in subStringLengths)
                        {
                            if (k <= 0)
                            {
                                zeroLengthsCount++;
                                if (maxZeroLengths < zeroLengthsCount)
                                    maxZeroLengths = zeroLengthsCount;
                            }
                            else
                                zeroLengthsCount = 0;
                            sum += k;
                        }

                        if (maxZeroLengths > 1)
                            return null;

                        int lastIndex = subStringLengths.Count() - 1;
                        if (lastIndex >= 0)
                        {
                            subStringLengths[lastIndex] += label.Length - sum;
                            if (subStringLengths[lastIndex] < 0)
                            {
                                subStringLengths[lastIndex - 1] -= subStringLengths[lastIndex];
                                subStringLengths[lastIndex] = 0;
                            }
                        }

                        FollowingTitle followingTitle = new FollowingTitle();

                        startIndex = 0;
                        double? previousAngle = null;
                        for (int i = 0; i < subStringLengths.Count(); i++)
                        {
                            if (subStringLengths[i] <= 0)
                                continue;

                            if (startIndex + subStringLengths[i] > label.Length)
                                return null;

                            SizeF size;
                            size = g.MeasureString(label.Substring(startIndex, subStringLengths[i]), f, zeroPoint, format);

                            int x0 = (i == 0 ? 0 : (int)Math.Round(deltas[i - 1] + rotationDeltas[i - 1]));

                            PointF[] v = new PointF[4];
                            v[0].X = x0;
                            v[0].Y = -size.Height / 2;

                            v[1].X = size.Width + x0;
                            v[1].Y = -size.Height / 2;

                            v[2].X = size.Width + x0;
                            v[2].Y = size.Height / 2;

                            v[3].X = x0;
                            v[3].Y = size.Height / 2;

                            float angle = (float)(180 / Math.PI * getAngle(PlanimetryEnvironment.NewCoordinate(Math.Abs(points[i].X * 2), points[i].Y),
                                                                           points[i],
                                                                           points[i + 1],
                                                  false));

                            if (previousAngle != null)
                            {
                                double angleDelta = Math.Abs(angle - previousAngle.Value);
                                if (angleDelta > 45 && 360 - angleDelta > 45)
                                    return null;
                            }

                            previousAngle = angle;

                            g.TranslateTransform((float)points[i].X, (float)points[i].Y);
                            g.RotateTransform(-(angle % 360));

                            g.Transform.TransformPoints(v);
                            for (int j = 0; j < 4; j++)
                            {
                                v[j].X = (float)(v[j].X / scaleFactor + viewBox.MinX);
                                v[j].Y = (float)(viewBox.MaxY - v[j].Y / scaleFactor);
                            }

                            FollowingTitleElement element =
                                new FollowingTitleElement(new PointF((float)points[i].X, (float)points[i].Y),
                                                          -(angle % 360),
                                                          new PointF(x0, -size.Height / 2),
                                                          label.Substring(startIndex, subStringLengths[i]),
                                                          v[0], v[1], v[2], v[3]);

                            startIndex += subStringLengths[i];

                            followingTitle.AddElement(element);

                            g.ResetTransform();
                        }
                        return followingTitle.EnvelopePolygon == null ? null : followingTitle;
                    }
                }
            }
            return null;
        }