private static bool SlotHeadBolt(TessellatedSolid solid, List <PrimitiveSurface> solidPrim,
                                         Dictionary <TemporaryFlat, List <TemporaryFlat> > equalPrimitives, List <TessellatedSolid> repeated)
        {
            repeated.Add(solid);
            var twoFlat = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 2);

            if (!twoFlat.Any())
            {
                return(false);
            }
            foreach (var candidateHex in twoFlat)
            {
                var candidateHexVal = equalPrimitives[candidateHex];
                var cos             = (candidateHexVal[0]).Normal.dotProduct((candidateHexVal[1]).Normal);
                if (!(Math.Abs(-1 - cos) < 0.0001))
                {
                    continue;
                }
                // I will add couple of conditions here:
                //    1. If the number of solid vertices in front of each flat is equal to another
                //    2. If the summation of the vertices in 1 is greater than the total # of verts
                //    3. and I also need to add some constraints for the for eample the area of the cylinder
                var leftVerts  = VertsInfrontOfFlat(solid, candidateHexVal[0]);
                var rightVerts = VertsInfrontOfFlat(solid, candidateHexVal[1]);
                if (Math.Abs(leftVerts - rightVerts) > 2 || leftVerts + rightVerts <= solid.Vertices.Length)
                {
                    continue;
                }
                if (!solidPrim.Where(p => p is Cylinder).Cast <Cylinder>().Any(c => c.IsPositive))
                {
                    continue;
                }
                var lengthAndRadius = FastenerEngagedLengthAndRadiusNoThread(solid, solidPrim);
                foreach (var repeatedSolid in repeated)
                {
                    var fastener = new Fastener
                    {
                        Solid            = repeatedSolid,
                        FastenerType     = FastenerTypeEnum.Bolt,
                        Tool             = Tool.FlatBlade,
                        RemovalDirection =
                            //RemovalDirectionFinderForSlot(candidateHexVal.Cast<Flat>().ToList(),
                            //    solidPrim.Where(p => p is Flat).Cast<Flat>().ToList()),
                            FastenerDetector.RemovalDirectionFinderUsingObb(repeatedSolid,
                                                                            BoundingGeometry.OrientedBoundingBoxDic[repeatedSolid]),
                        OverallLength =
                            GeometryFunctions.SortedLengthOfObbEdges(BoundingGeometry.OrientedBoundingBoxDic[solid])[2],
                        EngagedLength = lengthAndRadius[0],
                        Diameter      = lengthAndRadius[1] * 2.0,
                        Certainty     = 1.0
                    };
                    lock (FastenerDetector.Fasteners)
                        FastenerDetector.Fasteners.Add(fastener);
                }
                return(true);
            }
            return(false);
        }
        internal static Fastener PolynomialTrendDetector(TessellatedSolid solid)
        {
            // Assumptions:
            //    1. In fasteners, length is longer than width.
            var obb = BoundingGeometry.OrientedBoundingBoxDic[solid];
            //if (!solid.Name.Contains("STLB ASM")) return true;
            const int     k = 500;
            PolygonalFace f1;
            PolygonalFace f2;
            var           longestSide = GeometryFunctions.LongestPlaneOfObbDetector(obb, out f1, out f2);
            var           partitions  = new FastenerBoundingBoxPartition(solid, longestSide[0], 50);
            // 1. Take the middle point of the smallest edge of each triangle. Or the points of the 2nd longest edge of a side triangle
            // 2. Generate k points between them with equal distances.
            // 3. Generate rays using generated points.
            var midPoint1 = ShortestEdgeMidPointOfTriangle(longestSide[0]);
            var midPoint2 = ShortestEdgeMidPointOfTriangle(longestSide[1]);

            var kPointsBetweenMidPoints = KpointBtwPointsGenerator(midPoint1, midPoint2, k);

            double longestDist;
            var    distancePointToSolid = PointToSolidDistanceCalculatorWithPartitioning(solid, partitions.Partitions, kPointsBetweenMidPoints,
                                                                                         longestSide[0].Normal.multiply(-1.0), out longestDist);
            // one more step: Merge points with equal distances.
            List <int> originalInds;

            distancePointToSolid = MergingEqualDistances(distancePointToSolid, out originalInds, 0.001);
            int numberOfThreads;

            int[] threadStartEndPoints;
            if (ContainsThread(distancePointToSolid, out numberOfThreads, out threadStartEndPoints))
            {
                //PlotInMatlab(distancePointToSolid);
                var startEndThreadPoints =
                    Math.Abs(originalInds[threadStartEndPoints[0] + 2] - originalInds[threadStartEndPoints[1] - 2]);
                return(new Fastener
                {
                    Solid = solid,
                    NumberOfThreads = numberOfThreads,
                    FastenerType = FastenerTypeEnum.Bolt,
                    RemovalDirection =
                        FastenerDetector.RemovalDirectionFinderUsingObb(solid,
                                                                        BoundingGeometry.OrientedBoundingBoxDic[solid]),
                    OverallLength =
                        GeometryFunctions.SortedLengthOfObbEdges(BoundingGeometry.OrientedBoundingBoxDic[solid])[2],
                    EngagedLength =
                        (startEndThreadPoints + (startEndThreadPoints * 2) / (double)numberOfThreads) *
                        (GeometryFunctions.DistanceBetweenTwoVertices(midPoint1, midPoint2) / ((double)k + 1)),
                    Diameter =
                        DiameterOfFastenerFinderUsingPolynomial(distancePointToSolid, threadStartEndPoints,
                                                                longestSide[0], solid, longestDist),
                    Certainty = 1.0
                });
            }
            // Plot:
            //if (hasThread)
            //PlotInMatlab(distancePointToSolid);
            return(null);
        }
 private static void PreSelectedFastenerToFastenerClass(
     Dictionary <TessellatedSolid, List <PrimitiveSurface> > solidPrimitive,
     Dictionary <TessellatedSolid, List <TessellatedSolid> > multipleRefs)
 {
     foreach (var preFastener in FastenerDetector.PreSelectedFasteners)
     {
         var lengthAndRadius = FastenerEngagedLengthAndRadiusNoThread(preFastener, solidPrimitive[preFastener]);
         // if this part is repeated, add also those repeated parts to the fastener list
         var repeated             = new HashSet <TessellatedSolid>();
         var isAKeyInMultipleRefs = multipleRefs.Keys.Where(r => r == preFastener).ToList();
         if (isAKeyInMultipleRefs.Any())
         {
             repeated = new HashSet <TessellatedSolid>(multipleRefs[isAKeyInMultipleRefs[0]])
             {
                 preFastener
             }
         }
         ;
         else
         {
             // it is a part in a value list
             foreach (var key in multipleRefs.Keys)
             {
                 if (!multipleRefs[key].Contains(preFastener))
                 {
                     continue;
                 }
                 repeated = new HashSet <TessellatedSolid>(multipleRefs[key])
                 {
                     key
                 };
             }
         }
         foreach (var repeatedSolid in repeated)
         {
             var fastener = new Fastener
             {
                 Solid            = repeatedSolid,
                 FastenerType     = FastenerTypeEnum.Bolt,
                 RemovalDirection =
                     FastenerDetector.RemovalDirectionFinderUsingObb(preFastener,
                                                                     BoundingGeometry.OrientedBoundingBoxDic[preFastener]),
                 OverallLength =
                     GeometryFunctions.SortedLengthOfObbEdges(
                         BoundingGeometry.OrientedBoundingBoxDic[preFastener])[2],
                 EngagedLength = lengthAndRadius[0],
                 Diameter      = lengthAndRadius[1] * 2.0,
                 Certainty     = 1.0
             };
             FastenerDetector.Fasteners.Add(fastener);
         }
     }
 }
示例#4
0
        internal static void RunFastenerDetection(Dictionary <string, List <TessellatedSolid> > solids, int threaded)
        {
            PartsWithOneGeom = new List <TessellatedSolid>();
            foreach (var subAssem in solids.Values)
            {
                if (subAssem.Count == 1)
                {
                    PartsWithOneGeom.Add(subAssem[0]);
                }
            }
            // From repeated parts take only one of them:
            //------------------------------------------------------------------------------------------
            var multipleRefs = DuplicatePartsDetector(PartsWithOneGeom);

            // Detect fasteners
            //------------------------------------------------------------------------------------------
            FastenerDetector.Run(SolidPrimitive, multipleRefs, threaded, false);
        }
        private static void AddThreadedOrNonthreadedFastener(TessellatedSolid solid, List <TessellatedSolid> repeated,
                                                             List <PrimitiveSurface> prim, Tool tool, double toolSize)
        {
            var newFastener = FastenerPolynomialTrend.PolynomialTrendDetector(solid);

            if (newFastener != null)
            {
                newFastener.Tool     = tool;
                newFastener.ToolSize = toolSize;
                lock (FastenerDetector.Fasteners)
                    FastenerDetector.Fasteners.Add(newFastener);
                AutoThreadedFastenerDetection.AddRepeatedSolidstoFasteners(newFastener, repeated);
                //continue;
                return;
            }
            var lengthAndRadius =
                AutoNonthreadedFastenerDetection.FastenerEngagedLengthAndRadiusNoThread(solid, prim);
            var fastener = new Fastener
            {
                Solid            = solid,
                FastenerType     = FastenerTypeEnum.Bolt,
                Tool             = tool,
                ToolSize         = toolSize,
                RemovalDirection =
                    FastenerDetector.RemovalDirectionFinderUsingObb(solid,
                                                                    BoundingGeometry.OrientedBoundingBoxDic[solid]),
                OverallLength =
                    GeometryFunctions.SortedLengthOfObbEdges(BoundingGeometry.OrientedBoundingBoxDic[solid])[2],
                EngagedLength = lengthAndRadius[0],
                Diameter      = lengthAndRadius[1] * 2.0,
                Certainty     = 1.0
            };

            lock (FastenerDetector.Fasteners)
                FastenerDetector.Fasteners.Add(fastener);
            AutoThreadedFastenerDetection.AddRepeatedSolidstoFasteners(fastener, repeated);
        }
示例#6
0
        internal static int CommonHeadCheck(TessellatedSolid solid, List <PrimitiveSurface> solidPrim,
                                            Dictionary <TemporaryFlat, List <TemporaryFlat> > equalPrimitives, out double toolSize)
        {
            // 0: false (doesnt have any common head shape)
            // 1: HexBolt or Nut
            // 2: Allen
            // 3: Phillips
            // 4: Slot
            // 5: Phillips and Slot combo

            toolSize = 0.0;
            // check for hex bolt, nut and allen -------------------------------------------------------------
            var sixFlat   = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 6);
            var eightFlat = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 8);
            var twoFlat   = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 2);
            var fourFlat  = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 4);

            //if (!sixFlat.Any()) return 0;
            foreach (var candidateHex in sixFlat)
            {
                var candidateHexVal = equalPrimitives[candidateHex];
                var cos             = new List <double>();
                var firstPrimNormal = (candidateHexVal[0]).Normal;
                for (var i = 1; i < candidateHexVal.Count; i++)
                {
                    cos.Add(firstPrimNormal.dotProduct((candidateHexVal[i]).Normal));
                }
                // if it is a hex or allen bolt, the cos list must have two 1/2, two -1/2 and one -1
                if (cos.Count(c => Math.Abs(0.5 - c) < 0.0001) != 2 ||
                    cos.Count(c => Math.Abs(-0.5 - c) < 0.0001) != 2 ||
                    cos.Count(c => Math.Abs(-1 - c) < 0.0001) != 1)
                {
                    continue;
                }
                toolSize = AutoNonthreadedFastenerDetection.ToolSizeFinder(candidateHexVal);
                if (AutoNonthreadedFastenerDetection.IsItAllen(candidateHexVal))
                {
                    return(2);
                }
                return(1);
            }
            foreach (var candidateHex in eightFlat)
            {
                var candidateHexVal = equalPrimitives[candidateHex];
                var cos             = new List <double>();
                var firstPrimNormal = (candidateHexVal[0]).Normal;
                for (var i = 1; i < candidateHexVal.Count; i++)
                {
                    cos.Add(firstPrimNormal.dotProduct((candidateHexVal[i]).Normal));
                }
                var oneFlat = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 1);
                // Any flat that is adjacent to all of these eights?
                // if it is philips head, the cos list must have four 0, two -1 and one 1
                foreach (var sh in oneFlat)
                {
                    if (candidateHexVal.All(f => f.OuterEdges.Any(sh.OuterEdges.Contains)))
                    {
                        return(3);
                    }
                }

                /*if (cos.Count(c => Math.Abs(0.0 - c) < 0.0001) != 4 ||
                 *  cos.Count(c => Math.Abs(-1 - c) < 0.0001) != 2 ||
                 *  cos.Count(c => Math.Abs(1 - c) < 0.0001) != 1) continue;
                 * return 3;*/
                continue;
            }
            foreach (var candidateHex in twoFlat)
            {
                var candidateHexVal = equalPrimitives[candidateHex];
                var cos             = (candidateHexVal[0]).Normal.dotProduct((candidateHexVal[1]).Normal);
                if (!(Math.Abs(-1 - cos) < 0.0001))
                {
                    continue;
                }
                // I will add couple of conditions here:
                //    1. If the number of solid vertices in front of each flat is equal to another
                //    2. If the summation of the vertices in 1 is greater than the total # of verts
                //    3. and I also need to add some constraints for the for eample the area of the cylinder
                var leftVerts  = AutoNonthreadedFastenerDetection.VertsInfrontOfFlat(solid, candidateHexVal[0]);
                var rightVerts = AutoNonthreadedFastenerDetection.VertsInfrontOfFlat(solid, candidateHexVal[1]);
                if (Math.Abs(leftVerts - rightVerts) > 10 || leftVerts + rightVerts <= solid.Vertices.Length)
                {
                    continue;
                }
                return(4);
            }

            var eachSlot = 0;
            var flats    = new List <TemporaryFlat>();

            foreach (var candidateHex in fourFlat)
            {
                var candidateHexVal = equalPrimitives[candidateHex];
                var cos             = new List <double>();
                var firstPrimNormal = (candidateHexVal[0]).Normal;
                for (var i = 1; i < candidateHexVal.Count; i++)
                {
                    cos.Add(firstPrimNormal.dotProduct((candidateHexVal[i]).Normal));
                }
                // if it is a slot and phillips combo the cos list must have two -1 and one 1
                // and this needs to appear 2 times.
                if (cos.Count(c => Math.Abs(-1 - c) < 0.0001) != 2 ||
                    cos.Count(c => Math.Abs(1 - c) < 0.0001) != 1)
                {
                    continue;
                }
                flats.AddRange(candidateHexVal);
                eachSlot++;
            }
            if (eachSlot == 2)
            {
                return(5);
            }
            return(0);
        }
示例#7
0
 private static void PreSelectedFastenerToFastenerClass(
     Dictionary <TessellatedSolid, List <PrimitiveSurface> > solidPrimitive,
     Dictionary <TessellatedSolid, List <TessellatedSolid> > multipleRefs)
 {
     foreach (var preFastener in FastenerDetector.PreSelectedFasteners)
     {
         // if this part is repeated, add also those repeated parts to the fastener list
         var repeated             = new HashSet <TessellatedSolid>();
         var isAKeyInMultipleRefs = multipleRefs.Keys.Where(r => r == preFastener).ToList();
         if (isAKeyInMultipleRefs.Any())
         {
             repeated = new HashSet <TessellatedSolid>(multipleRefs[isAKeyInMultipleRefs[0]])
             {
                 preFastener
             }
         }
         ;
         else
         {
             // it is a part in a value list
             foreach (var key in multipleRefs.Keys)
             {
                 if (!multipleRefs[key].Contains(preFastener))
                 {
                     continue;
                 }
                 repeated = new HashSet <TessellatedSolid>(multipleRefs[key])
                 {
                     key
                 };
             }
         }
         var newFastener = FastenerPolynomialTrend.PolynomialTrendDetector(preFastener);
         if (newFastener != null)
         {
             foreach (var repeatedSolid in repeated)
             {
                 FastenerDetector.Fasteners.Add(new Fastener
                 {
                     Solid            = repeatedSolid,
                     NumberOfThreads  = newFastener.NumberOfThreads,
                     FastenerType     = newFastener.FastenerType,
                     RemovalDirection = newFastener.RemovalDirection,
                     OverallLength    = newFastener.OverallLength,
                     EngagedLength    = newFastener.EngagedLength,
                     Diameter         = newFastener.Diameter,
                     Certainty        = 1.0
                 });
             }
             return;
         }
         var overalLength =
             GeometryFunctions.SortedLengthOfObbEdges(BoundingGeometry.OrientedBoundingBoxDic[preFastener])[2];
         foreach (var repeatedSolid in repeated)
         {
             FastenerDetector.Fasteners.Add(new Fastener
             {
                 Solid            = repeatedSolid,
                 FastenerType     = FastenerTypeEnum.Bolt,
                 RemovalDirection =
                     FastenerDetector.RemovalDirectionFinderUsingObb(preFastener,
                                                                     BoundingGeometry.OrientedBoundingBoxDic[preFastener]),
                 OverallLength = overalLength,
                 EngagedLength = overalLength,
                 Diameter      = BoundingGeometry.BoundingCylinderDic[repeatedSolid].Radius,
                 Certainty     = 1.0
             });
         }
     }
 }
示例#8
0
        internal static void Run(
            Dictionary <TessellatedSolid, List <PrimitiveSurface> > solidPrimitive,
            Dictionary <TessellatedSolid, List <TessellatedSolid> > multipleRefs)
        {
            // This is mostly similar to the auto fastener detection with no thread, but instead of learning
            // from the area of the cylinder and for example flat, we will learn from the number of faces.
            // why? because if we have thread, we will have too many triangles. And this can be useful.
            // I can also detect helix and use this to detect the threaded fasteners

            // Important: if the fasteners are threaded using solidworks Fastener toolbox, it will not
            //            have helix. The threads will be small cones with the same axis and equal area.
            var width = 55;
            var check = 0;

            LoadingBar.start(width, 0);

            var smallParts = FastenerDetector.SmallObjectsDetector(DisassemblyDirectionsWithFastener.PartsWithOneGeom);

            PreSelectedFastenerToFastenerClass(solidPrimitive, multipleRefs);
            foreach (
                var preSelected in
                FastenerDetector.PreSelectedFasteners.Where(preSelected => !smallParts.Contains(preSelected)))
            {
                smallParts.Add(preSelected);
            }

            FastenerDetector.PotentialFastener = new Dictionary <TessellatedSolid, double>();
            foreach (var p in smallParts)
            {
                FastenerDetector.PotentialFastener.Add(p, 0.1);
            }
            var groupedPotentialFasteners = FastenerDetector.GroupingSmallParts(smallParts);
            var uniqueParts = new HashSet <TessellatedSolid>();

            foreach (var s in multipleRefs.Keys.Where(FastenerDetector.PotentialFastener.Keys.Contains))
            {
                uniqueParts.Add(s);
            }
            foreach (
                var preFastener in
                FastenerDetector.Fasteners.Where(preFastener => uniqueParts.Contains(preFastener.Solid)))
            {
                uniqueParts.Remove(preFastener.Solid);
            }

            var equalPrimitivesForEveryUniqueSolid = FastenerDetector.EqualFlatPrimitiveAreaFinder(uniqueParts,
                                                                                                   solidPrimitive);
            List <int> learnerVotes;
            var        learnerWeights = FastenerPerceptronLearner.ReadingLearnerWeightsAndVotesFromCsv(out learnerVotes);

            FastenerGaussianNaiveBayes.GNB();


            List <string> nameList = new List <string>();

            foreach (var part in uniqueParts)
            {
                nameList.Add(part.Name);
            }
            float nameRating;

            float ratingAverage = 0;
            float ratingMax     = -1;
            float ratingMin     = 100000000000;
            int   preCutoff     = 0;
            int   postCutoff    = 0;

            PartNameAnalysis.findCommonPreSuffixes(nameList, ref preCutoff, ref postCutoff);
            foreach (var part in uniqueParts)
            {
                nameRating     = PartNameAnalysis.SolidHasFastenerKeyword(part, preCutoff, postCutoff);
                ratingAverage += nameRating;
                ratingMax      = Math.Max(ratingMax, nameRating);
                ratingMin      = Math.Min(ratingMin, nameRating);
            }
            float proportion = 1 - (ratingMax - ratingMin) / 3;

            var refresh = (int)Math.Ceiling((float)uniqueParts.Count / (float)(width * 4));

            Parallel.ForEach(uniqueParts, solid =>
                             //foreach (var solid in uniqueParts)
            {
                if (check % refresh == 0)
                {
                    LoadingBar.refresh(width, ((float)check) / ((float)uniqueParts.Count));
                }
                check++;

                var initialCertainty = FastenerGaussianNaiveBayes.GaussianNaiveBayesClassifier(solidPrimitive[solid], solid);

                nameRating = (PartNameAnalysis.SolidHasFastenerKeyword(solid, preCutoff, postCutoff) - ratingMin) / (0.001f + ratingMax - ratingMin);

                FastenerDetector.PotentialFastener[solid] = (0.1 + initialCertainty) * proportion + (1 - nameRating) * (1 - proportion);
                foreach (var up in multipleRefs[solid])
                {
                    FastenerDetector.PotentialFastener[up] = (0.1 + initialCertainty) * proportion + (1 - nameRating) * (1 - proportion);
                }

                // if a fastener is detected using polynomial trend approach, it is definitely a fastener but not a nut.
                // if it is detected using any other approach, but not polynomial trend, it is a possible nut.
                double toolSize;
                var commonHead = CommonHeadCheck(solid, solidPrimitive[solid], equalPrimitivesForEveryUniqueSolid[solid],
                                                 out toolSize);
                if (commonHead != 0)
                {
                    var newFastener = FastenerPolynomialTrend.PolynomialTrendDetector(solid);
                    if (newFastener != null)
                    {
                        newFastener.ToolSize = toolSize;
                        if (commonHead == 1)
                        {
                            newFastener.Tool = Tool.HexWrench;
                            lock (FastenerDetector.Fasteners)
                                FastenerDetector.Fasteners.Add(newFastener);
                            AddRepeatedSolidstoFasteners(newFastener, multipleRefs[solid]);
                            //continue;
                            return;
                        }
                        if (commonHead == 2)
                        {
                            newFastener.Tool = Tool.Allen;
                            lock (FastenerDetector.Fasteners)
                                FastenerDetector.Fasteners.Add(newFastener);
                            AddRepeatedSolidstoFasteners(newFastener, multipleRefs[solid]);
                            //continue;
                            return;
                        }
                        if (commonHead == 3)
                        {
                            newFastener.Tool = Tool.PhillipsBlade;
                            lock (FastenerDetector.Fasteners)
                                FastenerDetector.Fasteners.Add(newFastener);
                            AddRepeatedSolidstoFasteners(newFastener, multipleRefs[solid]);
                            //continue;
                            return;
                        }
                        if (commonHead == 4)
                        {
                            newFastener.Tool = Tool.FlatBlade;
                            lock (FastenerDetector.Fasteners)
                                FastenerDetector.Fasteners.Add(newFastener);
                            AddRepeatedSolidstoFasteners(newFastener, multipleRefs[solid]);
                            //continue;
                            return;
                        }
                        if (commonHead == 5)
                        {
                            newFastener.Tool = Tool.PhillipsBlade;
                            lock (FastenerDetector.Fasteners)
                                FastenerDetector.Fasteners.Add(newFastener);
                            AddRepeatedSolidstoFasteners(newFastener, multipleRefs[solid]);
                            //continue;
                            return;
                        }
                    }
                    else // can be a nut
                    {
                        if (commonHead == 1)
                        {
                            FastenerDetector.Nuts.Add(new Nut
                            {
                                Solid         = solid,
                                NutType       = NutType.Hex,
                                Tool          = Tool.HexWrench,
                                ToolSize      = toolSize,
                                OverallLength = BoundingGeometry.BoundingCylinderDic[solid].Length,
                                Certainty     = initialCertainty // 0.9
                            });
                            foreach (var repeatedSolid in multipleRefs[solid])
                            {
                                lock (FastenerDetector.Nuts)
                                    FastenerDetector.Nuts.Add(new Nut
                                    {
                                        Solid         = repeatedSolid,
                                        NutType       = NutType.Hex,
                                        Tool          = Tool.HexWrench,
                                        ToolSize      = toolSize,
                                        OverallLength = BoundingGeometry.BoundingCylinderDic[solid].Length,
                                        Certainty     = initialCertainty // 0.9
                                    });
                            }
                            //continue;
                            return;
                        }
                    }
                }
                if (FastenerPerceptronLearner.FastenerPerceptronClassifier(solidPrimitive[solid], solid, learnerWeights,
                                                                           learnerVotes))
                {
                    var newFastener = FastenerPolynomialTrend.PolynomialTrendDetector(solid);
                    if (newFastener != null)
                    {
                        lock (FastenerDetector.Fasteners)
                            FastenerDetector.Fasteners.Add(newFastener);
                        AddRepeatedSolidstoFasteners(newFastener, multipleRefs[solid]);
                        //continue;
                        return;
                    }
                    // can be a nut
                    // use bounding cylinder to detect nuts.
                    // Since the nuts are small, the OBB function doesnt work accurately for them.
                    //  So, I cannot really trust this.
                    var newNut = NutPolynomialTrend.PolynomialTrendDetector(solid);
                    if (newNut != null)
                    // It is a nut with certainty == 1
                    {
                        lock (FastenerDetector.Nuts)
                            FastenerDetector.Nuts.Add(newNut);
                        AddRepeatedSolidstoNuts(newNut, multipleRefs[solid]);
                        //continue;
                        return;
                    }
                    // still can be a nut since the upper approach is not really accurate
                    // this 50 percent certainty can go up if the nut is mated with a
                    // detected fastener
                    foreach (var repeatedSolid in multipleRefs[solid])
                    {
                        lock (FastenerDetector.Nuts)
                            FastenerDetector.Nuts.Add(new Nut
                            {
                                Solid    = repeatedSolid,
                                Diameter = BoundingGeometry.BoundingCylinderDic[solid].Radius * 2.0,
                                // this is approximate
                                OverallLength = BoundingGeometry.BoundingCylinderDic[solid].Length,
                                Certainty     = initialCertainty // 0.5
                            });
                    }
                    //continue;
                    return;
                }
                // if it is not captured by any of the upper methods, give it another chance:
                if (ThreadDetector(solid, solidPrimitive[solid]))
                {
                    var newFastener = FastenerPolynomialTrend.PolynomialTrendDetector(solid);
                    if (newFastener != null)
                    {
                        newFastener.FastenerType = FastenerTypeEnum.Bolt;
                        lock (FastenerDetector.Fasteners)
                            FastenerDetector.Fasteners.Add(newFastener);
                        AddRepeatedSolidstoFasteners(newFastener, multipleRefs[solid]);
                        //continue;
                        return;
                    }
                    //if not, it is a nut:
                    foreach (var repeatedSolid in multipleRefs[solid])
                    {
                        lock (FastenerDetector.Nuts)
                            FastenerDetector.Nuts.Add(new Nut
                            {
                                Solid    = repeatedSolid,
                                Diameter = BoundingGeometry.BoundingCylinderDic[solid].Radius * 2.0,
                                // this is approximate
                                OverallLength = BoundingGeometry.BoundingCylinderDic[solid].Length * 2.0,
                                Certainty     = initialCertainty // 0.5
                            });
                    }
                }
            }
                             ); //
            // now use groupped small objects:
            AutoNonthreadedFastenerDetection.ConnectFastenersNutsAndWashers(groupedPotentialFasteners);
            LoadingBar.refresh(width, 1);
        }
        internal static void ConnectFastenersNutsAndWashers(List <List <TessellatedSolid> > groupedPotentialFasteners)
        {
            // Possible cases:
            //  1. Fastener
            //  2. Fastener-Nut
            //  3. Fastener-Nut-Washer
            //  4. Fastener-Washer
            //  5. Nut
            //  6. Nut-Washer
            var counter1 = 0;

            foreach (var fastener in FastenerDetector.Fasteners)
            {
                counter1++;
                // if there is a fastener, find its nuts and washers
                var groupList = groupedPotentialFasteners.Where(g => g.Contains(fastener.Solid)).ToList();

                //------------------------------------- Case 1 ------------------------------------
                if (groupList.Count == 0)
                {
                    continue;
                }
                var group        = groupList.ToList()[0];
                var numFasteners = group.Count(s => FastenerDetector.Fasteners.Any(f => f.Solid == s));
                if (numFasteners > 1)
                {
                    continue;
                }
                //group.Add(fastener.Solid);
                //if (group.Count == 1) continue;

                //----------------------------------- Cases 2,3,4 ---------------------------------
                var nutAndWasherRemovalDirection = DisassemblyDirections.DirectionsAndOpposits[fastener.RemovalDirection];
                var nuts    = FastenerDetector.Nuts.Where(n => group.Contains(n.Solid)).ToList();
                var nutList = new List <Nut>();
                if (nuts.Any())
                {
                    foreach (var nut in nuts)
                    {
                        nut.RemovalDirection = nutAndWasherRemovalDirection;
                        nut.Diameter         = fastener.Diameter;
                        nut.Certainty        = 1.0;
                        nutList.Add(nut);
                    }
                }
                var potentialWasher = group.Where(s => s != fastener.Solid && nuts.All(n => s != n.Solid)).ToList();
                // if there is a solid which is very very small comparing the bolt, it is a washer?
                // the possible washer can still be a nut that has not been detected before
                // take its obb. if the smallest edge to the longest edge is less than 0.2, it's a washer,
                // otherwise it can be a nut with low certainty
                var washers = new List <Washer>();
                foreach (var pWasher in potentialWasher)
                {
                    if (Math.Abs(pWasher.Volume) > Math.Abs(fastener.Solid.Volume))
                    {
                        continue;
                    }
                    var edgesOfObb =
                        GeometryFunctions.SortedLengthOfObbEdges(BoundingGeometry.OrientedBoundingBoxDic[pWasher]);
                    if ((edgesOfObb[0] / edgesOfObb[2]) < 0.2)
                    // shortest/ longest // this can be fuzzified to define ceratinty
                    {
                        washers.Add(new Washer
                        {
                            Solid            = pWasher,
                            Certainty        = 0.7,
                            RemovalDirection = nutAndWasherRemovalDirection
                        });
                    }
                    else
                    {
                        // it can be a nut
                        var uncertainNut = new Nut
                        {
                            Solid            = pWasher,
                            Certainty        = 0.4,
                            RemovalDirection = nutAndWasherRemovalDirection
                        };
                        nutList.Add(uncertainNut);
                        FastenerDetector.Nuts.Add(uncertainNut);
                    }
                }
                fastener.Nuts   = nutList;
                fastener.Washer = washers;
                FastenerDetector.Washers.UnionWith(washers);
            }
            // if there is a detected nut, but its fastener was not detected:
            // fastener is unknown
            var counter2 = 0;

            foreach (var nut in FastenerDetector.Nuts)
            {
                counter2++;
                if (FastenerDetector.Fasteners.All(f => f.Nuts == null || !f.Nuts.Contains(nut)))
                {
                    // this a nut that doesnt have any fastener.
                    var groupList = groupedPotentialFasteners.Where(g => g.Contains(nut.Solid)).ToList();

                    //------------------------------------- Case 5 ------------------------------------
                    if (groupList.Count == 0)
                    {
                        continue;
                    }
                    var group = groupList.ToList()[0];
                    group.Add(nut.Solid);
                    //if (group.Count == 1) continue;

                    //---------------------------------- Cases 2,6 --------------------------------
                    var           potentialFastener            = group.Where(s => s != nut.Solid).ToList();
                    Fastener      fastener                     = null;
                    var           nutAndWasherRemovalDirection = 0;
                    List <Washer> washers = new List <Washer>();
                    foreach (var pF in potentialFastener)
                    {
                        if (pF.Volume > nut.Solid.Volume && fastener == null)
                        {
                            // can be a fastener
                            fastener = new Fastener
                            {
                                Solid            = pF,
                                RemovalDirection =
                                    FastenerDetector.RemovalDirectionFinderUsingObb(pF,
                                                                                    BoundingGeometry.OrientedBoundingBoxDic[pF]),
                                OverallLength =
                                    GeometryFunctions.SortedLengthOfObbEdges(
                                        BoundingGeometry.OrientedBoundingBoxDic[pF])[2],
                                EngagedLength = GeometryFunctions.SortedLengthOfObbEdges( // TBD
                                    BoundingGeometry.OrientedBoundingBoxDic[pF])[2],
                                Diameter  = nut.Diameter,
                                Certainty = 0.3
                            };
                            nutAndWasherRemovalDirection = DisassemblyDirections.Directions.IndexOf(
                                DisassemblyDirections.Directions[fastener.RemovalDirection].multiply(-1.0));
                        }
                        if (pF.Volume < nut.Solid.Volume)
                        {
                            // can be a washer
                            washers.Add(new Washer
                            {
                                Solid     = pF,
                                Certainty = 0.2
                            });
                        }
                    }
                    // by this point these things can have happened:
                    // A nut that its unknown fastener is found
                    // or a nut that its washer is found without fastener
                    // a nut, with its fastener and washer
                    FastenerDetector.Washers.UnionWith(washers);
                    if (fastener != null)
                    {
                        FastenerDetector.Fasteners.Add(fastener);
                        nut.RemovalDirection = nutAndWasherRemovalDirection;
                        fastener.Nuts        = new List <Nut> {
                            nut
                        };
                        fastener.Washer = washers;
                    }
                    // but if the fastener is null, if any washer exists,
                    //  it is a washer for the nut. Example: there is a
                    //  rod with some threads on one of its ends.
                    //  I cannot find the rod, but I can possibly find the
                    //  nut and the washer. So I will also define washer as
                    //  a property for nut.
                    nut.Washer = washers;
                    // removal direction? I can say along a line, and
                    // the see if any of those directions are infinite.
                    // but this doesnt work for threaded nuts and rods
                }
            }
        }
        private static bool PhillipsSlotComboHeadBolt(TessellatedSolid solid, List <PrimitiveSurface> solidPrim,
                                                      Dictionary <TemporaryFlat, List <TemporaryFlat> > equalPrimitives, List <TessellatedSolid> repeated)
        {
            repeated.Add(solid);
            var fourFlat = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 4);

            if (fourFlat.Count < 2)
            {
                return(false);
            }
            var eachSlot = 0;
            var flats    = new List <TemporaryFlat>();

            foreach (var candidateHex in fourFlat)
            {
                var candidateHexVal = equalPrimitives[candidateHex];
                var cos             = new List <double>();
                var firstPrimNormal = (candidateHexVal[0]).Normal;
                for (var i = 1; i < candidateHexVal.Count; i++)
                {
                    cos.Add(firstPrimNormal.dotProduct((candidateHexVal[i]).Normal));
                }
                // if it is a slot and phillips combo the cos list must have two -1 and one 1
                // and this needs to appear 2 times.
                if (cos.Count(c => Math.Abs(-1 - c) < 0.0001) != 2 ||
                    cos.Count(c => Math.Abs(1 - c) < 0.0001) != 1)
                {
                    continue;
                }
                if (!solidPrim.Where(p => p is Cylinder).Cast <Cylinder>().Any(c => c.IsPositive))
                {
                    continue;
                }
                flats.AddRange(candidateHexVal);
                eachSlot++;
            }
            if (eachSlot != 2)
            {
                return(false);
            }
            var lengthAndRadius = FastenerEngagedLengthAndRadiusNoThread(solid, solidPrim);

            foreach (var repeatedSolid in repeated)
            {
                var fastener = new Fastener
                {
                    Solid            = repeatedSolid,
                    FastenerType     = FastenerTypeEnum.Bolt,
                    Tool             = Tool.PhillipsBlade,
                    RemovalDirection =
                        //RemovalDirectionFinderForAllenHexPhillips(flats.Cast<Flat>().ToList(),
                        //    BoundingGeometry.OrientedBoundingBoxDic[solid]),
                        FastenerDetector.RemovalDirectionFinderUsingObb(repeatedSolid, BoundingGeometry.OrientedBoundingBoxDic[repeatedSolid]),
                    OverallLength =
                        GeometryFunctions.SortedLengthOfObbEdges(BoundingGeometry.OrientedBoundingBoxDic[solid])[2],
                    EngagedLength = lengthAndRadius[0],
                    Diameter      = lengthAndRadius[1] * 2.0,
                    Certainty     = 1.0
                };
                lock (FastenerDetector.Fasteners)
                    FastenerDetector.Fasteners.Add(fastener);
            }
            return(true);
        }
        private static bool PhillipsHeadBolt(TessellatedSolid solid, List <PrimitiveSurface> solidPrim,
                                             Dictionary <TemporaryFlat, List <TemporaryFlat> > equalPrimitives, List <TessellatedSolid> repeated)
        {
            repeated.Add(solid);
            var eightFlat = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 8);

            if (!eightFlat.Any())
            {
                // before I return false, I will take the ones with 4 faces that have a close area and merge them.

                /*var fourEqual1 = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 4);
                 * var closeArea1 = new List<TemporaryFlat[]>();
                 * for (var i = 0; i < fourEqual1.Count - 1; i++)
                 * {
                 *  for (var j = i + 1; j < fourEqual1.Count; j++)
                 *  {
                 *      if (Math.Abs(fourEqual1[i].Area - fourEqual1[j].Area) < 0.1 * fourEqual1[i].Area)
                 *      {
                 *          closeArea1.Add(new[] { fourEqual1[i], fourEqual1[j] });
                 *          break;
                 *      }
                 *  }
                 * }
                 * foreach (var cA in closeArea1)
                 * {
                 *  equalPrimitives[cA[0]].AddRange(equalPrimitives[cA[1]]);
                 * }
                 * return PhillipsHeadBolt(solid, solidPrim, equalPrimitives, repeated);*/
                return(false);
            }
            foreach (var candidateHex in eightFlat)
            {
                var candidateHexVal = equalPrimitives[candidateHex];
                var cos             = new List <double>();
                var firstPrimNormal = (candidateHexVal[0]).Normal;
                for (var i = 1; i < candidateHexVal.Count; i++)
                {
                    cos.Add(firstPrimNormal.dotProduct((candidateHexVal[i]).Normal));
                }
                // if it is philips head, the cos list must have four 0, two -1 and one 1
                if (cos.Count(c => Math.Abs(0.0 - c) < 0.025) != 4 ||
                    cos.Count(c => Math.Abs(-1 - c) < 0.025) != 2 ||
                    cos.Count(c => Math.Abs(1 - c) < 0.001) != 1)
                {
                    continue;
                }
                var lengthAndRadius = FastenerEngagedLengthAndRadiusNoThread(solid, solidPrim);
                foreach (var repeatedSolid in repeated)
                {
                    var fastener = new Fastener
                    {
                        Solid            = repeatedSolid,
                        FastenerType     = FastenerTypeEnum.Bolt,
                        Tool             = Tool.PhillipsBlade,
                        RemovalDirection =
                            //RemovalDirectionFinderForAllenHexPhillips(candidateHexVal.Cast<Flat>().ToList(),
                            //    BoundingGeometry.OrientedBoundingBoxDic[solid]),
                            FastenerDetector.RemovalDirectionFinderUsingObb(repeatedSolid, BoundingGeometry.OrientedBoundingBoxDic[repeatedSolid]),
                        OverallLength =
                            GeometryFunctions.SortedLengthOfObbEdges(BoundingGeometry.OrientedBoundingBoxDic[solid])[2],
                        EngagedLength = lengthAndRadius[0],
                        Diameter      = lengthAndRadius[1] * 2.0,
                        Certainty     = 1.0
                    };
                    lock (FastenerDetector.Fasteners)
                        FastenerDetector.Fasteners.Add(fastener);
                }
                return(true);
            }
            // before I return false, I will take the ones with 4 faces that have a close area and merge them.

            /*var fourEqual = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 4);
             * var closeArea = new List<TemporaryFlat[]>();
             * for (var i = 0; i < fourEqual.Count - 1; i++)
             * {
             *  for (var j = i+1; j < fourEqual.Count; j++)
             *  {
             *      if (Math.Abs(fourEqual[i].Area - fourEqual[j].Area) < 0.01*fourEqual[i].Area)
             *      {
             *          closeArea.Add(new []{ fourEqual[i] , fourEqual[j] });
             *          break;
             *      }
             *  }
             * }
             * foreach (var cA in closeArea)
             * {
             *  equalPrimitives[cA[0]].AddRange(equalPrimitives[cA[1]]);
             * }
             * return PhillipsHeadBolt(solid, solidPrim, equalPrimitives, repeated);*/
            return(false);
        }
        private static bool HexBoltNutAllen(TessellatedSolid solid, List <PrimitiveSurface> solidPrim,
                                            Dictionary <TemporaryFlat, List <TemporaryFlat> > equalPrimitives, List <TessellatedSolid> repeated)
        {
            repeated.Add(solid);
            var sixFlat = FastenerDetector.EqualPrimitivesFinder(equalPrimitives, 6);

            if (!sixFlat.Any())
            {
                return(false);
            }
            foreach (var candidateHex in sixFlat)
            {
                var candidateHexVal = equalPrimitives[candidateHex];
                var cos             = new List <double>();
                var firstPrimNormal = (candidateHexVal[0]).Normal;
                for (var i = 1; i < candidateHexVal.Count; i++)
                {
                    cos.Add(firstPrimNormal.dotProduct((candidateHexVal[i]).Normal));
                }
                // if it is a hex or allen bolt, the cos list must have two 1/2, two -1/2 and one -1
                if (cos.Count(c => Math.Abs(0.5 - c) < 0.0001) != 2 ||
                    cos.Count(c => Math.Abs(-0.5 - c) < 0.0001) != 2 ||
                    cos.Count(c => Math.Abs(-1 - c) < 0.0001) != 1)
                {
                    continue;
                }
                var lengthAndRadius = FastenerEngagedLengthAndRadiusNoThread(solid, solidPrim);
                if (IsItAllen(candidateHexVal))
                {
                    // this is a socket bolt (allen)
                    foreach (var repeatedSolid in repeated)
                    {
                        var fastener = new Fastener
                        {
                            Solid            = repeatedSolid,
                            FastenerType     = FastenerTypeEnum.Bolt,
                            Tool             = Tool.Allen,
                            ToolSize         = ToolSizeFinder(candidateHexVal),
                            RemovalDirection =
                                //RemovalDirectionFinderForAllenHexPhillips(candidateHexVal.Cast<Flat>().ToList(),
                                //    BoundingGeometry.OrientedBoundingBoxDic[solid]),
                                FastenerDetector.RemovalDirectionFinderUsingObb(repeatedSolid, BoundingGeometry.OrientedBoundingBoxDic[repeatedSolid]),
                            OverallLength =
                                GeometryFunctions.SortedLengthOfObbEdges(BoundingGeometry.OrientedBoundingBoxDic[solid])[2],
                            EngagedLength = lengthAndRadius[0],
                            Diameter      = lengthAndRadius[1] * 2.0,
                            Certainty     = 1.0
                        };
                        lock (FastenerDetector.Fasteners)
                            FastenerDetector.Fasteners.Add(fastener);
                    }
                    return(true);
                }
                // else: it is a hex bolt or nut
                if (IsItNut(solidPrim.Where(p => p is Cylinder).Cast <Cylinder>().ToList(), solid))
                {
                    foreach (var repeatedSolid in repeated)
                    {
                        lock (FastenerDetector.Nuts)
                            FastenerDetector.Nuts.Add(new Nut
                            {
                                NutType       = NutType.Hex,
                                Solid         = repeatedSolid,
                                ToolSize      = ToolSizeFinder(candidateHexVal),
                                OverallLength = lengthAndRadius[0],
                                Diameter      = lengthAndRadius[1] * 2.0,
                                Certainty     = 1.0
                            });
                    }
                    return(true);
                }
                foreach (var repeatedSolid in repeated)
                {
                    lock (FastenerDetector.Fasteners)
                        FastenerDetector.Fasteners.Add(new Fastener
                        {
                            Solid            = repeatedSolid,
                            FastenerType     = FastenerTypeEnum.Bolt,
                            Tool             = Tool.HexWrench,
                            ToolSize         = ToolSizeFinder(candidateHexVal),
                            RemovalDirection =
                                //RemovalDirectionFinderForAllenHexPhillips(candidateHexVal.Cast<Flat>().ToList(),
                                //    BoundingGeometry.OrientedBoundingBoxDic[solid]),
                                FastenerDetector.RemovalDirectionFinderUsingObb(repeatedSolid, BoundingGeometry.OrientedBoundingBoxDic[repeatedSolid]),
                            OverallLength =
                                GeometryFunctions.SortedLengthOfObbEdges(BoundingGeometry.OrientedBoundingBoxDic[solid])
                                [2],
                            EngagedLength = lengthAndRadius[0],
                            Diameter      = lengthAndRadius[1] * 2.0,
                            Certainty     = 1.0
                        });
                }
                return(true);
            }
            return(false);
        }
        internal static void Run(
            Dictionary <TessellatedSolid, List <PrimitiveSurface> > solidPrimitive,
            Dictionary <TessellatedSolid, List <TessellatedSolid> > multipleRefs)
        {
            // This approach will use the symmetric shape of the fasteners' heads. If there is no thread,
            // we willl consider the area of the positive culinders for bolts and negative cylinder for
            // nuts.
            // This is an important point:
            //      1. Find the small objects using all of the solids
            //      2. Group them using small objects
            //      3. Detect the fasteners using multiple references. (for each similar object, detect one of them)
            var width = 55;
            var check = 0;

            LoadingBar.start(width, 0);

            var smallParts = FastenerDetector.SmallObjectsDetector(DisassemblyDirectionsWithFastener.PartsWithOneGeom);

            //new List<TessellatedSolid>(DisassemblyDirectionsWithFastener.PartsWithOneGeom);

            PreSelectedFastenerToFastenerClass(solidPrimitive, multipleRefs);
            foreach (
                var preSelected in
                FastenerDetector.PreSelectedFasteners.Where(preSelected => !smallParts.Contains(preSelected)))
            {
                smallParts.Add(preSelected);
            }

            FastenerDetector.PotentialFastener = new Dictionary <TessellatedSolid, double>();
            foreach (var p in smallParts)
            {
                FastenerDetector.PotentialFastener.Add(p, 0.1);
            }

            var groupedPotentialFasteners = FastenerDetector.GroupingSmallParts(smallParts);
            var uniqueParts = new HashSet <TessellatedSolid>();

            foreach (var s in multipleRefs.Keys.Where(FastenerDetector.PotentialFastener.Keys.Contains))
            {
                uniqueParts.Add(s);
            }
            foreach (
                var preFastener in
                FastenerDetector.Fasteners.Where(preFastener => uniqueParts.Contains(preFastener.Solid)))
            {
                uniqueParts.Remove(preFastener.Solid);
            }

            var equalPrimitivesForEveryUniqueSolid = FastenerDetector.EqualFlatPrimitiveAreaFinder(uniqueParts,
                                                                                                   solidPrimitive);
            var checkedSolids = new HashSet <TessellatedSolid>();

            FastenerGaussianNaiveBayes.GNB();



            List <string> nameList = new List <string>();

            foreach (var part in uniqueParts)
            {
                nameList.Add(part.Name);
            }
            float nameRating;

            float ratingAverage = 0;
            float ratingMax     = -1;
            float ratingMin     = 100000000000;
            int   preCutoff     = 0;
            int   postCutoff    = 0;

            PartNameAnalysis.findCommonPreSuffixes(nameList, ref preCutoff, ref postCutoff);
            foreach (var part in uniqueParts)
            {
                nameRating     = PartNameAnalysis.SolidHasFastenerKeyword(part, preCutoff, postCutoff);
                ratingAverage += nameRating;
                ratingMax      = Math.Max(ratingMax, nameRating);
                ratingMin      = Math.Min(ratingMin, nameRating);
            }
            float proportion = 1 - (ratingMax - ratingMin) / 3;



            var refresh = (int)Math.Ceiling((float)uniqueParts.Count / (float)(width * 4));

            Parallel.ForEach(uniqueParts, solid =>
                             //foreach (var solid in uniqueParts)
            {
                if (check % refresh == 0)
                {
                    LoadingBar.refresh(width, ((float)check) / ((float)uniqueParts.Count));
                }
                check++;

                var initialCertainty = FastenerGaussianNaiveBayes.GaussianNaiveBayesClassifier(solidPrimitive[solid],
                                                                                               solid);

                nameRating = (PartNameAnalysis.SolidHasFastenerKeyword(solid, preCutoff, postCutoff) - ratingMin) / (0.001f + ratingMax - ratingMin);

                FastenerDetector.PotentialFastener[solid] = (0.1 + initialCertainty) * proportion + (1 - nameRating) * (1 - proportion);
                foreach (var up in multipleRefs[solid])
                {
                    FastenerDetector.PotentialFastener[up] = (0.1 + initialCertainty) * proportion + (1 - nameRating) * (1 - proportion);
                }


                if (solidPrimitive[solid].Count == 0)
                {
                    return;
                }
                if (HexBoltNutAllen(solid, solidPrimitive[solid], equalPrimitivesForEveryUniqueSolid[solid],
                                    multipleRefs[solid]))
                {
                    //continue;
                    lock (checkedSolids)
                    {
                        checkedSolids.Add(solid);
                    }
                    return;
                }
                if (PhillipsHeadBolt(solid, solidPrimitive[solid], equalPrimitivesForEveryUniqueSolid[solid],
                                     multipleRefs[solid]))
                {
                    //continue;
                    lock (checkedSolids)
                    {
                        checkedSolids.Add(solid);
                    }
                    return;
                }
                if (SlotHeadBolt(solid, solidPrimitive[solid], equalPrimitivesForEveryUniqueSolid[solid],
                                 multipleRefs[solid]))
                {
                    //continue;
                    lock (checkedSolids)
                    {
                        checkedSolids.Add(solid);
                    }
                    return;
                }
                if (PhillipsSlotComboHeadBolt(solid, solidPrimitive[solid], equalPrimitivesForEveryUniqueSolid[solid],
                                              multipleRefs[solid]))
                {
                    //continue;
                    lock (checkedSolids)
                    {
                        checkedSolids.Add(solid);
                    }
                    return;
                }
                lock (checkedSolids)
                {
                    checkedSolids.Add(solid);
                }
            }
                             );//
            // when all of the fasteners are recognized, it's time to check the third level filter:
            // something is not acceptable: nut without fastener. If there is a nut without fastener,
            // I will try to look for that.
            // Is there anyway to detect more?
            ConnectFastenersNutsAndWashers(groupedPotentialFasteners);
            LoadingBar.refresh(width, 1);
        }
        internal static void Run(
            Dictionary <TessellatedSolid, List <PrimitiveSurface> > solidPrimitive,
            Dictionary <TessellatedSolid, List <TessellatedSolid> > multipleRefs)
        {
            var width = 55;
            var check = 0;

            LoadingBar.start(width, 0);

            var smallParts = FastenerDetector.SmallObjectsDetector(DisassemblyDirectionsWithFastener.PartsWithOneGeom);

            PreSelectedFastenerToFastenerClass(solidPrimitive, multipleRefs);
            foreach (
                var preSelected in
                FastenerDetector.PreSelectedFasteners.Where(preSelected => !smallParts.Contains(preSelected)))
            {
                smallParts.Add(preSelected);
            }

            FastenerDetector.PotentialFastener = new Dictionary <TessellatedSolid, double>();
            foreach (var p in smallParts)
            {
                FastenerDetector.PotentialFastener.Add(p, 0.1);
            }
            var groupedPotentialFasteners = FastenerDetector.GroupingSmallParts(smallParts);
            var uniqueParts = new HashSet <TessellatedSolid>();

            foreach (var s in multipleRefs.Keys.Where(FastenerDetector.PotentialFastener.Keys.Contains))
            {
                uniqueParts.Add(s);
            }
            foreach (
                var preFastener in
                FastenerDetector.Fasteners.Where(preFastener => uniqueParts.Contains(preFastener.Solid)))
            {
                uniqueParts.Remove(preFastener.Solid);
            }

            var equalPrimitivesForEveryUniqueSolid = FastenerDetector.EqualFlatPrimitiveAreaFinder(uniqueParts,
                                                                                                   solidPrimitive);
            List <int> learnerVotes;
            var        learnerWeights = FastenerPerceptronLearner.ReadingLearnerWeightsAndVotesFromCsv(out learnerVotes);


            List <string> nameList = new List <string>();

            foreach (var part in uniqueParts)
            {
                nameList.Add(part.Name);
            }
            int preCutoff  = 0;
            int postCutoff = 0;

            PartNameAnalysis.findCommonPreSuffixes(nameList, ref preCutoff, ref postCutoff);



            var refresh = (int)Math.Ceiling((float)uniqueParts.Count / (float)(width * 4));

            Parallel.ForEach(uniqueParts, solid =>
                             //foreach (var solid in uniqueParts)
            {
                if (check % refresh == 0)
                {
                    LoadingBar.refresh(width, ((float)check) / ((float)uniqueParts.Count));
                }
                check++;

                double toolSize;
                var commonHead = AutoThreadedFastenerDetection.CommonHeadCheck(solid, solidPrimitive[solid],
                                                                               equalPrimitivesForEveryUniqueSolid[solid], out toolSize);
                if (commonHead == 1)
                {
                    // can be a threaded hex fastener, threaded hex nut, nonthreaded hex fastener, nonthreaded hex nut
                    var newFastener = FastenerPolynomialTrend.PolynomialTrendDetector(solid);
                    if (newFastener != null)
                    {
                        // threaded hex fastener
                        newFastener.Tool     = Tool.HexWrench;
                        newFastener.ToolSize = toolSize;
                        lock (FastenerDetector.Fasteners)
                            FastenerDetector.Fasteners.Add(newFastener);
                        AutoThreadedFastenerDetection.AddRepeatedSolidstoFasteners(newFastener, multipleRefs[solid]);
                        //continue;
                        return;
                    }
                    var lengthAndRadius =
                        AutoNonthreadedFastenerDetection.FastenerEngagedLengthAndRadiusNoThread(solid, solidPrimitive[solid]);
                    if (AutoNonthreadedFastenerDetection.IsItNut(
                            solidPrimitive[solid].Where(p => p is Cylinder).Cast <Cylinder>().ToList(), solid))
                    {
                        var nut = new Nut
                        {
                            NutType       = NutType.Hex,
                            Solid         = solid,
                            ToolSize      = toolSize,
                            OverallLength = lengthAndRadius[0],
                            Diameter      = lengthAndRadius[1] * 2.0
                        };
                        lock (FastenerDetector.Nuts)
                            FastenerDetector.Nuts.Add(nut);
                        AutoThreadedFastenerDetection.AddRepeatedSolidstoNuts(nut, multipleRefs[solid]);
                    }
                    // nonthreaded hex fastener
                    var fas = new Fastener
                    {
                        Solid            = solid,
                        FastenerType     = FastenerTypeEnum.Bolt,
                        Tool             = Tool.HexWrench,
                        ToolSize         = toolSize,
                        RemovalDirection = FastenerDetector.RemovalDirectionFinderUsingObb(solid,
                                                                                           BoundingGeometry.OrientedBoundingBoxDic[solid]),
                        OverallLength =
                            GeometryFunctions.SortedLengthOfObbEdges(BoundingGeometry.OrientedBoundingBoxDic[solid])
                            [2],
                        EngagedLength = lengthAndRadius[0],
                        Diameter      = lengthAndRadius[1] * 2.0,
                        Certainty     = 1.0
                    };
                    lock (FastenerDetector.Fasteners)
                        FastenerDetector.Fasteners.Add(fas);
                    AutoThreadedFastenerDetection.AddRepeatedSolidstoFasteners(fas, multipleRefs[solid]);
                    return;
                }
                if (commonHead == 2)
                {
                    // can be a threaded allen, nonthreaded allen
                    AddThreadedOrNonthreadedFastener(solid, multipleRefs[solid], solidPrimitive[solid], Tool.Allen,
                                                     toolSize);
                    return;
                }
                if (commonHead == 3 || commonHead == 5)
                {
                    // can be a threaded philips fastener, nonthreaded philips fastener
                    AddThreadedOrNonthreadedFastener(solid, multipleRefs[solid], solidPrimitive[solid], Tool.PhillipsBlade,
                                                     toolSize);
                    return;
                }
                if (commonHead == 4)
                {
                    // can be a threaded flat fastener, nonthreaded flat fastener
                    AddThreadedOrNonthreadedFastener(solid, multipleRefs[solid], solidPrimitive[solid], Tool.FlatBlade,
                                                     toolSize);
                    return;
                }

                // if it's not a common head and not threaded, I cannot detect it.
                // so the rest will be similar to threaded fastener detector
                if (FastenerPerceptronLearner.FastenerPerceptronClassifier(solidPrimitive[solid], solid, learnerWeights, learnerVotes))
                {
                    var newFastener = FastenerPolynomialTrend.PolynomialTrendDetector(solid);
                    if (newFastener != null)
                    {
                        lock (FastenerDetector.Fasteners)
                            FastenerDetector.Fasteners.Add(newFastener);
                        AutoThreadedFastenerDetection.AddRepeatedSolidstoFasteners(newFastener, multipleRefs[solid]);
                        //continue;
                        return;
                    }
                    // can be a nut
                    // use bounding cylinder to detect nuts.
                    // Since the nuts are small, the OBB function doesnt work accurately for them.
                    //  So, I cannot really trust this.
                    var newNut = NutPolynomialTrend.PolynomialTrendDetector(solid);
                    if (newNut != null)
                    // It is a nut with certainty == 1
                    {
                        lock (FastenerDetector.Nuts)
                            FastenerDetector.Nuts.Add(newNut);
                        AutoThreadedFastenerDetection.AddRepeatedSolidstoNuts(newNut, multipleRefs[solid]);
                        //continue;
                        return;
                    }
                    // still can be a nut since the upper approach is not really accurate
                    // this 50 percent certainty can go up if the nut is mated with a
                    // detected fastener
                    foreach (var repeatedSolid in multipleRefs[solid])
                    {
                        lock (FastenerDetector.Nuts)
                            FastenerDetector.Nuts.Add(new Nut
                            {
                                Solid    = repeatedSolid,
                                Diameter = BoundingGeometry.BoundingCylinderDic[solid].Radius * 2.0,
                                // this is approximate
                                OverallLength = BoundingGeometry.BoundingCylinderDic[solid].Length,
                                Certainty     = 0.5
                            });
                    }
                    //continue;
                    return;
                }
                // if it is not captured by any of the upper methods, give it another chance:
                if (AutoThreadedFastenerDetection.ThreadDetector(solid, solidPrimitive[solid]))
                {
                    var newFastener = FastenerPolynomialTrend.PolynomialTrendDetector(solid);
                    if (newFastener != null)
                    {
                        newFastener.FastenerType = FastenerTypeEnum.Bolt;
                        lock (FastenerDetector.Fasteners)
                            FastenerDetector.Fasteners.Add(newFastener);
                        AutoThreadedFastenerDetection.AddRepeatedSolidstoFasteners(newFastener, multipleRefs[solid]);
                        //continue;
                        return;
                    }
                    //if not, it is a nut:
                    foreach (var repeatedSolid in multipleRefs[solid])
                    {
                        lock (FastenerDetector.Nuts)
                            FastenerDetector.Nuts.Add(new Nut
                            {
                                Solid    = repeatedSolid,
                                Diameter = BoundingGeometry.BoundingCylinderDic[solid].Radius * 2.0,
                                // this is approximate
                                OverallLength = BoundingGeometry.BoundingCylinderDic[solid].Length * 2.0,
                                Certainty     = 0.5
                            });
                    }
                }
            }
                             );
            // now use groupped small objects:
            AutoNonthreadedFastenerDetection.ConnectFastenersNutsAndWashers(groupedPotentialFasteners);
            LoadingBar.refresh(width, 1);
        }
        internal static void AddFastenersInformation(designGraph assemblyGraph, Dictionary <string, List <TessellatedSolid> > solidsNoFastener,
                                                     Dictionary <TessellatedSolid, List <PrimitiveSurface> > solidPrimitive)
        {
            var counter = 0;

            foreach (var fastener in FastenerDetector.Fasteners)
            {
                counter++;
                var lockedByTheFastener = PartsLockedByTheFastenerFinder(fastener.Solid, solidsNoFastener, solidPrimitive);
                AddFastenerToArc(assemblyGraph, lockedByTheFastener, fastener);
            }
            // So, by this point, if there is a fastener between two or more parts, a new local variable
            // is added to their arc which shows the direction of freedom of the fastener.
            // So if I want to seperate two parts or two subassemblies, now I know that there is a
            // fastener holding them to each other. And I also know the removal direction of the fastener

            // There is still a possibility here: if any of the potential fasteners are holding 2 or more parts
            // The point is that they can be either a washer, nut or fastener. But if it is a fastener, I need
            // to find the parts that it's holding and add it to their arc
            counter = 0;
            foreach (var possible in FastenerDetector.PotentialFastener.Keys)
            {
                counter++;
                var locked = PartsLockedByTheFastenerFinder(possible, solidsNoFastener, solidPrimitive);
                if (locked.Count < 2)
                {
                    if (locked.Count == 1)
                    {
                        var comp = (Component)assemblyGraph[locked[0]];
                        var pin  = new Fastener()
                        {
                            RemovalDirection =
                                FastenerDetector.RemovalDirectionFinderUsingObb(possible,
                                                                                BoundingGeometry.OrientedBoundingBoxDic[possible]),
                            Solid         = possible,
                            Diameter      = BoundingGeometry.BoundingCylinderDic[possible].Radius,
                            OverallLength = BoundingGeometry.BoundingCylinderDic[possible].Length
                        };
                        if (comp.Pins == null)
                        {
                            comp.Pins = new List <Fastener>();
                        }
                        comp.Pins.Add(pin);
                    }
                }
                PolygonalFace topPlane = null;
                var           fastener = new Fastener()
                {
                    RemovalDirection =
                        FastenerDetector.RemovalDirectionFinderUsingObbWithTopPlane(possible,
                                                                                    BoundingGeometry.OrientedBoundingBoxDic[possible], out topPlane),
                    Solid         = possible,
                    Diameter      = BoundingGeometry.BoundingCylinderDic[possible].Radius,
                    OverallLength = BoundingGeometry.BoundingCylinderDic[possible].Length
                };
                AddFastenerToArc(assemblyGraph, locked, fastener);

                /*(if (fastener.PartsLockedByFastener.Count > 2)
                 *  // if there are more than 2 parts locked by the fastener, sort them based on their distance to the top plane of the fastener
                 * {
                 *
                 * }*/
            }
        }