/// <summary> /// Insertion point /// </summary> /// <param name="region"></param> /// <param name="master"></param> public OctTree( BoundingBox masterRegion, IEnumerable <Vector3> masterColl, List <OctTree> baseList, float basesize = 0.010f) { List <Vector3> masterList = masterColl.ToList(); if (masterList.Count.Equals(0)) { return; } m_Points = masterList; m_baseSize = basesize; //Set region's points m_Region = masterRegion; //Get dimension of master box Vector3 dimensions = m_Region.Max - m_Region.Min; //Halfway point of box Vector3 half = dimensions / 2f; //Center of box Vector3 center = m_Region.Min + half; //Smallest size box, send back to baselist if (dimensions.X <= m_baseSize && dimensions.Y <= m_baseSize && dimensions.Z <= m_baseSize) { baseList.Add(this); Dims = dimensions; } else { bool firstpass = true; //Create set of boxes BoundingBox[] boxes = new BoundingBox[8]; //Create holder for points List <Vector3>[] boxPoints = new List <Vector3> [8]; m_PendingInsertion = new Queue <Vector3>(masterList); //Spin up all individual boxes boxes[0] = new BoundingBox(m_Region.Min, center); boxes[1] = new BoundingBox(new Vector3(center.X, m_Region.Min.Y, m_Region.Min.Z), new Vector3(m_Region.Max.X, center.Y, center.Z)); boxes[2] = new BoundingBox(new Vector3(center.X, m_Region.Min.Y, center.Z), new Vector3(m_Region.Max.X, center.Y, m_Region.Max.Z)); boxes[3] = new BoundingBox(new Vector3(m_Region.Min.X, m_Region.Min.Y, center.Z), new Vector3(center.X, center.Y, m_Region.Max.Z)); boxes[4] = new BoundingBox(new Vector3(m_Region.Min.X, center.Y, m_Region.Min.Z), new Vector3(center.X, m_Region.Max.Y, center.Z)); boxes[5] = new BoundingBox(new Vector3(center.X, center.Y, m_Region.Min.Z), new Vector3(m_Region.Max.X, m_Region.Max.Y, center.Z)); boxes[6] = new BoundingBox(center, m_Region.Max); boxes[7] = new BoundingBox(new Vector3(m_Region.Min.X, center.Y, center.Z), new Vector3(center.X, m_Region.Max.Y, m_Region.Max.Z)); while (m_PendingInsertion.Count > 0) { Vector3 temp = m_PendingInsertion.Dequeue(); for (int i = 0; i < boxes.Length; i++) { //instantite list on first pass if (firstpass) { boxPoints[i] = new List <Vector3>(); } //find container box until binnable if ( boxes[i].Contains(temp).Equals(ContainmentType.Contains) || boxes[i].Contains(temp).Equals(ContainmentType.Intersects)) { boxPoints[i].Add(temp); } } if (firstpass) { firstpass = false; } } for (int i = 0; i < boxPoints.Length; i++) { if (boxPoints[i].Count > 0) { //Assign child m_ChildNode[i] = new OctTree(boxes[i], boxPoints[i], baseList, basesize); } } } }
private void Generate() { m_stopwatch.Restart(); Vector3[] conglom = DataProcessing.conglomerateData(m_lastPointCloud, m_lastRotationAxis, 360f / m_lastScanNum); m_lastStats.TotalPoints = conglom.Length; //Dump points into octtree nodes List <OctTree> OctList = new List <OctTree>(); OctTree octTree = new OctTree(new BoundingBox(m_lastBoundingContext.Min, new Vector3(1, 1, 1)), conglom.Distinct(), OctList, (float)Pro_octreeResolution.Value); m_lastStats = new Stats(); m_lastStats.OctalNodes = OctList.Count; m_lastStats.OctalDim = OctList[0].Dims; //Do statistical analysis of points double octAverage = (float)OctList.Average(x => x.m_Points.Count); double octSD = OctList.Select(x => (double)x.m_Points.Count).StandardDeviation(); double octSOM = octSD / Math.Sqrt(OctList.Count); m_lastStats.Average = octAverage; m_lastStats.StandardDeviation = octSD; //Remove all outliers OctList.RemoveAll(x => x.m_Points.Count < octAverage - octSD / 4); OctList.ForEach(x => x.m_Points = new List <Vector3>() { new Vector3(x.m_Points.Average(q => q.X), x.m_Points.Average(q => q.Y), x.m_Points.Average(q => q.Z)) }); //Separate octree into its layers var layers = (from q in OctList group q by q.m_Region.Min.Y into r select new { r.Key, List = r.Select(x => x) } into s orderby s.Key select s).ToList(); List <IEnumerable <Vector3> > goodPoints = new List <IEnumerable <Vector3> >(); if (Pro_fillBottomCheck.IsChecked == true) { float averageY = layers.First().List.SelectMany(x => x.m_Points).Average(x => x.Y); //Make a bottom layer to be culled List <Vector3> bottomMax = new List <Vector3>(); for (float x = m_lastBoundingContext.Min.X; x < m_lastBoundingContext.Max.X; x += (float)Pro_octreeResolution.Value) { for (float z = m_lastBoundingContext.Min.Z; z < m_lastBoundingContext.Max.Z; z += (float)Pro_octreeResolution.Value) { bottomMax.Add(new Vector3(x, averageY, z)); } } //Get bottom layer angle, distance and point for grid var botLayerDetails = (from q in bottomMax select new { Angle = DataProcessing.GetAngle(q.X, q.Z, m_lastRotationAxis.X, m_lastRotationAxis.Z), Distance = Math.Sqrt(Math.Pow(q.X - m_lastRotationAxis.X, 2) + Math.Pow(q.Z - m_lastRotationAxis.Z, 2)), Point = q, } into r orderby r.Angle select r).ToList(); //Get bottom layer angle for var bottomTrue = layers.First().List.SelectMany(x => x.m_Points); var bottomTrueDetails = (from q in bottomTrue select new { Angle = DataProcessing.GetAngle(q.X, q.Z, m_lastRotationAxis.X, m_lastRotationAxis.Z), Distance = Math.Sqrt(Math.Pow(q.X - m_lastRotationAxis.X, 2) + Math.Pow(q.Z - m_lastRotationAxis.Z, 2)), Point = q, } into r orderby r.Angle select r).ToList(); double lastAngle = 0; List <Vector3> goodBottom = new List <Vector3>(); for (int i = 0; i < bottomTrueDetails.Count(); i++) { var checker = bottomTrueDetails[i]; var goodSection = from q in botLayerDetails where q.Angle > lastAngle && q.Angle <= checker.Angle && q.Distance < checker.Distance select q; goodBottom.AddRange(goodSection.Select(x => x.Point)); lastAngle = checker.Angle; } goodPoints.Add(goodBottom); } if (Pro_fillTopCheck.IsChecked == true) { float averageY = layers.Last().List.SelectMany(x => x.m_Points).Average(x => x.Y); //Make a bottom layer to be culled List <Vector3> topMax = new List <Vector3>(); for (float x = m_lastBoundingContext.Min.X; x < m_lastBoundingContext.Max.X; x += (float)Pro_octreeResolution.Value) { for (float z = m_lastBoundingContext.Min.Z; z < m_lastBoundingContext.Max.Z; z += (float)Pro_octreeResolution.Value) { topMax.Add(new Vector3(x, averageY, z)); } } //Get bottom layer angle, distance and point for grid var topLayerDetails = (from q in topMax select new { Angle = DataProcessing.GetAngle(q.X, q.Z, m_lastRotationAxis.X, m_lastRotationAxis.Z), Distance = Math.Sqrt(Math.Pow(q.X - m_lastRotationAxis.X, 2) + Math.Pow(q.Z - m_lastRotationAxis.Z, 2)), Point = q, } into r orderby r.Angle select r).ToList(); //Get bottom layer angle for var topTrue = layers.Last().List.SelectMany(x => x.m_Points); var topTrueDetails = (from q in topTrue select new { Angle = DataProcessing.GetAngle(q.X, q.Z, m_lastRotationAxis.X, m_lastRotationAxis.Z), Distance = Math.Sqrt(Math.Pow(q.X - m_lastRotationAxis.X, 2) + Math.Pow(q.Z - m_lastRotationAxis.Z, 2)), Point = q, } into r orderby r.Angle select r).ToList(); double lastAngle = 0; List <Vector3> goodTop = new List <Vector3>(); for (int i = 0; i < topTrueDetails.Count(); i++) { var checker = topTrueDetails[i]; var goodSection = from q in topLayerDetails where q.Angle > lastAngle && q.Angle <= checker.Angle && q.Distance < checker.Distance select q; goodTop.AddRange(goodSection.Select(x => x.Point)); lastAngle = checker.Angle; } goodPoints.Add(goodTop); } goodPoints.AddRange(OctList.Select(x => x.m_Points.ToList())); Vector3[] final = goodPoints.SelectMany(x => x).ToArray(); m_lastStats.ReducedPoints = final.Length; FileWriter writer = new FileWriter(final, "processing"); writer.WriteToFile(); writer.WriteToASC(); BatchFileManager bfm = new BatchFileManager(Directory.GetCurrentDirectory()); bfm.createBatchFile("pointcloud", Pro_samplingResolution.Value); }