//------------------------------------------------------------------------ Cubic
        //this method contains some intentional copy/paste
        private void RecursiveCubicSplit(SectionInfo section, double x0, double y0, double z0, double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3,
                                         int level, bool calcTangents, double fromT, double toT)
        {
            if (level > RecursionLimit)
            {
                return;
            }

            // is curve flat
            // http://www.malinc.se/m/DeCasteljauAndBezier.php
            // sqr(b) + sqr(c) + sqr(d) < sqr(a) * tolerance

            var dx01 = x0 - x1;
            var dy01 = y0 - y1;
            var dz01 = z0 - z1;

            var dx12 = x1 - x2;
            var dy12 = y1 - y2;
            var dz12 = z1 - z2;

            var dx23 = x2 - x3;
            var dy23 = y2 - y3;
            var dz23 = z2 - z3;

            var dx03 = x0 - x3;
            var dy03 = y0 - y3;
            var dz03 = z0 - z3;

            var a = dx03 * dx03 + dy03 * dy03 + dz03 * dz03;
            var b = dx01 * dx01 + dy01 * dy01 + dz01 * dz01;
            var c = dx12 * dx12 + dy12 * dy12 + dz12 * dz12;
            var d = dx23 * dx23 + dy23 * dy23 + dz23 * dz23;

            //idea.. optimize sqrt. Sqrt is expensive call, and 3 Sqrt calls is super expensive to say the least
            if (Math.Sqrt(b * c) + Math.Sqrt(c * d) + Math.Sqrt(b * d) < (a * toleranceRatioSquared - b - c - d) * .5 || a + b + c + d < DistanceTolerance)
            {
                return;
            }

            //at this point we know, the curve is not flat
            //split (Casteljau algorithm)
            var x01 = (x0 + x1) * .5;
            var y01 = (y0 + y1) * .5;
            var z01 = (z0 + z1) * .5;
            var x12 = (x1 + x2) * .5;
            var y12 = (y1 + y2) * .5;
            var z12 = (z1 + z2) * .5;
            var x23 = (x2 + x3) * .5;
            var y23 = (y2 + y3) * .5;
            var z23 = (z2 + z3) * .5;

            var x012 = (x01 + x12) * .5;
            var y012 = (y01 + y12) * .5;
            var z012 = (z01 + z12) * .5;
            var x123 = (x12 + x23) * .5;
            var y123 = (y12 + y23) * .5;
            var z123 = (z12 + z23) * .5;

            var x0123 = (x012 + x123) * .5;
            var y0123 = (y012 + y123) * .5;
            var z0123 = (z012 + z123) * .5;

            var t = calcTangents ? (fromT + toT) * .5 : .0;

            var pos = new Vector3((float)x0123, (float)y0123, (float)z0123);

            if (curve.SnapType == BGCurve.SnapTypeEnum.Curve)
            {
                curve.ApplySnapping(ref pos);
            }

            //split first section
            RecursiveCubicSplit(section, x0, y0, z0, x01, y01, z01, x012, y012, z012, x0123, y0123, z0123, level + 1, calcTangents, fromT, t);

            //add point
            SectionPointInfo pointInfo;
            var poolCursor = poolPointInfos.Count - 1;

            if (poolCursor >= 0)
            {
                pointInfo = poolPointInfos[poolCursor];
                poolPointInfos.RemoveAt(poolCursor);
            }
            else
            {
                pointInfo = new SectionPointInfo();
            }
            pointInfo.Position = pos;
            section.points.Add(pointInfo);

            //tangent
            if (calcTangents)
            {
                var tr = 1 - t;
                //idea.. optimize it
                pointInfo.Tangent = Vector3.Normalize(3 * ((float)(tr * tr)) * (section.OriginalFromControl - section.OriginalFrom) +
                                                      6 * (float)(tr * t) * (section.OriginalToControl - section.OriginalFromControl) +
                                                      3 * (float)(t * t) * (section.OriginalTo - section.OriginalToControl));
            }


            //split second section
            RecursiveCubicSplit(section, x0123, y0123, z0123, x123, y123, z123, x23, y23, z23, x3, y3, z3, level + 1, calcTangents, t, toT);
        }
        //approximate split section
        //this method contains some intentional copy/paste
        protected override void CalculateSplitSection(SectionInfo section, BGCurvePointI @from, BGCurvePointI to)
        {
            //if set to true, tangent formula will be used to calc tangents
            var calcTangents = cacheTangent && !config.UsePointPositionsToCalcTangents;


            var points = section.points;
            var count  = points.Count;

            //move all existing points to the pool (the number of points depends on the curvature)
            for (var i = 0; i < count; i++)
            {
                poolPointInfos.Add(points[i]);
            }
            points.Clear();

            //original points and control types
            var p0 = section.OriginalFrom;
            var p1 = section.OriginalFromControl;
            var p2 = section.OriginalToControl;
            var p3 = section.OriginalTo;

            //type of the curve (0-absent,3-both Bezier, 1 or 2 - one of the points has Bezier controls)
            var type = (section.OriginalFromControlType != BGCurvePoint.ControlTypeEnum.Absent ? 2 : 0) + (section.OriginalToControlType != BGCurvePoint.ControlTypeEnum.Absent ? 1 : 0);

            //=====================================   first point
            SectionPointInfo firstPoint;
            var poolCursor = poolPointInfos.Count - 1;

            if (poolCursor >= 0)
            {
                firstPoint = poolPointInfos[poolCursor];
                poolPointInfos.RemoveAt(poolCursor);
            }
            else
            {
                firstPoint = new SectionPointInfo();
            }
            firstPoint.Position = p0;
            firstPoint.DistanceToSectionStart = 0;
            points.Add(firstPoint);

            //====================================   split recursively
            switch (type)
            {
            case 3:
                RecursiveCubicSplit(section, p0.x, p0.y, p0.z, p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z, 0, calcTangents, 0, 1);
                break;

            case 2:
                RecursiveQuadraticSplit(section, p0.x, p0.y, p0.z, p1.x, p1.y, p1.z, p3.x, p3.y, p3.z, 0, false, calcTangents, 0, 1);
                break;

            case 1:
                RecursiveQuadraticSplit(section, p0.x, p0.y, p0.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z, 0, true, calcTangents, 0, 1);
                break;
            }

            //=====================================   last point
            SectionPointInfo lastPoint;

            poolCursor = poolPointInfos.Count - 1;
            if (poolCursor >= 0)
            {
                lastPoint = poolPointInfos[poolCursor];
                poolPointInfos.RemoveAt(poolCursor);
            }
            else
            {
                lastPoint = new SectionPointInfo();
            }
            lastPoint.Position = p3;
            points.Add(lastPoint);


            //calculate distances (and optionally tangents)
            //if set to true, points positions will be used to calculate tangents
            calcTangents = cacheTangent && config.UsePointPositionsToCalcTangents;
            var prevPoint = points[0];

            for (var i = 1; i < points.Count; i++)
            {
                var point = points[i];

                var pos     = point.Position;
                var prevPos = prevPoint.Position;

                var dX = (double)pos.x - prevPos.x;
                var dY = (double)pos.y - prevPos.y;
                var dZ = (double)pos.z - prevPos.z;

                //distance
                point.DistanceToSectionStart = prevPoint.DistanceToSectionStart + ((float)Math.Sqrt(dX * dX + dY * dY + dZ * dZ));

                //tangents
                if (calcTangents)
                {
                    point.Tangent = Vector3.Normalize(pos - prevPos);
                }

                prevPoint = point;
            }

            //set first and last points tangents (cause they was omitted during recursive split)
            if (cacheTangent)
            {
                if (config.UsePointPositionsToCalcTangents)
                {
                    firstPoint.Tangent = (points[1].Position - firstPoint.Position).normalized;
                    lastPoint.Tangent  = points[points.Count - 2].Tangent;
                }
                else
                {
                    switch (type)
                    {
                    case 0:
                        firstPoint.Tangent = lastPoint.Tangent = (lastPoint.Position - firstPoint.Position).normalized;
                        break;

                    case 1:
                        firstPoint.Tangent = Vector3.Normalize(section.OriginalToControl - section.OriginalFrom);
                        lastPoint.Tangent  = Vector3.Normalize(section.OriginalTo - section.OriginalToControl);
                        break;

                    case 2:
                        firstPoint.Tangent = Vector3.Normalize(section.OriginalFromControl - section.OriginalFrom);
                        lastPoint.Tangent  = Vector3.Normalize(section.OriginalTo - section.OriginalFromControl);
                        break;

                    case 3:
                        firstPoint.Tangent = Vector3.Normalize(section.OriginalFromControl - section.OriginalFrom);
                        lastPoint.Tangent  = Vector3.Normalize(section.OriginalTo - section.OriginalToControl);
                        break;
                    }
                }
            }
        }
        //------------------------------------------------------------------------ Quadratic
        //this method contains some intentional copy/paste
        private void RecursiveQuadraticSplit(SectionInfo section, double x0, double y0, double z0, double x1, double y1, double z1, double x2, double y2, double z2, int level, bool useSecond,
                                             bool calcTangents, double fromT, double toT)
        {
            if (level > RecursionLimit)
            {
                return;
            }

            // is curve flat
            // http://www.malinc.se/m/DeCasteljauAndBezier.php
            // sqr(b) + sqr(c) < sqr(a) * tolerance
            var dx01 = x0 - x1;
            var dy01 = y0 - y1;
            var dz01 = z0 - z1;

            var dx12 = x1 - x2;
            var dy12 = y1 - y2;
            var dz12 = z1 - z2;

            var dx02 = x0 - x2;
            var dy02 = y0 - y2;
            var dz02 = z0 - z2;

            var a = dx02 * dx02 + dy02 * dy02 + dz02 * dz02;
            var b = dx01 * dx01 + dy01 * dy01 + dz01 * dz01;
            var c = dx12 * dx12 + dy12 * dy12 + dz12 * dz12;

            var temp = a * toleranceRatioSquared - b - c;

            if (4 * b * c < temp * temp || a + b + c < DistanceTolerance)
            {
                return;
            }

            //at this point we know, the curve is not flat
            // split (Casteljau algorithm)
            var x01 = (x0 + x1) * .5;
            var y01 = (y0 + y1) * .5;
            var z01 = (z0 + z1) * .5;

            var x12 = (x1 + x2) * .5;
            var y12 = (y1 + y2) * .5;
            var z12 = (z1 + z2) * .5;

            var x012 = (x01 + x12) * .5;
            var y012 = (y01 + y12) * .5;
            var z012 = (z01 + z12) * .5;


            var t = calcTangents ? (fromT + toT) * .5 : .0;

            var pos = new Vector3((float)x012, (float)y012, (float)z012);

            //apply snapping
            if (curve.SnapType == BGCurve.SnapTypeEnum.Curve)
            {
                curve.ApplySnapping(ref pos);
            }

            //split first section
            RecursiveQuadraticSplit(section, x0, y0, z0, x01, y01, z01, x012, y012, z012, level + 1, useSecond, calcTangents, fromT, t);

            //add point
            SectionPointInfo pointInfo;
            var poolCursor = poolPointInfos.Count - 1;

            if (poolCursor >= 0)
            {
                pointInfo = poolPointInfos[poolCursor];
                poolPointInfos.RemoveAt(poolCursor);
            }
            else
            {
                pointInfo = new SectionPointInfo();
            }
            pointInfo.Position = pos;
            section.points.Add(pointInfo);

            //tangents
            if (calcTangents)
            {
                var control = useSecond ? section.OriginalToControl : section.OriginalFromControl;
                //idea.. optimize it
                pointInfo.Tangent = Vector3.Normalize(2 * (1 - (float)t) * (control - section.OriginalFrom) + 2 * (float)t * (section.OriginalTo - control));
            }

            //split second section
            RecursiveQuadraticSplit(section, x012, y012, z012, x12, y12, z12, x2, y2, z2, level + 1, useSecond, calcTangents, t, toT);
        }