public void NormalsFromEmptyArray() { var ps = new V3f[0]; var kd = ps.BuildKdTree(); var ns = ps.EstimateNormalsAsync(16, kd).Result; Assert.IsTrue(ns.Length == 0); }
public void CanEstimateNormals_FromZeroToThreePoints() { var r = new Random(); for (var n = 0; n < 4; n++) { var ps = new V3f[n].SetByIndex(_ => new V3f(r.NextDouble(), r.NextDouble(), r.NextDouble())); var kd = ps.BuildKdTree(); var ns = Normals.EstimateNormals(ps, 16, kd); Assert.IsTrue(ns.Length == n); } }
public void CreatingKdTreeDoesNotChangeOrderOfPoints() { var r = new Random(); var ps = new V3f[1000].SetByIndex(_ => new V3f(r.NextDouble(), r.NextDouble(), r.NextDouble())); var copy = ps.Copy(); var kd = ps.BuildKdTree(); Assert.IsTrue(kd != null); Assert.IsTrue(ps.Length == copy.Length); for (var i = 0; i < ps.Length; i++) { Assert.IsTrue(ps[i] == copy[i]); } }
/// <summary> /// </summary> private static PointSetNode GenerateLod(this PointSetNode self, long octreeSplitLimit, Action callback, CancellationToken ct) { if (self == null) { throw new ArgumentNullException(nameof(self)); } ct.ThrowIfCancellationRequested(); callback?.Invoke(); if (self.IsLeaf) { return(self.WithLod()); } if (self.Subnodes == null || self.Subnodes.Length != 8) { throw new InvalidOperationException(); } var subcells = self.Subnodes.Map(x => x?.Value.GenerateLod(octreeSplitLimit, callback, ct)); var subcellsTotalCount = (long)subcells.Sum(x => x?.PointCountTree); var needsCs = subcells.Any(x => x != null ? (x.HasColors || x.HasLodColors) : false); var needsNs = subcells.Any(x => x != null ? (x.HasNormals || x.HasLodNormals) : false); var needsIs = subcells.Any(x => x != null ? (x.HasIntensities || x.HasLodIntensities) : false); var fractions = new double[8].SetByIndex( ci => subcells[ci] != null ? (subcells[ci].PointCountTree / (double)subcellsTotalCount) : 0.0 ); var remainder = 0.1; var counts = fractions.Map(x => { var fn = octreeSplitLimit * x + remainder; var n = (int)fn; remainder = fn - n; return(n); }); var e = octreeSplitLimit - counts.Sum(); if (e != 0) { throw new InvalidOperationException(); } // generate LoD data ... var lodPs = new V3f[octreeSplitLimit]; var lodCs = needsCs ? new C4b[octreeSplitLimit] : null; var lodNs = needsNs ? new V3f[octreeSplitLimit] : null; var lodIs = needsIs ? new int[octreeSplitLimit] : null; var i = 0; for (var ci = 0; ci < 8; ci++) { if (counts[ci] == 0) { continue; } var subcell = subcells[ci]; if (subcell == null) { continue; } var subps = subcell.IsLeaf ? subcell.Positions.Value : subcell.LodPositions.Value; var subcs = needsCs ? (subcell.IsLeaf ? subcell.Colors.Value : subcell.LodColors.Value) : null; var subns = needsNs ? (subcell.IsLeaf ? subcell.Normals.Value : subcell.LodNormals.Value) : null; var subis = needsIs ? (subcell.IsLeaf ? subcell.Intensities.Value : subcell.LodIntensities.Value) : null; var jmax = subps.Length; var dj = (jmax + 0.49) / counts[ci]; var oldI = i; for (var j = 0.0; j < jmax; j += dj) { var jj = (int)j; lodPs[i] = (V3f)(((V3d)subps[jj] + subcell.Center) - self.Center); if (needsCs) { lodCs[i] = subcs[jj]; } if (needsNs) { lodNs[i] = subns[jj]; } if (needsIs) { lodIs[i] = subis[jj]; } i++; } } var lodKd = lodPs.BuildKdTree(); // store LoD data ... var lodPsId = Guid.NewGuid(); self.Storage.Add(lodPsId, lodPs, ct); var lodKdId = Guid.NewGuid(); self.Storage.Add(lodKdId, lodKd.Data, ct); var lodCsId = needsCs ? (Guid?)Guid.NewGuid() : null; if (needsCs) { self.Storage.Add(lodCsId.Value, lodCs, ct); } var lodNsId = needsNs ? (Guid?)Guid.NewGuid() : null; if (needsNs) { self.Storage.Add(lodNsId.Value, lodNs, ct); } var lodIsId = needsIs ? (Guid?)Guid.NewGuid() : null; if (needsIs) { self.Storage.Add(lodIsId.Value, lodIs, ct); } var result = self.WithLod(lodPsId, lodCsId, lodNsId, lodIsId, lodKdId, subcells); return(result); }