public GeneralizedDistanceTransform2D(
            Range rangeX, Range rangeY, Size gridSize)
        {
            if (rangeX.Outside || rangeY.Outside)
                throw new ArgumentException("Outside ranges are not allowed.");

            this.RangeX = rangeX;
            this.RangeY = rangeY;
            this.GridSize = gridSize;

            this.values = new double[this.GridSize.Width, this.GridSize.Height];
            this.bestIndices = new Tuple<int, int>[this.GridSize.Width, this.GridSize.Height];
            this.timeStamps = new int[this.GridSize.Width, this.GridSize.Height];

            this.transformsForFixedGridX = new GeneralizedDistanceTransform1D[this.GridSize.Width];
            for (int x = 0; x < this.GridSize.Width; ++x)
                this.transformsForFixedGridX[x] = new GeneralizedDistanceTransform1D(rangeY, this.GridSize.Height);
            
            this.transformsForFixedGridY = new GeneralizedDistanceTransform1D[this.GridSize.Height];
            this.usedTransformsForFixedGridY = new GeneralizedDistanceTransform1D[this.GridSize.Height];
            for (int y = 0; y < this.GridSize.Height; ++y)
                transformsForFixedGridY[y] = new GeneralizedDistanceTransform1D(rangeX, this.GridSize.Width);

            this.infiniteTransformX = new GeneralizedDistanceTransform1D(rangeX, this.GridSize.Width);
            this.infiniteTransformX.Compute(1, x => 1e+20);
        }
 public void TestRange5()
 {
     Range range1 = new Range(1, 2, true);
     Range range2 = new Range(1.1, 1.3, true);
     Assert.IsTrue(range1.IntersectsWith(range2));
     Assert.IsTrue(range2.IntersectsWith(range1));
     Assert.IsTrue(Double.IsPositiveInfinity(range1.Length));
     Assert.IsTrue(Double.IsPositiveInfinity(range2.Length));
 }
 public void TestRange4()
 {
     const double eps = 1e-10;
     
     Range range1 = new Range(1, 2, true);
     Range range2 = new Range(1.1, 1.3, false);
     Assert.IsFalse(range1.IntersectsWith(range2));
     Assert.IsFalse(range2.IntersectsWith(range1));
     Assert.IsTrue(Double.IsPositiveInfinity(range1.Length));
     Assert.AreEqual(range2.Length, 0.2, eps);
 }
 public void TestRange6()
 {
     const double eps = 1e-10;
     
     Range range1 = new Range(1, 1);
     Range range2 = new Range(1, 2);
     Assert.IsTrue(range1.IntersectsWith(range2));
     Assert.IsTrue(range2.IntersectsWith(range1));
     Assert.AreEqual(range1.Length, 0, eps);
     Assert.AreEqual(range2.Length, 1, eps);
 }
 public void TestRange2()
 {
     const double eps = 1e-10;
     
     Range range1 = new Range(1, 2);
     Range range2 = new Range(2.1, 2.5);
     Assert.IsFalse(range1.IntersectsWith(range2));
     Assert.IsFalse(range2.IntersectsWith(range1));
     Assert.AreEqual(range1.Length, 1, eps);
     Assert.AreEqual(range2.Length, 0.4, eps);
 }
        public LengthAngleSpaceSeparator(
            Vector segmentStart, Vector segmentEnd, Vector point, double allowedLength, double allowedAngle, bool swapDirection)
        {
            if (allowedAngle < -Math.PI || allowedAngle > Math.PI)
                throw new ArgumentOutOfRangeException("allowedAngle", "Allowed angle should be in [-pi, pi] range.");
            if (allowedLength < 0)
                throw new ArgumentOutOfRangeException("allowedLength", "Allowed length should be positive.");
            if ((segmentStart - segmentEnd).LengthSquared < 1e-6)
                throw new ArgumentException("Given segment must have non-zero length!");
            
            double distanceToSegmentSqr, alpha;
            point.DistanceToSegmentSquared(segmentStart, segmentEnd, out distanceToSegmentSqr, out alpha);

            Vector zeroAngleVec = segmentStart + (segmentEnd - segmentStart) * alpha - point;
            this.distanceSqr = zeroAngleVec.LengthSquared;

            if (this.distanceSqr < 1e-6)
            {
                this.singularMode = true;
                
                double angle = Vector.AngleBetween(Vector.UnitX, segmentEnd - segmentStart);
                double minAngle, maxAngle;
                if (angle < 0)
                {
                    minAngle = angle;
                    maxAngle = angle + Math.PI;
                }
                else
                {
                    maxAngle = angle;
                    minAngle = angle - Math.PI;
                }

                singularModeAngleRange = new Range(minAngle, maxAngle);
                if (!singularModeAngleRange.Contains(allowedAngle))
                    singularModeAngleRange = singularModeAngleRange.Invert();
            }
            else
            {
                this.singularMode = false;

                this.angleOffset = -Vector.AngleBetween(Vector.UnitX, zeroAngleVec);
                double offsetedAllowedAngle = this.OffsetAngle(allowedAngle);
                if (offsetedAllowedAngle < -Math.PI * 0.5 || offsetedAllowedAngle > Math.PI * 0.5)
                    throw new ArgumentOutOfRangeException("allowedAngle", "After translation to the coordinate system of the given segment, allowed angle must be in [-pi/2, pi/2] range.");
                this.sign = Math.Sign(this.SeparationValue(allowedLength, offsetedAllowedAngle));
            }

            this.swapDirection = swapDirection;
        }
 public bool IntersectsWith(Range other)
 {
     // Regular ranges
     if (!this.Outside && !other.Outside)
     {
         return
             this.Left >= other.Left && this.Left <= other.Right ||
             other.Left >= this.Left && other.Left <= this.Right;
     }
     // Two inverted ranges always intersect
     if (this.Outside && other.Outside)
         return true;
     // Regular range intersects with inverted if it's not inside
     if (this.Outside)
         return other.Left <= this.Left || other.Right >= this.Right;
     return this.Left <= other.Left || this.Right >= other.Right;
 }
        public GeneralizedDistanceTransform1D(Range range, int gridSize)
        {
            if (range.Outside)
                throw new ArgumentException("Outside ranges are not allowed.", "range");

            this.Range = range;
            this.GridSize = gridSize;
            // Our grid covers all the evenly distributed points from min to max in a way that each point is the center of its cell
            this.GridStepSize = range.Length / (this.GridSize - 1);

            // Allocate all the necessary things
            this.envelope = new int[this.GridSize];
            this.parabolaRange = new double[this.GridSize + 1];
            this.functionValues = new double[this.GridSize];
            this.values = new double[this.GridSize];
            this.bestIndices = new int[this.GridSize];
            this.timeStamps = new int[this.GridSize];
        }
 public void TestRange8()
 {
     Range range1 = new Range(1, 2);
     Range range2 = new Range(1, 2);
     Assert.IsTrue(range1.IntersectsWith(range2));
     Assert.IsTrue(range2.IntersectsWith(range1));
 }
 public void AddFinitePenaltyRangeY(Range rangeY)
 {
     this.IsComputed = false;
     foreach (GeneralizedDistanceTransform1D transform in transformsForFixedGridX)
         transform.AddFinitePenaltyRange(rangeY);
 }
        public void TestEdgeConstraintSplitsNonIntersection()
        {
            EdgeConstraints constraints = new EdgeConstraints(3, 5);
            List<EdgeConstraints> split = constraints.Split();
            Assert.IsTrue(split.Count == 2);

            Range range1 = new Range(split[0].MinWidth, split[0].MaxWidth, false);
            Range range2 = new Range(split[1].MinWidth, split[1].MaxWidth, false);
            Assert.IsFalse(range1.IntersectsWith(range2));
        }
 public void AddInterestRangeX(Range rangeX)
 {
     this.IsComputed = false;
     foreach (GeneralizedDistanceTransform1D transform in transformsForFixedGridY)
         transform.AddInterestRange(rangeX);
 }
        private static double CalculateSingleEdgeLowerBound(
            ShapeModel model, ShapeConstraints shapeConstraints, IList<ILengthAngleConstraints> lengthAngleConstraints)
        {
            // TODO: support models with multiple edges but without pairwise constraints
            Debug.Assert(model.Structure.Edges.Count == 1);
            Debug.Assert(lengthAngleConstraints.Count == 1);

            double result;

            // Calculate best possible edge width penalty
            EdgeConstraints edgeConstraints = shapeConstraints.EdgeConstraints[0];
            ShapeEdgeParams edgeParams = model.GetEdgeParams(0);
            Range lengthBoundary = lengthAngleConstraints[0].LengthBoundary;
            Range scaledLengthRange = new Range(lengthBoundary.Left * edgeParams.WidthToEdgeLengthRatio, lengthBoundary.Right * edgeParams.WidthToEdgeLengthRatio);
            Range widthRange = new Range(edgeConstraints.MinWidth, edgeConstraints.MaxWidth);
            if (scaledLengthRange.IntersectsWith(widthRange))
                result = 0;
            else
            {
                result = model.CalculateEdgeWidthEnergyTerm(0, widthRange.Right, lengthBoundary.Left);
                result = Math.Min(result, model.CalculateEdgeWidthEnergyTerm(0, widthRange.Left, lengthBoundary.Right));
            }

            return result;
        }
        private static void TestEdgeLimitsCommonImpl(
            VertexConstraints constraint1, VertexConstraints constraint2, out Range lengthRange, out Range angleRange)
        {
            BoxSetLengthAngleConstraints lengthAngleConstraints =
                BoxSetLengthAngleConstraints.FromVertexConstraints(constraint1, constraint2, 3, 0);
            GeneralizedDistanceTransform2D transform = new GeneralizedDistanceTransform2D(
                new Range(0, 35), new Range(-Math.PI * 2, Math.PI * 2), new Size(2000, 2000));
            AllowedLengthAngleChecker allowedLengthAngleChecker = new AllowedLengthAngleChecker(constraint1, constraint2, transform, 1, 0);

            lengthRange = lengthAngleConstraints.LengthBoundary;
            angleRange = lengthAngleConstraints.AngleBoundary;

            const int insideCheckCount = 1000;
            for (int i = 0; i < insideCheckCount; ++i)
            {
                Vector edgePoint1 =
                    constraint1.MinCoord +
                    new Vector(
                        Random.Double() * (constraint1.MaxCoord.X - constraint1.MinCoord.X),
                        Random.Double() * (constraint1.MaxCoord.Y - constraint1.MinCoord.Y));
                Vector edgePoint2 =
                    constraint2.MinCoord +
                    new Vector(
                        Random.Double() * (constraint2.MaxCoord.X - constraint2.MinCoord.X),
                        Random.Double() * (constraint2.MaxCoord.Y - constraint2.MinCoord.Y));

                Vector vec = edgePoint2 - edgePoint1;
                double length = vec.Length;
                double angle = Vector.AngleBetween(Vector.UnitX, vec);

                const double tolerance = 1e-10;
                Assert.IsTrue(lengthAngleConstraints.InRange(length, tolerance, angle, tolerance));
                Assert.IsTrue(lengthAngleConstraints.OverallRange.InRange(length, tolerance, angle, tolerance));
                Assert.IsTrue(allowedLengthAngleChecker.IsAllowed(length, angle));
            }

            const int outsideCheckCount = 1000;
            for (int i = 0; i < outsideCheckCount; ++i)
            {
                Vector edgePoint1 =
                    constraint1.MinCoord +
                    new Vector(
                        (Random.Double() * 2 - 0.5) * (constraint1.MaxCoord.X - constraint1.MinCoord.X),
                        (Random.Double() * 2 - 0.5) * (constraint1.MaxCoord.Y - constraint1.MinCoord.Y));
                Vector edgePoint2 =
                    constraint2.MinCoord +
                    new Vector(
                        (Random.Double() * 2 - 0.5) * (constraint2.MaxCoord.X - constraint2.MinCoord.X),
                        (Random.Double() * 2 - 0.5) * (constraint2.MaxCoord.Y - constraint2.MinCoord.Y));

                Vector vec = edgePoint2 - edgePoint1;
                double length = vec.Length;
                double angle = Vector.AngleBetween(Vector.UnitX, vec);

                // We've generated too long edge
                if (length > transform.RangeX.Right)
                    continue;

                bool definitelyOutside = !lengthAngleConstraints.OverallRange.InRange(length, 1e-6, angle, 1e-6);
                Assert.IsTrue(!definitelyOutside || !allowedLengthAngleChecker.IsAllowed(length, angle));
                Assert.IsTrue(!definitelyOutside || !lengthAngleConstraints.InRange(length, 1e-6, angle, 1e-6));
            }
        }
        private void AddRange(Range range, IList<Range> rangeCollection)
        {
            if (range.Outside)
                throw new ArgumentException("Outside ranges are not supported.", "range");
            if (!this.Range.Contains(range.Left) || !this.Range.Contains(range.Right))
                throw new ArgumentException("Given range is outside range of this distance tranform.", "range");

            rangeCollection.Add(range);
            this.IsComputed = false;
        }
 public void AddFinitePenaltyRange(Range range)
 {
     AddRange(range, this.finitePenaltyRanges);
 }
        public void TestVertexConstraintSplitsNonIntersection()
        {
            VertexConstraints constraints = new VertexConstraints(new Vector(0, 0), new Vector(1, 1));

            List<VertexConstraints> split = constraints.Split();
            Assert.IsTrue(split.Count == 4);
            for (int i = 0; i < split.Count; ++i)
            {
                for (int j = i + 1; j < split.Count; ++j)
                {
                    Range xRange1 = new Range(split[i].MinCoord.X, split[i].MaxCoord.X, false);
                    Range yRange1 = new Range(split[i].MinCoord.Y, split[i].MaxCoord.Y, false);
                    Range xRange2 = new Range(split[j].MinCoord.X, split[j].MaxCoord.X, false);
                    Range yRange2 = new Range(split[j].MinCoord.Y, split[j].MaxCoord.Y, false);

                    Assert.IsFalse(xRange1.IntersectsWith(xRange2) && yRange1.IntersectsWith(yRange2));
                }
            }
        }
        public static BoxLengthAngleConstraints FromVertexConstraints(VertexConstraints constraints1, VertexConstraints constraints2)
        {
            Range angleRange;

            Range xRange1 = new Range(constraints1.MinCoord.X, constraints1.MaxCoord.X);
            Range yRange1 = new Range(constraints1.MinCoord.Y, constraints1.MaxCoord.Y);
            Range xRange2 = new Range(constraints2.MinCoord.X, constraints2.MaxCoord.X);
            Range yRange2 = new Range(constraints2.MinCoord.Y, constraints2.MaxCoord.Y);

            bool xIntersection = xRange1.IntersectsWith(xRange2);
            bool yIntersection = yRange1.IntersectsWith(yRange2);

            double minLength = Double.PositiveInfinity, maxLength = 0;

            if (xIntersection && yIntersection)
            {
                // Special case: intersecting rectangles
                angleRange = new Range(-Math.PI, Math.PI);
                minLength = 0;
            }
            else
            {
                // Angle changes from PI to -PI when second constraint is to the left of the first one
                bool angleSignChanges = constraints1.MinCoord.X > constraints2.MaxCoord.X && yIntersection;

                double minAngle = angleSignChanges ? -Math.PI : Math.PI;
                double maxAngle = angleSignChanges ? Math.PI : -Math.PI;
                foreach (Vector point1 in constraints1.Corners)
                {
                    foreach (Vector point2 in constraints2.Corners)
                    {
                        double angle = Vector.AngleBetween(new Vector(1, 0), point2 - point1);
                        if (angleSignChanges)
                        {
                            if (angle < 0)
                                minAngle = Math.Max(minAngle, angle);
                            else
                                maxAngle = Math.Min(maxAngle, angle);
                        }
                        else
                        {
                            minAngle = Math.Min(minAngle, angle);
                            maxAngle = Math.Max(maxAngle, angle);
                        }
                    }
                }
                angleRange = new Range(minAngle, maxAngle, angleSignChanges);

                // One constraint is on top or on bottom of another
                if (xIntersection)
                {
                    // 1 on top of 2
                    if (constraints1.MinCoord.Y > constraints2.MaxCoord.Y)
                        minLength = Math.Min(minLength, constraints1.MinCoord.Y - constraints2.MaxCoord.Y);
                    // 2 on top of 1
                    else
                        minLength = Math.Min(minLength, constraints2.MinCoord.Y - constraints1.MaxCoord.Y);
                }
                else if (yIntersection)
                {
                    // 1 to the left of 2
                    if (constraints1.MaxCoord.X < constraints2.MinCoord.X)
                        minLength = Math.Min(minLength, constraints2.MinCoord.X - constraints1.MaxCoord.X);
                    // 2 to the left of 1
                    else
                        minLength = Math.Min(minLength, constraints1.MinCoord.X - constraints2.MaxCoord.X);
                }
            }

            foreach (Vector point1 in constraints1.Corners)
            {
                foreach (Vector point2 in constraints2.Corners)
                {
                    double length = (point1 - point2).Length;
                    minLength = Math.Min(minLength, length);
                    maxLength = Math.Max(maxLength, length);
                }
            }

            Range lengthRange = new Range(minLength, maxLength);

            return new BoxLengthAngleConstraints(lengthRange, angleRange);
        }
        private BoxLengthAngleConstraints(Range lengthBoundary, Range angleBoundary)
        {
            Debug.Assert(!lengthBoundary.Outside);

            this.LengthBoundary = lengthBoundary;
            this.AngleBoundary = angleBoundary;
        }
 public void AddInterestRange(Range range)
 {
     AddRange(range, this.interestRanges);
 }