Jump to content

Rectangle Packing 1.0.4

   (1 review)

5 Screenshots

About This File

The heart of this Marionette example is the RectPack node. A node with over 2600 lines of code! 

It supports dozens of packing algorithm to pack rectangles on another rectangular area in a space and time-saving way. 

The right combination of packing and bin algo gives the ability to pack rectangles in a way you want to have them on your machines (as close as possible, always cut complete board, less cuts etc.)


Actually not even myself know, which packing algo ist right for which use case. 

Also for professional use case, usually there is a machine dedicated software, which packs the port in a way which match best for the machine. 

So I see the main use case to pack parts on a board for milling them on a cnc-machine as example for model-making. Workflows which uses a guillotine-workflow (always cut the whole board) often have special requirements which maybe could be reached with the choice of packing algorithm or maybe can not. 


The Marionette Example based on foundational work:

http://www.secnot.com/   Python code for packing algorithm

Jukka Jylang - A Thousand Ways to Pack the Bin - A Practical Approach to Two-Dimensional Rectangle Bin Packing (2010)

Huang, E. Korf - Optimal Rectangle Packing: An Absolute Placement Approach (2013)


General Features:

- Input of basic data like length and width

- Input of additional data

- object input (node detects if part input are objects and process them like rectangles)

- sort by material

- use a list of stock materials



Not all input combinations works at the moment. Recommended to use the existing examples to get correct input values.

German Movie


Edited by DomC

What's New in Version 1.0.4   See changelog


corrected script for an error that was shown, if column C "PartName, TeileName" is not a number.

Now the Column "Drehbar" (Rotateable) in die Board list works as expected. If the Checkbox "Rotation erlauben" it checks for the Material List Value 0 or 1. If "Rotation erlauben" is not checkt it do not check the Column in Material list. So choose "Rotation erlauben" and make it 0 or 1 in the Material list in .

Additional Upload v2023 Version.

  • Like 12

User Feedback

Recommended Comments

I use the 2024 version but I never worked with Marionette or coded anything.


Where could i delete or reset that?

Link to comment

Hi Sorry for the late Reply

Here would be the file, i just saved it as a copy of yours and then added my own part-list from Excel.

I additionally noticed that the part-names don't show up either.


Thanks allready for your reply.

Werkteil Optimierung Kopie.vwx

Link to comment

Posted (edited)

For some reason (my dumbness?) i convert the column "TeileName" into a number, which fails and put out an error.

You can do to correct:
1. Without new version or edit the script insert a number here in Column C instead a text.

2. Or edit the script (quite easy) as following:
a. Edit the PIO object
b. Double click the wrapper DrawParts

c. Edit Formula to c+'\\rPos. '+str(a)+'.'+str(b)  instead of  c+'\\rPos. '+str(a)+'.'+str(int(b))


OK, maybe a little bit complicated. I post your corrected file and a new Version 1.0.3 
Thanks for reporting the issue.

Werkteil Optimierung Kopie_b.vwx

Edited by DomC
Link to comment

Thank You so much for your help!


Now I don't get any fail message anymore.


Do you know why somtimes the boards get turned or not, even though in the boardlist the button "Drehbar" is on 0?


The positions are "Setzkasten S. 500*300"

Edited by FlorianG99
Link to comment

Posted (edited)

On 1/25/2024 at 10:56 AM, FlorianG99 said:


Do you know why somtimes the boards get turned or not, even though in the boardlist the button "Drehbar" is on 0?


I think the column "Drehbar" was never implemented (In fact it had no impact if i write there 0 or 1 it just took the checkbox "Rotation erlauben" which was a global option for all materials). I improved the Script with Version 1.0.4 and now it reads the column as soon as i checked the checkbox "Rotation erlauben".

This setting will take care of the column "Drehbar" (Rotateable)

Edited by DomC
  • Like 1
Link to comment
On 1/26/2024 at 7:45 PM, DomC said:

I think the column "Drehbar" was never implemented (In fact it had no impact if i write there 0 or 1 it just took the checkbox "Rotation erlauben" which was a global option for all materials). I improved the Script with Version 1.0.4 and now it reads the column as soon as i checked the checkbox "Rotation erlauben".

This setting will take care of the column "Drehbar" (Rotateable)




my first idea was my error comes with Drehbar , i search a lot if i found it, it was allready allowed, and now ich think my error is . or , . how can i create a grate list for copy and paste for your list ? 


Link to comment

This are the options:

1. Saving/having an external (excel) list and make it fits to the input list of the Marionette. In Excel we can use formulas to have a list and a second sheet whitch grabs the list just for the needed columns. Then import the external list directly into the worksheet inside of Vectorworks.
2. If you are using interiorcad, you can configure a cutting list export, which fits to the needs and then follow step one
3. There is an internal list you could define with interiorcad which writes directly to a worksheet
4. Copy/paste can be tricky, because pasting in a worksheet may fail because you had to activate exactly the range of the pasted list first, which is hard. So import is the better option.
5. At least it could be an option to make (small) adaptions to the script so that it reads the needed columns automatically of any list. Or even directly for external file more even directly from part out of the drawing without doing any export/ import steps.

I think the simplest way is to export to excel, then delete the unwanted columns and import into the Worksheet with the worksheet menu.
That brings me to the idea to maybe make an example which directly reads one of the standard cutting list formats of interiorcad.


Link to comment


yesterday after 20 error , i find out , i select yellow window , and information Objekt , aktualisieren , thats it , no one error again 😄


and now im back in interiorcad, i need some little doors on button , i have found a lock , an import is easy , i have think the same with cup hinge



Link to comment
14 hours ago, laurankl said:

Dear DomC would it be possible to have the file downloaded for an old version?

Which Version you would need?

Link to comment

Hello Dom,

I use this precious piece of software to cut needed parts on a CNC machine, thank you very much!


I often have to indicate, in the output,  other conventional signs/geometry (for CNC extra work as blind holes for hinge housing, edging etc.).

I was trying to group the rect (panel), with color lines (edges) or circles (for blind holes), whenever needed, in the part-input layer.

While polylines (also with cut holes) and other geometric entities, were counted (using their BBox as rect), unfortunately, groups where not counted in the packing, they simply do not show in the final layout!


I have studied the various nodes and found a solution that may be useful.

The node modifies are in DrawParts -> Move

and I have changed it as follows (just the commented parts):


class Params(metaclass = Marionette.OrderedClass):
	this = Marionette.Node( 'Move' )
	this.SetDescription("This node will move an object or point by an offset in 2D or 3D. If the object is planar this node does not move it outside of its plane.")
	input = Marionette.PortIn( vs.Handle(0), 'in' )	 
	input.SetDescription('An object, vector or point to move')
	d = Marionette.PortIn( 0, 'offset') 
	d.SetDescription('The distance to move the object. It could be a 2D/3D vector or Point2/Point3 in the Input category')		
	output = Marionette.PortOut('out')	
	output.SetDescription('The result')

def RunNode(self):  
	d = self.Params.d.value
	i = self.Params.input.value		
	if type(i) is tuple:
		if len(i) == 3:
			o = [0,0,0]
			o[0] = i[0] + d[0]
			o[1] = i[1] + d[1]
			o[2] = i[2] + d[2]	
			o = [0,0]
			o[0] = i[0] + d[0]
			o[1] = i[1] + d[1]			
		self.Params.output.value = tuple(o)		

		planar = Marionette.Is2DObject(i)	
		if not planar:
			if len(d) == 2:
				d = [d[0], d[1], 0]
			vs.Move3DObj(i, d[0], d[1], d[2])

			vs.HMove(i, d[0], d[1])
			#this part allows the groups displaying correctly (planar objects only)
			if vs.GetTypeN(i) == 11:
				#read components in the group and duplicate them in the default container
				h = vs.FInGroup(i)
				while h != 0:
					vs.CreateDuplicateObject(h, None)					
					h = vs.NextObj(h)
			#end of my added code
		self.Params.output.value = i	


It seems that the duplicated object (created in the node Dup in Container) is there, but if the handle is of type Group, it does not show.

In my solution, I simply cycle inside the group components and duplicate them all in the default container (the plugin object).

This allows to show the group content, but I do not know where the original group is hiding…

Probably there is a better solution, I am not an expert of Marionette…


  • Like 1
Link to comment

Posted (edited)

The Script was made, screenplane planar objects was still a standard. it would still work with grouped screenplane objects.

It seems, that the dup-container node puts screenplane planar objects on the 2D representation of the PlugIn.
And grouped layerplane planar objects were putted on the 3D component of the plugIn. If we switch on top view we can see the groupes. 

Thanks for sharing that issue.

Edited by DomC
Link to comment


Yes, screen plane planar objects into groups are showing correctly.

I didn't notice that I was creating objects in the layer plane and only grouped objects disappeared in the packing…

So it is sufficient to change the Dup in container node like this (just the part between my comments) :


class Params(metaclass = Marionette.OrderedClass):
	By = 'DomC';import datetime; now = datetime.datetime.now(); y80f5 = now.year;m80f5 = now.month; d80f5 = now.day; h180f5 = now.hour; mi180f5 = now.minute; s180f5 = now.second; ms180f5 = now.microsecond; h2 = now.hour-12 if now.hour >=13 else now.hour; VersionChange1 = str(y80f5)+' '+str(m80f5)+' '+str(d80f5); VersionChange2 = str(y80f5)+'-'+str(m80f5)+'-'+str(d80f5)+'-'+str(h2)+'-'+str(mi180f5); 
	this = Marionette.Node( "Dup in Container" )
	this.SetDescription( 'Duplicates Objects in a Container or active Layer' )
	object = Marionette.PortIn(vs.Handle(0), 'obj')
	object.SetDescription( "The input object" )
	container = Marionette.PortIn(0)
	obj = Marionette.PortOut()
	obj.SetDescription( "The result object" )

def RunNode(self):
	l = vs.ActLayer()
	o = self.Params.object.value
	pioHandle = self.Params.container.value
	if pioHandle == 0:
		vs.Message('kein PlugIn')
		pioHandle = l

	created = vs.CreateDuplicateObject(o, pioHandle)

	#this part allows the groups displaying correctly
	if vs.GetTypeN(created) == 11:
		#read components in the group
		h = vs.FInGroup(created)
		while h != 0:
			vs.SetPlanarRef(h, 0) #set each object planar ref to top / plan
			h = vs.NextObj(h)
	#end of my added code

	self.Params.obj.value = created


  • Like 1
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Add a comment...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...