/// <summary> /// Returns new tree with regenerated normals using given estimateNormals function. /// </summary> private static PointSetNode RegenerateNormals(this PointSetNode self, Func <IList <V3d>, IList <V3f> > estimateNormals, Action callback, CancellationToken ct ) { if (self == null) { throw new ArgumentNullException(nameof(self)); } ct.ThrowIfCancellationRequested(); callback?.Invoke(); if (self.IsLeaf) { // generate and store normals var ns = estimateNormals(self.PositionsAbsolute).ToArray(); var nsId = Guid.NewGuid(); self.Storage.Add(nsId, ns, ct); // create node with new normals and LoD normals var r = self.WithNormals(nsId).WithLod(); return(r); } if (self.Subnodes == null || self.Subnodes.Length != 8) { throw new InvalidOperationException(); } var subcells = self.Subnodes.Map(x => x?.Value.RegenerateNormals(estimateNormals, callback, ct)); var subcellsTotalCount = (long)subcells.Sum(x => x?.PointCountTree); var octreeSplitLimit = self.LodPositions.Value.Length; 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 LodNormals ... var lodNs = new V3f[octreeSplitLimit]; var i = 0; for (var ci = 0; ci < 8; ci++) { if (counts[ci] == 0) { continue; } var subcell = subcells[ci]; if (subcell == null) { continue; } var subns = subcell.LodNormals.Value; var jmax = subns.Length; var dj = (jmax + 0.49) / counts[ci]; var oldI = i; for (var j = 0.0; j < jmax; j += dj) { var jj = (int)j; lodNs[i] = subns[jj]; i++; } } // store LoD data ... var lodNsId = Guid.NewGuid(); self.Storage.Add(lodNsId, lodNs, ct); var result = self.WithLod(self.LodPositionsId, self.LodColorsId, lodNsId, self.LodIntensitiesId, self.LodKdTreeId, subcells); return(result); }
/// <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); }
/// <summary> /// </summary> public static PointSetNode Delete(this PointSetNode node, Func <PointSetNode, bool> isNodeFullyInside, Func <PointSetNode, bool> isNodeFullyOutside, Func <V3d, bool> isPositionInside, CancellationToken ct ) { if (node == null) { return(null); } if (isNodeFullyInside(node)) { return(null); } if (isNodeFullyOutside(node)) { return(node); } if (node.IsLeaf) { Guid?newPsId = null; Guid?newCsId = null; Guid?newNsId = null; Guid?newIsId = null; Guid?newKdId = null; if (!node.HasPositions) { throw new InvalidOperationException(); } var ps = new List <V3f>(); var cs = node.HasColors ? new List <C4b>() : null; var ns = node.HasNormals ? new List <V3f>() : null; var js = node.HasIntensities ? new List <int>() : null; var oldPsAbsolute = node.PositionsAbsolute; var oldPs = node.Positions.Value; var oldCs = node.Colors?.Value; var oldNs = node.Normals?.Value; var oldIs = node.Intensities?.Value; for (var i = 0; i < oldPsAbsolute.Length; i++) { if (!isPositionInside(oldPsAbsolute[i])) { ps.Add(oldPs[i]); if (oldCs != null) { cs.Add(oldCs[i]); } if (oldNs != null) { ns.Add(oldNs[i]); } if (oldIs != null) { js.Add(oldIs[i]); } } } if (ps.Count > 0) { newPsId = Guid.NewGuid(); var psa = ps.ToArray(); node.Storage.Add(newPsId.Value, psa, ct); newKdId = Guid.NewGuid(); node.Storage.Add(newKdId.Value, psa.BuildKdTree().Data, ct); if (node.HasColors) { newCsId = Guid.NewGuid(); node.Storage.Add(newCsId.Value, cs.ToArray(), ct); } if (node.HasNormals) { newNsId = Guid.NewGuid(); node.Storage.Add(newNsId.Value, ns.ToArray(), ct); } if (node.HasIntensities) { newIsId = Guid.NewGuid(); node.Storage.Add(newIsId.Value, js.ToArray(), ct); } var result = new PointSetNode(node.Cell, ps.Count, newPsId, newCsId, newKdId, newNsId, newIsId, node.Storage); if (node.HasLodPositions) { result = result.WithLod(); } return(result); } else { return(null); } } else { Guid?newLodPsId = null; Guid?newLodCsId = null; Guid?newLodNsId = null; Guid?newLodIsId = null; Guid?newLodKdId = null; if (node.HasLodPositions) { var ps = node.HasLodPositions ? new List <V3f>() : null; var cs = node.HasLodColors ? new List <C4b>() : null; var ns = node.HasLodNormals ? new List <V3f>() : null; var js = node.HasLodIntensities ? new List <int>() : null; var oldLodPsAbsolute = node.LodPositionsAbsolute; var oldLodPs = node.LodPositions.Value; var oldLodCs = node.LodColors?.Value; var oldLodNs = node.LodNormals?.Value; var oldLodIs = node.LodIntensities?.Value; for (var i = 0; i < oldLodPsAbsolute.Length; i++) { if (!isPositionInside(oldLodPsAbsolute[i])) { ps.Add(oldLodPs[i]); if (oldLodCs != null) { cs.Add(oldLodCs[i]); } if (oldLodNs != null) { ns.Add(oldLodNs[i]); } if (oldLodIs != null) { js.Add(oldLodIs[i]); } } } if (ps.Count > 0) { newLodPsId = Guid.NewGuid(); var psa = ps.ToArray(); node.Storage.Add(newLodPsId.Value, psa, ct); newLodKdId = Guid.NewGuid(); node.Storage.Add(newLodKdId.Value, psa.BuildKdTree().Data, ct); if (node.HasLodColors) { newLodCsId = Guid.NewGuid(); node.Storage.Add(newLodCsId.Value, cs.ToArray(), ct); } if (node.HasLodNormals) { newLodNsId = Guid.NewGuid(); node.Storage.Add(newLodNsId.Value, ns.ToArray(), ct); } if (node.HasLodIntensities) { newLodIsId = Guid.NewGuid(); node.Storage.Add(newLodIsId.Value, js.ToArray(), ct); } } } var newSubnodes = node.Subnodes.Map(n => n?.Value.Delete(isNodeFullyInside, isNodeFullyOutside, isPositionInside, ct)); if (newSubnodes.All(n => n == null)) { return(null); } return(node.WithLod(newLodPsId, newLodCsId, newLodNsId, newLodIsId, newLodKdId, newSubnodes)); } }