#
# Copyright © 2020 MSC.Software Corporation
#  
# Permission to use Scripts
#  
# Permission is granted to MSC.Software customers with valid licenses of the MSC Apex™ software 
# to use script files posted on this page (collectively “Scripts”) solely for customers’ own authorized 
# internal use in conjunction with licensed use of the MSC Apex software.  
# No other use is permitted.  No license to MSC Apex or other software is granted under this Permission.
#  
# SCRIPTS ARE PROVIDED ON AN "AS-IS" BASIS, WITH NO WARRANTIES OF ANY KIND.  USE OF THE SCRIPTS IS AT CUSTOMER'S SOLE RISK.
#  
# IN NO EVENT SHALL MSC.SOFTWARE CORPORATION, ITS AFFILIATES, SUBSIDIARIES, SUPPLIERS, AGENTS, REPRESENTATIVES OR EMPLOYEES BE LIABLE FOR ANY 
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, TORT, 
# STRICT LIABILITY, OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION USE OF THE SCRIPTS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
#  
# Customer shall indemnify, defend and hold MSC.Software Corporation, its affiliates, subsidiaries, suppliers, agents, 
# employees or representatives (collectively, “Indemnified Parties”) harmless from and against any third-party claims 
# brought against Indemnified Parties rising out of or in connection with Customer’s use of the Scripts.
#
#   ABSTRACT: Calculates maass properties for selected solids and creates point 
#             masses at their centres of gravity.  Coord framea are created for the 
#             principal inertia axes.
#             Masses are only created for solids that have a material assigned to 
#			  them. Any selected solids that don't have a material are ignored and
#             noted in the log file.
#
#	ASSOCIATED FILES: dsPointMass_ui.py is the GUI component for this
#             
#   INPUT:    Selected solids
#
#   OUTPUT:   Point masses
#
#   SIDE EFFECTS: Hopefully none
#
#   ACTIONS:
#
#   REVISION  DATE                   AUTHOR
#   --------  -------------------    ---------------
#   1         17th Feb 2020	         Darrel Sinclair/MSC UK

#
#   REVISION number and reason
#   --------------------------
#   1	Creation


import apex
from apex.construct import Point3D, Point2D

import math

def main(dict={}):

	target = apex.entityCollection()
	mySolids = apex.entityCollection()

	myModel = apex.currentModel()

	# Get all the materials in the model
	modelmats = apex.catalog.getMaterials([{"path":myModel.getPath()}])

	# This dictionary will relate solids to their materials.
	# Using pathnames in the dictionary; using solid objects directly didn't seem to work.
	solidmats = {}

	# This loop populates the dictionary
	for mat in modelmats:

		# Find all the entities referenced by this material.	
		target = mat.getReferences()
		
		for ent in target:
			# If ent is a Part, find solids belonging to the Part
			if ent.getEntityType() == apex.EntityType.Part:
				# print(ent.getName())
				partSolids = ent.getSolids()
				
				for sol in partSolids:
					# print("   " + sol.getName())
					pname = sol.getPathName()
					
					# print("   " + pname)
					solidmats[pname] = {}
					solidmats[pname]["material"] = mat
					solidmats[pname]["part"] = ent.getName()
					
			elif ent.getEntityType() == apex.EntityType.Solid:
				# print(ent.getName())
				pname = ent.getPathName()
				
				# print(pname)
				solidmats[pname] = {}
				solidmats[pname]["material"] = mat
				solidmats[pname]["part"] = ent.getParent()
				
			else:
				print("No solids associated with material " + mat.getName() )
				
	_selected = apex.selection.getCurrentSelection()

	# Loop through all the selected solids and create point masses			
	for solident in _selected:

		# print(" ")
		
		solidpath = solident.getPathName()
		#print("solidpath = ", solidpath)
		
		# Check if the selected solid is valid (has a material)
		if solidpath in solidmats:
			mat = solidmats[solidpath]["material"]	
		else:
			print("**** " + solidpath + " has no material.  No point mass created.")
			continue
		
		# print(solident.getVolume())
		
		# print(mat.getName())
		
		density = mat.getDensity()
		
		# print("   Density = " + str(density))

		mass = solident.getMassNominal(density)

		# print("   Mass = " + str(mass))
		
		massxyz = solident.getMassCenterNominal()
		
		# print (massxyz.getX(), massxyz.getY(), massxyz.getZ())
		
		frame = solident.getPrincipalInertiaFrameNominal()
		
		inertias = solident.getPrincipalInertiaTensorNominal(density)
		
		massname = solident.getName()+"_mass"
		
		#print("   " + massname)
		
		# Set the current Part to be the Part the solid belongs to
		# Have to check if the Part belongs to an Assembly
		
		# Get model name - check if present "in" solidpath?
		dbname = myModel.getName()
		#print(dbname)
		
		#print("partname = ", solidmats[solidpath]["part"].name)
		#print("partpath = ", solidmats[solidpath]["part"].getPath())
		
		# Strip out the model name and the solid name from the full path, so that we get
		#   the (short form) path name to the Part.  This is necessary because the setCurrentPart
		#   function doesn't accept the full path...
		partname = solidpath.lstrip(dbname+"/")
		partname = partname.rstrip(solident.getName())
			
		#print(partname)
		myModel.setCurrentPart(partname)
		
		# Create point for mass location	
		masspoint = apex.geometry.createPointXYZ( massxyz.getX(), massxyz.getY(), massxyz.getZ() )
		
		# Create node on the point, 'cos we can't assign a mass to a free point...	
		# First need to find the vertex associated with the point
		# Get the pathname of the point
		pointname = masspoint.getPathName()
		
		# print( pointname )
		
		# Get "all" the vertices associated with the point in a dictionary
		vert = apex.geometry.getVertices( [{ 'path':pointname }] )

		# Now create the node based on the vertex
		massnode = apex.mesh.createNodeByPickLocation( vert[0], massxyz )
		
		# Generate a Location for the mass
		massloc = apex.construct.createLocationByEntity( massnode )
		
		# Generate Orientation for the mass
		massframe = apex.construct.createOrientationByCoordinateSystem(frame)

		# Create the point mass	
		try:
			apex.attribute.createPointMass( massname, 
											mass, 
											inertias["ixx"], 
											inertias["iyy"], 
											inertias["izz"], 
											inertias["ixy"], 
											inertias["ixz"], 
											inertias["iyz"], 
											apex.attribute.ApplicationMethod.Direct, 
											massloc,
											orientation = massframe )
										
			print("Point mass " + massname + " created in " + partname)
		except:
			print("Error creating point mass " + massname + " in " + partname)

