# coding: utf-8
#
# 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.
#
# Macro created by MSC Apex MSC_Apex Hawk PRE - Version CLR:535240
# Macro created on Dec 13, 2017 at 10:08:12
# 
# Macro Name = what_connectors_v02c
# 
# Macro Description = WORK_Identify Bolts and Creates 1d Connectors and Attributed Curves
# 
# Macro Hot Key = 
# 
# Initialize Apex environment for macro execution
# 
import apex
from apex.construct import Point3D, Point2D

import random
import os
####
import cProfile,pstats, io

def scaleVector(cord1,scale_fact):
	scaled_vect=[ scale_fact*cord1[0],scale_fact*cord1[1],scale_fact*cord1[2] ]
	return scaled_vect


def getLength(cord1, cord2=[0.,0.,0.]):
	dist = ((cord2[0] - cord1[0]) ** 2 + (cord2[1] - cord1[1]) ** 2 + (cord2[2] - cord1[2]) ** 2) ** 0.5
	return dist	
	
def getThreePointsInPlane(pA,pB):
	lgth=getLength(pA,pB)

	#normalize axis	
	v=[i/lgth for i in [pB[0]-pA[0], pB[1]-pA[1], pB[2]-pA[2]] ]
	#print(v)
	#get d:
	d=v[0]*pA[0]+v[1]*pA[1]+v[2]*pA[2]
	
	tol=1e-2

	# find two additional points that will lie in the plane
	# generate a random vector, and project it on to the plane
	
	# try new vB's until it is not colinear to vA
	while True:
		vA=[10*random.random(),10*random.random(),10*random.random()]
		vB=[10*random.random(),10*random.random(),10*random.random()]
		if len(cross(vA,vB)) > tol and len(cross(v,vA)) > tol and len(cross(v,vB)) > tol:
			break
			#

	proj_vA=cross(v,vA)
	proj_vB=cross(v,vB)

	#new approach, only need coordinates
	if 1==1:
		p01=[pA]
		p02=[pA[0] + proj_vA[0],pA[1] + proj_vA[1],pA[2] + proj_vA[2] ]
		p03=[pA[0] + proj_vB[0],pA[1] + proj_vB[1],pA[2] + proj_vB[2] ]
		
		return [p01,p02,p03]


	


def ReadBoltCurves(bolt_curves):
	#print("Processing ReadBoltCurve")
	#tmp_parts=model.getParts(True)
	# get all curves from model
	#bolt_curves = apex.entityCollection()
	#for tmp_part in tmp_parts:
	#	bolt_curves += tmp_part.getCurves()
	
	print("There are : " + str(len(bolt_curves)) + " bolt curves selected")
	
	BoltD={}
	#bolt_curves=bolt_curve_part.getCurves()
	
	
	
	attrib_names=['dim','type','washer_head','nut','washer_nut']
	for curve in bolt_curves:
		tmp_attributes=curve.getUserAttributes(attrib_names)
		attD={}
		# check if there is a dim in format 'm10' if not skip bolt
		# if type is not specified it will take default value 'hex' 
		# all other attributes will take default value False if not set
		for attrib in tmp_attributes:
			attD[attrib.getAttributeName()]=attrib.getStringValue()
		if 'dim' in attD.keys():
			dim=attD['dim']
		else:
			print("***Error no bolt dimension given")
			break
			
		if 'type' in attD.keys():
			type=attD['type']
		else:
			type='hex'
			
		if 'nut' in attD.keys():
			if attD['nut'].upper() == "YES" or attD['nut'].upper() == "TRUE":
				nut=True
			elif attD['nut'].upper() == "NO" or attD['nut'].upper() == "FALSE":
				nut=False
			else:
				# there are  nut dimensions provided in form  "nut_height nut_inner_dia nut_outer_dia top_face_dist_from_p2" 
				nut=attD['nut'].split()
		else:
			nut=False
			
		if 'washer_head' in attD.keys():
			if attD['washer_head'].upper() == "YES" or attD['washer_head'].upper() == "TRUE":
				washer_head=True
			elif attD['washer_head'].upper() == "NO" or attD['washer_head'].upper() == "FALSE":
				washer_head=False
			else:
				# there are  washer dimensions  provided in form  "washer_height washer_inner_dia washer_outer_dia top_face_dist_from_p2" 
				washer_head=attD['washer_head'].split()
		else:
			washer_head=False
			
		if 'washer_nut' in attD.keys():	
			if attD['washer_nut'].upper() == "YES" or attD['washer_nut'].upper() == "TRUE":
				washer_nut=True
			elif attD['washer_nut'].upper() == "NO" or attD['washer_nut'].upper() == "FALSE":
				washer_nut=False
			else:
				# there are  washer dimensions  provided in form  "washer_height washer_inner_dia washer_outer_dia top_face_dist_from_p2" 
				washer_nut=attD['washer_nut'].split()
		else:
			washer_nut=False
		
		verts= curve.getExteriorVertices()
		p2=[verts[0].getLocation().getX(), verts[0].getLocation().getY(), verts[0].getLocation().getZ()]
		p1=[verts[1].getLocation().getX(), verts[1].getLocation().getY(), verts[1].getLocation().getZ()]
		#print(p1)
		BoltD[curve.getPathName() ]=[p1,p2,dim,type,nut,washer_head,washer_nut]
		
		#create the bolt
		
	return 	BoltD
	


def CreateCircleAtPt(p1,p2,diam,bolt_part,model_1):
	# return surface
	tmp_pnts=getThreePointsInPlane(p1,p2)
	v_extrude=getDelta(tmp_pnts[1],p1)

	#p10 is the point at the extension of p1 p2 on the radius
	radius_vect=scaleVector(v_extrude,0.5*diam/getLength(v_extrude) )
	
	p10= addVectors(p1,radius_vect)
	# create curve
	
	_target = [apex.Coordinate( p1[0], p1[1], p1[2] ), apex.Coordinate( p10[0], p10[1], p10[2]) ]
	_iphysicalCollection = apex.IPhysicalCollection()	
	for point in _target :
		_iphysicalCollection.append(point)

	_axisVector = apex.construct.Vector3D( p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2] )
	_axisPoint = Point3D( p1[0], p1[1], p1[2])
		
	tmp_radius = apex.geometry.createCurve(target = _iphysicalCollection, behavior = apex.geometry.CurveBehavior.Spline)
	
	_target_source = apex.EntityCollection()
	_target_source.append(tmp_radius)
	
	result = apex.geometry.createGeometryRevolve(
		target = _target_source,
		axisVector = _axisVector,
		axisPoint = _axisPoint,
		angleOfRevolution = 360.,
		startOffsetAngle = 0.0
	)
	tmp_surfs=model_1.getCurrentPart().getSurfaces()	
	no_of_surfs=len(tmp_surfs)	
	print("Created Surface Name: " + tmp_surfs[no_of_surfs-1].getName() )
	
	
	# delete radius vect
	if len(_target_source) != 0:
		apex.deleteEntities(_target_source)
	
	return tmp_surfs[no_of_surfs-1] 
	
def CreateCylinder(p1,p2,diam,bolt_part,model_1):
	# Set Current Part
	model_name=model_1.getName()
	N=len(model_name) + 1
	parent_part_name=bolt_part.getPathName()[N:]
	print("Current Part name: " + parent_part_name )
	try:
		model_1.setCurrentPart(parent_part_name )
	except:
		pass
		print("Failed")
		curr_part=model_1.getCurrentPart()
		print("The actual current part is: " + curr_part.getName() )
	
	
	
	# create cylinder axis vect
	_CylAxisVect = apex.construct.Vector3D( p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2] )
	
	#  create surface to be swept
	surf1=CreateCircleAtPt(p1,p2,diam,bolt_part,model_1)

	_source = apex.EntityCollection()
	_source.append( surf1 )

	#part_1 = model_1.setCurrentPart(pathName = bolt_part.getName() )
	_result= apex.geometry.createGeometryExtrude(target = _source, extrudeVector = _CylAxisVect, extrusionDistance = getLength(p2,p1), startOffsetDistance = 0.0)
	
	tmp_solids=model_1.getCurrentPart().getSolids()
	no_of_solids=len(tmp_solids)	
	
	# Creted solid:
	new_solid=tmp_solids[no_of_solids-1]
	print("Created Solid: " + new_solid.getName() )
	
	entities_1 = apex.EntityCollection()
	#entities_1.append(tmp_solids[no_of_solids-1])
	entities_1.append(new_solid)
	
	
	
	#try:
	#	apex.reparent( parent = bolt_part, target = entities_1 )
	#except:
	#	pass
		
	
	return new_solid, surf1



def createLabelAtPoint(p1,text):
	#text1=apex.display.displayText(text,vert.asIPhysical())
	
	p1_3D=apex.construct.Point3D(p1[0],p1[1],p1[2])
	#p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
	#p3_3D=apex.construct.Point3D(p3[0],p3[1],p3[2])
	#p4_3D=apex.construct.Point3D(p4[0],p4[1],p4[2])						
	# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
	# highlight_stuff(p1_point,255,0,50)
	text1=apex.display.displayText(text,p1_3D)


def getDist2(vert1, vert2):
	point1=vert1.getLocation()
	point2=vert2.getLocation()
	cord1=[point1.getX(), point1.getY(),  point1.getZ()]
	cord2=[point2.getX(), point2.getY(),  point2.getZ()]
	dist = ((cord2[0] - cord1[0]) ** 2 + (cord2[1] - cord1[1]) ** 2 + (cord2[2] - cord1[2]) ** 2) ** 0.5
	return dist

	
def isCylindrical2(tmp_face,tmp_solid,tol=0.01):
	# assume extra vertices are removed
	# 1 Get Surface Mid point
	# 2 Check so that there are exaclty 2 edges
	# 3 Check so that both edges have equal length
	
	# 3 If Pass, then check ratio between area and circumference to get height
	# Return True and height
		
	# compare relative length difference. diff must ne smaller than 1% of the length
	midPoint = tmp_face.getCentroid()

	# Get All vertices from tmp_face
	tmp_verts=tmp_face.getVertices()
	tmp_edges=tmp_face.getEdges()
	
	if len(tmp_edges) == 2:
		# Case 1 no extra verts
		if abs(tmp_edges[0].getLength() - tmp_edges[1].getLength()  )/tmp_edges[0].getLength()   < tol:
			#height=tmp_face.getArea()/tmp_edges[0].getlength()
			return True
		else:
			#print("Fail 1")
			bbb=abs(tmp_edges[0].getLength() - tmp_edges[1].getLength()  )/tmp_edges[0].getLength()
			#print(bbb)
			return False
	else:
		# Case 2, Two segements with extra verts
		edge_loops=tmp_face.getExteriorEdgeLoops()
		tmp_edges=apex.EntityCollection()
		for edge_loop in edge_loops:
			# Check so that all edges in loop are within tol from bolt
			tmp_edges.extend(edge_loop.getEdges())

			distance_ = apex.measureDistance( source =tmp_solid,  target = tmp_edges )
			
			if max(distance_) < tol:
				print("All edges for face: " + str(tmp_face.getId() ) + " is within tolerance" )
				return True
			else:
				print(max(distance_))
				print("Fail 4a")
				return False
		
		
		
			# verts1=edge_loops[0].getVertices()
			# N1=len(verts1) -1
			# if N1 > 0:
				# Dist1=getDist2(verts1[0],verts1[N1] )
				# rat1=L1/Dist1
			# else:
				# #print("Fail 4a")
				# return False
			# verts2=edge_loops[1].getVertices()
			# N2=len(verts2) -1
			# if N2 > 0:
				# Dist2=getDist2(verts2[0],verts2[N2] )
				# rat2=L2/Dist2
			# else:
				# #print("Fail 4b")
				# return False
			# if abs(rat1 -3.14159) < 0.05 and abs(rat2 -3.14159) < 0.05:
				# print("Fail 5")
				# return True
			# else:
				# #print("Fail 6")
				# #print(abs(rat1 -3.14159))
				# #print(abs(rat2 -3.14159))
				# return False
		else:
			print("Fail 3")
			return False
	#else:
	#	print("Fail 2")
	#	print("number of edge loops: " + str(len(tmp_face.getExteriorEdgeLoops())))
	#	return False


	

def reparentAttributedSolidsToTemp():
	model_1 = apex.currentModel()

	solids = getAllSolids()
	tmp_solids = apex.entityCollection()
	_target = apex.entityCollection()

	#Create tmp_part if not existing
	try:
		tmp_part = model_1.getPart( name = "Temp_repository" )
	except:
		tmp_part = model_1.createPart()
		updatedPartName_ = tmp_part.update( name = "Temp_repository" )


	for solid in solids:
		try:
			tmp_attributes=solid.getUserAttributes(['ParentPart'])
			parent_part_name=tmp_attributes[0].getStringValue()
			_target.append(solid)
		except:
			continue
			
	try:
		#parent_part = model_1.getPart( name = parent_part_name )			
		apex.reparent( parent = tmp_part, target = _target )
	except:
		print("parent Part: " + parent_part_name + " could not be found")



def getAllParts():
	#get and return all solids from current model
	model_1 = apex.currentModel()
	parts = model_1.getParts(True)
	return parts
	
def getAllSolids():
	solids = apex.entityCollection()
	parts=getAllParts()
	for part in parts:
		solids += part.getSolids()
	return solids

def getAllSurfs():
	surfs = apex.entityCollection()
	parts=getAllParts()
	for part in parts:
		surfs += part.getSurfaces()
	return surfs

def getAllFaces():
	faces = apex.entityCollection()
	surfs=getAllSurfs()
	for surf in surfs:
		faces += surf.getFaces()
	solids=	getAllSolids()
	for solid in solids:
		faces += solid.getFaces()
	return faces

def getAllEdges():
	edges = apex.entityCollection()
	interior_edges=apex.entityCollection()
	exterior_edges=apex.entityCollection()
	surfs=getAllSurfs()
	for surf in surfs:
		edges += surf.getEdges()
	solids=	getAllSolids()
	for solid in solids:
		edges += solid.getEdges()	
		
	return edges

def getAllInteriorEdges():
	edges = apex.entityCollection()
	surfs=getAllSurfs()
	for surf in surfs:
		edges += surf.getInteriorEdges()
	return edges	
	
def getAllExteriorEdges():
	edges = apex.entityCollection()
	surfs=getAllSurfs()
	for surf in surfs:
		edges += surf.getExteriorEdges()
	return edges	

def getAllCurves():
	curves = apex.entityCollection()
	parts=getAllParts()
	for part in parts:
		curves += part.getCurves()
	return curves
	
def moveObjectToTempPart(solid,tmp_part_name="Temp_repository"):
	model_1 = apex.currentModel()

	_target = apex.entityCollection()
	_target.append(solid)

	#Create tmp_part if not existing
	try:
		tmp_part = model_1.getPart( pathName = tmp_part_name )
	except:
		tmp_part = model_1.createPart()
		updatedPartName_ = tmp_part.update( name = tmp_part_name )

	try:
		#parent_part = model_1.getPart( name = parent_part_name )			
		apex.reparent( parent = tmp_part, target = _target )
	except:
		pass
				

		
def addVectors(cord1, cord2):
	vect_sum=[cord2[0]+cord1[0], cord2[1]+cord1[1], cord2[2]+cord1[2]]
	return vect_sum	
			
	
def doSketch_1(part_1,diam,surface_1,FirstPass=False):
	origin=[0.,0.]
	radius=diam/2
	faces = surface_1.getFaces()
	#print("Number of faces: " + str(len(faces)))
	_referenceFace = faces[0]
	sketch_1 = part_1.createSketchOnSurface(
		name = 'Sketch 1',
		face = _referenceFace,
		reverseNormal = False,
		alignSketchViewWithViewport = False
	)
	if FirstPass == True:
		sketchpoint_1 = sketch_1.createPoint(
			name = "Sketchpoint 1",
			location = Point2D( -0.000000000001, 0.000000000002 )
		)
	else:
		circle_1 = sketch_1.createCircleCenterPoint(
			name = "Circle 1",
			centerPoint = Point2D( origin[0], origin[1] ),
			pointOnCircle = Point2D( radius, -0. )
		)
	result = sketch_1.completeSketch( fillSketches = True )
	return result

def get_BoltDimensions(type,name):
	if type=='HHCS' or type =='hex':
		M= {}
		#  name   diam height head_dia max_dia
		#changing m4 head 
		#0     1     2       3
		M['m3']=[3,2.12,5.5,6.4]
		#M['m4']=[4,2.92,7,8.1]
		M['m4']=[4,2.65,7,8.1]
		M['m6']=[6,4.15,10,11.5]
		M['m8']=[8,5.65,13,15]
		M['m10']=[10,6.63,17,18.48]
		M['m12']=[12,7.76,19,20.78]
		M['m14']=[14,9.09,22,24.25]
		M['m16']=[16,10.32,24,27.71]
		M['m20']=[20,12.88,30,34.64]
		M['m24']=[24,15.44,36,41.57]
		M['m30']=[30,19.48,46,53.12]
		M['m36']=[36,23.38,55,63.51]
		M['m42']=[42,26.97,65,75.06]
		M['m48']=[48,31.07,75,86.6]
		M['m56']=[56,36.2,85,98.15]
		M['m64']=[64,41.32,95,109.7]
		M['m72']=[72,46.45,105,121.24]
		M['m80']=[80,51.58,115,132.72]
		M['m90']=[90,57.74,130,150.11]
		M['m100']=[90,63.9,145,167.43]
		

		if name not in M.keys():
			print("Dimension: " + name + " does not exist")
			return -1,-1,-1
		else:
			return M[name][0],M[name][1],M[name][2]
	
	elif type=='TX':
		M= {}
		#  name   diam, height, head_dia, head_groove_dia
		#changing m4 head 
		#0     1     2       3
		M['m3']=[3,3,5.5,2.5]
		M['m4']=[4,4,7,3]
		M['m5']=[5,5,8.5,4]
		M['m6']=[6,6,10,5]
		M['m8']=[8,8,13,6]
		M['m10']=[10,10,16,8]
		M['m12']=[12,12,18,10]
		M['m16']=[16,16,24,14]
		M['m20']=[20,20,30,17]

		

		if name not in M.keys():
			print("Dimension: " + name + " does not exist")
			return -1,-1,-1
		else:
			return M[name][0],M[name][1],M[name][2]	,M[name][3]	
			
			
	elif type=='SH' or type =='hex socket' or type =='allen':
		#pls note the output order
		M={}
		#NameDIAMETER THREAD_LENGTH  HEAD Dia HEAD HEIGHT
		m['m1.6']=[1.6,15,3,1.7]
		m['m2']=[2,16,3.8,2]
		m['m2.5']=[2.5,17,4.5,2.5]
		m['m3']=[3,18,5.5,3]
		m['m4']=[4,20,7,4]
		m['m5']=[5,22,8.5,5]
		m['m6']=[6,24,10,6]
		m['m8']=[8,28,13,8]
		m['m10']=[10,32,16,10]
		m['m12']=[12,36,18,12]
		m['m14']=[14,40,21,14]
		m['m16']=[16,44,24,16]
		m['m18']=[18,48,27,18]
		m['m20']=[20,52,30,20]
		m['m22']=[22,56,33,22]
		m['m24']=[24,60,36,24]
		m['m27']=[27,66,40,27]
		m['m30']=[30,72,45,30]
		m['m33']=[33,78,50,33]
		m['m36']=[36,84,54,36]
		m['m42']=[42,96,63,42]
		m['m48']=[48,108,72,48]
		if name not in M.keys():
			print("Dimension: " + name + " does not exist")
			return -1,-1,-1
		else:
			return M[name][0],M[name][2],M[name][3]
	else:
		print("type " + type +  " can not be established")
		return -1,-1,-1
	

			
def SetAttributeOrigPath(solid):
	model_1 = apex.currentModel()
	model_name=model_1.getName()
	N=len(model_name) + 1
	parent_part_name=solid.getPath()
	# the leading part of the path contains the model name- this need to be stripped when searching part by name
	# strip chars coreresponding to length of model name +1 (for the "/" separator)
	# 
	solid.addUserAttribute(userAttributeName ="ParentPart", stringValue = parent_part_name[N:] )
	return

def highlight_stuff(item,r,g,b):
	col1=apex.ColorRGB(r,g,b)					
	#if surf.getVisibility() == 1:		
	item.highlight(col1,2,3)

def ScaleBoxPoints(box,scale_fact):

	# In IL FP1 the getBondingBox returns IPhy (not 3D points)
	IPhy1=box.getPoint1()
	IPhy2=box.getPoint2()
	
	# grow bounding box
	x1=IPhy1.getX()
	y1=IPhy1.getY()
	z1=IPhy1.getZ()

	x2=IPhy2.getX()
	y2=IPhy2.getY()
	z2=IPhy2.getZ()
	
	dx=(x2-x1)*scale_fact
	dy=(y2-y1)*scale_fact
	dz=(z2-z1)*scale_fact
	
	Point1=apex.Coordinate()
	Point2=apex.Coordinate()

	Point1.setX(x1-dx)
	Point1.setY(y1-dy)
	Point1.setZ(z1-dz)
	
	Point2.setX(x2+dx)
	Point2.setY(y2+dy)
	Point2.setZ(z2+dz)
	
	if 1==0:
		x1=Point1.getX()
		y1=Point1.getY()
		z1=Point1.getZ()

		x2=Point2.getX()
		y2=Point2.getY()
		z2=Point2.getZ()
		p1_point=apex.geometry.createPointXYZ(x1,y1,z1)
		text1=apex.display.displayText("p1" + str(scale_fact),Point1)
		
		p2_point=apex.geometry.createPointXYZ(x2,y2,z2)
		text1=apex.display.displayText("p2" + str(scale_fact),Point2)
	
	
	return Point1, Point2

def get3dPointsDelta(Pt1, Pt2):
	cord1=[Pt1.getX(), Pt1.getY(), Pt1.getZ() ]
	cord2=[Pt2.getX(), Pt2.getY(), Pt2.getZ() ]
	delta=[cord2[0]-cord1[0], cord2[1]-cord1[1], cord2[2]-cord1[2]]
	return delta

def getDelta(cord1, cord2):
	delta=[cord2[0]-cord1[0], cord2[1]-cord1[1], cord2[2]-cord1[2]]
	return delta	
	
def getDist(cord1, cord2=[0.,0.,0.]):
	dist = ((cord2[0] - cord1[0]) ** 2 + (cord2[1] - cord1[1]) ** 2 + (cord2[2] - cord1[2]) ** 2) ** 0.5
	return dist	
	
def get3dPointDist(Pt1,Pt2):
	p1=[Pt1.getX(), Pt1.getY(), Pt1.getZ()]
	p2=[Pt2.getX(), Pt2.getY(), Pt2.getZ()]
	return getDist(p1,p2)

def getDimensions(in_geom):

	pt0=in_geom.getLocation()
	#print("x0: " + str(pt0.getX()))
	
	box=in_geom.getAxisAlignedBoundingBox()
	
	pt1=box.getPoint1()
	pt2=box.getPoint2()

	p3p2=[abs(pt2.getX()-pt1.getX()),abs(pt2.getY()-pt1.getY()),abs(pt2.getZ()-pt1.getZ())]
	
	#print("BoxDimensions: " + "%8.2f" % (p3p2[0]) + " " +  "%8.2f" % (p3p2[1]) + " " + "%8.2f" % (p3p2[2]) )
		
	return p3p2, [pt1.getX(),pt1.getY(),pt1.getZ()], [pt2.getX(),pt2.getY(),pt2.getZ()]
	
#def getLength(cord1,cord2):
#	dist = ((cord2[0] - cord1[0]) ** 2 + (cord2[1] - cord1[1]) ** 2 + (cord2[2] - cord1[2]) ** 2) ** 0.5
#	return dist

def calcHoleCenter(edge):
	# This function does a very simple calculation to find the center of the hole feature
	# It is this function that may be incorrect if more than one edge is used to represent the hole
	edgeParam = edge.getParametricRange()
	startPoint = edge.evaluateEdgeParametricCoordinate(edgeParam["uStart"])
	uStart = edgeParam["uStart"]
	uEnd   = edgeParam["uEnd"]
	uHalf = uStart + 0.5*(uEnd-uStart)
	halfPoint = edge.evaluateEdgeParametricCoordinate(uHalf)
	x = 0.5*(startPoint.getX()+halfPoint.getX())
	y = 0.5*(startPoint.getY()+halfPoint.getY())
	z = 0.5*(startPoint.getZ()+halfPoint.getZ())
	point = apex.geometry.createPointXYZ(x, y, z)
	# I originally wanted to use POINT3D but could not get this to work further down in the code.  I am not sure
	# if the problem was me or the API or other.  Thus, instead I am creating points.  These will be deleted later.
	#point = Point3D(x, y, z)
	return point	

def getPoinAtFace(face):
	# This function does a very simple calculation to find the center of the hole feature
	# It is this function that may be incorrect if more than one edge is used to represent the hole
	#midPoint = face.getCentroid()
	midPoint = face.getCentroid()
	x = midPoint.getX()
	y = midPoint.getY()
	z = midPoint.getZ()
	point = apex.geometry.createPointXYZ(x, y, z)
	# I originally wanted to use POINT3D but could not get this to work further down in the code.  I am not sure
	# if the problem was me or the API or other.  Thus, instead I am creating points.  These will be deleted later.
	#point = Point3D(x, y, z)
	return point	

def isCircular(tmp_face,tol=0.1):
	# 1 Get Surface Mid point
	# 2 Chec so that all verts have same distance to center point
	# 3 If Pass, then check ratio between area and circumference
	# if 0.5*R then return True

	midPoint = tmp_face.getCentroid()

	# Get All vertices from tmp_face
	tmp_verts=tmp_face.getVertices()
	tmp_edges=tmp_face.getEdges()
	
	if len(tmp_verts) > 0:
		point3d=tmp_verts[0].getLocation()
		tmp_R=get3dPointDist(midPoint,point3d)

		Pass=True
		for tmp_vert in tmp_verts:
			if abs(get3dPointDist(midPoint,tmp_vert.getLocation()) - tmp_R) > tol:
				Pass=False
				break

			
		if Pass:
			# Check area vs circumference
			
			tmp_circumf=0
			for tmp_edge in tmp_edges:
				tmp_circumf+=tmp_edge.getLength()
				
			if abs((tmp_face.getArea()/tmp_circumf) - 0.5*tmp_R	) < tol:
				#print("Face is circular")
				return True
			else:
				return False
		else:
			return False
	else:
		# No vertices, so has to be a loop
		# need to add for the possibility of ellipsoids?
		# Check if there are one or several edges, if 1 edge its a circle, if two its a ring
		if len(tmp_edges) ==1:
			#1 edge -> circle
			return True
		else:
			# more edges -> Ring (or something strange)
			return False	

def isCylindrical(tmp_face,tol=0.01):
	# assume extra vertices are removed
	# 1 Get Surface Mid point
	# 2 Check so that there are exaclty 2 edges
	# 3 Check so that both edges have equal length
	
	# 3 If Pass, then check ratio between area and circumference to get height
	# Return True and height
		
	# compare relative length difference. diff must ne smaller than 1% of the length
	midPoint = tmp_face.getCentroid()

	# Get All vertices from tmp_face
	tmp_verts=tmp_face.getVertices()
	tmp_edges=tmp_face.getEdges()
	
	if len(tmp_edges) != 2:
		return False
	else:
		if abs(tmp_edges[0].getLength() - tmp_edges[1].getLength()  )/tmp_edges[0].getLength()   < tol:
			#height=tmp_face.getArea()/tmp_edges[0].getlength()
			return True
		else:
			return False	
		
def cross(u, v):
	u1 = u[0]
	u2 = u[1]
	u3 = u[2]
	v1 = v[0]
	v2 = v[1]
	v3 = v[2]
	vect = [u2 * v3 - u3 * v2, u3 * v1 - u1 * v3, u1 * v2 - u2 * v1]
	return vect			
		
def createCurveFromCords(cord1,cord2):
		
	#_target = [		
	#Point3D( cord1[0],  cord1[1], cord1[2] ),
	#Point3D( cord2[0],  cord2[1], cord2[2] )
	#]

	p1=apex.Coordinate( cord1[0],  cord1[1], cord1[2] )
	p2=apex.Coordinate( cord2[0],  cord2[1], cord2[2] )
	
	_target = apex.IPhysicalCollection()
	_target.append(p1)
	_target.append(p2)
	
	# will return an entity
	entity = apex.geometry.createCurve(
		target = _target,
		behavior = apex.geometry.CurveBehavior.Polyline
	)
	
	return entity.asCurve()

def identify_washers(curve,solid,CircFaceCtrPointL,clear_vertex_flag=True):
	#print("===== Identifying washers")
	# Check 1b: A Type 1 washer has 4 faces and 2 edges
	# Check 2b: All faces centroids are on the bolt axis
	# Check 2b: There need to be two circular faces with no more than 20% of bolt length
	# Check 3b: check diameter of inner hole (not impletemented)
	
	WasherType="NA"
	washer_height=""
	washer_outer_dia=""
	washer_inner_dia=""
	WasherFlag=False
	
	p3p2=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
	bolt_length=getDist(p3p2)
	#print("bolt_length: " + "%8.2f" % (bolt_length))
	tol=5
	#
	#tol=0.5
	ConcentricDistOnAxisL=[]
	CircularAreaL=[]
	pointsL=[]
	CircFaceL = apex.entityCollection()
	
	GoFlag=True

	faces=solid.getFaces()
	
	
	if len(faces)==4:
		if clear_vertex_flag:
			_target = solid.getVertices()
			print("I am in checking verts")
			try:	
				result = apex.geometry.removeVertex(targetVertex = _target, keepAtCurvatureChange = True)
			except:
				print("No vertex removed")
			GoFlag=True
			print("Washer Pass Check 1a")
	else:
		GoFlag=False
		print("Washer Failed Check 1a")	
	
	#Check 1b
	if GoFlag==True:
		edges=solid.getEdges()
		if len(edges)==4 and len(faces)==4:
			print("Washer Pass Check 1b")
			pass
		else:
			print("Washer Failed Check 1b")
			#highlight_stuff(solid,255,0,255)
			#print(len(edges))
			#print(len(faces))
			GoFlag=False

		# find top and bot faces
		if GoFlag==True:
			#Check 2b
			
			# need to find  faces with centroids on p3p2
			for face in faces:
				ptA=face.getCentroid()
				p3pA=getDelta(CircFaceCtrPointL[0], [ptA.getX(),ptA.getY(),ptA.getZ()])
				if getDist(cross(p3pA,p3p2)) < tol:
					#print( getDist(cross(p3pA,p3p2)) )
					#print("p3pA lies on axis")
					#getPoinAtFace(face)
					ConcentricDistOnAxisL.append(getDist(p3pA))
					CircularAreaL.append(face.getArea())
					CircFaceL.append(face)
					pointsL.append([ptA.getX(),ptA.getY(),ptA.getZ()])
				else:
					print(getDist(cross(p3pA,p3p2)))
			if len(ConcentricDistOnAxisL)==4:
				pass
				#print("Pass Check 2b")
			else:
				print("Failed Check 2b")
				print(len(ConcentricDistOnAxisL))
				#print(len(faces))
				#print(len(CircFaceL))
				GoFlag=False
				
		if GoFlag==True:
			# Get distance between Top and bottom faces
			indx=sorted(range(len(ConcentricDistOnAxisL)), key=lambda k: ConcentricDistOnAxisL[k])
			
			washer_height=abs(ConcentricDistOnAxisL[indx[-1]] - ConcentricDistOnAxisL[indx[0]])
			height_ratio= washer_height/bolt_length
			
			TopFace=CircFaceL[indx[0]]
			ptA=TopFace.getCentroid()
			
			if height_ratio < 0.20:
				print("Identified Washer")
				print("Washer height: " + "%8.2f" % (washer_height))
				edgeLenL=sorted([edge.getLength() for edge in edges])
				washer_outer_dia=edgeLenL[0]/3.14126
				washer_inner_dia=edgeLenL[-1]/3.14126
				print("Washer outer dia: " +  "%8.2f" % (washer_inner_dia))
				print("Washer inner dia: " +  "%8.2f" % (washer_outer_dia))
				
				# distance from p2
				p2pA=getDelta(CircFaceCtrPointL[1], [ptA.getX(),ptA.getY(),ptA.getZ()])
				
				
				# if washer is on  upper third it is a head washer otherwise a nut washer
				if ConcentricDistOnAxisL[indx[0]]/bolt_length < 0.33:
					WasherType="Head"
					SetAttributeOrigPath(solid)
					#moveObjectToTempPart(solid,"Temp_repository")
					
					highlight_stuff(solid,0,0,255)
					curve.addUserAttribute(userAttributeName ="washer_head", stringValue = ( ("%6.2f %6.2f %6.2f %6.2f")  % (washer_height,washer_outer_dia,washer_inner_dia,getDist(p2pA)) ) )
				else:
					WasherType="Nut"
					SetAttributeOrigPath(solid)
					#moveObjectToTempPart(solid,"Temp_repository")
					highlight_stuff(solid,255,0,0)
					curve.addUserAttribute(userAttributeName ="washer_nut", stringValue = ( ("%6.2f %6.2f %6.2f %6.2f")  % (washer_height,washer_outer_dia,washer_inner_dia,getDist(p2pA)) ) )
				WasherFlag=True
		
	return WasherFlag
	
def identify_nut(curve,solid,BoltCircFaceCtrPointL,tmp_dia,clear_vertex_flag=True):
	print("===== Identifying nuts======")
	# Check 1c: A Type 1 nut has 23 faces and 46 edges
	# Check 2c: 5 faces have centroids  on the bolt axis
	# Check 3c: check diameter of inner hole (not impletemented)
	
	NutType="NA"
	nut_height=""
	nut_outer_dia=""
	nut_inner_dia=""
	NutFlag=False
	
	p3p2=getDelta(BoltCircFaceCtrPointL[0], BoltCircFaceCtrPointL[1])
	#print(p3p2)
	bolt_length=getDist(p3p2)
	#print("bolt_length: " + str(bolt_length))
	
	ConcentricDistOnAxisL=[]
	ConcentricAreaL=[]
	pointsL=[]
	CircFaceL = apex.entityCollection()
	
	GoFlag=True

	faces=solid.getFaces()
	
	
	NoofFacesCheckL=[23,17,13,18,19,21,23,24,25,40,11]
	
	if len(faces) in NoofFacesCheckL: 
	
		#
		if clear_vertex_flag:
			_target = solid.getVertices()
			print("I am in checking verts")
			try:	
				result = apex.geometry.removeVertex(targetVertex = _target, keepAtCurvatureChange = True)
			except:
				print("No vertex removed")
		GoFlag=True
		print("Nut passed Check 1a")
	else:
		print("Nut Failed Check 1a")
		GoFlag=False
	
	#Check 1c
	if GoFlag:
		edges=solid.getEdges()
		print("There are " + str(len(edges)) + "edges for nut candidate")
		if len(edges)==46 and len(faces)==23:
			print("pass Check 1c   a")
			pass
		elif len(edges)==47 and len(faces)==24:
			print("pass Check 1c   a")
			pass	
			
			
		elif len(edges)==36 and len(faces)==17:
			print("pass Check 1c    b")
			pass
		elif len(edges)==24 and len(faces)==13:
			print("pass Check 1c    c")
			pass
		elif len(edges)==50 and len(faces)==23:
			print("pass Check 1c    c")
			pass	
		elif len(edges)==37 and len(faces)==19:
			print("pass Check 1c    c")
			pass
		elif len(edges)==48 and len(faces)==21:
			print("pass Check 1c    c")
			pass	
		elif len(edges)==86 and len(faces)==40:
			print("pass Check 1c    c")
			pass			
		elif len(edges)==22 and len(faces)==11:
			print("pass Check 1c    c")
			pass
		elif len(edges)==34 and len(faces)==17:
			print("pass Check 1c    c")
			pass
		elif len(edges)==41 and len(faces)==18:
			print("pass Check 1c    c")
			pass
		elif len(edges)==48 and len(faces)==25:
			print("pass Check 1c    c")
			pass			
		elif len(edges)==44 and len(faces)==21:
			print("pass Check 1c    c")
			pass	
		else:
			print("Failed Check 1c")
			print("No of edges: " + str(len(edges) ) )
			print("No of faces: " + str(len(faces) ) )
			GoFlag=False
			print("Go Flag1 " + str(GoFlag) )

	# find top and bot faces
	if GoFlag==True:
		print("No of Faces: " + str(len(faces) ) )
		CircFace={}
		CircFaceCtrPointL=[]
		CircularAreaL=[]
		ConcentricFaceL= apex.entityCollection()
		NonCircFaceL=apex.entityCollection()
		for tmp_face in faces:
			if isCircular(tmp_face): 
				tmp_pt=tmp_face.getCentroid()
				CircFaceCtrPointL.append([tmp_pt.getX(),tmp_pt.getY(),tmp_pt.getZ()])
				CircularAreaL.append(tmp_face.getArea())
				CircFaceL.append(tmp_face)
			else:
				NonCircFaceL.append(tmp_face)
		print("No of CircularFaces: "  + str(len(CircFaceL ) ) )
		
		i=0
		CylindricalFaceIndex=[]
		min_edge=1000
		for face in faces:
			if isCylindrical(face,tol=0.01):
				CylindricalFaceIndex.append(i)
				for edge in face.getEdges():
					tmp_l=edge.getLength()
					if tmp_l < min_edge:
						min_edge=tmp_l
						nut_hole_ind=i
			i+=1
		print("Number of Cylindrical Faces: " + str(len(CylindricalFaceIndex)))
		
		
		
		
		
		# need to find  faces with centroids on p3p2
		for face in faces:
			ptA=face.getCentroid()
			p3pA=getDelta(BoltCircFaceCtrPointL[0], [ptA.getX(),ptA.getY(),ptA.getZ()])
			#
			
			if tmp_dia < 10:
				tol=0.01
			elif tmp_dia < 15:
				tol=1.0
			elif tmp_dia < 20:
				tol=1.5
			elif tmp_dia < 30:
				tol=2.0
			else:
				tol=1.5
				
			#if len(faces)==40:
			#	tol=tol*2.5
			
			#tol=0.2	
			
			if getDist(cross(p3pA,p3p2))/getDist(p3pA) < tol:
				#print("p3pA lies on axis")
				#getPoinAtFace(face)
				ConcentricDistOnAxisL.append(getDist(p3pA))
				ConcentricAreaL.append(face.getArea())
				ConcentricFaceL.append(face)
				pointsL.append([ptA.getX(),ptA.getY(),ptA.getZ()])
				
			elif getDist(cross(p3pA,p3p2))/getDist(p3pA) < 5*tol:
				print( "Face id: " + str(face.getId() ) )
				print( getDist(cross(p3pA,p3p2))/getDist(p3pA) )	
				
				tmp_pointA=apex.geometry.createPointXYZ(ptA.getX(),ptA.getY(),ptA.getZ())
				p3=BoltCircFaceCtrPointL[0]
				
				tmp_point3=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
				tmp_curve=createCurveFromCords([ptA.getX(),ptA.getY(),ptA.getZ()],p3)
			#else:
			#	print("dist to axis: " + str(getDist(cross(p3pA,p3p2))))
			
			
		print("No of CircularFaces: "  + str(len(CircFaceL ) ) )
		print("No of ConcentricFaces: " + str(len(ConcentricFaceL) ) )
			
		AllowedNumberOfConcentricFacesL=[5,3,5,6,7]
		#if len(ConcentricDistOnAxisL)==5 or len(ConcentricDistOnAxisL)==5 or len(ConcentricDistOnAxisL)==3 or len(ConcentricDistOnAxisL)==7:
		if len(ConcentricDistOnAxisL) in AllowedNumberOfConcentricFacesL:
			pass
			#print("Pass Check 2c")
		else:
			print(len(ConcentricDistOnAxisL))
			print("No Nut Identified, Failed Check 2c")
			GoFlag=False
			
	if GoFlag==True:
		# Get distance between Top and bottom faces
		indx=sorted(range(len(ConcentricDistOnAxisL)), key=lambda k: ConcentricDistOnAxisL[k])
		
		nut_type="hex"
		
		if len(faces)== 17 and len(ConcentricDistOnAxisL)==5 and len(edges)==36:
			top_face=ConcentricFaceL[indx[-1]]
			bot_face=ConcentricFaceL[indx[0]]
			#NutType2 Flange Nut
			nut_type="hex_flange"
			
			len_circl_list=len(ConcentricFaceL)
			
			#print("Len: " + str(len_circl_list) )
			inner_cylinder_index=int((len_circl_list-1)/2 +1)
			
			inner_cylinder=ConcentricFaceL[indx[inner_cylinder_index]]
			
			ptA=top_face.getCentroid()
			ptB=bot_face.getCentroid()
			
			pBpA=getDelta([ptB.getX(),ptB.getY(),ptB.getZ()], [ptA.getX(),ptA.getY(),ptA.getZ()])
			nut_height=getDist(pBpA)
			top_face_edge_lenL=sorted([edge.getLength() for edge in top_face.getEdges()])		
			#nut_outer_dia=sum(top_face_edge_lenL[:-1])/3.14126
			
			
			bot_face_edge_lenL=sorted([edge.getLength() for edge in bot_face.getEdges()])		
			nut_outer_dia=bot_face_edge_lenL[-1]/3.14126
			
			inner_cyl_edges=inner_cylinder.getEdges()
			#print("length of inner cyl edges: " + str(len(inner_cyl_edges)))
			nut_inner_dia=inner_cyl_edges[0].getLength()/3.14126
			
		elif len(faces)== 18 and len(ConcentricDistOnAxisL)==6 and len(edges)==41:
			print("Hex flange Nut; 18,6,41")
			
			#top_face=ConcentricFaceL[indx[-1]]
			#bot_face=ConcentricFaceL[indx[0]]
			
			#change for SA chassis
			top_face=ConcentricFaceL[indx[0]]
			bot_face=ConcentricFaceL[indx[-1]]
			
			
			
			#NutType2 Flange Nut
			nut_type="hex_flange"
			
			len_circl_list=len(ConcentricFaceL)
			
			#print("Len: " + str(len_circl_list) )
			inner_cylinder_index=int((len_circl_list-1)/2 +1)
			
			inner_cylinder=ConcentricFaceL[indx[inner_cylinder_index]]
			
			ptA=top_face.getCentroid()
			ptB=bot_face.getCentroid()
			
			pBpA=getDelta([ptB.getX(),ptB.getY(),ptB.getZ()], [ptA.getX(),ptA.getY(),ptA.getZ()])
			nut_height=getDist(pBpA)
			top_face_edge_lenL=sorted([edge.getLength() for edge in top_face.getEdges()])		
			#nut_outer_dia=sum(top_face_edge_lenL[:-1])/3.14126
			
			
			bot_face_edge_lenL=sorted([edge.getLength() for edge in bot_face.getEdges()])		
			nut_outer_dia=bot_face_edge_lenL[-1]/3.14126
			
			inner_cyl_edges=inner_cylinder.getEdges()
			#print("length of inner cyl edges: " + str(len(inner_cyl_edges)))
			nut_inner_dia=inner_cyl_edges[0].getLength()/3.14126	
			
		elif len(faces)== 25 and len(ConcentricDistOnAxisL)==7 and len(edges)==48:
			print("Hex flange Nut; 25,7,48")
			top_face=ConcentricFaceL[indx[-1]]
			bot_face=ConcentricFaceL[indx[0]]
			#NutType2 Flange Nut
			nut_type="hex_flange"
			
			len_circl_list=len(ConcentricFaceL)
			
			#print("Len: " + str(len_circl_list) )
			inner_cylinder_index=int((len_circl_list-1)/2 +1)
			
			inner_cylinder=ConcentricFaceL[indx[inner_cylinder_index]]
			
			ptA=top_face.getCentroid()
			ptB=bot_face.getCentroid()
			
			pBpA=getDelta([ptB.getX(),ptB.getY(),ptB.getZ()], [ptA.getX(),ptA.getY(),ptA.getZ()])
			nut_height=getDist(pBpA)
			top_face_edge_lenL=sorted([edge.getLength() for edge in top_face.getEdges()])		
			#nut_outer_dia=sum(top_face_edge_lenL[:-1])/3.14126
			
			
			bot_face_edge_lenL=sorted([edge.getLength() for edge in bot_face.getEdges()])		
			nut_outer_dia=bot_face_edge_lenL[-1]/3.14126
			
			inner_cyl_edges=inner_cylinder.getEdges()
			#print("length of inner cyl edges: " + str(len(inner_cyl_edges)))
			nut_inner_dia=inner_cyl_edges[0].getLength()/3.14126	
			
			
		elif len(faces)== 40 and len(ConcentricDistOnAxisL)==7:
				
			nn=0
			# while nn < len(indx):
				# tmp_pnt=pointsL[indx[nn]]
				# tmpnt_3D=apex.construct.Point3D(tmp_pnt[0],tmp_pnt[1],tmp_pnt[2])
				# p2_point=apex.geometry.createPointXYZ(tmp_pnt[0],tmp_pnt[1],tmp_pnt[2])
				
				
				# tmp_label="point_" + str(nn)
				# tmp_txt=apex.display.displayText(tmp_label,tmpnt_3D)
				# nn+=1
		
			top_face=ConcentricFaceL[indx[0]]
			bot_face=ConcentricFaceL[indx[-1]]
			#NutType2 Flange Nut
			len_circl_list=len(ConcentricFaceL)
			
			#print("Len: " + str(len_circl_list) )
			#inner_cylinder_index=int((len_circl_list-1)/2 +1)
			#inner_cylinder=ConcentricFaceL[indx[inner_cylinder_index]]
			inner_cylinder=faces[CylindricalFaceIndex[0]]
			
			ptA=top_face.getCentroid()
			ptB=bot_face.getCentroid()
			
			top_face=ConcentricFaceL[indx[-1]]
			bot_face=ConcentricFaceL[indx[0]]
			

			pBpA=getDelta([ptB.getX(),ptB.getY(),ptB.getZ()], [ptA.getX(),ptA.getY(),ptA.getZ()])
			nut_height=getDist(pBpA)
			top_face_edge_lenL=sorted([edge.getLength() for edge in top_face.getEdges()])		
			#nut_outer_dia=sum(top_face_edge_lenL[:-1])/3.14126
			
			
			bot_face_edge_lenL=sorted([edge.getLength() for edge in bot_face.getEdges()])
			# longest edge is inner edge, the 3 second longes form outer diam
			
			nut_outer_dia=(bot_face_edge_lenL[-2]+ bot_face_edge_lenL[-3] + bot_face_edge_lenL[-4]) /3.14126
			
			
			inner_cyl_edges=inner_cylinder.getEdges()
			#print("length of inner cyl edges: " + str(len(inner_cyl_edges)))
			nut_inner_dia=inner_cyl_edges[0].getLength()/3.14126	
	

		elif len(faces)== 11 and len(ConcentricDistOnAxisL)==5 and len(CylindricalFaceIndex)==1:
			top_face=ConcentricFaceL[indx[0]]
			bot_face=ConcentricFaceL[indx[-1]]
			#NutType2 Flange Nut
			
			cyl_ind=CylindricalFaceIndex[0]
			inner_cylinder=faces[cyl_ind]	
			#len_circl_list=len(ConcentricFaceL)
			
			#print("Len: " + str(len_circl_list) )
			#inner_cylinder_index=int((len_circl_list-1)/2 +1)
			
			#inner_cylinder=ConcentricFaceL[indx[inner_cylinder_index]]
			
			ptA=top_face.getCentroid()
			ptB=bot_face.getCentroid()
			
			pBpA=getDelta([ptB.getX(),ptB.getY(),ptB.getZ()], [ptA.getX(),ptA.getY(),ptA.getZ()])
			nut_height=getDist(pBpA)
			top_face_edge_lenL=sorted([edge.getLength() for edge in top_face.getEdges()])		
			#nut_outer_dia=sum(top_face_edge_lenL[:-1])/3.14126
			
			
			bot_face_edge_lenL=sorted([edge.getLength() for edge in bot_face.getEdges()])		
			nut_outer_dia=bot_face_edge_lenL[-1]/3.14126
			
			
			inner_cyl_edges=inner_cylinder.getEdges()
			#print("length of inner cyl edges: " + str(len(inner_cyl_edges)))
			nut_inner_dia=inner_cyl_edges[0].getLength()/3.14126

		elif len(faces)== 17 and ( len(ConcentricDistOnAxisL)==5 or len(ConcentricDistOnAxisL)== 3 ) and len(edges) ==34 :
			# this can be either a scania type hex flange nut or a nut with some conical shape
			# check ratio of top_face and bot_face
			
			#top_face=ConcentricFaceL[indx[-1]]
			#bot_face=ConcentricFaceL[indx[0]]
			
			top_face=ConcentricFaceL[indx[0]]
			bot_face=ConcentricFaceL[indx[-1]]
			
			
			#NutType2 Flange Nut
			len_circl_list=len(ConcentricFaceL)
			
			#print("Len: " + str(len_circl_list) )
			inner_cylinder_index=int((len_circl_list-1)/2 +1)
			
			inner_cylinder=ConcentricFaceL[indx[inner_cylinder_index]]
			
			ptA=top_face.getCentroid()
			ptB=bot_face.getCentroid()
			
			pBpA=getDelta([ptB.getX(),ptB.getY(),ptB.getZ()], [ptA.getX(),ptA.getY(),ptA.getZ()])
			nut_height=getDist(pBpA)
			top_face_edge_lenL=sorted([edge.getLength() for edge in top_face.getEdges()])		
			#nut_outer_dia=sum(top_face_edge_lenL[:-1])/3.14126
			
			ratio2=nut_height/tmp_dia
			print(solid.getName() + " Ratio is: " + str(ratio2) )
			inner_cyl_edges=inner_cylinder.getEdges()
			nut_inner_dia=inner_cyl_edges[0].getLength()/3.14126
			if abs(1-ratio2) > 0.001:
				nut_type="hex"
				
				# outer diam is distance from any vert on top face to center of face
				vert=top_face.getVertices()[0]
				print(vert.getId())
				distance_=getDist([ptA.getX(),ptA.getY(),ptA.getZ()],[vert.getX(),vert.getY(),vert.getZ()])
				print("dist" +  str(distance_) )
				nut_outer_dia=round(2*distance_,1)
				
				
			else:
				nut_type="hex_flange"
				
				top_face_edge_lenL=sorted([edge.getLength() for edge in top_face.getEdges()])		
				nut_outer_dia=top_face_edge_lenL[-1]/3.14126
					
				#print("length of inner cyl edges: " + str(len(inner_cyl_edges)))
				
				
			print("Scania Type " + nut_type + " nut")
			

			
			
			
		else:
			top_face=ConcentricFaceL[indx[0]]
			bot_face=ConcentricFaceL[indx[-1]]
			#Inner cylinder is in the middle of the circular face list
			len_circl_list=len(ConcentricFaceL)
				
			#print("Len: " + str(len_circl_list) )
			inner_cylinder_index=int((len_circl_list-1)/2 +1)
			
			inner_cylinder=ConcentricFaceL[indx[inner_cylinder_index]]
			
			ptA=top_face.getCentroid()
			ptB=bot_face.getCentroid()
			
			pBpA=getDelta([ptB.getX(),ptB.getY(),ptB.getZ()], [ptA.getX(),ptA.getY(),ptA.getZ()])
			nut_height=getDist(pBpA)
			top_face_edge_lenL=sorted([edge.getLength() for edge in top_face.getEdges()])
			nut_outer_dia=sum(top_face_edge_lenL[:-1])/3.14126
			
			inner_cyl_edges=inner_cylinder.getEdges()
			#print("length of inner cyl edges: " + str(len(inner_cyl_edges)))
			nut_inner_dia=inner_cyl_edges[0].getLength()/3.14126
			
		print("=====Identified nut=======")
		print("Nut Type: " + nut_type +  " Nut height: " +  "%8.2f" % (nut_height))
		
		#edgeLenL=sorted([edge.getLength() for edge in edges])
		
		
		# outer edge is split in segments...sum all but the longest (inner) edge


		#nut_outer_dia=edgeLenL[0]/3.14126
		#highlight_stuff(inner_cylinder,0,255,0)

		
		print("Nut outer dia: " +  "%8.2f" % (nut_outer_dia))
		print("Nut inner dia: " +  "%8.2f" % (nut_inner_dia))
		
		# distance from p2
		p2pA=getDelta(BoltCircFaceCtrPointL[1], [ptA.getX(),ptA.getY(),ptA.getZ()])
		#p2pA=getDelta(BoltCircFaceCtrPointL[1], [ptA[0],ptA[1],ptA[2]])
		# uppdating attribute
		if nut_type=="hex_flange":
			curve.addUserAttribute(userAttributeName ="nut", stringValue = ( ("%10s %6.2f %6.2f %6.2f")  % ("hex_flange",nut_inner_dia,nut_outer_dia,getDist(p2pA)) ) )
		else:
			curve.addUserAttribute(userAttributeName ="nut", stringValue = ( ("%6.2f %6.2f %6.2f %6.2f")  % (nut_height,nut_inner_dia,nut_outer_dia,getDist(p2pA)) ) )
		# move solid to temp-part, adding paths name to attribute	
		SetAttributeOrigPath(solid)
		#moveObjectToTempPart(solid,"Temp_repository")
		highlight_stuff(solid,0,255,0)
		
		NutFlag=True
		
	return NutFlag
	
def identifyBolts(searchTolerance, max_no_of_solids,_selected,matlName="",fastenerType="Flexible Link", distributionType="Rigid - RBE2",clear_vertex_flag=True):
	# Use a tolerance error corresponding to 1% of master volume
	
	# Two bolt types identified
	# Type1 Simplified Hex Bolt Check_1: 17 faces, Check_2: 2 Cirlces, Check_3: 3 Non Circular faces with center point on bolt axix
	# Type2 ProE HexBolt: 20 faces, Check_2: 2 Cirlces, Check_3: 6 faces
	# Type3 Zodiac HexBolt: 15 faces, Check_2: 1 Cirlces, Check_3: 8 faces (hole in lower stem)
	# Type4 TASUK HexBolt: 18 faces, Check_2: 2 Cirlces, Check_3: 8 faces 
	# Type5 Viessmann  HexBolt: 25 faces, Check_2: 2 Cirlces, Check_3: 5 concentric faces 
	# Type6 Schindler Idealized  Check_1: 5 faces, 2 Circle, 5 Concentric faces
	# Type20 Airbus Allen Check_1: 16 faces,  
	# Type21 Airbus Allen Check_1: 24 faces, 1 Circle, 9 concentric faces
	# Type22 Airbus Allen Check_1: 24 faces, 1 Circl3, 12 Concentric faces
	# Type30 Zodiac Countersunk Flat:
	# Type50 Schindler Combined Nut  Check_1: 7 faces, 2 Circle, 3 Concentric faces
	ActualFastenerLength=True

	model_1 = apex.currentModel()
	parts = apex.entityCollection()

	parts += model_1.getParts(True)
	print("Number of parts : " + str(len(parts)))


	solids = apex.entityCollection()
	visible_solids = apex.entityCollection()
	bolt_solids = apex.entityCollection()
	
	for part in parts:
	
		if part.getName().find("Temp_repository") < 0:
			solids += part.getSolids()
		else:
			print("skip already processed bolts")
	#print("Number of solids: " + str(len(solids)))
	
	BoltCountD={}

	cnt=0
	visible_solids_nameL=[]
	
	if len(_selected) != 0:
		#search only the selected solids
		visible_solids.extend(_selected)
		cnt=len(visible_solids)
		visible_solids_nameL=[selected_solid.getPathName() for selected_solid in visible_solids]
	else:
		# Search all visible solids
		for solid in solids:
			#check if there is exactly one solid body visible
			if solid.getVisibility() == 1:
				cnt+=1
				visible_solids.append(solid)
				visible_solids_nameL.append(solid.getPathName())
			
	if cnt==0:
		print("Warning...At least one solid need to be visible")
	elif cnt > max_no_of_solids:
		print("Warning, number of parts to find copies of is higher than " +str(max_no_of_solids) )
	else:
		print("There are " + str(len(visible_solids)) + " bolt candidates  visible to check ")
		# BoltD is a dict where volume is matched against each bolt
		#pi=3.141
		#points=apex.entityCollection()
		bolt_curves=apex.entityCollection()
		
		# remove vertices
		# this is time consuming. consider to check only on bolt, nut and washer candidates
		if 1==0:
		#if clear_vertex_flag:
			_target = apex.entityCollection()
			for tmp_solid in visible_solids:
				_target += tmp_solid.getVertices()
			try:	
				result = apex.geometry.removeVertex(targetVertex = _target, keepAtCurvatureChange = True)
			except:
				print("No vertex removed")
			
		# remove extra curves (TAS UK bolt)
		_target = apex.entityCollection()
		for tmp_solid in visible_solids:
			_target += tmp_solid.getEdges()	
		try:	
			apex.deleteEntities(_target)
		except:
			pass
		
		
		for tmp_solid in visible_solids:
			tmp_faces=tmp_solid.getFaces()
			BoltType="Unknown"
			
			# check so that bolt qualifies for type Hex_0 (this is the only type we detect currently)
			# Characteristics: 
			# check1: Count faces
			# check2: Count Circular faces
			# check3: Count NonCircular, Concentric faces
			# ...more checks added if needed
			
			# more checks on dimensions can be addded if needed
			print(" I am in 1")
			#Check 1:
			GoFlag=True
			NoofFacesCheckL=[5,7,8,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,30,49]
			if len(tmp_faces) not in NoofFacesCheckL: 
				#
				print("Failing Check 1: Not a Recognized  bolt, there are " + str(len(tmp_faces)) + " faces" )
				GoFlag=False
			else:
				print("Number of Faces " + str(len(tmp_faces)) + " faces" )
			#Check 2	
			if GoFlag:
				# Look for circles, check area vs length of all its edges
				# 1 Get Surface Mid point
				# 2 Chec so that all verts have same distace to center point
				# 3 If Pass, then check ratio between area and length of edges
				CircFace={}
				CircFaceCtrPointL=[]
				CircularAreaL=[]
				CircFaceL= apex.entityCollection()
				NonCircFaceL=apex.entityCollection()
				for tmp_face in tmp_faces:
					if isCircular(tmp_face): 
						tmp_pt=tmp_face.getCentroid()
						CircFaceCtrPointL.append([tmp_pt.getX(),tmp_pt.getY(),tmp_pt.getZ()])
						CircularAreaL.append(tmp_face.getArea())
						CircFaceL.append(tmp_face)
					else:
						NonCircFaceL.append(tmp_face)
						
				CircDistOnAxisL=[]
				for face in CircFaceL:
					ptA=face.getCentroid()
					p3pA=getDelta(CircFaceCtrPointL[0], [ptA.getX(),ptA.getY(),ptA.getZ()])
					CircDistOnAxisL.append(getDist(p3pA))								
				print("Number of Circular Faces: " + str(len(CircularAreaL)))							
				
				if len(CircularAreaL) == 2:
					#TypeFlagL=["Type1","Type2","Type4","Type5","Type6","Type50"]
					pass
				elif len(CircularAreaL) == 1:
					#TypeFlagL=["Type3","Type20","Type21","Type22","Type30"]
					pass
				else:
					#print("Failing Check 2: Not a recognized fastener  type")
					GoFlag=False
			
			#Check 3:
			if GoFlag:

				if clear_vertex_flag:
					print("I am in checking verts")
					_target = tmp_solid.getVertices()
					try:	
						result = apex.geometry.removeVertex(targetVertex = _target, keepAtCurvatureChange = True)
					except:
						print("No vertex removed")





			
				CylindricalFaceIndex=[]
				i=0
				min_edge=1000
				for face in tmp_faces:
					if isCylindrical(face,tol=0.01):
						CylindricalFaceIndex.append(i)
						for edge in face.getEdges():
							tmp_l=edge.getLength()
							if tmp_l < min_edge:
								min_edge=tmp_l
								bolt_stem_ind=i
					i+=1
				print("Number of Cylindrical Faces: " + str(len(CylindricalFaceIndex)))
				tmp_dia=min_edge/3.14159
				print("Diameter " + str(tmp_dia)+ " bolt stem index=" + str(i) )
				
				if tmp_dia < 10:
					tol=0.8
				elif tmp_dia < 15:
					tol=1
				elif tmp_dia < 20:
					tol=0.75
				elif tmp_dia < 30:
					tol=1.2
				else:
					tol=1.5
					
				if len(tmp_faces) ==25:
					# special case for hex washer bolts
					print("HexWasher candidate, increase tolerance")
					tol=tol*4			
					
				elif len(tmp_faces) ==21:
					# special case for hex washer bolts
					print("HexWasher candidate, increase tolerance")
					tol=tol*2.5	
					
				elif len(tmp_faces) ==49:
					# special case for scania torx  bolts
					print("HexWasher candidate, increase tolerance")
					tol=tol*2.5		
					
					
					
				print("Tolerance: " + str(tol) )
				# need to get an axis for bolt. Take first cylinder, if bolt all cylinder faces are concentric
				if len(CylindricalFaceIndex) <= 0:
					#no cylindrical faces, not a bolt
					GoFlag=False
				elif len(CylindricalFaceIndex) == 1 and len(CircFaceL) ==2:
					bolt_axis_vect=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
				
				else:
					#get axis vector from first two  cylindrical faces, use midpoints
					try:
						tmp_cyl_face1=tmp_faces[CylindricalFaceIndex[0]]
						tmp_cyl_face2=tmp_faces[CylindricalFaceIndex[1]]

						axis_pt1=tmp_cyl_face1.getCentroid()
						axis_pt2=tmp_cyl_face2.getCentroid()
					except:
						GoFlag=False
					
					#_target = [
					#Point3D( 1.633697763824180e+04, 2.387140581567530e+03, 7.420764531655700e+02 )
					#print( str(axis_pt1.getX()) + "  " + str(axis_pt1.getY()) + "  " + str(axis_pt1.getZ()) )																					
					#result1 = apex.geometry.createPointXYZ( axis_pt1.getX(),axis_pt1.getY(),axis_pt1.getZ() )
					#result2 = apex.geometry.createPointXYZ( axis_pt2.getX(),axis_pt2.getY(),axis_pt2.getZ() )				
					if GoFlag:
						bolt_axis_vect=getDelta([axis_pt1.getX(),axis_pt1.getY(),axis_pt1.getZ()], [axis_pt2.getX(),axis_pt2.getY(),axis_pt2.getZ()])
					
				if GoFlag:
					#head (larger face) is index 0 in list and refers. 
					# head is p3 tip is p2
					if len(CircFaceL) ==2 :	
						if CircularAreaL[0] < CircularAreaL[1]:
							CircularAreaL.reverse()
							CircFaceCtrPointL.reverse()

					
					#tol=0.25
					# need to find other faces with centroids on bolt_axis_vect (p3p2)
					ConcentricDistOnAxisL=[]
					ConcentricCircularAreaL=[]
					#NearConcentricDistOnAxisL=[]
					#NearConcentricFaceL=[]
					#NearConcentricCircularAreaL=[]
					pointsL=[]
					ConcentricFaceL = apex.entityCollection()
					for face in NonCircFaceL:
						ptA=face.getCentroid()
						# get distance between face centroid and top face (p3), check if parallell
						p3pA=getDelta(CircFaceCtrPointL[0], [ptA.getX(),ptA.getY(),ptA.getZ()])
						if getDist(cross(p3pA,bolt_axis_vect)) < tol:
							#print("p3pA lies on axis")
							#getPoinAtFace(face)
							ConcentricDistOnAxisL.append(getDist(p3pA))
							#ConcentricCircularAreaL.append(face.getArea())
							ConcentricFaceL.append(face)
							pointsL.append([ptA.getX(),ptA.getY(),ptA.getZ()])
						elif getDist(cross(p3pA,bolt_axis_vect)) < 5*tol:
							print( face.getId() )
							print( getDist(cross(p3pA,bolt_axis_vect)) )
							
							#
							#pf_3D=apex.construct.Point3D(face.getX(),face.getY(),face.getZ())				
							#text_pf=apex.display.displayText(face.getId(),pf_3D)

							
							
							
						else:
							pass
					
					print("Number of Concentric Faces: " + str(len(ConcentricFaceL)))
					NumberOfEdges=len(tmp_solid.getEdges() )
					print("Number of Edges " + str(NumberOfEdges ) )
					#print("Number of  Faces: " + str(len(tmp_faces)))

					#Get the type
					if len(tmp_faces)==17 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==1 and len(ConcentricFaceL) ==3:
						BoltType="Type1"
					elif len(tmp_faces) == 20 and len(ConcentricFaceL) == 6:
						BoltType="Type2"
					elif len(tmp_faces) == 15 and len(ConcentricFaceL) == 8:
						BoltType="Type3"
					elif len(tmp_faces) == 18 and len(ConcentricFaceL) == 4:
						BoltType="Type4"
					elif len(tmp_faces)==19 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==1 and len(ConcentricFaceL) ==5 and NumberOfEdges ==35 :
						BoltType="Type8"	
					elif len(tmp_faces)==19 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==1 and len(ConcentricFaceL) ==2 and NumberOfEdges ==35 :
						BoltType="Type9"	
						
					elif len(tmp_faces) == 25 and len(CircFaceL)==2 and len(ConcentricFaceL) == 5 and len(CylindricalFaceIndex) == 1:
						BoltType="Type51"
					elif len(tmp_faces) == 25 and len(ConcentricFaceL) == 5:
						BoltType="Type5"
					elif len(tmp_faces) == 5  and len(CylindricalFaceIndex) == 2 and len(ConcentricFaceL) == 3:
						BoltType="Type6"
					elif len(tmp_faces) == 16 and len(ConcentricFaceL) == 9:
						BoltType="Type20"
					elif len(tmp_faces) == 24 and len(ConcentricFaceL) == 9:
						BoltType="Type21"
					elif len(tmp_faces) == 24 and len(ConcentricFaceL) == 12:
						BoltType="Type22"
					elif len(tmp_faces) == 22 and len(ConcentricFaceL) == 5:
						BoltType="Type30"
					elif len(tmp_faces) == 7 and len(ConcentricFaceL) == 5:
						BoltType="Type50"	
					
					elif len(tmp_faces)==25 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==1 and len(ConcentricFaceL) ==5 :
						BoltType="Type11"
						
					elif len(tmp_faces)==19 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==1 and len(ConcentricFaceL) ==5 and NumberOfEdges ==37 :
						BoltType="Type11"	
						
					
					elif len(tmp_faces)==21 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==2 and len(ConcentricFaceL) ==6:
						BoltType="Type12"
						
					elif len(tmp_faces)==23 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==2 and len(ConcentricFaceL) ==9:
						BoltType="Type14"	
						

					elif len(tmp_faces)==19 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==2 and len(ConcentricFaceL) ==5:
						BoltType="Type7"		
						
					elif len(tmp_faces)==21 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==3 and len(ConcentricFaceL) ==7:
						BoltType="Type41"
					
					elif len(tmp_faces)==30 and len(CircFaceL)==1 and len(CylindricalFaceIndex)==3 and len(ConcentricFaceL) ==11:
						BoltType="Type61"
						
					elif len(tmp_faces)==28 and len(CircFaceL)==1 and len(CylindricalFaceIndex)==2 and len(ConcentricFaceL) ==9:
						BoltType="Type62"	
						
					elif len(tmp_faces)==30 and len(CircFaceL)==1 and len(CylindricalFaceIndex)==3 and len(ConcentricFaceL) ==10:
						BoltType="Type63"	
						
						
					
					elif len(tmp_faces)==5 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==1 and len(ConcentricFaceL) ==3:
						BoltType="Type71"  # can also be Type81"
						
					elif len(tmp_faces)==8 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==2 and len(ConcentricFaceL) ==6:
						BoltType="Type82"  # can also be Type81"	
						
					elif len(tmp_faces)==49 and len(CircFaceL)==1 and len(CylindricalFaceIndex)==2 and len(ConcentricFaceL) ==10:
						BoltType="Type83"  # Torx Scania Type
						
					elif len(tmp_faces)==26 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==1 and len(ConcentricFaceL) ==6 :
						BoltType="Type110"
					elif len(tmp_faces)==27 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==3 and len(ConcentricFaceL) ==7 :
						BoltType="Type111"
					elif len(tmp_faces)==14 and len(CircFaceL)==2 and len(CylindricalFaceIndex)==2 and len(ConcentricFaceL) ==6:
						BoltType="Type112"  # Ligier Allen Type	2
					elif len(tmp_faces)==14 and len(CircFaceL)==1 and len(CylindricalFaceIndex)==2 and len(ConcentricFaceL) ==7:
						BoltType="Type112"  # Ligier Allen Type	2
						
					else:
						BoltType="Unknown"
						GoFlag=False
					print("BoltType: " + BoltType )
					print("solid: " + tmp_solid.getName() )
					print("=====================================")
				
				profile_flag=False
				
				if BoltType == "Unknown" and  profile_flag==True :
				#if profile_flag==True :
					indx=sorted(range(len(ConcentricDistOnAxisL)), key=lambda k: ConcentricDistOnAxisL[k])
					print("I am in 1000")					
					#Profile the bolt
					print(indx)
					nn=0
					while nn < len(indx):
						tmp_pnt=pointsL[indx[nn]]
						tmpnt_3D=apex.construct.Point3D(tmp_pnt[0],tmp_pnt[1],tmp_pnt[2])
						tmp_label="point_" + str(nn)
						print(tmp_label)
						tmp_txt=apex.display.displayText(tmp_label,tmpnt_3D)
						nn+=1
					
				
				
				# All three checks passed
				if GoFlag and BoltType != "Unknown":
					indx=sorted(range(len(ConcentricDistOnAxisL)), key=lambda k: ConcentricDistOnAxisL[k])	
					#print(indx)

					# Bolt head is p3
					# Bolt tip is  p2
					# Bolt stem_head is p1
					# p3p2 full bolt length
					# p1p2 stem length

					
					if BoltType=="Type50":
						p1=pointsL[indx[1]]
						p2=CircFaceCtrPointL[1]
						p3p2=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
						p3=CircFaceCtrPointL[0]
						bolt_lgth=getDist(p2,p1)
						bolt_stem_cyl=tmp_faces[CylindricalFaceIndex[1]]
						#text4=apex.display.displayText("bolt_stem_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						bolt_diam=bolt_stem_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_dia=CircFaceL[1].getEdges()[0].getLength()/3.14126
						nut_dia=CircFaceL[0].getEdges()[0].getLength()/3.14126
						head_lgth=getDist(p3,p1)
						nut_height=head_lgth

						p4=pointsL[indx[3]]
						p2pA=getDelta(p2,p4)					
						#p1_3D=apex.construct.Point3D(p1[0],p1[1],p1[2])
						#p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						#p3_3D=apex.construct.Point3D(p3[0],p3[1],p3[2])
						#p4_3D=apex.construct.Point3D(p4[0],p4[1],p4[2])						
						# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						# highlight_stuff(p1_point,255,0,50)
						# text1=apex.display.displayText("p1",p1_3D)
						# p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						# text2=apex.display.displayText("p2",p2_3D)
						# highlight_stuff(p2_point,255,255,50)	
						# p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						# highlight_stuff(p3_point,0,255,50)
						# text3=apex.display.displayText("p3",p3_3D)
						# p4_point=apex.geometry.createPointXYZ(p4[0],p4[1],p4[2])
						# highlight_stuff(p4_point,0,255,50)
						# text3=apex.display.displayText("p4",p4_3D)
						bolt_head_type='hex'
					
					elif BoltType=="Type6":
						p1=pointsL[indx[1]]
						p2=CircFaceCtrPointL[1]
						p3p2=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
						p3=CircFaceCtrPointL[0]
						bolt_lgth=getDist(p2,p1)
						bolt_stem_cyl=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						bolt_diam=bolt_stem_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_cyl=tmp_faces[CylindricalFaceIndex[1]]
						head_lgth=getDist(p3,p1)
						#
						# p1_3D=apex.construct.Point3D(p1[0],p1[1],p1[2])
						# p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						# p3_3D=apex.construct.Point3D(p3[0],p3[1],p3[2])
						# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						# highlight_stuff(p1_point,255,0,50)
						# text1=apex.display.displayText("p1",p1_3D)
						# p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						# text2=apex.display.displayText("p2",p2_3D)
						# highlight_stuff(p2_point,255,255,50)	
						# p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						# highlight_stuff(p3_point,0,255,50)
						# text3=apex.display.displayText("p3",p3_3D)
						bolt_head_type='hex'

					elif BoltType=="Type1":
						p1=pointsL[indx[0]]
						p2=CircFaceCtrPointL[1]
						p3p2=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
						p3=CircFaceCtrPointL[0]
						bolt_lgth=getDist(p3p2)-ConcentricDistOnAxisL[indx[0]]
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						bolt_diam=tmp_dia
						head_lgth=ConcentricDistOnAxisL[indx[0]]
						bolt_head_type='hex'
						
					elif BoltType=="Type7":
						p1=pointsL[indx[2]]
						p2=CircFaceCtrPointL[1]
						p3p2=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
						p2p1=getDelta(p2, p1)
						p3=CircFaceCtrPointL[0]
						bolt_lgth=getDist(p2p1)
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						bolt_diam=tmp_dia
						head_lgth=getDist(p3p2)-getDist(p2p1)
						#bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='hex'	
						
					elif BoltType=="Type41":
						p1=pointsL[indx[0]]
						p2=CircFaceCtrPointL[1]
						p3p2=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
						p2p1=getDelta(p2, p1)
						p3=CircFaceCtrPointL[0]
						bolt_lgth=getDist(p2p1)
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						bolt_diam=tmp_dia
						head_lgth=getDist(p3p2)-getDist(p2p1)
						#bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						#bolt_head_type='hex_sleeve'
						bolt_head_type='hex'	
										
					elif BoltType=="Type61":
						p1=pointsL[indx[5]]
						p3=pointsL[indx[-1]]						
						p2=CircFaceCtrPointL[0]
						p3p2=getDelta(p3, p2)
						p2p1=getDelta(p2, p1)
						
						bolt_lgth=getDist(p2p1)
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						bolt_diam=tmp_dia
						head_lgth=getDist(p3p2)-getDist(p2p1)
						#bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='TX'

					elif BoltType=="Type62":
						p1=pointsL[indx[3]]
						p3=pointsL[indx[-1]]						
						p2=CircFaceCtrPointL[0]
						p3p2=getDelta(p3, p2)
						p2p1=getDelta(p2, p1)
						createLabelAtPoint(p1,"p1")
						createLabelAtPoint(p3,"p3")
						createLabelAtPoint(p2,"p2")
						bolt_lgth=getDist(p2p1)
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						bolt_diam=tmp_dia
						head_lgth=getDist(p3p2)-getDist(p2p1)
						#bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='TX'	

					elif BoltType=="Type63":
						p1=pointsL[indx[5]]
						p3=pointsL[indx[-1]]						
						p2=CircFaceCtrPointL[0]
						p3p2=getDelta(p3, p2)
						p2p1=getDelta(p2, p1)
						createLabelAtPoint(p1,"p1")
						createLabelAtPoint(p3,"p3")
						createLabelAtPoint(p2,"p2")
						bolt_lgth=getDist(p2p1)
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						bolt_diam=tmp_dia
						head_lgth=getDist(p3p2)-getDist(p2p1)
						#bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='TX'		
						
					elif BoltType=="Type71":
						p1=CircFaceCtrPointL[1]	
						p3=CircFaceCtrPointL[1]						
						p2=CircFaceCtrPointL[0]
						p3p2=getDelta(p3, p2)
						p2p1=getDelta(p2, p1)
						
						#If Type 71, then COG is between p2 and p3
						cog=tmp_solid.getCentroid()
						p0=[cog.getX(),cog.getY(),cog.getZ()]
						p0p2=getDelta(p0,p1)
						if abs( (abs(getDist(p0p2) - getDist(p3p2)) / getDist(p3p2)) - 0.5 ) <0.05:
							print("Type 71")
							bolt_head_type='pin'
						else:
							print("Type 81")
							
							p1=pointsL[indx[0]]
							p2=CircFaceCtrPointL[1]
							p3p2=getDelta(p3, p2)
							p2p1=getDelta(p2, p1)
							bolt_head_type='rivet'
							
						bolt_lgth=getDist(p2p1)
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						bolt_diam=tmp_dia
						head_lgth=getDist(p3p2)-getDist(p2p1)
						
					elif BoltType=="Type82":
						p2=CircFaceCtrPointL[1]	
						p1=pointsL[indx[1]]					
						p3=CircFaceCtrPointL[0]
						p3p2=getDelta(p3, p2)
						p2p1=getDelta(p2, p1)
						bolt_diam=tmp_dia
						bolt_lgth=getDist(p2p1)
						head_lgth=getDist(p3p2)-getDist(p2p1)
						#bolt_head_type='rivet'
						bolt_head_type='hex'

					elif BoltType=="Type11" or  BoltType=="Type110":
						p1=pointsL[indx[2]]
						p2=CircFaceCtrPointL[1]
						p3p2=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
						p2p1=getDelta(p2, p1)
						p3=CircFaceCtrPointL[0]
						bolt_lgth=getDist(p2p1)
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						bolt_diam=tmp_dia
						head_lgth=getDist(p3p2)-getDist(p2p1)
						#bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='hex_flange'	
						
						
					elif BoltType=="Type112":
					
						apex.display.clearAllGraphicsText()
						# this is the center location for the concentric face furhtest away
						p1_3D=ConcentricFaceL[indx[-5]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						
						p3=CircFaceCtrPointL[0]
						p3_3D=apex.construct.Point3D(p3[0],p3[1],p3[2])
						
						p2_3D=ConcentricFaceL[indx[-1]].getCentroid()
						p2=[p2_3D.getX(),p2_3D.getY(),p2_3D.getZ()]
						#
						p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						highlight_stuff(p1_point,255,0,50)
						text1=apex.display.displayText("p1",p1_3D)
						p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						text2=apex.display.displayText("p2",p2_3D)
						highlight_stuff(p2_point,255,255,50)	
						p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						highlight_stuff(p3_point,0,255,50)
						text3=apex.display.displayText("p3",p3_3D)
						#						
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						#bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl_up",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[0]]
						#text5=apex.display.displayText("bolt_stem_cyl_lo",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						bolt_head_cyl=tmp_faces[CylindricalFaceIndex[1]]
						#text6=apex.display.displayText("bolt_head_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						#
						#bolt_diam=CircFaceL[0].getEdges()[0].getLength()/3.14126
						bolt_diam=tmp_dia
						bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='hex socket'

					
					
					
					
						# #p1=pointsL[indx[2]]
						# p1=pointsL[indx[3]]
						# p2=CircFaceCtrPointL[1]
						# p3p2=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
						# p2p1=getDelta(p2, p1)
						# p3=CircFaceCtrPointL[0]
						# bolt_lgth=getDist(p2p1)
						# chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						# bolt_diam=tmp_dia
						# head_lgth=getDist(p3p2)-getDist(p2p1)
						# #bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						# bolt_head_type='hex_flange'	
						
						
					elif BoltType=="Type111":
						p1=pointsL[indx[2]]
						p2=CircFaceCtrPointL[1]
						p3p2=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
						p2p1=getDelta(p2, p1)
						p3=CircFaceCtrPointL[0]
						bolt_lgth=getDist(p2p1)
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						# bolt cylinder is split use round to get to nearest dimension
						bolt_diam=round(tmp_dia,0)
						print("Bolt diam changed from: " + str(tmp_dia) + " to " + str(bolt_diam) )
						head_lgth=getDist(p3p2)-getDist(p2p1)
						#bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='hex_flange'	
						
						
						
					elif BoltType=="Type12":
						p1=pointsL[indx[4]]
						p2=CircFaceCtrPointL[1]
						p3p2=getDelta(CircFaceCtrPointL[0], CircFaceCtrPointL[1])
						p2p1=getDelta(p2, p1)
						p3=CircFaceCtrPointL[0]
						bolt_lgth=getDist(p2p1)
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						bolt_diam=tmp_dia
						head_lgth=getDist(p3p2)-getDist(p2p1)
						#bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='hex_flange'	


					elif BoltType=="Type14":
						# S.A chassis bolt with sleeve
						apex.display.clearAllGraphicsText()
						p1_3D=ConcentricFaceL[indx[-7]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[1]
						p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						p3_3D=ConcentricFaceL[indx[0]].getCentroid()
						
						p3=[p3_3D.getX(),p3_3D.getY(),p3_3D.getZ()]
						#p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])						
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl_up",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[1]]
						#text5=apex.display.displayText("bolt_stem_cyl_lo",tmp_faces[CylindricalFaceIndex[2]].getCentroid())
						#bolt_head_cyl=tmp_faces[CylindricalFaceIndex[1]]
						#text6=apex.display.displayText("bolt_head_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						#
						bolt_diam=tmp_faces[CylindricalFaceIndex[0]].getEdges()[0].getLength()/3.14126
						#bolt_diam=CircFaceL[0].getEdges()[0].getLength()/3.14126
						#bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='hex'
						


						
								
					elif BoltType=="Type2":
						p1=pointsL[indx[2]]
						p2=CircFaceCtrPointL[1]
						p3=CircFaceCtrPointL[0]
						p3p2=getDelta(p3,p2)
						bolt_lgth=getDist(p3p2)-ConcentricDistOnAxisL[indx[2]]
						chamfer_lgth=2*(getDist(p3p2) - ConcentricDistOnAxisL[indx[-1]])
						# get the equal length edges from the large cylindrical face (threaded)
						edge1=ConcentricFaceL[indx[-2]].getEdges()[0]
						bolt_diam=edge1.getLength()/3.14126
						head_lgth=ConcentricDistOnAxisL[indx[0]]
						bolt_head_type='hex'
					
					elif BoltType=="Type3":
						apex.display.clearAllGraphicsText()
						# this is the center location for the concentric face furhtest away
						p1_3D=ConcentricFaceL[indx[-4]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[0]
						p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						p3_3D=ConcentricFaceL[indx[-1]].getCentroid()
						p3=[p3_3D.getX(),p3_3D.getY(),p3_3D.getZ()]
						#						
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl_up",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[2]]
						#text5=apex.display.displayText("bolt_stem_cyl_lo",tmp_faces[CylindricalFaceIndex[2]].getCentroid())
						bolt_head_cyl=tmp_faces[CylindricalFaceIndex[1]]
						#text6=apex.display.displayText("bolt_head_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						#
						bolt_diam=CircFaceL[0].getEdges()[0].getLength()/3.14126
						bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='hex'
						
					elif BoltType=="Type4":
						apex.display.clearAllGraphicsText()
						# this is the center location for the concentric face furhtest away
						p1_3D=ConcentricFaceL[indx[-4]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[1]
						p3=CircFaceCtrPointL[0]
						p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						p3_3D=apex.construct.Point3D(p3[0],p3[1],p3[2])
						
						#
						# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						# highlight_stuff(p1_point,255,0,50)
						# text1=apex.display.displayText("p1",p1_3D)
						# p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						# text2=apex.display.displayText("p2",p2_3D)
						# highlight_stuff(p2_point,255,255,50)	
						# p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						# highlight_stuff(p3_point,0,255,50)
						# text3=apex.display.displayText("p3",p3_3D)
						#						
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl_up",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						#bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[2]]
						#text5=apex.display.displayText("bolt_stem_cyl_lo",tmp_faces[CylindricalFaceIndex[2]].getCentroid())
						#text6=apex.display.displayText("bolt_head_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						#
						bolt_diam=tmp_faces[CylindricalFaceIndex[0]].getEdges()[0].getLength()/3.14126
						
						bolt_head_diam=(4*CircularAreaL[0]/3.14126)**0.5
						bolt_head_type='hex'
						
					elif BoltType=="Type5":
						apex.display.clearAllGraphicsText()
						# this is the center location for the concentric face furthest away
						p1_3D=ConcentricFaceL[indx[-4]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[1]
						p3=CircFaceCtrPointL[0]
						p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						p3_3D=apex.construct.Point3D(p3[0],p3[1],p3[2])
						
						#
						# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						# highlight_stuff(p1_point,255,0,50)
						# text1=apex.display.displayText("p1",p1_3D)
						# p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						# text2=apex.display.displayText("p2",p2_3D)
						# highlight_stuff(p2_point,255,255,50)	
						# p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						# highlight_stuff(p3_point,0,255,50)
						# text3=apex.display.displayText("p3",p3_3D)
						#						
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl_up",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						#bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[2]]
						#text5=apex.display.displayText("bolt_stem_cyl_lo",tmp_faces[CylindricalFaceIndex[2]].getCentroid())
						#text6=apex.display.displayText("bolt_head_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						#
						bolt_diam=tmp_faces[CylindricalFaceIndex[0]].getEdges()[0].getLength()/3.14126
						
						bolt_head_diam=(4*CircularAreaL[0]/3.14126)**0.5
						bolt_head_type='hex'	
					
					elif BoltType=="Type8":
						apex.display.clearAllGraphicsText()
						# this is the center location for the concentric face furhtest away
						#p1_3D=ConcentricFaceL[indx[-4]].getCentroid()
						p1_3D=ConcentricFaceL[indx[-3]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[1]
						p3=CircFaceCtrPointL[0]
						p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						p3_3D=apex.construct.Point3D(p3[0],p3[1],p3[2])
						
						#
						# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						# highlight_stuff(p1_point,255,0,50)
						# text1=apex.display.displayText("p1",p1_3D)
						# p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						# text2=apex.display.displayText("p2",p2_3D)
						# highlight_stuff(p2_point,255,255,50)	
						# p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						# highlight_stuff(p3_point,0,255,50)
						# text3=apex.display.displayText("p3",p3_3D)
						#						
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl_up",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						#bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[2]]
						#text5=apex.display.displayText("bolt_stem_cyl_lo",tmp_faces[CylindricalFaceIndex[2]].getCentroid())
						#text6=apex.display.displayText("bolt_head_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						#
						bolt_diam=tmp_faces[CylindricalFaceIndex[0]].getEdges()[0].getLength()/3.14126
						
						bolt_head_diam=(4*CircularAreaL[0]/3.14126)**0.5
						bolt_head_type='hex_flange'					
										
					elif BoltType=="Type9":
						apex.display.clearAllGraphicsText()
						# this is the center location for the concentric face furhtest away
						#p1_3D=ConcentricFaceL[indx[-4]].getCentroid()
						p1_3D=ConcentricFaceL[indx[0]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[1]
						p3=CircFaceCtrPointL[0]
						p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						p3_3D=apex.construct.Point3D(p3[0],p3[1],p3[2])
						
						#
						# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						# highlight_stuff(p1_point,255,0,50)
						# text1=apex.display.displayText("p1",p1_3D)
						# p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						# text2=apex.display.displayText("p2",p2_3D)
						# highlight_stuff(p2_point,255,255,50)	
						# p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						# highlight_stuff(p3_point,0,255,50)
						# text3=apex.display.displayText("p3",p3_3D)
						#						
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl_up",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						#bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[2]]
						#text5=apex.display.displayText("bolt_stem_cyl_lo",tmp_faces[CylindricalFaceIndex[2]].getCentroid())
						#text6=apex.display.displayText("bolt_head_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						#
						bolt_diam=tmp_faces[CylindricalFaceIndex[0]].getEdges()[0].getLength()/3.14126
						
						bolt_head_diam=(4*CircularAreaL[0]/3.14126)**0.5
						bolt_head_type='hex'					
																				
					elif BoltType=="Type51":
						print("I am in bolt51")
						apex.display.clearAllGraphicsText()
						# this is the center location for the concentric face furthest away
						p1_3D=ConcentricFaceL[indx[-3]].getCentroid()
						#p1=pointsL[indx[0]]
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[1]
						
						p3=CircFaceCtrPointL[0]
						p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						p3_3D=apex.construct.Point3D(p3[0],p3[1],p3[2])
						
						#
						# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						# highlight_stuff(p1_point,255,0,50)
						# text1=apex.display.displayText("p1",p1_3D)
						# p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						# text2=apex.display.displayText("p2",p2_3D)
						# highlight_stuff(p2_point,255,255,50)	
						# p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						# highlight_stuff(p3_point,0,255,50)
						# text3=apex.display.displayText("p3",p3_3D)
						#						
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl_up",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						#bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[2]]
						#text5=apex.display.displayText("bolt_stem_cyl_lo",tmp_faces[CylindricalFaceIndex[2]].getCentroid())
						#text6=apex.display.displayText("bolt_head_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						#
						bolt_diam=tmp_faces[CylindricalFaceIndex[0]].getEdges()[0].getLength()/3.14126
						
						bolt_head_diam=(4*CircularAreaL[0]/3.14126)**0.5
						bolt_head_type='hex_flange'
					
					elif BoltType=="Type20":
						# this is the center location for the concentric face furhtest away
						p1_3D=ConcentricFaceL[indx[-5]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[0]
						p3_3D=ConcentricFaceL[indx[-1]].getCentroid()
						p3=[p3_3D.getX(),p3_3D.getY(),p3_3D.getZ()]
						# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						# highlight_stuff(p1_point,255,0,50)
						# p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						# highlight_stuff(p2_point,255,255,50)	
						# p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						# highlight_stuff(p3_point,0,255,50)	
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[1]]
						bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[2]]
						bolt_head_cyl=tmp_faces[CylindricalFaceIndex[0]]
						#
						bolt_diam=bolt_stem_cyl_lo.getEdges()[0].getLength()/3.14126
						bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='hex socket'
											
					elif BoltType=="Type21":
						# this is the center location for the concentric face furhtest away
						p1_3D=ConcentricFaceL[indx[-5]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[0]
						p3_3D=ConcentricFaceL[indx[-1]].getCentroid()
						p3=[p3_3D.getX(),p3_3D.getY(),p3_3D.getZ()]
						# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						# highlight_stuff(p1_point,255,0,50)
						# p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						# highlight_stuff(p2_point,255,255,50)	
						# p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						# highlight_stuff(p3_point,0,255,50)	
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[2]]
						bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[1]]
						bolt_head_cyl=tmp_faces[CylindricalFaceIndex[0]]
						#
						bolt_diam=bolt_stem_cyl_lo.getEdges()[0].getLength()/3.14126
						bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='hex socket'
						
					elif BoltType=="Type22":
						# this is the center location for the concentric face furhtest away
						p1_3D=ConcentricFaceL[indx[-5]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[0]
						p3_3D=ConcentricFaceL[indx[-1]].getCentroid()
						p3=[p3_3D.getX(),p3_3D.getY(),p3_3D.getZ()]
						# p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						# highlight_stuff(p1_point,255,0,50)
						# p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						# highlight_stuff(p2_point,255,255,50)	
						# p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						# highlight_stuff(p3_point,0,255,50)	
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[2]]
						bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[1]]
						bolt_head_cyl=tmp_faces[CylindricalFaceIndex[0]]
						#
						bolt_diam=bolt_stem_cyl_lo.getEdges()[0].getLength()/3.14126
						bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='hex socket'	
								
					elif BoltType=="Type30":
						apex.display.clearAllGraphicsText()
						# this is the center location for the concentric face furhtest away
						p1_3D=ConcentricFaceL[indx[-4]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						p2=CircFaceCtrPointL[0]
						p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						p3_3D=ConcentricFaceL[indx[-1]].getCentroid()
						p3=[p3_3D.getX(),p3_3D.getY(),p3_3D.getZ()]
						#
						p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						highlight_stuff(p1_point,255,0,50)
						text1=apex.display.displayText("p1",p1_3D)
						p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						text2=apex.display.displayText("p2",p2_3D)
						highlight_stuff(p2_point,255,255,50)	
						p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						highlight_stuff(p3_point,0,255,50)
						text3=apex.display.displayText("p3",p3_3D)
						#						
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						#bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl_up",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						bolt_stem_cyl_lo=tmp_faces[CylindricalFaceIndex[0]]
						text5=apex.display.displayText("bolt_stem_cyl_lo",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						bolt_head_cyl=tmp_faces[CylindricalFaceIndex[1]]
						text6=apex.display.displayText("bolt_head_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						#
						bolt_diam=CircFaceL[0].getEdges()[0].getLength()/3.14126
						bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='FH'

					elif BoltType=="Type83":
						apex.display.clearAllGraphicsText()
						# this is top of stem
						p1_3D=ConcentricFaceL[indx[-8]].getCentroid()
						p1=[p1_3D.getX(),p1_3D.getY(),p1_3D.getZ()]
						# This is end point
						p2=CircFaceCtrPointL[0]
						p2_3D=apex.construct.Point3D(p2[0],p2[1],p2[2])
						# this is head 
						p3_3D=ConcentricFaceL[indx[-1]].getCentroid()
						p3=[p3_3D.getX(),p3_3D.getY(),p3_3D.getZ()]
						#
						p1_point=apex.geometry.createPointXYZ(p1[0],p1[1],p1[2])
						highlight_stuff(p1_point,255,0,50)
						text1=apex.display.displayText("p1",p1_3D)
						p2_point=apex.geometry.createPointXYZ(p2[0],p2[1],p2[2])
						text2=apex.display.displayText("p2",p2_3D)
						highlight_stuff(p2_point,255,255,50)	
						p3_point=apex.geometry.createPointXYZ(p3[0],p3[1],p3[2])
						highlight_stuff(p3_point,0,255,50)
						text3=apex.display.displayText("p3",p3_3D)
						#						
						p3p2=getDelta(p3,p2)
						p3p1=getDelta(p3,p1)
						p2p1=getDelta(p2,p1)
						head_lgth=getDist(p3p1)
						bolt_lgth=getDist(p2p1)
						# calculate bolt diam based on cylindrical face
						#CylindricalFaceIndex
						#bolt_stem_cyl_up=tmp_faces[CylindricalFaceIndex[0]]
						#text4=apex.display.displayText("bolt_stem_cyl_up",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						bolt_stem_cyl=tmp_faces[CylindricalFaceIndex[1]]
						text5=apex.display.displayText("bolt_stem_cyl",tmp_faces[CylindricalFaceIndex[1]].getCentroid())
						
						
						bolt_head_cyl=tmp_faces[CylindricalFaceIndex[0]]
						text6=apex.display.displayText("bolt_head_cyl",tmp_faces[CylindricalFaceIndex[0]].getCentroid())
						#
						bolt_diam=bolt_stem_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_diam=bolt_head_cyl.getEdges()[0].getLength()/3.14126
						bolt_head_type='TX'
										
					else:
						print("What")
						
					
					print("%-25s%-8.2f" % ("Head heigt",head_lgth) )
					try:
						print("%-25s%-8.2f" % ("Head diam",bolt_head_diam) )
					except:
						pass
					print("%-25s%-8.2f" % ("Bolt length",bolt_lgth))
					#print("Chamfer_lgth: " + str(chamfer_lgth))
					print("%-25s%-8.2f" % ("Bolt diam",bolt_diam))
					#attrib_names=['dim','type','head_washer','nut','nut_washer']
					
					# Move curve to parent Solid part
					model_name=model_1.getName()
					N=len(model_name) +1
					parent_part_name=tmp_solid.getPath()[N:]
					print("part name: " + parent_part_name )
					
					
					part_ = model_1.setCurrentPart(pathName = parent_part_name )
					
					curve=createCurveFromCords(p1,p2)
					highlight_stuff(curve,255,255,0)
					
					print("####curve name: " + curve.getName() )

					#parent_part_name=tmp_solid.getPath()[N:]


					# move curve to bolt part
					#_target = apex.entityCollection()
					#_target.append(curve)
					
					#try:
					#	parent_part = model_1.getPart( pathName = parent_part_name )
					#	#curve.addUserAttribute(userAttributeName ="BoltPart", stringValue = parent_part_name[N:] )
					#	apex.reparent( parent = parent_part, target = _target )
					#except:
					#	pass
					

					#reparent orig bolt and set highlights
					SetAttributeOrigPath(tmp_solid)
					# remove the solids from the list
					visible_solids_nameL.remove(tmp_solid.getPathName())
					#moveObjectToTempPart(tmp_solid,"Temp_repository")
					highlight_stuff(tmp_solid,255,0,255)

					# identify washers and nuts
					
					#near_solids=get_solids_near_bolt(tmp_solid,0.5)
					near_solids=get_solids_near_bolt(tmp_solid,0.5,visible_solids_nameL)

							
					print("No of near solids:" + str(len(near_solids)))

					# unless identified, set washer head/nut to "no", might be overwritten below
					curve.addUserAttribute(userAttributeName ="washer_head", stringValue = "no" )
					curve.addUserAttribute(userAttributeName ="washer_nut", stringValue = "no" )
					curve.addUserAttribute(userAttributeName ="nut", stringValue = "no" )

					for near_solid in near_solids:
						# Identify washers and nuts...will consider hole and fastener info later
						if BoltType != "Type50":
							NutFlag=identify_nut(curve,near_solid,[p3,p2],bolt_diam,clear_vertex_flag)
						else:
							# nut is integrated in Type50...
							curve.addUserAttribute(userAttributeName ="nut", stringValue = ( ("%6.2f %6.2f %6.2f %6.2f")  % (nut_height,bolt_diam,nut_dia,getDist(p2pA)) ) )								
						WasherFlag=identify_washers(curve,near_solid,[p3,p2],clear_vertex_flag)
						
						if NutFlag:
							visible_solids_nameL.remove(near_solid.getPathName())
						if WasherFlag:
							visible_solids_nameL.remove(near_solid.getPathName())
							
					bolt_name='m' + str(round(bolt_diam) )	
					curve.addUserAttribute(userAttributeName ="dim", stringValue = bolt_name )
					curve.addUserAttribute(userAttributeName ="type", stringValue = bolt_head_type )

					
					if bolt_name not in BoltCountD.keys():
						BoltCountD[bolt_name]=1
					else:
						BoltCountD[bolt_name]+=1
						
						
					bolt_solids.append(tmp_solid)
					
					# Create 1d bolts
					# first, sort the edges,and faces to separate entity lists
					if fastenerType == "Flexible Link" or fastenerType == "Rigid Link" or fastenerType == "Bushing":		
						connect_obj_lists, connect_ctr_list = arrange_objects_to_connect2(curve,tmp_solid,visible_solids_nameL,searchTolerance)
						
						if len(connect_obj_lists) != 0:
							tmp_connector=create_1d_bolt(curve,"Flexible Link",connect_obj_lists,connect_ctr_list,bolt_diam,fastenerType,distributionType,matlName)
							#create_1d_bolt(curve,connector_type,objects_list,connect_ctr_list,dia=10, fastenerType="Flexible Link", distributionType="Rigid - RBE2")
						else:
							print("No connections created")
					
	print("============SUMMARY==================")
	print("Number of solids in model        : " + str(len(solids)))
	print("Number of visible solids searched: " + str(cnt) )
	#print("\nNumber and type of bolts found among visible solids")
	print("")
	print("%8s %8s" % ("Bolt Type"," Number "))
	print("%9s-%9s" % ("---------","---------"))
	for key in  BoltCountD.keys():
		print("%9s:%9d" % (key,BoltCountD[key]))
	print("")
	# move solid to temp-part, adding paths name to attribute	
	#tmp_repository_part=SetAttributeOrigPath(bolt_solids)
	
	return
		
def get_solids_near_bolt(bolt_solid,grow_box_rate=0.25,solid_names=[]):
	#grow_box_rate=0.25
	## Solids ##
	#model_1 = apex.currentModel()
	#dummy_part = model_1.createPart()
	#solids=getAllSolids()
	# get all solids in each zoom box
	zoombox=1
	if 1==1:
		z_solid_name=bolt_solid.getName()
		tmpBoxBodies = apex.entityCollection()
		z_box=bolt_solid.getAxisAlignedBoundingBox()
		
	bolt_full_name_path= bolt_solid.getPath() + "/" + bolt_solid.getName()
	IPhysCol=apex.IPhysicalCollection()	
	for name in solid_names:
		solid=apex.getSolid( pathName = name )
		#solid.clearHighlight()
		# exclude actual bolt solid...Change 20190924... need to check full path...change 20200108
		


		if name != bolt_full_name_path  :
			IPhysCol.append(solid.asEntity())
		
	proxy=apex.ProximitySearch()
	proxy.insertCollection(IPhysCol)
	
	
	P1, P2 = ScaleBoxPoints(z_box,grow_box_rate)
	proxy_solids=proxy.findObjectsAABoundingBox(P1, P2)
	#proxy_solids=proxy.findObjectsAABoundingBox(z_box.getPoint1(), z_box.getPoint2())
	#
	# Handle solids that are entirely inside the zoom_box
	print("Len of proxy solids: " + str(len(proxy_solids)))
	#if len(proxy_solids) > 0 :
		#Add attribute but do not split
		#print("I am in here")
		
		#for proxy_solid in proxy_solids:
			#highlight_stuff(proxy_solid,255,255,0)
			#proxy_solid.addUserAttribute(userAttributeName = "box_status", stringValue = "inside" )
			
	return proxy_solids		
	
def old_get_faces_near_bolt(bolt_solid,grow_box_rate=0.25,solid_names=[]):
	#
	## Faces ##
	
	faces=apex.entityCollection()
	# get all faces that are not belonging to bolt, nut or washer
	# These objects have user attribute "ParentPart"
	#solids=getAllSolids()
	#print("No of solids: " + str(len(solids)))
	for name in solid_names:
		solid=apex.getSolid( pathName = name )
		print("solid: " + solid.getName() )
		#tmp_attributes=solid.getUserAttributes(['ParentPart'])
		#if len(tmp_attributes) == 0:
		#	connector_solid=False
		#else:
		#	connector_solid=True
		#
		#if  connector_solid == False:
		faces += solid.getFaces()
		
	# for tmp_face in faces:
		# tmp_pnt=tmp_face.getCentroid()
		# tmpnt_3D=apex.construct.Point3D(tmp_pnt.getX(),tmp_pnt.getY(),tmp_pnt.getZ())
		# tmp_label="face_" + str(tmp_face.getId())
		# print(tmp_label)
		# tmp_txt=apex.display.displayText(tmp_label,tmpnt_3D)	
		
	
	print("No of solids: " + str(len(solid_names) ) )

	
	# get all solids in each zoom box
	zoombox=1
	if 1==1:
		z_solid_name=bolt_solid.getName()
		tmpBoxBodies = apex.entityCollection()
		
		z_box=bolt_solid.getAxisAlignedBoundingBox()
		
		
	IPhysCol=apex.IPhysicalCollection()	
	for face in faces:
		#solid.clearHighlight()
		IPhysCol.append(face.asEntity())
		
	proxy=apex.ProximitySearch()
	proxy.insertCollection(IPhysCol)
	
	
	P1, P2 = ScaleBoxPoints(z_box,grow_box_rate)
	proxy_faces=proxy.findObjectsAABoundingBox(P1, P2)
	#
	# Handle solids that are entirely inside the zoom_box
	#print("Len of proxy faces: " + str(len(proxy_faces)))
		
	return proxy_faces


def get_faces_near_bolt(bolt_solid,distance_tolerance=0.25,solid_names=[]):
	#
	## Faces ##
	
	faces=apex.entityCollection()
	# get all faces that are not belonging to bolt, nut or washer
	# These objects have user attribute "ParentPart"
	#solids=getAllSolids()
	#print("No of solids: " + str(len(solids)))
	for name in solid_names:
		solid=apex.getSolid( pathName = name )
		print("solid: " + solid.getName() )
		#tmp_attributes=solid.getUserAttributes(['ParentPart'])
		#if len(tmp_attributes) == 0:
		#	connector_solid=False
		#else:
		#	connector_solid=True
		#
		#if  connector_solid == False:
		faces += solid.getFaces()
		
	# for tmp_face in faces:
		# tmp_pnt=tmp_face.getCentroid()
		# tmpnt_3D=apex.construct.Point3D(tmp_pnt.getX(),tmp_pnt.getY(),tmp_pnt.getZ())
		# tmp_label="face_" + str(tmp_face.getId())
		# print(tmp_label)
		# tmp_txt=apex.display.displayText(tmp_label,tmpnt_3D)	
		
	
	print("***No of solids: " + str(len(solid_names) ) )
	if len(solid_names) > 0:
	
		distance_ = apex.measureDistance( source =bolt_solid,  target = faces )
		
		indx=sorted(range(len(distance_)), key=lambda k: distance_[k])
		
		proxy_faces=apex.EntityCollection()
		n=0
		N=len(distance_)
		while distance_[indx[n]] < distance_tolerance and n < N:
		
			# Now, check so that all edgex of the face also falls within "distance_tolerance" from the bolt
			tmp_face2_=faces[indx[n]]
			print("Face within distance found")
			if isCylindrical2(tmp_face2_,bolt_solid,distance_tolerance):
				proxy_faces.append(tmp_face2_)
				print("found cylindrical face: " + str(tmp_face2_.getId() ) )
			else:
				print("Face: " + str(tmp_face2_.getId() ) + " is not cylindrical" )
			
			#tmp_edges2_=tmp_face2_.getEdges()
			#distance2_ = apex.measureDistance( source =bolt_solid,  target = tmp_edges2_ )
			#if max(distance2_) < distance_tolerance:
			#	proxy_faces.append(tmp_face2_)
			
			n+=1
	
	else:
		proxy_faces=[]
			
	return proxy_faces

def get_edges_near_bolt(bolt_solid,distance_tolerance=0.25):
	#
	## Faces ##
	
	edges=apex.entityCollection()
	# get all surfaces

	surfs=getAllSurfs()
	#print("No of surfs: " + str(len(surfs)))
	for surf in surfs:
		edges += surf.getEdges()
	
	
	print("No of edges: " + str(len(edges) ) )
	
	if len(edges) > 0:
	
		distance_ = apex.measureDistance( source =bolt_solid,  target = edges )
		
		indx=sorted(range(len(distance_)), key=lambda k: distance_[k])
		
		proxy_edges=apex.EntityCollection()
		n=0
		N=len(distance_)
		while distance_[indx[n]] < distance_tolerance and n < N:
			# Now, check so that all edgex of the face also falls within "distance_tolerance" from the bolt
			tmp_edge2_=edges[indx[n]]
			#tmp_edges2_=tmp_face2_.getEdges()
			#distance2_ = apex.measureDistance( source =bolt_solid,  target = tmp_edges2_ )
			#if max(distance2_) < distance_tolerance:
			proxy_edges.append(tmp_edge2_)
			
			n+=1
			
	else:
		proxy_edges=[]
			
	return proxy_edges
	
def old_get_edges_near_bolt(bolt_solid,grow_box_rate=0.25):
	#
	## Faces ##
	
	edges=apex.entityCollection()
	# get all surfaces

	surfs=getAllSurfs()
	#print("No of surfs: " + str(len(surfs)))
	for surf in surfs:
		edges += surf.getEdges()
	
	# get all edges in each zoom box
	zoombox=1
	if 1==1:
		z_solid_name=bolt_solid.getName()
		tmpBoxBodies = apex.entityCollection()
		z_box=bolt_solid.getAxisAlignedBoundingBox()
			
	IPhysCol=apex.IPhysicalCollection()	
	for edge in edges:
		IPhysCol.append(edge.asEntity())
		
	proxy=apex.ProximitySearch()
	proxy.insertCollection(IPhysCol)
	
	
	P1, P2 = ScaleBoxPoints(z_box,grow_box_rate)
	proxy_edges=proxy.findObjectsAABoundingBox(P1, P2)
	#
	# Handle edges that are entirely inside the zoom_box
	#print("Len of proxy edges: " + str(len(proxy_edges)))
		
	return proxy_edges	

def create_1d_bolt(curve,connector_type,objects_list,connect_ctr_list,dia=10, fastenerType="Flexible Link", distributionType="Rigid - RBE2", materialName="BoltMat"):
	#objects list is a list of ApexEntityCollections containing surfs or curves for each connected part /currently 2 is supported)
	no_of_sheets=len(objects_list)
	GoFlag=True
	if no_of_sheets == 0:
		print("**Warning**, No connections found")
		GoFlag=False
	elif no_of_sheets == 1:
		print("**Warning**, Just one layer found, at least two is required")
		GoFlag=False
	elif no_of_sheets > 2:
		print(str(no_of_sheets) + " found, Multi-layer connection is created")
		GoFlag="Multi"
	else:
		print(str(no_of_sheets) + " found, Two layer connection is created")
		GoFlag="Single"
	
	
	#check so that all entities belong to the same assy
	#Assy
	if GoFlag:
		AssyList=[]
		for entities in objects_list:
			entity_part=""
			if len(entities) == 0:
				print("**Error**: no geoemtries to connect")
				GoFlag=False
			else:
				path1=entities[0].getBody().getPath()
				if path1.split("/")[1] not in AssyList:
					AssyList.append(path1.split("/")[1])
				#print("path1: " + path1)
		
		if len(AssyList) > 1: 
			print("**Error**: Conected objects are not in the same assy")
			GoFlag=False
			
		# get washer / nut info for imprints
		if 1==0:
			tmp_bolts=apex.entityCollection()
			tmp_bolts.append(curve)
			tmp_BoltD=ReadBoltCurves(tmp_bolts)
			#BoltD[curve.getPath()]=[p1,p2,dim,type,nut,washer_head,washer_nut]
			[p1,p2,dim,type,nut,washer_head,washer_nut]=tmp_BoltD[curve.getPath()]
			
			tip_edges=apex.entityCollection()
			head_edges=apex.entityCollection()
			if washer_nut != "no":
				washer_nut_dia=washer_nut[2]
				surf1=CreateCircleAtPt(p1,p2,washer_nut_dia)
				tip_edges=surf1.getEdges()
			elif nut != "no":
				nut_dia=nut[2]
				surf1=CreateCircleAtPt(p1,p2,nut_dia)
				tip_edges=surf1.getEdges()
			else:
				# no nut
				pass
			if washer_head != False:
				washer_head_dia=washer_head[2]
				surf1=CreateCircleAtPt(p1,p2,washer_head_dia)
				head_edges=surf1.getEdges()
			else:
				[bolt_dia, bolt_height, bolt_head_dia]=get_BoltDimensions(type,name)
				surf1=CreateCircleAtPt(p1,p2,bolt_head_dia)
				head_edges=surf1.getEdges()

			# for curves, find associated body and its edges and split
			splitBodies=apex.entityCollection()
			if len(tip_edges) > 0:
				#split each curve with washer, append new faces 
				for edge in objects_list[0]:
					if edge.getBody() not in splitBodies:
						splitBodies += edge.getBody().getFaces()
			_target = splitBodies
			_splitter = head_edges	
			result = apex.geometry.splitSurfacesWithCurves(target = _target, splitter = _splitter, useOnlyNearbySplitters = False )

		if GoFlag:

			if connector_type=="Flexible Link" or connector_type=="Rigid Link" or connector_type=="Bushing":
				if GoFlag=="Multi" or GoFlag=="Single":
					create_Multi_Sheet_FlexibleLink(objects_list,connect_ctr_list,dia,materialName,fastenerType,distributionType)
				else:
					pass
			else:
				print("Only Attributed Curve Generated")
				pass


def v_lgth(cord2):
	lgth = ((cord2[0]) ** 2 + (cord2[1]) ** 2 + (cord2[2]) ** 2) ** 0.5
	return lgth


def scalar(u, v):
	u1 = u[0]
	u2 = u[1]
	u3 = u[2]
	v1 = v[0]
	v2 = v[1]
	v3 = v[2]
	out_scalar = (u1 * v1 + u2 * v2 + u3 * v3) / v_lgth(v)
	return out_scalar


def GetDistProj(cord1, cord2, vA=[0, 0, 0]):
	if vA == [0, 0, 0]:
		# This function will not give the desired result when the RBE3 indep grids are  not in the same plane as the center grid
		dist = ((cord2[0] - cord1[0]) ** 2 + (cord2[1] - cord1[1]) ** 2 + (cord2[2] - cord1[2]) ** 2) ** 0.5
	# print "A: Min dist is: " + str(dist)
	else:
		# need direction of the hole
		# v1 is the projection of the distance to the hole axis
		# v2 is the vector perpendicular to hole axis
		# v0 is the vector between hole center and rbe center
		v0 = [cord2[0] - cord1[0], cord2[1] - cord1[1], cord2[2] - cord1[2]]
		s1 = scalar(v0, vA)
		# print "==== vA, v1==="
		# print "vA (bar)   :" + str(vA)
		# print "v1 (dot): " + str(s1)
		
		#dist = (v_lgth(v0) ** 2 - s1 ** 2) ** 0.5
		dist=s1
		# 
	# print "B: Min dist is: " + str(dist)
	return dist
	

def arrange_objects_to_connect2(curve,tmp_solid,solid_names,searchTolerance=0.25):
	# this function arranges all edes and faces in lists so that each list is one attachment region
	# omits circular faces 
	# only considers visible geometry
	# omit bolt parts
	# returns objects based on distance from bolt head
	# returns list of locations on bolt axis for end points
	#
	#assum axis_pt1 is always head and axis_pt2 is tip

	ActualFastenerLength=True
	
	tmp_curves=apex.entityCollection()
	tmp_curves.append(curve)
	BoltDict=ReadBoltCurves(tmp_curves)
	if len(BoltDict.keys()) > 1:
		print("what")
	else:
		tmp=list(BoltDict.keys())
		item=tmp[0]
	bolt_data=BoltDict[item]
	#print(bolt_data)
	p1=bolt_data[0]
	p2=bolt_data[1]
	Nut=bolt_data[4]
	WasherNut=bolt_data[6]
	bolt_axis_vect=getDelta(p2,p1)

	#grow_factors=[ .09, 0.10, 0.20, 0.30, 0.55 ]
	grow_factors=[ 1.0 ]
	bolt_length=getDist(p1,p2)
	for grow_fact in grow_factors:
		
		near_faces=get_faces_near_bolt(tmp_solid,searchTolerance*grow_fact,solid_names)
		near_edges=get_edges_near_bolt(tmp_solid,searchTolerance*grow_fact)
		
		print("No of near faces:" + str(len(near_faces)))
		print("No of near edges:" + str(len(near_edges)))
		
		
		BodyD={}
		bolt_length=getDist(bolt_axis_vect)	
		
		# first, arrange all objects based on parent body
		
		# only consider faces and curves between bolt head and nut
		try:	
			tmp_attributes=curve.getUserAttributes(['nut'])
			print(tmp_attributes)
			NutStrL=tmp_attributes[0].getStringValue().split()
			NutDistFromTip=float(NutStrL[3])		
		except:
			NutDistFromTip=0.0
		
		NutDistFromBolt=bolt_length-NutDistFromTip	
			
		
		
		for face in near_faces:
			GoFlag=True
			try:
				tmp_body=face.getBody()
			except:
				print("No body found for " + str(face.getId() ) )
				GoFlag=False
			if GoFlag:
			
			
			
				#tmp_path=tmp_body.getPath()
				if len(tmp_body.getUserAttributes(['ParentPart']) ) > 0 :
				
				#if tmp_path.find("Temp_repository") > 0:
					BoltFlag=True
				else:
					BoltFlag=False
				#
				if BoltFlag == False:
					if not isCircular(face) and tmp_body.getVisibility() == 1:
						tmp_loc=face.getCentroid()
						# get edge pos on bolt
						dist_on_bolt=GetDistProj([tmp_loc.getX(),tmp_loc.getY(),tmp_loc.getZ()], p1, bolt_axis_vect)
						perc_on_bolt=100*round(dist_on_bolt/bolt_length,1)
						print("face_dist_on_bolt: " + str(dist_on_bolt) )
						print("face_perc_on_bolt: " + str(perc_on_bolt) )
						
						if dist_on_bolt <= NutDistFromBolt and dist_on_bolt >= 0:
							# form unique name with body name and, curve percentage on  axis (10,20,30...)
							tmp_body_name=tmp_body.getPathName() + '_' + str(perc_on_bolt)
						
							if tmp_body_name not in BodyD.keys():
								BodyD[tmp_body_name]=apex.entityCollection()
							BodyD[tmp_body_name].append(face)
						else:
							print("skip face")
		face_keys=BodyD.keys()
		#
		
		for edge in near_edges:
			#
			tmp_body=edge.getBody()
			tmp_path=tmp_body.getPath()
			# try:
				# BoltFlag=tmp_body.asSolid().isHighlighted()		
			# except:
				# BoltFlag=False
			if len( tmp_body.getUserAttributes(['ParentPart']) ) > 0 :	
			#if tmp_path.find("Temp_repository") > 0:
				BoltFlag=True
				#print("Bolt")
			else:
				BoltFlag=False
				#print("genuine")
					
			if BoltFlag == False:
				#
				if tmp_body.getVisibility() == 1:
					tmp_loc=edge.getLocation()
					# get edge pos on bolt
					dist_on_bolt=GetDistProj([tmp_loc.getX(),tmp_loc.getY(),tmp_loc.getZ()], p1, bolt_axis_vect)
					perc_on_bolt=100*round(dist_on_bolt/bolt_length,1)
					
					#print("edge_dist_on_bolt: " + str(dist_on_bolt) )
					#print("edge_perc_on_bolt: " + str(perc_on_bolt) )
					
				
					# form unique name with body name and, curve percentage on  axis (10,20,30...)
					tmp_body_name=tmp_body.getPathName() + '_' + str(perc_on_bolt)
					# Rule: If there is already a face and and edge is found for the same body and the same perc location, skipe edge
					#if tmp_body_name not in face_keys:
					if dist_on_bolt <= NutDistFromBolt and dist_on_bolt >= 0:
						if 1 == 1:
							if tmp_body_name not in BodyD.keys():
								BodyD[tmp_body_name]=apex.entityCollection()
							BodyD[tmp_body_name].append(edge)
							#print("appending edge " + str(len(BodyD[tmp_body_name])))
						else:
							print("edge in face keys")
							print(tmp_body.getPathName())
							print(tmp_body_name)
					else:
						print("skip edge")
		
		if len(BodyD.keys()) >= 2:
			print("GrowFactor: " + str(grow_fact) + "...found " + str(len(BodyD.keys())) + " layers ... continue")
			CreateFastenerFlag=True
			break
		elif grow_fact==grow_factors[-1]:
			print("***Warning*** Maximum GrowFactor reached: " + str(grow_fact) + "\n there are not enough visible faces or edges found to create fastener")
			CreateFastenerFlag=False
		else:
			print("GrowFactor: " + str(grow_fact) + "...found " + str(len(BodyD.keys())) + " layers ... keep searching")
			CreateFastenerFlag=True
			
	#for key in BodyD.keys():
	#	print(key)
	#	print(len(BodyD[key]))
	#	print("===")
	# ===out side of grow_factor loop			
					
	if 	CreateFastenerFlag == True:			
		# Next, add collection  of bodies to a List
		# Extract position of each object and add to separate list
		BodyL=[]
		BodyLocL=[]	
		for body_name in BodyD.keys():
			BodyL.append(BodyD[body_name])
			#first body:
			tmp_bdy=BodyD[body_name][0]
			try:
				#face
				tmp_loc=tmp_bdy.getCentroid()
				#print("found face")
			except:
				#edge
				tmp_loc=tmp_bdy.getLocation()
				#print("found edge")
			#appending to BodyLocL
			BodyLocL.append(tmp_loc)
			
			#print("body: " + body_name )
			#for item in BodyD[body_name]:
			#	print("    " + str(item.getId()))
				
		# need to sort attachment regions based on distance from bolt head (pta)
		BdyDistOnVectL=[]
		for bdy_loc in BodyLocL:
			bdy_dist_on_vect=GetDistProj([bdy_loc.getX(),bdy_loc.getY(),bdy_loc.getZ()], p1, bolt_axis_vect)
			BdyDistOnVectL.append(bdy_dist_on_vect)
		
		indx=sorted(range(len(BdyDistOnVectL)), key=lambda k: BdyDistOnVectL[k])
		
		#print("indx")
		#print(indx)
		
		#print("body_dist on vect")
		print(BdyDistOnVectL)
		sortedBodyL=[]
		sortedCtrPointL=[]
		for i in indx:
			#print("i=" + str(i) )
			sortedBodyL.append(BodyL[i])
			scale_fact=BdyDistOnVectL[i]/getDist(bolt_axis_vect)
			#print("scale_fact")
			#print(scale_fact)
			tmp_ctr_loc=[ p1[0] - scale_fact*bolt_axis_vect[0] ,p1[1] - scale_fact*bolt_axis_vect[1] ,p1[2] - scale_fact*bolt_axis_vect[2] ] 
			sortedCtrPointL.append(tmp_ctr_loc)
			
			#p1_point=apex.geometry.createPointXYZ(tmp_ctr_loc[0],tmp_ctr_loc[1],tmp_ctr_loc[2])
			#highlight_stuff(p1_point,255,0,50)
			
		#adjust sortedCtrPointL (first and last) if true bolt length is requested
		if 	ActualFastenerLength==True:	
			#bolt start is where stem meets bolt head
			sortedCtrPointL[0]=p1
			#bolt end is top of the nu
			sortedCtrPointL[0]=p1
			
			try:
				tmp=len(Nut)
				NutFlag=True
			except(TypeError):
				NutFlag=False
				
			try:
				tmp=len(WasherNut)
				WasherNutFlag=True
			except(TypeError):
				WasherNutFlag=False	
			
			
			if NutFlag:
				nut_top_location=float(Nut[3])
				nut_top_scale_fact=1*nut_top_location/getDist(p1,p2)				
				nut_start=addVectors([item*nut_top_scale_fact for item in bolt_axis_vect],p2)
				sortedCtrPointL[-1]=nut_start
			elif WasherNutFlag:
				washer_nut_top_location=float(WasherNut[3])
				washer_nut_bot_scale_fact=1*(washer_nut_top_location-washer_height)/getDist(p1,p2)
				washer_nut_end=addVectors([item*washer_nut_bot_scale_fact for item in bolt_axis_vect],p2)
				sortedCtrPointL[-1]=washer_nut_end
			else:
				print("There is no nut or nut washer, using p2 as bolt location")
				sortedCtrPointL[-1]=p2
	else:
		sortedBodyL=[]
		sortedCtrPointL=[]
	

	# Merge the layers where faces belong to same body
	
	n=0
	N=len(sortedBodyL)
	mergedSortedBodyL=[]
	mergedSortedCtrPointL=[]
	
	print("No of entries in orig list:" + str(N) )
	for apapap in sortedBodyL:
		print("*****")
		for item in apapap:
			print(str(item.getId()))
	
	while n < N-1:
		#check if subsequent faces belong to same body
		body_name=sortedBodyL[n][0].getBody().getName()
		tmp_entity_collection=sortedBodyL[n]
		mergedSortedCtrPointL.append(sortedCtrPointL[n])
		
		
		goFlag=True
		while sortedBodyL[n+1][0].getBody().getName() == body_name:
			print(sortedBodyL[n+1][0].getBody().getName())
			#_attachRegion1.extend(sortedBodyL[n+1])
			print("Length: sortedBodyL[n]" + str(len(sortedBodyL[n])))
			print("Length: sortedBodyL[n+1]" + str(len(sortedBodyL[n+1])))
			tmp_entity_collection.extend(sortedBodyL[n+1])
			#for item in sortedBodyL[n+1]:
			#	mergedSortedBodyL[n].append(item)
			n+=1
			print("Inner loop n: " + str(n) )
			if n==N-1:
				break			
		mergedSortedBodyL.append(tmp_entity_collection)
		print("###Body: " + body_name + " has " + str(len(tmp_entity_collection))   + " entities" )
		n+=1
		print("n: " + str(n) )

	if n==N-1 :
		print("I am in here")
		# capture the case where the last layer is unique (and is not merged in the loop above)
		mergedSortedBodyL.append(sortedBodyL[n])
		mergedSortedCtrPointL.append(sortedCtrPointL[n])


	print("Number of layers: " + str(len(mergedSortedBodyL)))
	print("Number of points: " + str(len(mergedSortedCtrPointL)))
	
	
	
	return mergedSortedBodyL, mergedSortedCtrPointL

def calcDistance(loc1, loc2):
	#loc1 = n1.asNode().getLocation()
	#loc2 = n2.asNode().getLocation()
	dx = loc2.getX() - loc1.getX()
	dy = loc2.getY() - loc1.getY()
	dz = loc2.getZ() - loc1.getZ()
	
	dist =  ( dx**2 + dy**2 + dz**2 ) ** 0.5
	
	#if dist > 0.0: dist = math.sqrt(dist)
	
	return dist

def calcBushingStiffness(dia, length, matl):
	# this calculation is per Nastran QRG entry for PFAST
	E  = matl.getElasticModulus()
	NU = matl.getPoissonRatio()
	G = E/2.0/(1.0+NU)
	
	if length <= 0.0: length = 1.0
	
	PI = 3.14159
	
	A = PI*dia**2/4.0
	I = PI*dia**4/64.0
	J = 2*I
	
	AS = 0.75*A
	
	s1 = E*A/length
	s2 = G*AS/length
	s3 = s2
	s4 = G*J/length
	s5 = E*I/length + G*AS*length/3.0
	s6 = s5
	
	stiff = [s1, s2, s3, s4, s5, s6]
	
	return stiff

def bushingProps(s1, s2, s3, s4, s5, s6):
	#import apex
	connProps = apex.attribute.createConnectorProperties(
          stiffness               = 0.0,
          damping                 = 0.0,
          translationalStiffnessX = s1,
          translationalStiffnessY = s2,
          translationalStiffnessZ = s3,
          rotationalStiffnessX    = s4,
          rotationalStiffnessY    = s5,
          rotationalStiffnessZ    = s6,
          translationalDampingX   = 0.0,
          translationalDampingY   = 0.0,
          translationalDampingZ   = 0.0,
          rotationalDampingX      = 0.0,
          rotationalDampingY      = 0.0,
          rotationalDampingZ      = 0.0,
          diameter                = 0.0,
          linkMaterial            = None)
	return connProps

def flexibleLinkProps(dia, matl):
	connProps = apex.attribute.createConnectorProperties(
          stiffness               = 0.0,
          damping                 = 0.0,
          translationalStiffnessX = 0.0,
          translationalStiffnessY = 0.0,
          translationalStiffnessZ = 0.0,
          rotationalStiffnessX    = 0.0,
          rotationalStiffnessY    = 0.0,
          rotationalStiffnessZ    = 0.0,
          translationalDampingX   = 0.0,
          translationalDampingY   = 0.0,
          translationalDampingZ   = 0.0,
          rotationalDampingX      = 0.0,
          rotationalDampingY      = 0.0,
          rotationalDampingZ      = 0.0,
          diameter                = dia,
          linkMaterial            = matl)
	return connProps

def rigidLinkProps():
	connProps = apex.attribute.createConnectorProperties(
          stiffness               = 0.0,
          damping                 = 0.0,
          translationalStiffnessX = 0.0,
          translationalStiffnessY = 0.0,
          translationalStiffnessZ = 0.0,
          rotationalStiffnessX    = 0.0,
          rotationalStiffnessY    = 0.0,
          rotationalStiffnessZ    = 0.0,
          translationalDampingX   = 0.0,
          translationalDampingY   = 0.0,
          translationalDampingZ   = 0.0,
          rotationalDampingX      = 0.0,
          rotationalDampingY      = 0.0,
          rotationalDampingZ      = 0.0,
          diameter                = 0.0,
          linkMaterial            = None)
	return connProps

			
def create_Multi_Sheet_FlexibleLink(objects_list,connect_ctr_list,dia=10, mat_name="Bolt_Mat",fastenerType="Flexible Link",distributionType="Rigid - RBE2"):

	try:
		_material = apex.catalog.getMaterial(name = mat_name)
	except:
		# create Bolt Material
		_material = apex.catalog.createMaterial( name = "Material 1", description = "", color = [ 64, 254, 250 ] )
		_material.update(name = "Bolt_Mat")
		_material.update(elasticModulus = 205000.)
		_material.update(poissonRatio = 0.29)
		_material.update(density = 0.00000785)





	if distributionType=="Rigid - RBE2":
		dist_type=apex.attribute.DistributionType.Rigid
	else:
		dist_type=apex.attribute.DistributionType.Compliant

	
	if fastenerType=="Flexible Link":
		_connectorType=apex.attribute.ConnectorType.FlexibleLink
		connectorProps=flexibleLinkProps(dia, _material)
		connector_name="M" + str(round(dia)) + "_Flexible_1D_Bolt"
		
	elif fastenerType=="Rigid Link":
		_connectorType=apex.attribute.ConnectorType.RigidLink
		connectorProps=rigidLinkProps()
		connector_name="M" + str(round(dia)) + "_Rigid_1D_Bolt"
		
	elif fastenerType=="Bushing":
		#Bushing
		_connectorType = apex.attribute.ConnectorType.Bushing
		# connector props need to be calculated for each individual cbush as lengt is differnt
		connector_name="M" + str(round(dia)) + "_Bushing_1D_Bolt"
	
	else:
		print("Wrong Connector Type")
	
	
	#get part from each object
	model_1 = apex.currentModel()
	PartNameL=[]
	
	model_name=model_1.getName()
	N=len(model_name) +1
	#parent_part_name=tmp_solid.getPath()[N:]
	
	
	for obj in objects_list:
		#part=obj[0].getBody().getParent()
		#print(part.getPathName())
		#PartNameL.append( part.getPathName()[N:] )
		
		bdy=obj[0].getBody()
		print(bdy.getPath())
		PartNameL.append( bdy.getPath()[N:] )
		
		# it seems as if part returned is not righ, instead,  get PathName and chop last part of string
		#PartNameL.append( os.path.dirname(part.getPathName())[N:] )
		
	try:
		_material = apex.catalog.getMaterial(name = mat_name)
	except:
		# create Bolt Material
		_material = apex.catalog.createMaterial( name = "Material 1", description = "", color = [ 64, 254, 250 ] )
		_material.update(name = "Bolt_Mat")
		_material.update(elasticModulus = 205000.)
		_material.update(poissonRatio = 0.29)
		_material.update(density = 0.00000785)

	
	# manage n layers
	# Layer 1 to n-1 is managed in similar fashion


	MidNodeL=apex.entityCollection()
	if len(objects_list) > 2:
		n=0
		N=len(objects_list) -1
		print("There are " + str(N) + " layers in list")
	
		# first connector 
		## end 1 ###
		pt1=connect_ctr_list[n]
		# Create Point at bolt head for first connector
		_endPoint1 = apex.construct.createLocationByCoordinates(x =pt1[0], y = pt1[1], z = pt1[2])
		_attachRegion1=objects_list[n]
		
		### end 2 ### a node in same part as layer need to be created
		pt2=connect_ctr_list[n+1]
		print( PartNameL[n+1] )
		#part_=model_1.getPart( pathName = PartNameL[n+1] )
		part_ = model_1.setCurrentPart(pathName = PartNameL[n+1] )
		pt2_3D = apex.geometry.createPointXYZ( x = pt2[0], y = pt2[1], z = pt2[2] ).asPoint()
		entities_ = apex.entityCollection()
		entities_.append(pt2_3D)
		#apex.reparent( parent = part_, target = entities_ )
		_target = pt2_3D.getVertices()[0]
		node_ = apex.mesh.createNodeByPickLocation( target = _target, location = Point3D( pt2[0],pt2[1], pt2[2] ) )
		_endPoint2 = apex.construct.createLocationByEntity(entity = node_)
		
		if fastenerType=="Bushing":
			fastenerLength = calcDistance(_endPoint1, _endPoint2)
			bushStiff = calcBushingStiffness(dia, fastenerLength, _material)
			connectorProps = bushingProps(bushStiff[0], bushStiff[1], bushStiff[2], bushStiff[3], bushStiff[4], bushStiff[5])
		
		
		connector_1 = apex.attribute.createConnector(
			name = connector_name,
			connectorType = _connectorType,
			connectorProperties = connectorProps,
			applicationMethod1 = apex.attribute.ApplicationMethod.Remote,
			applicationMethod2 = apex.attribute.ApplicationMethod.Remote,
			end1InterfacePoint = _endPoint1, 
			end2InterfacePoint = _endPoint2, 
			end1AttachmentRegion = _attachRegion1, 
			#end1DistributionMethod = apex.attribute.DistributionType.Compliant,
			end1DistributionMethod = dist_type,
			end2DistributionMethod = dist_type,
			description = ""   #defaults to " "
		)
		
		#add node 2 to list to be reused. 
		MidNodeL.append(node_) # dummy node for index 0
		MidNodeL.append(node_) # This node will be used
		
		n+=1
	
	
	
	
		while n < N-1:
		
			## end 1 ###
			pt1=connect_ctr_list[n]
			# Create Point at bolt head for first connector
			#_endPoint1 = apex.construct.createLocationByCoordinates(x =pt1[0], y = pt1[1], z = pt1[2])
			_endPoint1 = apex.construct.createLocationByEntity(entity = MidNodeL[n] )
			_attachRegion1 = objects_list[n]
					
			### end 2 ### a node in same part as layer need to be created
			pt2=connect_ctr_list[n+1]
			print( PartNameL[n+1] )
			#part_=model_1.getPart( pathName = PartNameL[n+1] )
			part_ = model_1.setCurrentPart(pathName = PartNameL[n+1] )
			
			pt2_3D = apex.geometry.createPointXYZ( x = pt2[0], y = pt2[1], z = pt2[2] ).asPoint()
			entities_ = apex.entityCollection()
			entities_.append(pt2_3D)
			
			#apex.reparent( parent = part_, target = entities_ )
			
			_target = pt2_3D.getVertices()[0]
			node_ = apex.mesh.createNodeByPickLocation( target = _target, location = Point3D( pt2[0],pt2[1], pt2[2] ) )
			_endPoint2 = apex.construct.createLocationByEntity(entity = node_)

			
			if fastenerType=="Bushing":
				fastenerLength = calcDistance(_endPoint1, _endPoint2)
				bushStiff = calcBushingStiffness(dia, fastenerLength, _material)
				connectorProps = bushingProps(bushStiff[0], bushStiff[1], bushStiff[2], bushStiff[3], bushStiff[4], bushStiff[5])
			
			
			connector_1 = apex.attribute.createConnector(
				name = connector_name,
				connectorType = _connectorType,
				connectorProperties = connectorProps,
				applicationMethod1 = apex.attribute.ApplicationMethod.Remote,
				applicationMethod2 = apex.attribute.ApplicationMethod.Remote,
				end1InterfacePoint = _endPoint1, 
				end2InterfacePoint = _endPoint2, 
				end1AttachmentRegion = _attachRegion1, 
				end1DistributionMethod = dist_type,
				end2DistributionMethod = dist_type,
				description = ""   #defaults to " "
			)
			MidNodeL.append(node_)
			n+=1
			
		## Final connector n=N-1
		
		_attachRegion1 = objects_list[N-1]
		#hooking up to last node used
		_endPoint1 = apex.construct.createLocationByEntity(entity = node_)
		#
		_attachRegion2 = objects_list[N]
		pt2=connect_ctr_list[N]
		_endPoint2 = apex.construct.createLocationByCoordinates(x = pt2[0], y = pt2[1], z = pt2[2])
		
		
		if fastenerType=="Bushing":
			fastenerLength = calcDistance(_endPoint1, _endPoint2)
			bushStiff = calcBushingStiffness(dia, fastenerLength, _material)
			connectorProps = bushingProps(bushStiff[0], bushStiff[1], bushStiff[2], bushStiff[3], bushStiff[4], bushStiff[5])
		
		
		connector_2 = apex.attribute.createConnector(
			name = connector_name,
			connectorType = _connectorType,
			connectorProperties = connectorProps,
			applicationMethod1 = apex.attribute.ApplicationMethod.Remote,
			applicationMethod2 = apex.attribute.ApplicationMethod.Remote,
			end1InterfacePoint = _endPoint1, 
			end2InterfacePoint = _endPoint2, 
			end1AttachmentRegion = _attachRegion1,  
			end1DistributionMethod = dist_type, 
			end2AttachmentRegion = _attachRegion2, 
			end2DistributionMethod = dist_type,
			description = ""   #defaults to " "
		)			
			
					
	elif len(objects_list) == 2 :
		## end 1 ###
		pt1=connect_ctr_list[0]
		# Create Point at bolt head for first connector
		_endPoint1 = apex.construct.createLocationByCoordinates(x =pt1[0], y = pt1[1], z = pt1[2])
		_attachRegion1 = objects_list[0]
		
		### end 2 ### a node in same part as layer need to be created
		pt2=connect_ctr_list[1]
		# Create Point at bolt head for first connector
		_endPoint2 = apex.construct.createLocationByCoordinates(x =pt2[0], y = pt2[1], z = pt2[2])
		_attachRegion2 = objects_list[1]
		
		
		if fastenerType=="Bushing":
			fastenerLength = calcDistance(_endPoint1, _endPoint2)
			bushStiff = calcBushingStiffness(dia, fastenerLength, _material)
			connectorProps = bushingProps(bushStiff[0], bushStiff[1], bushStiff[2], bushStiff[3], bushStiff[4], bushStiff[5])

		connector_1 = apex.attribute.createConnector(
			name = connector_name,
			connectorType = _connectorType,
			connectorProperties = connectorProps,
			applicationMethod1 = apex.attribute.ApplicationMethod.Remote,
			applicationMethod2 = apex.attribute.ApplicationMethod.Remote,
			end1InterfacePoint = _endPoint1, 
			end2InterfacePoint = _endPoint2, 
			end1AttachmentRegion = _attachRegion1, 
			#end1DistributionMethod = apex.attribute.DistributionType.Compliant, 
			end1DistributionMethod = dist_type, 
			end2AttachmentRegion = _attachRegion2, 
			#end2DistributionMethod = apex.attribute.DistributionType.Compliant,
			end2DistributionMethod = dist_type, 
			description = ""   #defaults to " "
		)



def exectuteOnExit(dict):
	apex.disableShowOutput()
	#apex.enableShowOutput()
	# clear highlights
	#solids= getAllSolids()
	#for solid in solids:
	#	solid.clearHighlight()	
	
	solids= getAllSolids()

	delete_objects=apex.EntityCollection()

	for solid in solids:
		if solid.isHighlighted():
			delete_objects.append(solid)
		#solid.clearHighlight()
	if len(delete_objects) > 0:	
		apex.deleteEntities(delete_objects)
	else:
		print("no_solids")

	delete_objects=apex.EntityCollection()

	


	if 1==0:
		# move bolt parts to TempPart
		model_1 = apex.currentModel()

		solids = getAllSolids()
		tmp_solids = apex.entityCollection()
		_target = apex.entityCollection()

		#Create tmp_part if not existing
		# try:
			# tmp_part = model_1.getPart( name = "Temp_repository" )
		# except:
			# tmp_part = model_1.createPart()
			# updatedPartName_ = tmp_part.update( name = "Temp_repository" )


		for solid in solids:
			try:
				tmp_attributes=solid.getUserAttributes(['ParentPart'])
				parent_part_name=tmp_attributes[0].getStringValue()
				_target.append(solid)
			except:
				continue
		
		try:
			#parent_part = model_1.getPart( name = parent_part_name )			
			#apex.reparent( parent = tmp_part, target = _target )
			apex.deleteEntities(_target)
		except:
			#print("parent Part: " + parent_part_name + " could not be found")
			pass
		
		# hide temporary part
		#tmp_part.hide()


	
if __name__ == "__main__":
	# standalone version for debugging
	dict={}
	
	dict["material"]="Steel"
	dict["fastenerType"]="Curve Only"
	dict["diameter"]="5.0"
	dict["distributionType"]="Rigid"
	dict["ClearXtraVerts"]="True"
	main(dict)


def main(dict={}):

	# pr = cProfile.Profile()
	
	# apex.setScriptUnitSystem(unitSystemName = r'''mm-kg-s-N''')
	# apex.disableUndoRedo()
	# apex.disableRecoveryFileRecording()
	
	# pr.enable()
	
	#rel_tol=0.01
	# maximum number of parts to search in one run
	max_no_of_solids=20000

	## Start Connect to GUI code ####
	
	_selected = apex.selection.getCurrentSelection()
	
	myModel = apex.currentModel()
	
	#connName      = dict["name"]
	matlName      = dict["material"]
	fastenerType  = dict["fastenerType"]
	#fastenerDia   = float(dict["diameter"])
	distributionType  = dict["distributionType"]
	#clear_vertex_flag= dict["ClearXtraVerts"]
	try:
		searchTolerance= float(dict["SearchTol"])
	except:
		searchTolerance=0.25
	
	
	#print("Name:           "+connName)
	print("Material:       "+matlName)
	print("Fastener type:  "+fastenerType)
	#print("Diameter:       "+str(fastenerDia))
	print("Distribution Type:      "+ distributionType )
	
	if fastenerType == "":
		print("Fastener Type is BLANK!")
		return
	
	#if fastenerDia <= 0.0:
	#	print("Invalid fastener diameter!")
	#	return
	
	if matlName == "None" and  fastenerType != "Rigid Link":
		print("No material selected!")
		return
	if fastenerType != "Rigid Link" :	
		# get the material that goes with the selected material name
		fastenerMatl = None
		matls = apex.catalog.getMaterials([{"path":myModel.getName()+"/Materials"}])
		for matl in matls:
			if matl.getName() == matlName:
				fastenerMatl = matl
				break
		if fastenerMatl == None:
			print("Unknown material?")
			return
	


	## End of Connect to GUI code ####


	# create the bolts curves
	
	#  first, turn off all parts that appears to be unmeshed solids with corresponding midsurface
	#  Rule 1: There are one solid and one surf in the part. 
	#  Rule 2: Either solid or midsurf is meshed. Hide Unmeshed
	#  Rule 3: If both parts are meshed or unmeshed keep only visible (that is, do nothing)
	
	
	parts=getAllParts()
	entities_to_hide=apex.entityCollection()
	
	
	for part in parts:
		surfs=part.getSurfaces()
		solids=part.getSolids()
		#Rule 1:
		if len(surfs)==1 and len(solids)==1:
			#pass Rule 1:
			solid_meshes=solids[0].getMeshes()
			surf_meshes=surfs[0].getMeshes()
			#Rule 2
			if len(surf_meshes) > 0 and len(solid_meshes) == 0:
				solids[0].hide()
			elif len(surf_meshes) == 0 and len(solid_meshes) > 0:
				surfs[0].hide()
			else:
				#in other cases, do nuffing
				pass
			
			
	#print("clear_vertex_flag:" + str(clear_vertex_flag) )	
	
	#identifyBolts(searchTolerance, max_no_of_solids,_selected,matlName,fastenerType,distributionType,clear_vertex_flag)
	
	# if there are no curves selected, process all visible curves
	if len(_selected) == 0:
		# 1b: just process visible curves
		curves=getAllCurves()
		visible_curves=apex.entityCollection()
		for curve in curves:
			if curve.getVisibility() == 1:
				visible_curves.append(curve)
	else:
		visible_curves=_selected
		
		
	visible_solids_nameL=[]
	

	solids=getAllSolids()
	
	# Search all visible solids
	for solid in solids:
		#check if there is exactly one solid body visible
		if solid.getVisibility() == 1:
			#visible_solids.append(solid)
			visible_solids_nameL.append(solid.getPathName())	
		
		
		
		

	#Create tmp_part if not existing
	try:
		tmp_part = myModel.getPart( name = "Temp_repository" )
	except:
		tmp_part = myModel.createPart()
		updatedPartName_ = tmp_part.update( name = "Temp_repository" )

	BoltDict=ReadBoltCurves(visible_curves)
	
	print(BoltDict)
	print("===")
	for item in BoltDict.keys():
		print("aaa")
		print(item)
	
		bolt_data=BoltDict[item]
		#print(bolt_data)
		p1=bolt_data[0]
		p2=bolt_data[1]
		name=bolt_data[2]   # this is m8 or m12
		#type=bolt_data[3]
		#Nut=bolt_data[4]
		#WasherHead=bolt_data[5]
		#WasherNut=bolt_data[6]
		
		diam=float(name.replace('m','').replace('M',''))
		tmp_solid, surf1=CreateCylinder(p1,p2,diam,tmp_part,myModel)
		
		_tmp_geom=apex.EntityCollection()
		_tmp_geom.append(surf1)
		
		# delete surf
		if len(_tmp_geom) != 0:
			apex.deleteEntities(_tmp_geom)
		
		
		curve = apex.getCurve( pathName = item )
		
		
		connect_obj_lists, connect_ctr_list= arrange_objects_to_connect2(curve,tmp_solid,visible_solids_nameL,searchTolerance)
		
		_tmp_geom=apex.EntityCollection()
		_tmp_geom.append(tmp_solid)
		
		# delete solid
		if len(_tmp_geom) != 0:
			apex.deleteEntities(_tmp_geom)
		
	
		if len(connect_obj_lists) != 0:
			tmp_connector=create_1d_bolt(curve,"Flexible Link",connect_obj_lists,connect_ctr_list,diam,fastenerType,distributionType,matlName)			
		else:
			print("No connections created")
		

	
	
	#pr.disable()
	#s = io.StringIO()
	#sortby = 'cumulative'
	#ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
	#ps.print_stats()
	#print(s.getvalue())
	
	apex.enableUndoRedo()
	apex.enableRecoveryFileRecording()

				