How can I add new function to rhino3dm? (cpython)

I see the repository but I can’t navigate all the files and folders.
Where exactly are the functions located?

The bindings are here: rhino3dm/src/bindings at main · mcneel/rhino3dm · GitHub

oh, I see, so if a new function is added here it will appear in the Python, JS and .net api?

When all the right things have been done, yes. That is the idea.

:rofl: :sob: :thinking: :crazy_face:

Why must everything be hard…

PowerShell 7.4.1

PS E:\> cd appdev/git/rhino3dm/src
PS E:\AppDev\GIT\rhino3dm\src> dir

    Directory: E:\AppDev\GIT\rhino3dm\src

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----           20-Mar-24    18:13                bindings
d----           20-Mar-24    18:15                build
d----           20-Mar-24    18:13                docgen
d----           20-Mar-24    18:13                dotnet
d----           20-Mar-24    18:13                js
d----           20-Mar-24    18:13                lib
d----           20-Mar-24    18:13                librhino3dm_native
d----           20-Mar-24    18:13                methodgen
d----           20-Mar-24    18:13                rhino3dm
-a---           20-Mar-24    18:13           2696 build_dotnet.py
-a---           20-Mar-24    18:13            911 build_javascript.py
-a---           20-Mar-24    18:13           4590 CMakeLists.txt
-a---           20-Mar-24    18:13           1690 create_python_vcxproj.py
-a---           20-Mar-24    18:13          31194 ios.toolchain.cmake
-a---           20-Mar-24    18:13              5 version.txt

PS E:\AppDev\GIT\rhino3dm\src> python create_python_vcxproj.py
-- Selecting Windows SDK version 10.0.22000.0 to target Windows 10.0.19045.
-- The C compiler identification is MSVC 19.37.32825.0
-- The CXX compiler identification is MSVC 19.37.32825.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.37.32822/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.37.32822/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
Python Compile
CMake Error at CMakeLists.txt:18 (add_subdirectory):
  The source directory

    E:/AppDev/GIT/rhino3dm/src/lib/pybind11

  does not contain a CMakeLists.txt file.


NODE=
CMake Error at CMakeLists.txt:33 (file):
  file RENAME failed to rename

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_gl.cpp

  to

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_gl.skip

  because: The system cannot find the file specified.





CMake Error at CMakeLists.txt:34 (file):
  file RENAME failed to rename

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_unicode_cp932.cpp

  to

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_unicode_cp932.skip

  because: The system cannot find the file specified.





CMake Error at CMakeLists.txt:35 (file):
  file RENAME failed to rename

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_unicode_cp949.cpp

  to

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_unicode_cp949.skip

  because: The system cannot find the file specified.





CMake Error at CMakeLists.txt:37 (file):
  file RENAME failed to rename

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_gl.skip

  to

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_gl.cpp

  because: The system cannot find the file specified.





CMake Error at CMakeLists.txt:38 (file):
  file RENAME failed to rename

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_unicode_cp932.skip

  to

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_unicode_cp932.cpp

  because: The system cannot find the file specified.





CMake Error at CMakeLists.txt:39 (file):
  file RENAME failed to rename

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_unicode_cp949.skip

  to

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/opennurbs_unicode_cp949.cpp

  because: The system cannot find the file specified.





CMake Error at CMakeLists.txt:41 (file):
  file RENAME failed to rename

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/android_uuid/gen_uuid_nt.c

  to

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/android_uuid/gen_uuid_nt.skip

  because: The system cannot find the path specified.





CMake Error at CMakeLists.txt:43 (file):
  file RENAME failed to rename

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/android_uuid/gen_uuid_nt.skip

  to

    E:/AppDev/GIT/rhino3dm/src/lib/opennurbs/android_uuid/gen_uuid_nt.c

  because: The system cannot find the path specified.





CMake Error at CMakeLists.txt:105 (pybind11_add_module):
  Unknown CMake command "pybind11_add_module".


-- Configuring incomplete, errors occurred!
Traceback (most recent call last):
  File "E:\AppDev\GIT\rhino3dm\src\create_python_vcxproj.py", line 43, in <module>
    createproject()
  File "E:\AppDev\GIT\rhino3dm\src\create_python_vcxproj.py", line 30, in createproject
    for line in fileinput.input("_rhino3dm.vcxproj", inplace=1):
  File "C:\Python311\Lib\fileinput.py", line 251, in __next__
    line = self._readline()
           ^^^^^^^^^^^^^^^^
  File "C:\Python311\Lib\fileinput.py", line 337, in _readline
    os.rename(self._filename, self._backupfilename)
FileNotFoundError: [WinError 2] The system cannot find the file specified: '_rhino3dm.vcxproj' -> '_rhino3dm.vcxproj.bak'
PS E:\AppDev\GIT\rhino3dm\src>

Or, you could just ask it to do it for you…

– Dale

I wanted to challenge myself and take some burden from you, especially since I don’t know if others need/want it as I do.

It just makes more sense to me that you can create Nurbs Curves and Surfaces like so:

def NurbsCurve(degree:int, knots_count:int, knot_vector:list, multiplicity:list, control_points_count:int, control_points:list, weights_list:list, is_rational:bool, continuity:int):
    # etc...
    return nurbs_curve_object

respectively,

def NurbsSurface(degree_u:int, degree_v:int, knots_count_u:int, knots_count_v:int, knot_vector_u:list, knot_vector_v:list, multiplicity_u:list, multiplicity_v:list, control_points_count:int, control_points:list, weights_list:list, is_rational:bool):
    # etc...
    return nurbs_surface_object

You don’t have all of the source. This repository has git submodules that need to be retrieved.

git submodule update —init

Thanks.
Do I run this inside the rhino3dm folder or one folder above?

Or in the future when you clone a repository use: git clone --recursive https://github.com/mcneel/rhino3dm . That will also handle all submodules.

1 Like

ok I’ll try this now, seems easier :stuck_out_tongue:

PS E:\AppDev\GIT\rhino3dm\src> python .\create_python_vcxproj.py
-- Selecting Windows SDK version 10.0.22000.0 to target Windows 10.0.19045.
-- The C compiler identification is MSVC 19.37.32825.0
-- The CXX compiler identification is MSVC 19.37.32825.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.37.32822/bin/Hostx64/x64/cl.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.37.32822/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
Python Compile
-- pybind11 v2.12.0 dev1
CMake Warning (dev) at lib/pybind11/tools/FindPythonLibsNew.cmake:98 (find_package):
  Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules
  are removed.  Run "cmake --help-policy CMP0148" for policy details.  Use
  the cmake_policy command to set the policy and suppress this warning.

Call Stack (most recent call first):
  lib/pybind11/tools/pybind11Tools.cmake:50 (find_package)
  lib/pybind11/tools/pybind11Common.cmake:188 (include)
  lib/pybind11/CMakeLists.txt:211 (include)
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Found PythonInterp: C:/Python311/python.exe (found suitable version "3.11.3", minimum required is "3.6")
-- Found PythonLibs: C:/Python311/libs/python311.lib
-- Performing Test HAS_MSVC_GL_LTCG
-- Performing Test HAS_MSVC_GL_LTCG - Success
NODE=
CMake Warning (dev) at lib/draco/CMakeLists.txt:37 (include):
  Policy CMP0148 is not set: The FindPythonInterp and FindPythonLibs modules
  are removed.  Run "cmake --help-policy CMP0148" for policy details.  Use
  the cmake_policy command to set the policy and suppress this warning.

This warning is for project developers.  Use -Wno-dev to suppress it.

-- Found PythonInterp: C:/Python311/python.exe (found version "3.11.3")
-- Configuring done (10.3s)
-- Generating done (1.2s)
-- Build files have been written to: E:/AppDev/GIT/rhino3dm/src/build/py311_64bit
PS E:\AppDev\GIT\rhino3dm\src>

Looks promising :slight_smile:

So…

if my function is like this:

BND_NurbsCurve* CreateFromDegreeKnotsPoints(int degree, int nr_knots, int nr_points, std::vector<double> knot_vector, std::vector<int> multiplicities, std::vector<double> control_points, std::vector<double> weights, bool is_rational)
{
  if ((degree >= nr_points) && (degree < 1))
  {
    return nullptr;
  }

  if (knot_vector.size() != multiplicities.size())
  {
    return nullptr;
  }

  if (control_points.size() / 3 != nr_points)
  {
    return nullptr;
  }

  if (weights.size() != nr_points)
  {
    return nullptr;
  }

  // hardcoding dimension to 3
  int dim = 3;
  int order = degree + 1;

  // constructing the point list from array(vector) of doubles
  std::vector<ON_3dPoint> cv_list;

  for (int i = 0; i < nr_points; i++)
  {
    ON_3dPoint pt = ON_3dPoint(control_points.at(i), control_points.at(i + 1), control_points.at(i + 2));
    cv_list.push_back(pt);
    i = i + 3;
  }

  ON_NurbsCurve* crv = new ON_NurbsCurve(dim, is_rational, order, nr_points);
  for (int i = 0; i < nr_points; i++)
  {
    crv->SetCV(i, cv_list.at(i));
  }

  // constructing the knot vector from knots + multiplicities (TO BE DECIDED: removing first and last knot or not?)
  int kn_idx = 0;
  for (int k = 0; k < knot_vector.size(); k++)
  {
    for (int m = 0; m < multiplicities.size(); m++)
    {
      int multiplicity = multiplicities.at(m);
      int knot = knot_vector.at(k);

      if (multiplicity == 1)
      {
        crv->SetKnot(kn_idx, knot);
        kn_idx = kn_idx + 1;
      }
      else if (multiplicity > 1)
      {
        for (int mi = 0; mi < multiplicity; mi++)
        {
          crv->SetKnot(kn_idx, knot);
          kn_idx = kn_idx + 1;
        }
      }
    }
  }
  

  return new BND_NurbsCurve(crv, nullptr);
}

How would it be added to the python bindings?

Mainly, what I wonder is: will python know how to convert python list to std::vector to call the function?

build succeeded in DEBUG but in RELEASE I got this error:

36>LINK : fatal error LNK1181: cannot open input file 'draco_static\Release\draco.lib'
36>Done building project "_rhino3dm.vcxproj" -- FAILED.
37>------ Build started: Project: ALL_BUILD, Configuration: Release x64 ------
37>Building Custom Rule E:/AppDev/GIT/rhino3dm/src/CMakeLists.txt
38>------ Skipped Build: Project: INSTALL, Configuration: Release x64 ------
38>Project not selected to build for this solution configuration 
========== Build: 36 succeeded, 1 failed, 0 up-to-date, 1 skipped ==========
========== Build started at 22:38 and took 06:52.699 minutes ==========

Could anyone please explain how can I test the debug build?
I got these files in the DEBUG folder:
image

To get a Python module to test use python3 setup.py build. The setup.py is in the root of the repository. This will create a build folder in which is a folder with name that tells you for what python version and operating system. On my M2 it looks like this:

╭─jesterking ∞ at jesterM2 in ~/dev/li/rhino3dm/build/lib.macosx-10.9-universal2-cpython-310 on main✘✘✘ 24-03-21 - 9:04:42
╰─⠠⠵ ls                                                                                                                                                    on main|✚2
rhino3dm

This rhino3dm folder you see is the Python module. When you cd into that folder containing rhino3dm you can run Python and import rhino3dm. Use print(rhino3dm) to verify you loaded the freshly built one. After that you can write any code you need to test.

Copy rhino3dm to a place where you have your other code that relies on this and run your script to see your additions worked.

Thanks,

What I did yesterday was I used the .pyd file generated with VisualStudio compiling the project.
However, for some reason my new function wasn’t there when I tried print(dir(rhino3dm.NurbsCurve))

I assume something in here:

I did not do correctly, or there is also another place where I need to add my new function.

update:
This is my entry:

.def_static("CreateFromDegreeKnotsPoints", &BND_NurbsCurve::CreateFromDegreeKnotsPoints, py::arg("degree"), py::arg("nr_knots"), py::arg("nr_points"), py::arg("knot_vector"), py::arg("multiplicities"), py::arg("control_points"), py::arg("weights"), py::arg("is_rational")) //(int degree, int nr_knots, int nr_points, std::vector<double> knot_vector, std::vector<int> multiplicities, std::vector<double> control_points, std::vector<double> weights, bool is_rational)
    

I’ve no idea if that’s ok, I don’t know if std::vector type is properly converted. The build succeeds.

You indeed need to add the .def_static entry. std::vector should work. You can check elsewhere in the code how this is done, for instance in the PointCloud bindings.

Of course you can also look at the BND_NurbsCurve::Create2 implementation and see how an enumerable object is used there.

do I have to put my function with “2” in here?

#if defined(ON_PYTHON_COMPILE)

#endif

Yes, if you add only for Python.

Add it after the def_static for CreateFromEllipse.

(Why) does the order matter?

Order could matter, but in this case it doesn’t. It looked like the most reasonable place naming-wise.