protected static IndexBuffer ToTrianglesBuffer
        (
            Rhino.Geometry.Mesh mesh, Primitive.Part part,
            out int triangleCount
        )
        {
            triangleCount = part.FaceCount;
            {
                var faces = mesh.Faces;
                for (int f = part.StartFaceIndex; f < part.EndFaceIndex; ++f)
                {
                    if (faces[f].IsQuad)
                    {
                        triangleCount++;
                    }
                }
            }

            if (triangleCount > 0)
            {
                var ib = new IndexBuffer(triangleCount * IndexTriangle.GetSizeInShortInts());
                ib.Map(triangleCount * IndexTriangle.GetSizeInShortInts());

                using (var istream = ib.GetIndexStreamTriangle())
                {
                    var faces = mesh.Faces;
                    for (int f = part.StartFaceIndex; f < part.EndFaceIndex; ++f)
                    {
                        var face = faces[f];

                        istream.AddTriangle(new IndexTriangle(face.A - part.StartVertexIndex, face.B - part.StartVertexIndex, face.C - part.StartVertexIndex));
                        if (face.IsQuad)
                        {
                            istream.AddTriangle(new IndexTriangle(face.C - part.StartVertexIndex, face.D - part.StartVertexIndex, face.A - part.StartVertexIndex));
                        }
                    }
                }

                ib.Unmap();
                return(ib);
            }

            return(null);
        }
        void AddPointCloudPreviews(PointCloud previewCloud)
        {
            int verticesCount = previewCloud.Count;

            if (verticesCount > VertexThreshold)
            {
                primitives = new Primitive[(verticesCount / VertexThreshold) + ((verticesCount % VertexThreshold) > 0 ? 1 : 0)];
                for (int c = 0; c < verticesCount / VertexThreshold; ++c)
                {
                    var part = new Primitive.Part(c * VertexThreshold, (c + 1) * VertexThreshold);
                    primitives[c] = new ObjectPrimitive(rhinoObject, previewCloud, part);
                }

                if ((verticesCount % VertexThreshold) > 0)
                {
                    var part = new Primitive.Part((primitives.Length - 1) * VertexThreshold, verticesCount);
                    primitives[primitives.Length - 1] = new ObjectPrimitive(rhinoObject, previewCloud, part);
                }
            }
            else
            {
                primitives = new Primitive[] { new ObjectPrimitive(rhinoObject, previewCloud) }
            };
        }

        void AddMeshPreviews(Mesh previewMesh)
        {
            int verticesCount = previewMesh.Vertices.Count;

            if (verticesCount > VertexThreshold || previewMesh.Faces.Count > VertexThreshold)
            {
                // If it's insane big show as point clouds
                if (previewMesh.Faces.Count > (VertexThreshold - 1) * 16)
                {
                    primitives = new Primitive[(verticesCount / VertexThreshold) + ((verticesCount % VertexThreshold) > 0 ? 1 : 0)];
                    for (int c = 0; c < verticesCount / VertexThreshold; ++c)
                    {
                        var part = new Primitive.Part(c * VertexThreshold, (c + 1) * VertexThreshold);
                        primitives[c] = new ObjectPrimitive(rhinoObject, previewMesh, part);
                    }

                    if ((verticesCount % VertexThreshold) > 0)
                    {
                        var part = new Primitive.Part((primitives.Length - 1) * VertexThreshold, verticesCount);
                        primitives[primitives.Length - 1] = new ObjectPrimitive(rhinoObject, previewMesh, part);
                    }

                    // Mesh.Reduce is slow in this case
                    //previewMesh = previewMesh.DuplicateMesh();
                    //previewMesh.Reduce((BigMeshThreshold - 1) * 16, true, 5, true);
                }

                // Split the mesh into partitions
                else if (previewMesh.CreatePartitions(VertexThreshold, int.MaxValue))
                {
                    int partitionCount = previewMesh.PartitionCount;
                    primitives = new Primitive[partitionCount];
                    for (int p = 0; p < partitionCount; ++p)
                    {
                        primitives[p] = new ObjectPrimitive(rhinoObject, previewMesh, previewMesh.GetPartition(p));
                    }
                }
            }
            else
            {
                primitives = new Primitive[] { new ObjectPrimitive(rhinoObject, previewMesh) }
            };
        }
        protected static int ToPointsBuffer
        (
            Rhino.Geometry.PointCloud pointCloud,
            Primitive.Part part,
            out VertexFormatBits vertexFormatBits,
            out VertexBuffer vb, out int vertexCount,
            out IndexBuffer ib
        )
        {
            int pointsCount = part.VertexCount;
            int normalCount = pointCloud.ContainsNormals ? pointsCount : 0;
            int colorsCount = pointCloud.ContainsColors  ? pointsCount : 0;

            bool hasPoints  = pointsCount > 0;
            bool hasNormals = normalCount == pointsCount;
            bool hasColors  = colorsCount == pointsCount;

            if (hasPoints)
            {
                if (hasNormals)
                {
                    if (hasColors)
                    {
                        vertexFormatBits = VertexFormatBits.PositionNormalColored;
                        vb = new VertexBuffer(pointsCount * VertexPositionNormalColored.GetSizeInFloats());
                        vb.Map(pointsCount * VertexPositionNormalColored.GetSizeInFloats());

                        using (var vstream = vb.GetVertexStreamPositionNormalColored())
                        {
                            for (int p = part.StartVertexIndex; p < part.EndVertexIndex; ++p)
                            {
                                var point = pointCloud[p];
                                var c     = new ColorWithTransparency(point.Color.R, point.Color.G, point.Color.B, 255u - point.Color.A);
                                vstream.AddVertex(new VertexPositionNormalColored(RawEncoder.ToHost(point.Location), RawEncoder.ToHost(point.Normal), c));
                            }
                        }

                        vb.Unmap();
                    }
                    else
                    {
                        vertexFormatBits = VertexFormatBits.PositionNormal;
                        vb = new VertexBuffer(pointsCount * VertexPositionNormal.GetSizeInFloats());
                        vb.Map(pointsCount * VertexPositionNormal.GetSizeInFloats());

                        using (var vstream = vb.GetVertexStreamPositionNormal())
                        {
                            for (int p = part.StartVertexIndex; p < part.EndVertexIndex; ++p)
                            {
                                var point = pointCloud[p];
                                vstream.AddVertex(new VertexPositionNormal(RawEncoder.ToHost(point.Location), RawEncoder.ToHost(point.Normal)));
                            }
                        }

                        vb.Unmap();
                    }
                }
                else
                {
                    if (hasColors)
                    {
                        vertexFormatBits = VertexFormatBits.PositionColored;
                        vb = new VertexBuffer(pointsCount * VertexPositionColored.GetSizeInFloats());
                        vb.Map(pointsCount * VertexPositionColored.GetSizeInFloats());

                        using (var vstream = vb.GetVertexStreamPositionColored())
                        {
                            for (int p = part.StartVertexIndex; p < part.EndVertexIndex; ++p)
                            {
                                var point = pointCloud[p];
                                var c     = new ColorWithTransparency(point.Color.R, point.Color.G, point.Color.B, 255u - point.Color.A);
                                vstream.AddVertex(new VertexPositionColored(RawEncoder.ToHost(point.Location), c));
                            }
                        }

                        vb.Unmap();
                    }
                    else
                    {
                        vertexFormatBits = VertexFormatBits.Position;
                        vb = new VertexBuffer(pointsCount * VertexPosition.GetSizeInFloats());
                        vb.Map(pointsCount * VertexPosition.GetSizeInFloats());

                        using (var vstream = vb.GetVertexStreamPosition())
                        {
                            for (int p = part.StartVertexIndex; p < part.EndVertexIndex; ++p)
                            {
                                var point = pointCloud[p];
                                vstream.AddVertex(new VertexPosition(RawEncoder.ToHost(point.Location)));
                            }
                        }

                        vb.Unmap();
                    }
                }

                ib = IndexPointsBuffer(pointsCount);
            }
            else
            {
                vertexFormatBits = 0;
                vb = null;
                ib = null;
            }

            vertexCount = pointsCount;
            return(pointsCount);
        }
        protected static IndexBuffer ToEdgeBuffer
        (
            Rhino.Geometry.Mesh mesh,
            Primitive.Part part,
            out int linesCount
        )
        {
            if (part.VertexCount != mesh.Vertices.Count)
            {
                if (part.VertexCount > 0)
                {
                    linesCount = -part.VertexCount;
                    return(IndexPointsBuffer(part.VertexCount));
                }

                linesCount = 0;
            }
            else
            {
                var edgeIndices = new List <IndexPair>();
                if (mesh.Ngons.Count > 0)
                {
                    foreach (var ngon in mesh.Ngons)
                    {
                        var boundary = ngon.BoundaryVertexIndexList();
                        if ((boundary?.Length ?? 0) > 1)
                        {
                            for (int b = 0; b < boundary.Length - 1; ++b)
                            {
                                edgeIndices.Add(new IndexPair((int)boundary[b], (int)boundary[b + 1]));
                            }

                            edgeIndices.Add(new IndexPair((int)boundary[boundary.Length - 1], (int)boundary[0]));
                        }
                    }
                }
                else
                {
                    var vertices  = mesh.TopologyVertices;
                    var edges     = mesh.TopologyEdges;
                    var edgeCount = edges.Count;
                    for (int e = 0; e < edgeCount; ++e)
                    {
                        if (edges.IsEdgeUnwelded(e) || edges.GetConnectedFaces(e).Length < 2)
                        {
                            var pair = edges.GetTopologyVertices(e);
                            pair.I = vertices.MeshVertexIndices(pair.I)[0];
                            pair.J = vertices.MeshVertexIndices(pair.J)[0];
                            edgeIndices.Add(pair);
                        }
                    }
                }

                linesCount = edgeIndices.Count;
                if (linesCount > 0)
                {
                    var ib = new IndexBuffer(linesCount * IndexLine.GetSizeInShortInts());
                    ib.Map(linesCount * IndexLine.GetSizeInShortInts());
                    using (var istream = ib.GetIndexStreamLine())
                    {
                        foreach (var edge in edgeIndices)
                        {
                            Debug.Assert(0 <= edge.I && edge.I < part.VertexCount);
                            Debug.Assert(0 <= edge.J && edge.J < part.VertexCount);
                            istream.AddLine(new IndexLine(edge.I, edge.J));
                        }
                    }
                    ib.Unmap();

                    return(ib);
                }
            }

            return(null);
        }
        protected static VertexBuffer ToVertexBuffer
        (
            Rhino.Geometry.Mesh mesh,
            Primitive.Part part,
            out VertexFormatBits vertexFormatBits,
            System.Drawing.Color color = default
        )
        {
            int verticesCount = part.EndVertexIndex - part.StartVertexIndex;
            int normalCount   = mesh.Normals.Count == mesh.Vertices.Count ? verticesCount : 0;
            int colorsCount   = color.IsEmpty ? (mesh.VertexColors.Count == mesh.Vertices.Count ? verticesCount : 0) : verticesCount;

            bool hasVertices = verticesCount > 0;
            bool hasNormals  = normalCount > 0;
            bool hasColors   = colorsCount > 0;

            if (hasVertices)
            {
                var vertices = mesh.Vertices;
                if (hasNormals)
                {
                    var normals = mesh.Normals;
                    if (hasColors)
                    {
                        vertexFormatBits = VertexFormatBits.PositionNormalColored;
                        var colors = mesh.VertexColors;
                        var vb     = new VertexBuffer(verticesCount * VertexPositionNormalColored.GetSizeInFloats());
                        vb.Map(verticesCount * VertexPositionNormalColored.GetSizeInFloats());
                        using (var stream = vb.GetVertexStreamPositionNormalColored())
                        {
                            for (int v = part.StartVertexIndex; v < part.EndVertexIndex; ++v)
                            {
                                var  c = !color.IsEmpty ? color : colors[v];
                                uint T = Math.Max(1, 255u - c.A);
                                stream.AddVertex(new VertexPositionNormalColored(RawEncoder.ToHost(vertices[v]), RawEncoder.ToHost(normals[v]), new ColorWithTransparency(c.R, c.G, c.B, T)));
                            }
                        }
                        vb.Unmap();
                        return(vb);
                    }
                    else
                    {
                        vertexFormatBits = VertexFormatBits.PositionNormal;
                        var vb = new VertexBuffer(verticesCount * VertexPositionNormal.GetSizeInFloats());
                        vb.Map(verticesCount * VertexPositionNormal.GetSizeInFloats());
                        using (var stream = vb.GetVertexStreamPositionNormal())
                        {
                            for (int v = part.StartVertexIndex; v < part.EndVertexIndex; ++v)
                            {
                                stream.AddVertex(new VertexPositionNormal(RawEncoder.ToHost(vertices[v]), RawEncoder.ToHost(normals[v])));
                            }
                        }
                        vb.Unmap();
                        return(vb);
                    }
                }
                else
                {
                    if (hasColors)
                    {
                        vertexFormatBits = VertexFormatBits.PositionColored;
                        var colors = mesh.VertexColors;
                        var vb     = new VertexBuffer(verticesCount * VertexPositionColored.GetSizeInFloats());
                        vb.Map(verticesCount * VertexPositionColored.GetSizeInFloats());
                        using (var stream = vb.GetVertexStreamPositionColored())
                        {
                            for (int v = part.StartVertexIndex; v < part.EndVertexIndex; ++v)
                            {
                                var  c = !color.IsEmpty ? color : colors[v];
                                uint T = Math.Max(1, 255u - c.A);
                                stream.AddVertex(new VertexPositionColored(RawEncoder.ToHost(vertices[v]), new ColorWithTransparency(c.R, c.G, c.B, T)));
                            }
                        }
                        vb.Unmap();
                        return(vb);
                    }
                    else
                    {
                        vertexFormatBits = VertexFormatBits.Position;
                        var vb = new VertexBuffer(verticesCount * VertexPosition.GetSizeInFloats());
                        vb.Map(verticesCount * VertexPosition.GetSizeInFloats());
                        using (var stream = vb.GetVertexStreamPosition())
                        {
                            for (int v = part.StartVertexIndex; v < part.EndVertexIndex; ++v)
                            {
                                stream.AddVertex(new VertexPosition(RawEncoder.ToHost(vertices[v])));
                            }
                        }
                        vb.Unmap();
                        return(vb);
                    }
                }
            }

            vertexFormatBits = 0;
            return(null);
        }