private VerticalPrismalSurface CreateSurface(bool isClosedBase = false, bool isClosedSurface = false, bool isCounterclockwise = true)
        {
            var baseVertices = new List <Vector2>
            {
                new Vector2(1.1, 2.2),
                new Vector2(3.5, -5.3),
                new Vector2(-7.8, 9.1),
                new Vector2(-1.8, 0.1),
                new Vector2(3.8, 6.1)
            };

            IPolyline @base;

            if (isClosedBase)
            {
                var line = new ClosedPolyline(baseVertices);
                @base = line.SignedArea() > 0 ^ isCounterclockwise ? line : line.AsReverse();
            }
            else
            {
                @base = new OpenPolyline(baseVertices);
            }

            var zRange = new Interval(-3.14, 2.88);

            return(new VerticalPrismalSurface(@base, zRange, isClosedSurface));
        }
        public void Face_Normal_is_consistent_with_edge_order_for_convex_base([ValueSource("GetFaceOrientationTestCases")] FaceOrientationTestCase testCase)
        {
            var baseVertices = new List <Vector2>
            {
                new Vector2(-1, +2),
                new Vector2(+1, +2),
                new Vector2(+2, +1),
                new Vector2(+2, -1),
                new Vector2(+1, -2),
                new Vector2(-1, -2),
                new Vector2(-2, -1),
                new Vector2(-2, +1)
            };

            var line  = new ClosedPolyline(baseVertices);
            var @base = testCase.IsOrientedOutside ? line.AsReverse() : line;

            var zRange = new Interval(-3.14, 2.88);

            var surface = new VerticalPrismalSurface(@base, zRange, isClosed: true);

            IPolysurfaceFace face;

            if (testCase.IsVertical)
            {
                face = surface.GetVerticalFace(2);
            }
            else
            {
                face = surface.GetHorizontalFace(testCase.Side);
            }

            var edge1 = face.Edges.ElementAt(0);
            var edge2 = face.Edges.ElementAt(1);

            var expected = edge1.Vector().Cross(edge2.Vector());

            Expect(face.Normal * expected, Is.Positive);
        }
        public void ClosedPolyline_SignedArea_is_correct_for_horizontal_triangle()
        {
            var x = -0.13;
            var y = 3.14;
            var a = 1.44;
            var h = 2.72;

            IEnumerable <Vector2> triangleVertices = new List <Vector2>
            {
                new Vector2(x, y),
                new Vector2(5.555, y + h),
                new Vector2(x + a, y)
            };

            var area = a * h / 2;

            var negativeTriangle = new ClosedPolyline(triangleVertices.ToList());

            Expect(negativeTriangle.SignedArea(), Is.EqualTo(-area).Within(_tolerance));

            var positiveTriangle = new ClosedPolyline(triangleVertices.Reverse().ToList());

            Expect(positiveTriangle.SignedArea(), Is.EqualTo(area).Within(_tolerance));
        }