//This is to cater for situations where the mesh has duplicate points; these are when the same combination of x/y/z values are repeated
        //in the vertices collection
        public void Consolidate()
        {
            var vPts = Enumerable.Range(0, Vertices.Count() / 3).Select(v => new Point3D(Vertices[v * 3], Vertices[(v * 3) + 1], Vertices[(v * 3) + 2])).ToList();

            //This algorithm is O(N^2) at the moment
            var indexConsolidateMappings = new Dictionary <int, int>();
            var newPts = new List <Point3D>();

            for (var i = 0; i < vPts.Count(); i++)
            {
                var found = false;
                for (int j = 0; j < newPts.Count(); j++)
                {
                    if (vPts[i].Equals(newPts[j], Helper.PointComparisonEpsilon))
                    {
                        indexConsolidateMappings.Add(i, j);
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    var newIndex = newPts.Count();
                    newPts.Add(vPts[i]);
                    indexConsolidateMappings.Add(i, newIndex);
                }
            }

            var newFaces = Faces.ToList();
            var f        = 0;

            do
            {
                var numInFace = (newFaces[f] == 0) ? 3 : 4;
                if ((f + numInFace) < newFaces.Count())
                {
                    f++;
                    for (var v = 0; v < numInFace; v++)
                    {
                        if (indexConsolidateMappings.ContainsKey(newFaces[f + v]))
                        {
                            newFaces[f + v] = indexConsolidateMappings[newFaces[f + v]];
                        }
                    }
                }
                f += numInFace;
            } while (f < newFaces.Count());

            Faces    = newFaces;
            Vertices = newPts.SelectMany(p => new[] { p.X, p.Y, p.Z }).ToList();
        }
Example #2
0
        public FaceIndexableItem(Item item) :  base(item)
        {
            var faceIdentification = new AzureFaceIdentification();

            var mediaItem   = new MediaItem(item);
            var mediaStream = mediaItem.GetMediaStream();

            if (mediaStream != null)
            {
                Faces =
                    !IsNullOrEmpty(item["FaceMetadata"])
                        ? JsonConvert.DeserializeObject <FaceMetadata[]>(item["FaceMetadata"])
                        : new FaceMetadata[0];
                IdentifiedPersons = !IsNullOrEmpty(item["IdentifiedPersons"])
                    ? JsonConvert.DeserializeObject <IdentifiedPerson[]>(item["IdentifiedPersons"])
                    : new IdentifiedPerson[0];

                Suggestions = faceIdentification.Identify(Faces.ToList(), mediaStream);
            }
        }
Example #3
0
        /// <summary>
        ///     Updates the with.
        /// </summary>
        /// <param name="face">The face.</param>
        public bool BuildIfCylinderIsHole(bool isPositive)
        {
            if (isPositive)
            {
                throw new Exception("BuildIfCylinderIsHole assumes that the faces have already been collected, " +
                                    "such that the cylinder is negative");
            }
            IsPositive = false;

            //To truly be a hole, there should be two loops of vertices that form circles on either ends of the faces.
            //These are easy to capture because all the edges between them should be shared by two of the faces
            //Start by collecting the edges at either end. Each edge belongs to only two faces, so any edge that only
            //comes up once, must be at the edge of the cylinder (assuming it is a cylinder).
            var edges = new HashSet <Edge>();

            foreach (var face in Faces)
            {
                foreach (var edge in face.Edges)
                {
                    if (edges.Contains(edge))
                    {
                        edges.Remove(edge);
                    }
                    else
                    {
                        edges.Add(edge);
                    }
                }
            }
            //Now we can loop through the edges to form two loops
            if (edges.Count < 5)
            {
                return(false);                 //5 is the minimum number of vertices to look remotely circular (8 is more likely)
            }
            var(allLoopsClosed, edgeLoops, loops) = GetLoops(edges, true);
            if (loops.Count != 2)
            {
                return(false);                  //There must be two and only two loops.
            }
            Loop1     = new HashSet <Vertex>(loops[0]);
            Loop2     = new HashSet <Vertex>(loops[1]);
            EdgeLoop1 = edgeLoops[0];
            EdgeLoop2 = edgeLoops[1];

            //Next, we need to get the central axis.
            //The ends of the cylinder could be any shape (flat, curved, angled) and the shapes
            //on each end do not need to match. This rules out using the vertex loops to form
            //a plane (most accurate) and creating a plane from edge midpoints (next most accurate).
            //The next most accurate thing is to use the edge vectors to set the axis.
            //This is more precise than taking a bunch of cross products with the faces.
            //And it is more universal than creating a plane from the loops, since it works
            //for holes that enter and exit at an angle.
            var throughEdgeVectors = new Dictionary <Vertex, double[]>();
            var dotFromSharpestEdgesConnectedToVertex = new Dictionary <Vertex, double>();

            foreach (var edge in InnerEdges)
            {
                //Skip those edges that are on "flat" surfaces
                var dot = edge.OwnedFace.Normal.dotProduct(edge.OtherFace.Normal);
                if (dot.IsPracticallySame(1.0, Constants.ErrorForFaceInSurface))
                {
                    continue;
                }
                //This uses a for loop to remove duplicate code, to decide which vertex to check with which loop
                for (var i = 0; i < 2; i++)
                {
                    var A         = i == 0 ? edge.To : edge.From;
                    var B         = i == 0 ? edge.From : edge.To;
                    var direction = edge.Vector.normalize();
                    //Positive if B is further along
                    var previousDistance = direction.dotProduct(B.Position);
                    var sign             = Math.Sign(direction.dotProduct(B.Position) - direction.dotProduct(A.Position));
                    if (Loop1.Contains(A))
                    {
                        bool reachedEnd = Loop2.Contains(B);
                        if (!reachedEnd)
                        {
                            //Check if this edge needs to "extended" to reach the end of the cylinder
                            var previousEdge   = edge;
                            var previousVertex = B;
                            while (!reachedEnd)
                            {
                                var  maxDot        = 0.0;
                                Edge extensionEdge = null;
                                foreach (var otherEdge in previousVertex.Edges.Where(e => e != previousEdge))
                                {
                                    //This other edge must be contained in the InnerEdges and along the same direction
                                    if (!InnerEdges.Contains(otherEdge))
                                    {
                                        continue;
                                    }
                                    var edgeDot = Math.Abs(otherEdge.Vector.normalize().dotProduct(previousEdge.Vector.normalize()));
                                    if (!edgeDot.IsPracticallySame(1.0, Constants.ErrorForFaceInSurface))
                                    {
                                        continue;
                                    }
                                    var distance = sign * (direction.dotProduct(otherEdge.OtherVertex(previousVertex).Position) - previousDistance);
                                    if (!distance.IsGreaterThanNonNegligible())
                                    {
                                        continue;                                         //This vertex is not any further along
                                    }
                                    //Choose the edge that is most along the previous edge
                                    if (edgeDot > maxDot)
                                    {
                                        maxDot        = edgeDot;
                                        extensionEdge = otherEdge;
                                    }
                                }
                                if (extensionEdge == null)
                                {
                                    break;                        //go to the next edge
                                }
                                if (Loop2.Contains(extensionEdge.OtherVertex(previousVertex)))
                                {
                                    reachedEnd = true;
                                    B          = extensionEdge.OtherVertex(previousVertex);
                                }
                                else
                                {
                                    previousVertex = extensionEdge.OtherVertex(previousVertex);
                                    previousEdge   = extensionEdge;
                                }
                            }
                        }
                        //If there was a vertex from the edge or edges in the second loop.
                        if (reachedEnd)
                        {
                            if (!dotFromSharpestEdgesConnectedToVertex.ContainsKey(A))
                            {
                                throughEdgeVectors.Add(A, B.Position.subtract(A.Position));
                                dotFromSharpestEdgesConnectedToVertex.Add(A, edge.InternalAngle);
                            }
                            else if (dot < dotFromSharpestEdgesConnectedToVertex[A])
                            {
                                throughEdgeVectors[A] = B.Position.subtract(A.Position);
                                dotFromSharpestEdgesConnectedToVertex[A] = dot;
                            }
                            break; //Go to the next edge
                        }
                    }
                }
            }
            if (throughEdgeVectors.Count < 3)
            {
                return(false);
            }

            //Estimate the axis from the sum of the through edge vectors
            //The axis points from loop 1 to loop 2, since we always start the edge vector from Loop1
            var edgeVectors = new List <double[]>(throughEdgeVectors.Values);
            var numEdges    = edgeVectors.Count;
            var axis        = new double[] { 0.0, 0.0, 0.0 };

            foreach (var edgeVector in edgeVectors)
            {
                axis = axis.add(edgeVector);
            }
            Axis = axis.normalize();

            /* to adjust the Axis, we will average the cross products of the new face with all the old faces */
            //Since we will be taking cross products, we need to be sure not to have faces along the same normal
            var faces = MiscFunctions.FacesWithDistinctNormals(Faces.ToList());
            var n     = faces.Count;

            //Check if the loops are circular along the axis
            var path1 = MiscFunctions.Get2DProjectionPointsAsLight(Loop1, Axis, out var backTransform);
            var poly  = new PolygonLight(path1);

            if (!PolygonOperations.IsCircular(new Polygon(poly), out var centerCircle, Constants.MediumConfidence))
            {
                return(false);
            }
            var path2 = MiscFunctions.Get2DProjectionPointsAsLight(Loop2, Axis, out var backTransform2);
            var poly2 = new PolygonLight(path2);

            if (!PolygonOperations.IsCircular(new Polygon(poly2), out var centerCircle2, Constants.MediumConfidence))
            {
                return(false);
            }
            Radius = (centerCircle.Radius + centerCircle2.Radius) / 2; //Average
            //Set the Anchor/Center point
            var center = centerCircle.Center.Add(centerCircle2.Center).divide(2);

            Anchor = MiscFunctions.Convert2DVectorTo3DVector(center, backTransform);
            return(true);
        }