Export 3dm file as STL in Rhinocompute/Rhino3dm

Hi @fraguada ,

This is my code

Convert 3dm to stl

//using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HelloRhinoCompute
{
    static class Convert3dm
    {
        public static string Convert3dmToPdf(string data)
        {
            var decodedData = Convert.FromBase64String(data);
            var file3dm = Rhino.FileIO.File3dm.FromByteArray(decodedData);

            // create headless doc and add geometry from 3dm file
            var doc = Rhino.RhinoDoc.CreateHeadless(null);
            foreach (var obj in file3dm.Objects)
            {
                doc.Objects.Add(obj.Geometry, obj.Attributes);
            }

            // set "front" view for export
            var view = doc.Views.Add("view1", Rhino.Display.DefinedViewportProjection.Front, new System.Drawing.Rectangle(0,0,1,1), false);
            doc.Views.ActiveView = view;

            // export!
            var id = Guid.NewGuid();
            string path = Path.GetTempPath();
            string filename = Path.Combine(path, "convert3dm_" + id + ".pdf");
            
            if (!doc.Export(filename))
            {
                throw new InvalidOperationException("Export failed");
            }
            
            byte[] bytes = File.ReadAllBytes(filename);
            return Convert.ToBase64String(bytes);
        }

        public static async Task<string> Convert3dmToStl(string data)
        {
            // Decode base64 data to bytes
            var decodedData = Convert.FromBase64String(data);
            var file3dm = Rhino.FileIO.File3dm.FromByteArray(decodedData);

            // Create a headless Rhino document
            var doc = Rhino.RhinoDoc.CreateHeadless(null);

            // Add all objects from the 3DM file to the document
            foreach (var obj in file3dm.Objects)
            {
                doc.Objects.Add(obj.Geometry, obj.Attributes);
            }

            // Generate a unique filename for the STL export
            var id = Guid.NewGuid();
            string path = Path.GetTempPath();
            string filename = Path.Combine(path, "convert3dm_" + id + ".stl");

            // Define the STL export options
            var stlOptions = new Rhino.FileIO.FileWriteOptions
            {
                WriteSelectedObjectsOnly = false, // Export all objects in the document
                SuppressAllInput = true            // Ensure headless export runs without prompts
            };

            // Export the document as an STL file
            if (!doc.WriteFile(filename, stlOptions))
            {
                throw new InvalidOperationException("Failed to export the document as STL");
            }

            // Read the STL file into a byte array
            byte[] bytes = File.ReadAllBytes(filename);

            // Clean up the headless document
            doc.Dispose();

            // Return the STL file encoded as a Base64 string
            return Convert.ToBase64String(bytes);
        }



    }
}

Plugin.cs

using Rhino.PlugIns;
using Rhino.UI.Forms;
//using System.Management;

namespace HelloRhinoCompute
{
    ///<summary>
    /// <para>Every RhinoCommon .rhp assembly must have one and only one PlugIn-derived
    /// class. DO NOT create instances of this class yourself. It is the
    /// responsibility of Rhino to create an instance of this class.</para>
    /// <para>To complete plug-in information, please also see all PlugInDescription
    /// attributes in AssemblyInfo.cs (you might need to click "Project" ->
    /// "Show All Files" to see it in the "Solution Explorer" window).</para>
    ///</summary>
    public class HelloRhinoComputePlugIn : Rhino.PlugIns.PlugIn

    {
        public HelloRhinoComputePlugIn()
        {
            Instance = this;
        }

        ///<summary>Gets the only instance of the HelloRhinoCommonPlugIn plug-in.</summary>
        public static HelloRhinoComputePlugIn Instance
        {
            get; private set;
        }

        // You can override methods here to change the plug-in behavior on
        // loading and shut down, add options pages to the Rhino _Option command
        // and maintain plug-in wide options in a document.

        public override PlugInLoadTime LoadTime => PlugInLoadTime.AtStartup;

        protected override LoadReturnCode OnLoad(ref string errorMessage)
        {
            Rhino.RhinoApp.WriteLine("Loading HelloRhinoComputePlugIn!");

            // register custom compute endpoints
            Rhino.Runtime.HostUtils.RegisterComputeEndpoint("sample", typeof(Convert3dm));

            return base.OnLoad(ref errorMessage);
        }
    }
}

And this should all work fine

I’m having trouble with my Rhinocompute integration so the problem is here

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3DM to STL Converter</title>
    <script src="https://files.mcneel.com/rhino3dm/js/rhino3dm.js"></script>
    <script src="https://files.mcneel.com/rhinocompute/js/compute.rhino3d.js"></script>
</head>
<body>
    <h1>3DM to STL Conversion</h1>

    <input type="file" id="upload3dm" accept=".3dm" />
    <button id="convertBtn">Convert to STL</button>

    <p id="status">Status: Waiting for file upload...</p>
    <a id="downloadLink" style="display:none;">Download STL</a>

    <script>
        // Configure RhinoCompute
        RhinoCompute.url = 'http://xx.xx.xx.xx/'; // Your Rhino Compute server URL
        RhinoCompute.apiKey = 'xxx'; // Your API key

        window.onload = function () {
            console.log("Window loaded");
            const convertBtn = document.getElementById("convertBtn");
            convertBtn.addEventListener("click", handleFileUpload);
        };

        async function handleFileUpload() {
            const fileInput = document.getElementById("upload3dm");
            if (fileInput.files.length === 0) {
                document.getElementById("status").textContent = "Please upload a 3DM file.";
                return;
            }

            const file = fileInput.files[0];
            const reader = new FileReader();

            reader.onload = async function (event) {
                const fileData = event.target.result;
                document.getElementById("status").textContent = "Converting 3DM to STL...";
                await convert3dmToStl(fileData);
            };

            reader.readAsArrayBuffer(file);
        }

        async function convert3dmToStl(fileBuffer) {
            try {
                let base64Data = arrayBufferToBase64(fileBuffer);

                // Use the correct RhinoCompute method and endpoint
                const res = await RhinoCompute.computeFetch('sample/convert3dmtostl-string', [base64Data], true);

                // Convert base64 STL back to ArrayBuffer
                const stlBuffer = base64ToArrayBuffer(res);

                // Create a download link for the STL file
                createDownloadLink(stlBuffer, "converted_model.stl");

                document.getElementById("status").textContent = "Conversion successful! You can download the STL file.";
            } catch (error) {
                console.error("Error converting 3DM to STL:", error);
                document.getElementById("status").textContent = "Error during conversion: " + error.message;
            }
        }

        function createDownloadLink(stlBuffer, fileName) {
            const blob = new Blob([stlBuffer], { type: 'application/sla' });
            const url = window.URL.createObjectURL(blob);
            const downloadLink = document.getElementById("downloadLink");

            downloadLink.href = url;
            downloadLink.download = fileName;
            downloadLink.style.display = 'block';
            downloadLink.textContent = "Download STL";
        }

        function arrayBufferToBase64(buffer) {
            let binary = '';
            let bytes = new Uint8Array(buffer);
            let len = bytes.byteLength;
            for (let i = 0; i < len; i++) {
                binary += String.fromCharCode(bytes[i]);
            }
            return window.btoa(binary);
        }

        function base64ToArrayBuffer(base64) {
            let binaryString = window.atob(base64);
            let len = binaryString.length;
            let bytes = new Uint8Array(len);
            for (let i = 0; i < len; i++) {
                bytes[i] = binaryString.charCodeAt(i);
            }
            return bytes.buffer;
        }
    </script>
</body>
</html><!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3DM to STL Converter</title>
    <script src="https://files.mcneel.com/rhino3dm/js/rhino3dm.js"></script>
    <script src="https://files.mcneel.com/rhinocompute/js/compute.rhino3d.js"></script>
</head>
<body>
    <h1>3DM to STL Conversion</h1>

    <input type="file" id="upload3dm" accept=".3dm" />
    <button id="convertBtn">Convert to STL</button>

    <p id="status">Status: Waiting for file upload...</p>
    <a id="downloadLink" style="display:none;">Download STL</a>

    <script>
        // Configure RhinoCompute
        RhinoCompute.url = 'http://xx.xx.xx.xx/'; // Your Rhino Compute server URL
        RhinoCompute.apiKey = 'xxx'; // Your API key

        window.onload = function () {
            console.log("Window loaded");
            const convertBtn = document.getElementById("convertBtn");
            convertBtn.addEventListener("click", handleFileUpload);
        };

        async function handleFileUpload() {
            const fileInput = document.getElementById("upload3dm");
            if (fileInput.files.length === 0) {
                document.getElementById("status").textContent = "Please upload a 3DM file.";
                return;
            }

            const file = fileInput.files[0];
            const reader = new FileReader();

            reader.onload = async function (event) {
                const fileData = event.target.result;
                document.getElementById("status").textContent = "Converting 3DM to STL...";
                await convert3dmToStl(fileData);
            };

            reader.readAsArrayBuffer(file);
        }

        async function convert3dmToStl(fileBuffer) {
            try {
                let base64Data = arrayBufferToBase64(fileBuffer);

                // Use the correct RhinoCompute method and endpoint
                const res = await RhinoCompute.computeFetch('sample/convert3dmtostl-string', [base64Data], true);

                // Convert base64 STL back to ArrayBuffer
                const stlBuffer = base64ToArrayBuffer(res);

                // Create a download link for the STL file
                createDownloadLink(stlBuffer, "converted_model.stl");

                document.getElementById("status").textContent = "Conversion successful! You can download the STL file.";
            } catch (error) {
                console.error("Error converting 3DM to STL:", error);
                document.getElementById("status").textContent = "Error during conversion: " + error.message;
            }
        }

        function createDownloadLink(stlBuffer, fileName) {
            const blob = new Blob([stlBuffer], { type: 'application/sla' });
            const url = window.URL.createObjectURL(blob);
            const downloadLink = document.getElementById("downloadLink");

            downloadLink.href = url;
            downloadLink.download = fileName;
            downloadLink.style.display = 'block';
            downloadLink.textContent = "Download STL";
        }

        function arrayBufferToBase64(buffer) {
            let binary = '';
            let bytes = new Uint8Array(buffer);
            let len = bytes.byteLength;
            for (let i = 0; i < len; i++) {
                binary += String.fromCharCode(bytes[i]);
            }
            return window.btoa(binary);
        }

        function base64ToArrayBuffer(base64) {
            let binaryString = window.atob(base64);
            let len = binaryString.length;
            let bytes = new Uint8Array(len);
            for (let i = 0; i < len; i++) {
                bytes[i] = binaryString.charCodeAt(i);
            }
            return bytes.buffer;
        }
    </script>
</body>
</html>

While If I do it like this for a pre-existing endpoint it works

        let args = [geometry];

        // Calculate AreaMassProperties
        let areaMassPropertiesEndpoint = 'http://xx/rhino/geometry/areamassproperties/compute-brep';
        let areaMassPropertiesResponse = await fetch(areaMassPropertiesEndpoint, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'RhinoComputeKey': 'xx', 
                'User-Agent': `compute.rhino3d.js/0.11.0` 
            },
            body: JSON.stringify(args)
        });

I get 401 unhautorized but Im using the correct RhinoComputeKey…

Can someone help me sort this out getting angst. Working with the standard endpoint is fine, however when I introduce a custom made plugin endpoint it stops working…

Thanks

Can help? @AndyPayne

Anyone can help? @fraguada
Im on urgent timeline please :frowning:

Solved it was not existing the endpoint … New thread asking for endpoint

2 Likes