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: 119
Joined: Mon Jul 28, 2014 8:35 pm

OpenNURBS 3DM file support?

Post by zohozer »

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
Founder
Posts: 13640
Joined: Tue Feb 17, 2009 9:16 pm
Location: Brussels
Contact:

Re: OpenNURBS 3DM file support?

Post by yorik »

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

Re: OpenNURBS 3DM file support?

Post by zohozer »

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
Veteran
Posts: 2579
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: OpenNURBS 3DM file support?

Post by Chris_G »

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 3041 times
zohozer
Posts: 119
Joined: Mon Jul 28, 2014 8:35 pm

Re: OpenNURBS 3DM file support?

Post by zohozer »

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

Re: OpenNURBS 3DM file support?

Post by Chris_G »

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
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: OpenNURBS 3DM file support?

Post by keithsloan52 »

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

Re: OpenNURBS 3DM file support?

Post by Chris_G »

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
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: OpenNURBS 3DM file support?

Post by Kunda1 »

I notated issue #337.
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
keithsloan52
Veteran
Posts: 2756
Joined: Mon Feb 27, 2012 5:31 pm

Re: OpenNURBS 3DM file support?

Post by keithsloan52 »

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?
Post Reply