// // (C) Copyright 2003-2019 by Autodesk, Inc. // // Permission to use, copy, modify, and distribute this software in // object code form for any purpose and without fee is hereby granted, // provided that the above copyright notice appears in all copies and // that both that copyright notice and the limited warranty and // restricted rights notice below appear in all supporting // documentation. // // AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. // AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE // UNINTERRUPTED OR ERROR FREE. // // Use, duplication, or disclosure by the U.S. Government is subject to // restrictions set forth in FAR 52.227-19 (Commercial Computer // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. // using System; using System.Collections.Generic; using System.Xml.Linq; using System.Linq; using Autodesk.Revit.DB; using Autodesk.Revit.DB.PointClouds; using Autodesk.Revit.UI; namespace Revit.SDK.Samples.CS.PointCloudEngine { /// /// The base class for all IPointCloudAccess implementations in this sample. /// public class PointCloudAccessBase { #region Class Member Variables private double m_scale = 1.0; private List m_storedCells; private Outline m_outline = null; #endregion #region Class Methods /// /// Constructs a new instance of the base class. /// protected PointCloudAccessBase() { m_storedCells = new List(); } /// /// Adds a new cell to the point cloud. /// /// The lower left point. /// The upper right point. /// The color. /// True to randomize point number and location, false for a regular arrangement of points. protected void AddCell(XYZ lowerLeft, XYZ upperRight, int color, bool randomize) { PointCloudCellStorage storage = new PointCloudCellStorage(lowerLeft, upperRight, color, randomize); storage.GeneratePoints(); m_storedCells.Add(storage); AddCellToOutline(storage); } /// /// Adds a new cell to the point cloud. /// /// The lower left point. /// The upper right point. /// The color. protected void AddCell(XYZ lowerLeft, XYZ upperRight, int color) { AddCell(lowerLeft, upperRight, color, false); } /// /// Adds a cell to the stored outline of the point cloud. If the cell boundaries extend beyond the current outline, the outline /// is adjusted. /// /// private void AddCellToOutline(PointCloudCellStorage storage) { XYZ lowerLeft = storage.LowerLeft; XYZ upperRight = storage.UpperRight; if (m_outline == null) m_outline = new Outline(lowerLeft, upperRight); else { XYZ minimumPoint = m_outline.MinimumPoint; m_outline.MinimumPoint = new XYZ(Math.Min(minimumPoint.X, lowerLeft.X), Math.Min(minimumPoint.Y, lowerLeft.Y), Math.Min(minimumPoint.Z, lowerLeft.Z)); XYZ maximumPoint = m_outline.MaximumPoint; m_outline.MaximumPoint = new XYZ(Math.Max(maximumPoint.X, upperRight.X), Math.Max(maximumPoint.Y, upperRight.Y), Math.Max(maximumPoint.Z, upperRight.Z)); } } /// /// Gets the outline calculated from all cells in the point cloud. /// /// protected Outline GetOutline() { return m_outline; } /// /// Gets the scale stored for this point cloud. /// /// protected double GetScale() { return m_scale; } /// /// Saves the contents of the point cloud into an XML element. /// /// The XML element in which to save the point cloud properties. public virtual void SerializeObjectData(XElement rootElement) { XElement scaleElement = XmlUtils.GetXElement(m_scale, "Scale"); rootElement.Add(scaleElement); int count = m_storedCells.Count; for (int i = 0; i < count; i++) { XElement cellElement = new XElement("Cell"); m_storedCells[i].SerializeObjectData(cellElement); rootElement.Add(cellElement); } } /// /// The internal implementation for point cloud read requests from Revit. /// /// Both IPointCloudAccess.ReadPoints() and IPointSetIterator.ReadPoints() are served by this method. /// The point cloud filter. /// The point cloud buffer. /// The maximum number of points in the buffer. /// The start index for points. Pass 0 if called from IPointCloudAccess.ReadPoints() or if this is the first /// call to IPointSetIterator.ReadPoints(). Pass the previous cumulative number of read points for second and successive calls to /// IPointSetIterator.ReadPoints(). /// The number of points read. protected unsafe int ReadSomePoints(PointCloudFilter rFilter, IntPtr buffer, int nBufferSize, int startIndex) { // Get the pointer to the buffer. CloudPoint* cpBuffer = (CloudPoint*)buffer.ToPointer(); int pointIndex = 0; int currentIndex = startIndex; int totalPoints = 0; int startCell = 0; Outline fullOutline = GetOutline(); // Test each cell until the first cell with needed points is found. for (int i = 0; i < m_storedCells.Count; i++) { PointCloudCellStorage cell = m_storedCells[i]; // Pass the cell outline to the filter. int filterResult = rFilter.TestCell(cell.LowerLeft, cell.UpperRight); // Filter result == -1 means the cell is completely out of scope for the filter. if (filterResult == -1) continue; // The cell is at least partially in scope. If the cell has more points than // the number read in previous calls, we should start with this cell. // If it has less points than the number read, the cell was already processed and we // should move to the next one. totalPoints += cell.NumberOfPoints; if (currentIndex < totalPoints) { startCell = i; currentIndex = Math.Max(0, startIndex - totalPoints); break; } } // Start with the current candidate cell and read cells until there are no more to read. for (int i = startCell; i < m_storedCells.Count; i++) { // Test the cell against the filter. PointCloudCellStorage cell = m_storedCells[i]; int filterResult = rFilter.TestCell(cell.LowerLeft, cell.UpperRight); // Filter result == -1 means the cell is entirely out of scope, skip it. if (filterResult == -1) continue; // Filter result == 0 means some part of the cell is in scope. // Prepare for cell is called to process the cell's points. if (filterResult == 0) rFilter.PrepareForCell(fullOutline.MinimumPoint, fullOutline.MaximumPoint, cell.NumberOfPoints); // Loop through all points in the cell. for (int j = currentIndex; j < cell.NumberOfPoints; j++) { // If we need to test the point for acceptance, use the filter to do so. // If filter result == 1 the entire cell was in scope, no need to test. if (filterResult == 0) { if (!rFilter.TestPoint(cell.PointsBuffer[j])) continue; } // Add the point to the buffer and increment the counter *(cpBuffer + pointIndex) = cell.PointsBuffer[j]; pointIndex++; // Stop when the max number of points is reached if (pointIndex >= nBufferSize) { break; } } // Stop when the max number of points is reached if (pointIndex >= nBufferSize) { break; } currentIndex = 0; } return pointIndex; } /// /// Sets up a point cloud from an XML root element. /// /// The root element. protected void SetupFrom(XElement rootElement) { // Read scale, if it exists. foreach (XElement scaleElement in rootElement.Elements("Scale")) { double scale = XmlUtils.GetDouble(scaleElement); if (scale < 0.0) { TaskDialog.Show("Scale error", "The value of scale is not a valid number greater than zero."); } else { m_scale = scale; } } // Read cells. m_storedCells = new List(); foreach (XElement cellElement in rootElement.Elements("Cell")) { PointCloudCellStorage cell = new PointCloudCellStorage(cellElement); m_storedCells.Add(cell); AddCellToOutline(cell); cell.GeneratePoints(); } } #endregion /// /// The implementation for an IPointSetIterator for a file-based or predefined point cloud. /// protected class PointCloudAccessBaseIterator : IPointSetIterator { #region Class Member Variables private PointCloudFilter m_filter; private int m_currentIndex; private PointCloudAccessBase m_access; private bool m_done = false; #endregion #region Class Methods /// /// Constructs a new instance of the point cloud iterator. /// /// The access. /// The filter used for this iteration. public PointCloudAccessBaseIterator(PointCloudAccessBase access, PointCloudFilter filter) { m_access = access; m_filter = filter; m_currentIndex = 0; } #endregion #region IPointSetIterator Members /// /// Implementation of IPointSetIterator.ReadPoints() /// /// The point buffer. /// The buffer size. /// The number of points read. public int ReadPoints(IntPtr buffer, int nBufferSize) { if (m_done) { return 0; } int found = m_access.ReadSomePoints(m_filter, buffer, nBufferSize, m_currentIndex); m_currentIndex += found; if (found > nBufferSize) { m_done = true; } return found; } /// /// Implementation of IPointSetIterator.Free() /// public void Free() { m_done = true; } #endregion } } }