Jump to content
Developer Wiki and Function Reference Links ×

Simple Marionette to Plot British Standard Tree Shadows


Recommended Posts

Hi guys,

 

I have never worked with Marionettes before but I had a first stab today. Some time ago I was provided with a Marionette that was supposed to plot a geometry based on an Existing Tree (ET) object. I wanted to use it in my new project only to learn that it doesn't actually work.

 

What it should be doing is plotting a simple shadow diagram around each ET based on the tree's height. The BS5837 suggest creating these by "plotting a segment, with a radius from the centre of the stem equal to the height of the tree, drawn from due north-west to due east". This means basically a portion of a circle from 135 degrees to 0 degrees where the radius = ET height.

 

This is what I noticed in the Marionette I was given:

1. The script uses the wrong value as height - it gets the height of the 2D object (ET's 2D projection geometry) rather than the ET symbol's height parameter. I can't find a node that extracts that info.

2. The script uses the wrong value as centre - it gets the geometric centre of the 2D object (ET's 2D projection geometry) rather than the ET symbol's insertion point. I think I managed to overcome that with Get Symbol Info node. At least one success today!

3. The script plots an arc. Rather than an arc, it would be much more useful if the script returned a closed polyline resulting from a section of a circle or a polyline made of two straight sections and an arc section between them.

 

At this stage, I can't get my head around the Marionette enough to get it work. Any ideas or example nodes?

Link to comment

Hello,

 

assuming your Tree is a 3D symbol and not a PIO, these steps might help:

 

Regarding #1.:

I think you can get the 3D height of your 3D symbol by subtracting the output "nBotz" from the output "nTopz" of the "Get 3D Info" Node.

Unfortunately this Node seemes to be brocken (hopefully only on my computer). If not, here is the code for a custom node that only outputs the 3D height. To create the node, replace the code of any existing node, with the code below.


@Marionette.NodeDefinition
class Params(metaclass = Marionette.OrderedClass):
#APPEARANCE
	#Name
	this = Marionette.Node( 'get 3D height' )
	this.SetDescription( 'get 3D height' )

	#Input Ports
	x = Marionette.PortIn( None, '3D Object' )
	x.SetDescription( "3D Object" )

	#OIP Controls

	#Output Ports
	y = Marionette.PortOut('n')
	y.SetDescription( "n" )

#BEHAVIOR
	
def RunNode(self):
	#inputs
	x = self.Params.x.value

	#script
	_, __, n = vs.Get3DInfo(x)

	#outputs
	self.Params.y.value = n

(please only use this, if the "Get 3D Info" node does not work).

 

Regarding #3.:

Given you have drawn the correct arc, you can than turn this into a closed polyline by drawing a line from the center point of the arc to the start point of the arc and another line from the center to the end point of the arc. To create a polyline from that just put the arc and the two lines into the "compose" node.

 

And this is how this Marionette could look like:

grafik.thumb.png.4f7a147dfa11161056554f8d169fec1b.png

 

 

Regards,

Letti

Edited by Letti R
  • Love 1
Link to comment

Thank you, @Letti R.

I was trying to do exactly that with the lines but I didn't have a clue how to compose the idea together. The Get 3D Info didn't work for me either so thanks a lot for the custom code.

 

One thing to note is that the ET must have the 3D geometry enabled - otherwise it won't work either.

 

Do you know what node can read/ extract parameters from plug-ins like ETs, e.g. height, canopy spread etc?

Link to comment

Hello @Michal Zarzecki,

 

unfortunately i dont know how to get the values directly from the ET Plugin objects.

 

However here is what i tried (with Python and Marionette combined), but i do NOT think that this is the correct way to do it:

depending on how the PIO is built, you can maybe get the information from a datarecord that might be attached to the PIO. You can do this with the "Get Record Field" node. Therefore you will have to look up the name of the record and the name of the field. The problem here is, that the value you get from the record depends on the document units settings.

 

Here are the problems:

  • If you are reading the height value of a 49m tall tree with this method, while your document units are set to kilometers with a displayed precision of 1 (0.1), you will get 0km as the height of the tree, because the value its rounded down.
  • The value is given as a string. If the document does not display the unit mark, this string can be easily turned into a float number with the "float()" function of Python, but not if the document does display the unit mark. I think i am missing something here, because i would assume vectorworks has a function to convert such strings into floats, but i have not found that function yet.

 

So if i had to "solve" this now i wold do it like this (but i really hope someone else will show us the proper way of doing this):

  • Get the current unit info of the document with the function "vs.GetPrimaryUnitInfo()" and "store" it
  • Set the unit settings of the document to what is needed (mm, with high precision and dont show unit marks) with the function "vs.PrimaryUnits()"
  • Read the data from the record and convert it to a float with the function "float()"
  • Draw the arc and the lines and compose them
  • Set the unit settings to what they were before

 

I hope that someone else can provide you with a better idea and better information, because i am clearly way out of my depth here.

 

Regards,

Letti

Edited by Letti R
Link to comment
  • Marionette Maven

@Michal Zarzecki @Letti R

Can you tell me what issue you're having with the Get 3D Info node? 
I'm not having an issue here, but would like to make sure there's not something I'm missing. 

As for string to number conversions, I don't believe Vectorworks has any built in functions for that, and we haven't yet written a comprehensive node to do the necessary handling. Letti's approach is similar to how I would tackle this, but I am also working on writing a conversion node, just I haven't had much free time to tackle it while testing the new version of VW. Hopefully after release when things slow down a bit I can make more progress.

Link to comment

@Marissa Farrell

 

When I built the Marionette in line with @Letti R's structure using the Get 3D Info node, it returned an empty group 🤷‍♂️.

 

Ideally, this functionality would be part of the Existing Tree tool 😁

 

I also wonder, if it was possible plot a more precise shadow diagram based on the solar animation - like a vector record of the projected shadow 🤔.

Link to comment

Hello,

 

@Marissa Farrell

The problem with the 3D info is, that you can deactivate the 3D part of this PIO and than the 3D info will return a hight of 0. Which ofcourse is correct, but not what we need.

 

@Michal Zarzecki

I wrote my solution (as pointed out in my previous post) into a node. Please do some tests, if this node gives the correct results.

I also added a "deviation angle" that you can set, if north is not "straight up".

 


@Marionette.NodeDefinition
class Params(metaclass = Marionette.OrderedClass):
#APPEARANCE
	#Name
	this = Marionette.Node( 'draw_shadow_of_PIO_"Existing Tree"' )
	this.SetDescription( 'draw_shadow_of_PIO_"Existing Tree"' )

	#Input Ports
	obj_in = Marionette.PortIn( vs.Handle( 0 ), 'hObj' )
	obj_in.SetDescription( "The input object" )
	
	angle_in = Marionette.PortIn(0, 'nAng')
	angle_in.SetDescription( "The deviation angle, if north is not in y direction" )

	#OIP Controls

	#Output Ports
	h_out = Marionette.PortOut('obj')
	h_out.SetDescription( "obj" )
	

#BEHAVIOR
			
def RunNode(self):
	# INPUTS
	obj = self.Params.obj_in.value
	angle = self.Params.angle_in.value

	
	# FUNCTIONS
	def get_parametric_record_dict(handle):
		record_handle = vs.GetParametricRecord(handle)
		record_name = vs.GetName(record_handle)
			
		record_dict = {}
		field_number = 1
		
		while vs.GetFldName(record_handle, field_number) != "":
			record_dict[vs.GetFldName(record_handle, field_number)] = vs.GetRField(handle, record_name, vs.GetFldName(record_handle, field_number))
			field_number += 1
			
		return(record_dict)
	
	def selection_to_handle():
		handles = []
		def callback_selection_to_handle(handle):
			handles.append(handle)
		vs.ForEachObject(callback_selection_to_handle, "(NOTINDLVP & NOTINREFDLVP & (VSEL=TRUE))")
		return(handles)
	
	
	# SCRIPT
	# only run script if the object is a PIO with the parametric record "Existing Tree" attached
	if vs.GetTypeN(obj) == 86:
		if vs.GetName(vs.GetParametricRecord(obj)) == "Existing Tree":
			
			# save unit info of document
			temp_style, temp_prec, temp_dimPrec, temp_format, temp_angPrec, temp_showMark, temp_dispFrac = vs.GetPrimaryUnitInfo()
			
			# set unit of document to mm and high precision and dont show unit mark
			vs.PrimaryUnits(7, 10, 10, 2, 7, False, False)
			
			# get PIO info
			center = vs.GetSymLoc(obj)
			height = float(get_parametric_record_dict(obj)["Height"])

			# draw arc and lines and compose them
			vs.DSelectAll()
			drawn_ojects = []
			
			vs.ArcByCenter(center[0], center[1], height, 135, -135)
			drawn_ojects.append(vs.LNewObj())
			
			dir_vector_arc_start = vs.Ang2Vec(135, 1)
			vs.MoveTo(center)
			vs.LineTo((center[0] + dir_vector_arc_start[0] * height, center[1] + dir_vector_arc_start[1] * height))
			drawn_ojects.append(vs.LNewObj())
			
			vs.MoveTo(center)
			vs.LineTo((center[0] + height, center[1]))
			drawn_ojects.append(vs.LNewObj())
			
			vs.DoMenuTextByName('Compose', 0)
			poly_line = selection_to_handle()
			
			if len(poly_line) != 1:
				for item in poly_line:
					vs.DelObject(item)
				output_object = None

			else:
				output_object = poly_line[0]
				# rotate polyline, to account for the fact that north is not always in direction (0, 1)
				vs.HRotate(output_object, center, angle)
			
			# set unit of document to what was seved before
			vs.PrimaryUnits(temp_style, temp_prec, temp_dimPrec, temp_format, temp_angPrec, temp_showMark, temp_dispFrac)


	# OUTPUTS
	self.Params.h_out.value = output_object

 

Regards,

Letti

Link to comment

@Letti R, that's absolutely amazing what you've done! In fact, I used the code you provided before and built the routine/ Marionette based on Adam's first proposal and it all worked perfectly. I need to double check but I think I requested some time ago that this functionality be added to the ET tool.

 

I know nothing about scripts but are they VW version/ release-specific? Will I be able to use the same script in, say, v2024? Or, there is a way to convert them from one version to another? - just like the models can be saved for different releases.

 

Many thanks for your help with this - it will save me a lot of time next time.

Link to comment

Hello,

 

what i forgot to write the last time is, that the last node / code i posted also works if the 3D part of the existing tree PIO is disabled.

 

From my understanding (and im hoping someone will correct me if im wrong) Marionette Networks are not version specific if you only use the nodes that are provided from Vectorworks. Costum Python scripts (and therefore custom Marionette nodes) for Vectorworks should also not be strictly version specific, but i once had to change some parts in a custom script because it stopped working on the newer version.

My guess ist you should be able to use this custom script / the custom nodes atleast for some versions.

 

Regards,

Letti

Link to comment
7 hours ago, Michal Zarzecki said:

I know nothing about scripts but are they VW version/ release-specific? Will I be able to use the same script in, say, v2024? Or, there is a way to convert them from one version to another? - just like the models can be saved for different releases.

Usually scripts and Marionettes are not version specific.  Occasionally there may be changes made to the underlying code, or some functions or objects will be modified that will break scripts. But they can usually be edited and correct fairly easily.

 

I have a script I use at least weekly that was written in 2008 and last edited in VW2017 that still runs fine in VW2023.

  • Like 1
Link to comment
  • Marionette Maven
On 8/31/2023 at 12:00 PM, Michal Zarzecki said:

When I built the Marionette in line with @Letti R's structure using the Get 3D Info node, it returned an empty group 🤷‍♂️.

 

On 8/31/2023 at 4:24 PM, Letti R said:

The problem with the 3D info is, that you can deactivate the 3D part of this PIO and than the 3D info will return a hight of 0. Which ofcourse is correct, but not what we need.

 

So it sounds to me that there isn't anything actually wrong with the 3D Info node, but that whatever you're feeding it into isn't handling a zero input correctly. Thanks for clarifying!

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.

Guest
Reply to this topic...

×   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...