OpenNURBS 3DM file support?

Have some feature requests, feedback, cool stuff to share, or want to know where FreeCAD is going? This is the place.
Forum rules
Be nice to others! Read the FreeCAD code of conduct!
zohozer
Posts: 86
Joined: Mon Jul 28, 2014 8:35 pm

OpenNURBS 3DM file support?

Postby zohozer » Tue Jul 28, 2015 8:16 am

I think that a good adition to FreeCAD will be the ability to read and write the OpenNURBS file format .3DM

3DM is the de-facto file-format for Rhino and Moi3D.

The SDK is free to use and implement in any application. OpenNURBS Toolkit: http://wiki.mcneel.com/developer/opennurbs/home

The openNURBS Initiative provides CAD, CAM, CAE, and computer graphics software developers the tools to accurately transfer 3-D geometry between applications.
User avatar
yorik
Site Admin
Posts: 11845
Joined: Tue Feb 17, 2009 9:16 pm
Location: São Paulo, Brazil
Contact:

Re: OpenNURBS 3DM file support?

Postby yorik » Tue Jul 28, 2015 2:02 pm

It's actually a very old feature request! issue #337
Just waiting for a good soul decided to work on this ;)
zohozer
Posts: 86
Joined: Mon Jul 28, 2014 8:35 pm

Re: OpenNURBS 3DM file support?

Postby zohozer » Sun Mar 22, 2020 3:34 pm

As of 2020 this feature it is steel needed!

McNeel just release better libraries for Python, Javascript and .NET:

rhino3dm is a set of libraries based on the OpenNURBS geometry library with a "RhinoCommon" style. This provides the ability to access and manipulate geometry through .NET, Python or JavaScript applications independent of Rhino.

Libraries based on OpenNURBS with a RhinoCommon style

P.S: As I do use Rhino a lot, I will be very pleased to see a native implementation of the .3dm file format into FreeCAD. Also .3dm-s are supported by the Moi3D software.
User avatar
Chris_G
Posts: 1307
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: OpenNURBS 3DM file support?

Postby Chris_G » Sun Mar 22, 2020 9:37 pm

I am following the progress of rhino3dm.
But I think it is not complete yet.
I gave a try last week; I was able to import surfaces.
But I haven't found a way to get the trimming edges yet.
rhino.png
rhino.png (135.2 KiB) Viewed 339 times
zohozer
Posts: 86
Joined: Mon Jul 28, 2014 8:35 pm

Re: OpenNURBS 3DM file support?

Postby zohozer » Mon Mar 23, 2020 10:36 am

Good to see some progress. Do you have a script to share? I am eager to test this. Thank you.
User avatar
Chris_G
Posts: 1307
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: OpenNURBS 3DM file support?

Postby Chris_G » Mon Mar 23, 2020 11:54 am

Here it is.
You must install rhino3dm python binding first.
On linux, I simply had to do :

Code: Select all

pip install --user rhino3dm

Code: Select all

import os
import FreeCAD as App
import FreeCADGui as Gui
try:
	import rhino3dm as r3
except ModuleNotFoundError:
	App.Console.PrintError("You must install rhino3dm first !")

att = ["ApplicationName",
    "ApplicationUrl",
    "ApplicationDetails",
    "CreatedBy",
    "LastEditedBy",
    "Revision"]

class File3dm:
	def __init__(self, path):
		self.f3dm = r3.File3dm.Read(path)
	def parse_objects(self, doc=None):
		if not doc:
			doc = App.newDocument("3dm import")
		part = doc.addObject('App::Part','Part')
		for i in range(len(self.f3dm.Objects)):
			obj_fullname = "{}".format(self.f3dm.Objects[i].Geometry)
			first_split = obj_fullname.split(".")
			second_split = first_split[-1].split(" ")
			print("-----------------\n{}".format(second_split[0]))
			obj = self.import_geometry(doc, self.f3dm.Objects[i].Geometry)
			if obj:
				part.addObject(obj)
	def import_geometry(self, doc, geo):
		if isinstance(geo, r3.Brep): #str(geo.ObjectType) == "ObjectType.Brep":
			#print("Brep object")
			print("is solid : {}".format(geo.IsSolid))
			print("is manifold : {}".format(geo.IsManifold))
			print("is surface : {}".format(geo.IsSurface))
			print("has {} faces".format(len(geo.Faces)))
			print("has {} surfaces".format(len(geo.Surfaces)))
			print("has {} edges".format(len(geo.Edges)))
			shapes = []
			for i in range(len(geo.Faces)):
				#print(geo.Faces[i])
				s = self.create_surface(geo.Faces[i])
				shapes.append(s.toShape())
				#print("Face {} has {} edges".format(i,len(geo.Faces[i].Edges)))
			com = Part.Compound(shapes)
			obj = doc.addObject("Part::Feature","Faces")
			obj.Shape = com
#			shapes = []
#			for i in range(len(geo.Edges)):
#				#print(geo.Faces[i])
#				c = self.create_curve(geo.Edges[i])
#				shapes.append(c.toShape())
#			com = Part.Compound(shapes)
#			obj = doc.addObject("Part::Feature","Edges")
#			obj.Shape = com
			return obj
		if isinstance(geo, r3.Curve):
			print("Curve object")

	def create_curve(self, edge):
		nc = edge.ToNurbsCurve()
		#print("{} x {}".format(nu.Degree(0), nu.Degree(1)))
		pts = []
		weights = []
		for u in range(len(nc.Points)):
			p = nc.Points[u]
			#print(App.Vector(p.X,p.Y,p.Z))
			pts.append(App.Vector(p.X,p.Y,p.Z))
			weights.append(p.W)
		ku, mu = self.getFCKnots(nc.Knots)
		periodic = False #mu[0] <= nu.Degree(0)
		bs = Part.BSplineCurve()
		bs.buildFromPolesMultsKnots(pts, mu, ku, periodic, nc.Degree, weights)
		if mu[0] < (nc.Degree+1):
			bs.setPeriodic()
		return bs

	def create_surface(self, surf):
		nu = surf.ToNurbsSurface()
		#print("{} x {}".format(nu.Degree(0), nu.Degree(1)))
		pts = []
		weights = []
		for u in range(nu.Points.CountU):
			row = []
			wrow = []
			for v in range(nu.Points.CountV):
				p = nu.Points[u,v]
				#print(App.Vector(p.X,p.Y,p.Z))
				row.append(App.Vector(p.X,p.Y,p.Z))
				wrow.append(p.W)
			pts.append(row)
			weights.append(wrow)
		ku, mu = self.getFCKnots(nu.KnotsU)
		kv, mv = self.getFCKnots(nu.KnotsV)
		uperiodic = False #mu[0] <= nu.Degree(0)
		vperiodic = False #mv[0] <= nu.Degree(1)
#		print(list(nu.KnotsU))
#		print(ku, mu)
#		print(kv, mv)
#		vflatknots = list(nu.KnotsV)
#		print("{}\n{}".format(uflatknots, vflatknots))
		bs = Part.BSplineSurface()
		bs.buildFromPolesMultsKnots(pts, mu, mv, ku, kv, uperiodic, vperiodic, nu.Degree(0), nu.Degree(1), weights)
		if mu[0] < (nu.Degree(0)+1):
			bs.setUPeriodic()
		if mv[0] < (nu.Degree(1)+1):
			bs.setVPeriodic()
		return bs
	def getFCKnots(self,fknots):
		k = list(fknots)
		mults = []
		knots = list(set(k))
		knots.sort()
		for kn in knots:
			mults.append(k.count(kn))
		mults[0] += 1
		mults[-1] += 1
		return knots, mults




doc = App.ActiveDocument
if doc is None:
	doc = App.newDocument("3dm_file")

from PySide import QtGui
dialogCaption = "Select a 3dm file"
dialogDir = ""
dialogFilter = "Rhino 3dm (*.3dm);;All files (*.*)"
fname = QtGui.QFileDialog.getOpenFileName(None, dialogCaption, dialogDir, dialogFilter)
fname = fname[0]

fi = File3dm(fname)
fi.parse_objects(doc)
Gui.SendMsgToActiveView("ViewFit")
keithsloan52
Posts: 1233
Joined: Mon Feb 27, 2012 5:31 pm

Re: OpenNURBS 3DM file support?

Postby keithsloan52 » Mon Mar 23, 2020 12:50 pm

@Chris_G - Are you going to add a Workbench that has an importer?
User avatar
Chris_G
Posts: 1307
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: OpenNURBS 3DM file support?

Postby Chris_G » Mon Mar 23, 2020 1:03 pm

I don't know.
I just did a quick test out of curiosity.
But writing a whole importer may be more work than I can handle.
User avatar
Kunda1
Posts: 7332
Joined: Thu Jan 05, 2017 9:03 pm

Re: OpenNURBS 3DM file support?

Postby Kunda1 » Mon Mar 23, 2020 1:30 pm

I notated issue #337.
Want to contribute back to FC? Checkout:
#lowhangingfruit | Use the Source, Luke. | How to Help FreeCAD | How to report FC bugs and features
keithsloan52
Posts: 1233
Joined: Mon Feb 27, 2012 5:31 pm

Re: OpenNURBS 3DM file support?

Postby keithsloan52 » Mon Mar 23, 2020 2:00 pm

Chris_G wrote:
Mon Mar 23, 2020 1:03 pm
I don't know.
I just did a quick test out of curiosity.
But writing a whole importer may be more work than I can handle.
Okay if I fork your CurvesWB repro and have a go at adding an importer for 3DM based on your test code?
i.e. Would you be okay with handling any Pull requests that I then make?