Example #1
0
 /// <summary>
 /// The contour segment joining vertices A and B moving from A to B.  The constructed segment will
 /// map the arguemnt A to either this.A or this.B and map the argument B to the other member
 /// this.A or this.B such that dZ_dSinAB less or equal 0.0.  Thus, the segment A-->B is always
 /// compatible with a clockwise polygon (where Z is larger inside the polygon) and where A.Next is B
 /// and B.Prev is A.
 /// </summary>
 /// <param name="A">The starting vertex.</param>
 /// <param name="B">The ending vertex.</param>
 public ContourSegment(IsoContours Parent, ContourVertex A, ContourVertex B)
 {
     this.Parent = Parent;
     dx          = B.x - A.x;
     dy          = B.y - A.y;
     ds          = Math.Sqrt(dx * dx + dy * dy);
     dx_ds       = dx / ds;
     dy_ds       = dy / ds;
     dZ_dx       = 0.5 * (A.dZ_dx + B.dZ_dx);
     dZ_dy       = 0.5 * (A.dZ_dy + B.dZ_dy);
     dZ_dCosAB   = dZ_dx * dx_ds + dZ_dy * dy_ds;
     dZ_dSinAB   = -dZ_dx * dy_ds + dZ_dy * dx_ds;
     if (dZ_dSinAB <= 0.0)
     {
         this.A = A;
         this.B = B;
     }
     else
     {
         this.A    = B;
         this.B    = A;
         dx        = -dx;
         dy        = -dy;
         dx_ds     = -dx_ds;
         dy_ds     = -dy_ds;
         dZ_dx     = -dZ_dx;
         dZ_dy     = -dZ_dy;
         dZ_dCosAB = -dZ_dCosAB;
         dZ_dSinAB = -dZ_dSinAB;
     }
     this.Affinity = (1.0 + (A.dZ_dx * B.dZ_dx + A.dZ_dy * B.dZ_dy) / (A.dZ_dGradient * B.dZ_dGradient)) / ds;
     //this.Affinity = 1.0/ds;
 }
Example #2
0
        /// <summary>
        /// Construct a new contour with the specified ID
        /// </summary>
        /// <param name="IdContour">An integer ID number associated with this contour.</param>
        /// <param name="cv">A ContourVertex in the contour.</param>
        /// <param name="nMax">The maximum number of vertices that the contour could contain.</param>
        public Contour(int IdContour, ContourVertex cv, int nMax, Rect DataRect)
        {
            this.DataRect  = DataRect;
            this.idContour = IdContour;
            if (cv.IdContour >= 0 && cv.IdContour != idContour)
            {
                Vertices = new ContourVertex[0];
            }
            int           ct      = 1;
            ContourVertex cvFirst = cv;
            ContourVertex cvLast  = cv;

            //	Make cvLast keep going until end.
            while (cvLast.Next != null)
            {
                if (cvLast.Next == cvFirst)
                {
                    this.IsClosed = true;
                    break;
                }
                else
                {
                    ct++;
                    cvLast = cvLast.Next;
                }
                if (ct > nMax)
                {
                    throw new Exception("Loop continues too far.");
                }
            }
            if (!IsClosed)
            {
                while (cvFirst.Prev != null)
                {
                    ct++;
                    cvFirst = cvFirst.Prev;
                    if (ct > nMax)
                    {
                        throw new Exception("Loop continues too far.");
                    }
                }
            }
            Vertices = new ContourVertex[ct];
            cv       = cvFirst;
            for (int i = 0; i < ct; i++)
            {
                Vertices[i]  = cv;
                cv.IdContour = IdContour;
                cv           = cv.Next;
            }
        }
Example #3
0
        /// <summary>
        /// Constructs the iso-value contours for the function tabulated in Z.
        /// </summary>
        /// <param name="Z"></param>
        /// <param name="IsoZ">The value of Z isolated by the contours.</param>
        public IsoContours(double[,] Z, double IsoZ)
        {
            this.Z    = Z;
            this.IsoZ = IsoZ;
            int iLen = Z.GetLength(0);
            int jLen = Z.GetLength(1);

            this.MeshRect = new Rect(0.0f, 0.0f, Z.GetLength(1) - 1, Z.GetLength(0) - 1);
            ContourVertex[,] zCrossings = new ContourVertex[iLen, jLen];
            double u0, v0, u1, v1, z00, z01, z10, z11, dz0, dz1;

            bool[]        crosses = new bool[4];
            ContourVertex cv;
            int           i, j, ii, jj, ctCrosses, ia, ja;

            for (ii = 1; ii < iLen; ii++)
            {
                i = ii - 1;
                for (jj = 1; jj < jLen; jj++)
                {
                    //	In the comments below, let
                    //		"Left" refers to lower col indexes of 'Z'
                    //		"Right" refers to higher col indexes of 'Z'
                    //		"Top" refers to higher row indexes of 'Z'
                    //		"Bottom" refers to lower row indexes of 'Z'
                    j         = jj - 1;
                    ctCrosses = 0;
                    //zmid = 0.25* (Z[ii,jj] + Z[i,jj] + Z[ii,j] + Z[i,j]);
                    z00 = Z[i, j];
                    z10 = Z[ii, j];
                    z01 = Z[i, jj];
                    z11 = Z[ii, jj];
                    //	Left
                    crosses[0] = (z00 < IsoZ) != (z10 < IsoZ);
                    //	Right
                    crosses[1] = (z01 < IsoZ) != (z11 < IsoZ);
                    //	Top
                    crosses[2] = (z10 < IsoZ) != (z11 < IsoZ);
                    //	Bottom
                    crosses[3] = (z00 < IsoZ) != (z01 < IsoZ);
                    //	Count crossings
                    if (crosses[0])
                    {
                        ctCrosses++;
                    }
                    if (crosses[1])
                    {
                        ctCrosses++;
                    }
                    if (crosses[2])
                    {
                        ctCrosses++;
                    }
                    if (crosses[3])
                    {
                        ctCrosses++;
                    }
                    //  ctCrosses==0 --> No intersection (do nothing)
                    //	ctCrosses==1 --> Not possible (do nothing)
                    //	ctCrosses==3 --> Not possible (do nothing)
                    //	ctCrosses==4 --> Ambiguous  (do nothing).
                    if (ctCrosses == 2) //	The contour intersects the current cell.
                    {
                        cv = new ContourVertex(this);
                        if (crosses[0] && crosses[1]) //	Horizontal signature
                        {
                            //	Left
                            dz0 = z10 - z00;
                            u0  = Math.Abs(z00 - IsoZ);
                            v0  = Math.Abs(z10 - IsoZ);
                            u0  = u0 / (u0 + v0);
                            //	Right
                            dz1 = z11 - z01;
                            u1  = Math.Abs(z01 - IsoZ);
                            v1  = Math.Abs(z11 - IsoZ);
                            u1  = u1 / (u1 + v1);
                            //	Position
                            cv.x = (double)j + 0.5;
                            cv.y = (double)i + 0.5 * (u0 + u1);
                            //	Angle
                            if (dz0 > 0.0)
                            {
                                cv.GradientRadians = Math.Atan2(u1 - u0, 1.0) + D.Pi_1_2;
                            }
                            else
                            {
                                cv.GradientRadians = Math.Atan2(u1 - u0, 1.0) - D.Pi_1_2;
                            }
                            //	Gradient
                            cv.dZ_dy        = 0.5 * (dz0 + dz1);
                            cv.dZ_dGradient = cv.dZ_dy / Math.Sin(cv.GradientRadians);
                            cv.dZ_dx        = cv.dZ_dGradient * Math.Cos(cv.GradientRadians);
                            //	Storage
                            zCrossings[i, j] = cv;
                            Vertices.Add(cv);
                        }
                        else if (crosses[2] && crosses[3])      //	Vertical signature
                        {
                            //	Bottom
                            dz0 = z01 - z00;
                            u0  = Math.Abs(z00 - IsoZ);
                            v0  = Math.Abs(z01 - IsoZ);
                            u0  = u0 / (u0 + v0);
                            //	Top
                            dz1 = z11 - z10;
                            u1  = Math.Abs(z10 - IsoZ);
                            v1  = Math.Abs(z11 - IsoZ);
                            u1  = u1 / (u1 + v1);
                            //	Position
                            cv.x = (double)j + 0.5 * (u0 + u1);
                            cv.y = (double)i + 0.5;
                            //	Angle
                            if (dz0 > 0.0)
                            {
                                cv.GradientRadians = Math.Atan2(u0 - u1, 1.0);
                            }
                            else
                            {
                                cv.GradientRadians = Math.Atan2(u0 - u1, 1.0) + Math.PI;
                            }
                            //	Gradient
                            cv.dZ_dx        = 0.5 * (dz0 + dz1);
                            cv.dZ_dGradient = cv.dZ_dx / Math.Cos(cv.GradientRadians);
                            cv.dZ_dy        = cv.dZ_dGradient * Math.Sin(cv.GradientRadians);
                            //	Storage
                            zCrossings[i, j] = cv;
                            Vertices.Add(cv);
                        }
                        else //	Mixed signature
                        {
                            if (crosses[0]) // Left
                            {
                                if (crosses[2]) // Top
                                {
                                    //	Top-left corner is cut off z10
                                    //	Top
                                    dz0 = z11 - z10; // dz_dx along top
                                    u0  = u1 = Math.Abs(z10 - IsoZ);
                                    v0  = Math.Abs(z11 - IsoZ);
                                    u0  = u0 / (u0 + v0);
                                    //	Left
                                    dz1 = z10 - z00; // dz_dy along left
                                    //u1 = Math.Abs(z10-IsoZ);
                                    v1 = Math.Abs(z00 - IsoZ);
                                    u1 = u1 / (u1 + v1);
                                    //	Position  (midpoint of hypotenuse)
                                    cv.x = (double)j + 0.5 * u0;  // distance from left
                                    cv.y = (double)ii - 0.5 * u1; // distance from top
                                    //	Angle
                                    if (dz1 > 0.0)
                                    {
                                        cv.GradientRadians = Math.Atan2(u1, u0) + D.Pi_1_2;
                                    }
                                    else
                                    {
                                        cv.GradientRadians = Math.Atan2(u1, u0) - D.Pi_1_2;
                                    }
                                    //	Gradient
                                    cv.dZ_dx = dz0;
                                    cv.dZ_dy = dz1;
                                    if (Math.Abs(dz0) > Math.Abs(dz1))
                                    {
                                        cv.dZ_dGradient = cv.dZ_dx / Math.Cos(cv.GradientRadians);
                                    }
                                    else
                                    {
                                        cv.dZ_dGradient = cv.dZ_dy / Math.Sin(cv.GradientRadians);
                                    }
                                    //	Storage
                                    zCrossings[i, j] = cv;
                                    Vertices.Add(cv);
                                }
                                else if (crosses[3]) // Bottom
                                {
                                    //	Bottom-left corner is cut off  z00
                                    //	Bottom
                                    dz0 = z01 - z00; // dz_dx along bottom
                                    u0  = u1 = Math.Abs(z00 - IsoZ);
                                    v0  = Math.Abs(z01 - IsoZ);
                                    u0  = u0 / (u0 + v0);
                                    //	Left
                                    dz1 = z10 - z00; // dz_dy along left
                                    //u1 = Math.Abs(z00-IsoZ);
                                    v1 = Math.Abs(z10 - IsoZ);
                                    u1 = u1 / (u1 + v1);
                                    //	Position  (midpoint of hypotenuse)
                                    cv.x = (double)j + 0.5 * u0; // distance from left
                                    cv.y = (double)i + 0.5 * u1; // distance from bottom
                                    //	Angle
                                    if (dz1 > 0.0)
                                    {
                                        cv.GradientRadians = D.Pi_1_2 - Math.Atan2(u1, u0); //Math.PI-Math.Atan2(u1,u0)-RADIANS_1_4;
                                    }
                                    else
                                    {
                                        cv.GradientRadians = D.Pi_3_2 - Math.Atan2(u1, u0); //Math.PI-Math.Atan2(u1,u0)+RADIANS_1_4;
                                    }
                                    //	Gradient
                                    cv.dZ_dx = dz0;
                                    cv.dZ_dy = dz1;
                                    if (Math.Abs(dz0) > Math.Abs(dz1))
                                    {
                                        cv.dZ_dGradient = cv.dZ_dx / Math.Cos(cv.GradientRadians);
                                    }
                                    else
                                    {
                                        cv.dZ_dGradient = cv.dZ_dy / Math.Sin(cv.GradientRadians);
                                    }
                                    //	Storage
                                    zCrossings[i, j] = cv;
                                    Vertices.Add(cv);
                                }
                            }
                            if (crosses[1])     // Right
                            {
                                if (crosses[2]) // Top
                                {
                                    //	Top-right corner is cut off z11
                                    //	Top
                                    dz0 = z11 - z10; // dz_dx along top
                                    u0  = u1 = Math.Abs(z11 - IsoZ);
                                    v0  = Math.Abs(z10 - IsoZ);
                                    u0  = u0 / (u0 + v0);
                                    //	Right
                                    dz1 = z11 - z01; // dz_dy along right
                                    //u1 = Math.Abs(z11-IsoZ);
                                    v1 = Math.Abs(z01 - IsoZ);
                                    u1 = u1 / (u1 + v1);
                                    //	Position  (midpoint of hypotenuse)
                                    cv.x = (double)jj - 0.5 * u0; // distance from right
                                    cv.y = (double)ii - 0.5 * u1; // distance from top
                                    //	Angle
                                    if (dz1 > 0.0)
                                    {
                                        cv.GradientRadians = D.Pi_3_2 - Math.Atan2(u1, u0); //Math.PI-Math.Atan2(u1,u0)-RADIANS_1_4;
                                    }
                                    else
                                    {
                                        cv.GradientRadians = D.Pi_3_2 - Math.Atan2(u1, u0); //Math.PI-Math.Atan2(u1,u0)+RADIANS_1_4;
                                    }
                                    //	Gradient
                                    cv.dZ_dx = dz0;
                                    cv.dZ_dy = dz1;
                                    if (Math.Abs(dz0) > Math.Abs(dz1))
                                    {
                                        cv.dZ_dGradient = cv.dZ_dx / Math.Cos(cv.GradientRadians);
                                    }
                                    else
                                    {
                                        cv.dZ_dGradient = cv.dZ_dy / Math.Sin(cv.GradientRadians);
                                    }
                                    //	Storage
                                    zCrossings[i, j] = cv;
                                    Vertices.Add(cv);
                                }
                                else if (crosses[3]) // Bottom
                                {
                                    //	Bottom-right corner is cut off  z01
                                    //	Bottom
                                    dz0 = z01 - z00; // dz_dx
                                    u0  = u1 = Math.Abs(z01 - IsoZ);
                                    v0  = Math.Abs(z00 - IsoZ);
                                    u0  = u0 / (u0 + v0);
                                    //	Right
                                    dz1 = z11 - z01; // dz_dy
                                    //u1 = Math.Abs(z01-IsoZ);
                                    v1 = Math.Abs(z11 - IsoZ);
                                    u1 = u1 / (u1 + v1);
                                    //	Position  (midpoint of hypotenuse)
                                    cv.x = (double)jj - 0.5 * u0; // distance from right
                                    cv.y = (double)i + 0.5 * u1;  // distance from bottom
                                    //	Angle
                                    if (dz1 > 0.0)
                                    {
                                        cv.GradientRadians = Math.Atan2(u1, u0) + D.Pi_1_2;
                                    }
                                    else
                                    {
                                        cv.GradientRadians = Math.Atan2(u1, u0) - D.Pi_1_2;
                                    }
                                    //	Gradient
                                    cv.dZ_dx = dz0;
                                    cv.dZ_dy = dz1;
                                    if (Math.Abs(dz0) > Math.Abs(dz1))
                                    {
                                        cv.dZ_dGradient = cv.dZ_dx / Math.Cos(cv.GradientRadians);
                                    }
                                    else
                                    {
                                        cv.dZ_dGradient = cv.dZ_dy / Math.Sin(cv.GradientRadians);
                                    }
                                    //	Storage
                                    zCrossings[i, j] = cv;
                                    Vertices.Add(cv);
                                }
                            }
                        }
                        //	ContourSegment relations
                        ia = i - 1;
                        ja = j - 1;
                        if (ia >= 0 && zCrossings[ia, j] != null)
                        {
                            Segments.Add(new ContourSegment(this, cv, zCrossings[ia, j]));
                        }
                        if (ia >= 0 && ja >= 0 && zCrossings[ia, ja] != null)
                        {
                            Segments.Add(new ContourSegment(this, cv, zCrossings[ia, ja]));
                        }
                        if (ja >= 0 && zCrossings[i, ja] != null)
                        {
                            Segments.Add(new ContourSegment(this, cv, zCrossings[i, ja]));
                        }
                    }
                }
            }
            //	Sort the potential contour segments by ascending affinity
            Segments.Sort();
            //	Segment processing logic.
            i = Segments.Count - 1;
            //	Trim any segments that have overlapping vertices
            while (i >= 0 && Segments[i].ds <= 0.0)
            {
                Segments.RemoveAt(i--);
            }
            //	Join the unjoined vertices.
            while (i >= 0)
            {
                Segments[i--].JoinUnjoinedVertices();
            }
            //	Contour formation logic, begin by sorting vertices by descending LowestAffinity.
            Vertices.Sort();
            Vertices.Reverse();
            int nMax = Vertices.Count;

            j = 0;
            for (i = 0; i < Vertices.Count && nMax > 3; i++)
            {
                if (Vertices[i].IdContour == int.MinValue)
                {
                    Contours.Add(new Contour(j, Vertices[i], nMax, MeshRect));
                    nMax -= Contours[j].Vertices.Length;
                    j++;
                }
            }
            //	Sort by contour length descending (longest contour first, shortest last)
            Contours.Sort();
            Contours.Reverse();
            //	Adjust the vertex coordinates to the DataRect
            for (i = 0; i < Contours.Count; i++)
            {
                Contours[i].IdContour = i;
            }
        }