instances to separate layers

Guys here's the script idea:
I need a script that would move all the instances (of multiple definitions) from the current selection to the separate layers
For example in the current selection I have 10 instances called AAA_ , 10 instances called BBB_ , 10 instances called CCC_ etc..
The script would create the separate layers in accordance to instances names, but starting with prefix z_ :
z_AAA
z_BBB
z_CCC
and move the instances to related layers (all AAA_ instances will be moved to layer z_AAA, BBBs to layer z_BBB etc)
P.S. Instances have numbers in its names, typically AAA_001, AAA_002 etc - the script should ignore them
P.P.S. Ideally I'd like the this script to continue and create dedicated Forest Sets based on those new layers, but maybe I'm asking too much :)
The script is needed for creating reference forests based CAD blocks coming from Sketchup, Autocad, etc..
Thank you

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
1rv's picture

discussion went in slightly wrong direction

Hello Kostadin, Simon and others
please take a look at at 2 primitive max files called before and after which might explain my goal better. Also take a look at eponymous printscreens.
In the printscreen and max file called "after": I want the script to create AT LEAST properly named layers (1) and put eponymous instances in them, and maybe (as bonus) it'd create proerly named forest SETS (2) with inner option set to "from layers".
Thank you for attention!
Link:
https://drive.google.com/file/d/1Zn9c6VYeJeF77G1RihW70dS8OtYApTdw/view?u...
p.s. I added the 3rd file to show the forest created via forest set ("by layer")
p.p.s: How to populate Forest Sets from a layer:
https://www.youtube.com/watch?v=TSsa9mS5A9Q&t=760s

SimonBourgeois's picture

Hi

I have tested the script with your provided scene and it creates three layers Z_blue,Z_green,Z_red and the forest sets called ForestSet001,ForestSet002 and ForestSet003, from layers is checked and the layer is loaded . if i compare with your scene "2_after" it is exaclty the same exept the name of the layers doesn't have the Z_.
I see that in your third scene you named the forestsets with red green and blue, i can add the renaming of the forestset obj, also for some reason when i tested yesterday the forest set appeared with a visible sized but when i tried in your scene they where created with size set to zero, currently their position are based on the first instance of a group of instance,in my test scene all object were aligned and created in the same order so the forestsets were well aligned with the objects, but in your scene, objects are randomly distributed, it would make more sense to position them evently spaced at the origin.here is a modified version of the code that remove the "Z_" and add a default size of 100 to the created ForestSet and set their positions and names,i 've also added the corresponding forest obj with "reference" on and with the corresponding layer loaded as in your third scene.

(
	local imGetInstances = InstanceMgr.GetInstances
 
	fn collectUniqueNodes nodes: = 
	(
		local handles = #{}
		for o in nodes where not handles[GetHandleByAnim o] collect
		(
			imGetInstances o &inst
			for i in inst do handles[GetHandleByAnim i] = true
			o
		)
	)	
 
	local uniquenodes = collectUniqueNodes nodes:selection as array
	local forestsetArr=#()
	local forestArr=#()
 
	for obj in uniquenodes do
	(
		imGetInstances obj &instances
		trueInstances = for n in instances where (areNodesInstances obj n) collect n
		if trueInstances.count > 1 do
		(
			local layername = trimRight obj.name "0123456789_"
 
			if (objLayer = layerManager.newLayerFromName layerName) != undefined then objLayer.addNodes trueInstances
			else (layerManager.getLayerFromName layerName).addNodes trueInstances
 
			if isForestPack != undefined do 
			(
				ForestSetname = "ForestSet_"+layername
				local ForestSetobj= ForestSet name:ForestSetname pos:[0,0,0] layerimport:on layernames:layername iconSize:100
				append ForestSetArr ForestSetobj
 
				local ForestObj
				if Forest_Pro != undefined then
				(
					ForestObj= Forest_Pro 	pos:[0,-120,0] iconSize:100
				) 
				else
				(
					ForestObj= Forest_Lite pos:[0,-120,0] iconSize:100
				)
				ForestObj.name = "Forest_"+layername
				ForestObj.distmode = 2
				ForestObj.distrefnodes[1] = ForestSetobj
				append ForestArr ForestObj
			)
		)
	)
 
	if ForestSetArr.count > 1 do
	(
		for i = 0 to ForestSetArr.count-1 do (ForestSetArr[i+1].pos.x=i*100;ForestArr[i+1].pos.x=i*100)
	)
 
) 

if you select all objects in your first scene and execute the script you will get the same result as your third scene, let me know if i misunderstood your needs. :)

1rv's picture

Already rock solid script

Works smoothly, HUGE thank you Simon! And wow! you went even further by creating matching forests! It even works without errors if eponymous sets\forests already exist.
Somehow I forgot to rename new layers with "z_" prefix in my final.max example, could you please update the script so it would do add this prefix in layer name?

The only problem is the typical situation when instances are named like Group#1071_001, Group#1071_002, Group#1071_003 where Group#1071 is the typical Sketchup naming and "_00x" is numbering added by importer.. Currently the script cuts off everything after # sign. Could it please preserve it (in given example the "Group#1071" should remain) everywhere? It should cut off starting with "_" and on.
Thank you Simon!

SimonBourgeois's picture

you're welcome

Glad i could help :)
I reintroduce the "z_" in the layers names, and i changed the way it remove "_00x" but keep in mind that it will always remove 4 characters at the end of your instance objects names, if imported objects has a different naming convention, you can modify those lines:
"local layername = "z_"+ substring obj.name 1 (obj.name.count - 4)
local forestname = substring obj.name 1 (obj.name.count - 4)"
changing the "4" to the number of characters that needs to be removed.
i also introduced an additionnal check, as forest has 2 versions, the forest object is different between versions and in my previous code it was checking if the Pro version or Lite version exist but it wasn't checking if both versions were installed, now it creates a pro version object if both are installed.

(
	local imGetInstances = InstanceMgr.GetInstances
 
	fn collectUniqueNodes nodes: = 
	(
		local handles = #{}
		for o in nodes where not handles[GetHandleByAnim o] collect
		(
			imGetInstances o &inst
			for i in inst do handles[GetHandleByAnim i] = true
			o
		)
	)	
 
	local uniquenodes = collectUniqueNodes nodes:selection as array
	local forestsetArr=#()
	local forestArr=#()
 
	for obj in uniquenodes do
	(
		imGetInstances obj &instances
		trueInstances = for n in instances where (areNodesInstances obj n) collect n
		if trueInstances.count > 1 do
		(
			local layername =  "z_"+ substring obj.name 1 (obj.name.count - 4)
			local forestname = substring obj.name 1 (obj.name.count - 4)
 
			if (objLayer = layerManager.newLayerFromName layerName) != undefined then objLayer.addNodes trueInstances
			else (layerManager.getLayerFromName layerName).addNodes trueInstances
 
			if isForestPack != undefined do 
			(
				ForestSetname = "ForestSet_"+forestname
				local forestsetobj = ForestSet name:ForestSetname pos:[0,0,0] layerimport:on layernames:layername iconSize:100
				append forestsetArr forestsetobj
 
				local Forestobj
				if Forest_Pro != undefined or (Forest_Pro != undefined and Forest_Lite != undefined) then
				(
					Forestobj = Forest_Pro 	pos:[0,-120,0] iconSize:100
				) 
				else if Forest_Lite != undefined do
				(
					Forestobj = Forest_Lite pos:[0,-120,0] iconSize:100
				)
				Forestobj.name = "Forest_"+forestname
				Forestobj.distmode = 2
				Forestobj.distrefnodes[1] = forestsetobj
				append forestArr Forestobj
 
			)
		)
	)
 
	if forestsetArr.count > 1 do
	(
		for i = 0 to forestsetArr.count-1 do (forestsetArr[i+1].pos.x=i*100;forestArr[i+1].pos.x=i*100)
	)
 
) 
1rv's picture

So far so good! This is great

So far so good! This is great boring dumb work eliminator, thank you so much! and the text after # is preserved nicely :)
Its a pity Itoo still didn't implement icon text feature for their objects and forest sets are not in their lister ..
P.S. I'd publish it so it wouldn't get lost

SimonBourgeois's picture

That is why i began scripting, no more repetitive task...

I had the same idea about text icon, but forgot to add it, here is the modified version with a text for each forest objects, i added parenting links so that you can move the forest object with its text and its corresponding forest set,it return an error when trying to parent the forestset to the forest object, so i parented the forestset to the text and the text to the forest object :).
here is the modified script:

(
	local imGetInstances = InstanceMgr.GetInstances
 
	fn collectUniqueNodes nodes: = 
	(
		local handles = #{}
		for o in nodes where not handles[GetHandleByAnim o] collect
		(
			imGetInstances o &inst
			for i in inst do handles[GetHandleByAnim i] = true
			o
		)
	)	
 
	local uniquenodes = collectUniqueNodes nodes:selection as array
	local forestsetArr=#()
	local forestArr=#()
 
	for obj in uniquenodes do
	(
		imGetInstances obj &instances
		trueInstances = for n in instances where (areNodesInstances obj n) collect n
		if trueInstances.count > 1 do
		(
			local layername =  "Z_"+ substring obj.name 1 (obj.name.count - 4)
			local forestname = substring obj.name 1 (obj.name.count - 4)
 
			if (objLayer = layerManager.newLayerFromName layerName) != undefined then objLayer.addNodes trueInstances
			else (layerManager.getLayerFromName layerName).addNodes trueInstances
 
			if isForestPack != undefined do 
			(
				ForestSetname = "ForestSet_"+forestname
				local forestsetobj = ForestSet name:ForestSetname pos:[0,0,0] layerimport:on layernames:layername iconSize:100
				append forestsetArr forestsetobj
 
				local Forestobj
				if Forest_Pro != undefined or (Forest_Pro != undefined and Forest_Lite != undefined) then
				(
					Forestobj = Forest_Pro 	pos:[0,-120,0] iconSize:100
				) 
				else if Forest_Lite != undefined do
				(
					Forestobj = Forest_Lite pos:[0,-120,0] iconSize:100
				)
				Forestobj.name = "Forest_"+forestname
				Forestobj.distmode = 2
				Forestobj.distrefnodes[1] = forestsetobj
				append forestArr Forestobj
 
			)
			local txticon = text text:forestname pos:[0,-210,0] size:20
			txticon.wireColor = color 254 159 0
			txticon.parent = Forestobj
			forestsetobj.parent = txticon
		)
	)
 
	if forestsetArr.count > 1 do
	(
		for i = 0 to forestsetArr.count-1 do (forestArr[i+1].pos.x=i*100)
	)
 
) 

Sure you can publish it, maybe it would be better if i make a macroscript, to have a button in the UI, let me know if you think it would be better or if it's ok as it is...

1rv's picture

Linked text is an awesome

Linked text is an awesome improvement - thank you so much.
If you indeed consider an mcr with UI it might be a good idea creating a field for custom prefix in layer name, making forest set and FP creation optional.
Thanks!

SimonBourgeois's picture

Version with UI

your welcome :)
I've added the checkboxes for ForestSet and ForestPack, i've also added the custom suffix.
you can download the file here:
https://www.mediafire.com/file/fmjw8fi9qher7oo/Instances_to_layers_For_F...

1rv's picture

Fantastique! Merci beaucoup!

Fantastique! Merci beaucoup!

SimonBourgeois's picture

De rien :)

De rien :)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.