rsta_loadCommonFunction #("FN_RSTA_xml") escapeenable = true struct IpInstance ( ArchetypeName = "", ContainerRef = "", IplGroup = "", Pos = [0,0,0], Rot = [0,0,0], Scale = [1,1,1] ) struct sIpIPLGroup ( name, centroid, colour, count = 0 ) struct IpInstanceProcessor ( _ipInstances = #(), _metaPath = @"x:\americas\art\dev\environment\Terrain\houdini\gtav_dlc\islandx\metas\h4_islandx_placement.meta", _islandPositionOffset = [0, 0, 0], _medianPlacementPoint = [4896.09,-5391.85,0], _sw_placementCont, _nw_placementCont, _ne_placementCont, _se_placementCont, _sw_instanceCounter = 0, _nw_instanceCounter = 0, _ne_instanceCounter = 0, _se_instanceCounter = 0, _IPL_Attr = getattrindex "Gta Object" "IPL Group", _NEWDLC_ATTR = getattrindex "Gta Object" "New DLC", _IPL_COUNT_LIMIT = 10, _IPL_MAXITEMS = 900, -- _IPLGROUPS = #((DataPar)), --These inital centroids are created by hand, based on the main point set and judgind where likely point density centres will be _swIplInitialCentroids = #((sIpIPLGroup name:"h4_sw_ipl_00" centroid:[4803.75,-5927.7,0] colour:(color 218 105 104)), (sIpIPLGroup name:"h4_sw_ipl_01" centroid:[4863.74,-5453.32,0] colour:(color 232 115 57)), (sIpIPLGroup name:"h4_sw_ipl_02" centroid:[4709.87,-5647.74,0] colour:(color 240 120 3)), (sIpIPLGroup name:"h4_sw_ipl_03" centroid:[4869.29,-5661.07,0] colour:(color 186 201 53)), (sIpIPLGroup name:"h4_sw_ipl_04" centroid:[4810.97,-5826.6,0] colour:(color 84 213 83)), (sIpIPLGroup name:"h4_sw_ipl_05" centroid:[4835.41,-5527.2,0] colour:(color 62 200 243)), (sIpIPLGroup name:"h4_sw_ipl_06" centroid:[4819.85,-5606.63,0] colour:(color 21 22 99)), (sIpIPLGroup name:"h4_sw_ipl_07" centroid:[4758.2,-5781.61,0] colour:(color 145 29 144)), (sIpIPLGroup name:"h4_sw_ipl_08" centroid:[4755.42,-5523.31,0] colour:(color 208 82 201)), (sIpIPLGroup name:"h4_sw_ipl_09" centroid:[4794.86,-5713.29,0] colour:(color 242 158 159))), _seIplInitialCentroids = #((sIpIPLGroup name:"h4_se_ipl_00" centroid:[5456.97,-5828.2,0] colour:(color 255 0 255)), (sIpIPLGroup name:"h4_se_ipl_01" centroid:[4990.22,-5434.08,0] colour:(color 166 84 82)), (sIpIPLGroup name:"h4_se_ipl_02" centroid:[4920.74,-5638.09,0] colour:(color 124 73 39)), (sIpIPLGroup name:"h4_se_ipl_03" centroid:[4930.21,-5837.68,0] colour:(color 112 111 106)), (sIpIPLGroup name:"h4_se_ipl_04" centroid:[5503.71,-5478.3,0] colour:(color 133 230 51)), (sIpIPLGroup name:"h4_se_ipl_05" centroid:[5220.75,-5471.35,0] colour:(color 206 214 206)), (sIpIPLGroup name:"h4_se_ipl_06" centroid:[5285.81,-5567.98,0] colour:(color 95 173 230)), (sIpIPLGroup name:"h4_se_ipl_07" centroid:[5332.54,-5725.25,0] colour:(color 66 68 239)), (sIpIPLGroup name:"h4_se_ipl_08" centroid:[5088.11,-5558.51,0] colour:(color 232 41 230)), (sIpIPLGroup name:"h4_se_ipl_09" centroid:[5156.96,-5685.46,0] colour:(color 208 82 201))), _neIplInitialCentroids = #((sIpIPLGroup name:"h4_ne_ipl_00" centroid:[5054.64,-5328.6,0] colour:(color 0 255 255)), (sIpIPLGroup name:"h4_ne_ipl_01" centroid:[5479.71,-5286.29,0] colour:(color 69 7 2 )), (sIpIPLGroup name:"h4_ne_ipl_02" centroid:[4989.58,-4574.47,0] colour:(color 114 58 19)), (sIpIPLGroup name:"h4_ne_ipl_03" centroid:[5085.59,-4854.27,0] colour:(color 255 251 59)), (sIpIPLGroup name:"h4_ne_ipl_04" centroid:[5232.12,-5051.96,0] colour:(color 26 65 22)), (sIpIPLGroup name:"h4_ne_ipl_05" centroid:[5243.49,-5281.23,0] colour:(color 26 80 31)), (sIpIPLGroup name:"h4_ne_ipl_06" centroid:[5016.74,-4966.7,0] colour:(color 131 132 133)), (sIpIPLGroup name:"h4_ne_ipl_07" centroid:[5026.85,-4729.85,0] colour:(color 53 53 184)), (sIpIPLGroup name:"h4_ne_ipl_08" centroid:[5362.86,-4878.91,0] colour:(color 141 17 139)), (sIpIPLGroup name:"h4_ne_ipl_09" centroid:[4995.9,-5083.54,0] colour:(color 80 22 25))), _nwIplInitialCentroids = #((sIpIPLGroup name:"h4_nw_ipl_00" centroid:[4863.86,-5276.45,0] colour:(color 255 255 0)), (sIpIPLGroup name:"h4_nw_ipl_01" centroid:[4658.81,-4465.67,0] colour:(color 149 76 53 )), (sIpIPLGroup name:"h4_nw_ipl_02" centroid:[4608.4,-4851.28,0] colour:(color 205 99 12)), (sIpIPLGroup name:"h4_nw_ipl_03" centroid:[4825.45,-4628.87,0] colour:(color 168 172 29)), (sIpIPLGroup name:"h4_nw_ipl_04" centroid:[4792.4,-4368.05,0] colour:(color 20 138 18)), (sIpIPLGroup name:"h4_nw_ipl_05" centroid:[4585.36,-4578.5,0] colour:(color 140 187 154)), (sIpIPLGroup name:"h4_nw_ipl_06" centroid:[4263.36,-4344.47,0] colour:(color 87 92 248)), (sIpIPLGroup name:"h4_nw_ipl_07" centroid:[4321.83,-4442.55,0] colour:(color 14 5 201)), (sIpIPLGroup name:"h4_nw_ipl_08" centroid:[3877.75,-4721.77,0] colour:(color 80 1 77)), (sIpIPLGroup name:"h4_nw_ipl_09" centroid:[4774.84,-4500.03,0] colour:(color 199 64 66))), _pointClusterList = #(), _pointClusterPaths = #(), _iplGroupDefs = Dictionary(), fn GetIPLName item = ( return (getattr item _IPL_Attr) ), fn SetIPLWireColours objList = ( for item in objList do ( local iplName = GetIPLName item item.wirecolor = (_iplGroupDefs.GetValue iplName).colour ) ), fn IPLItemCount iplName = ( local matchingItems = for item in objects where (classof item == RsRefobject) and ((getattr item _IPL_Attr) == iplName) collect item return matchingItems.count ), fn SelectIPLContent iplname = ( clearSelection() select (local matchingItems = for item in objects where (classof item == RsRefobject) and ((getattr item _IPL_Attr) == iplName) collect item) ), --Create an initial division of points in fn ClusterPointAroundIPLGroups centroids points = ( _pointClusterList = #() -- for c in centroids do -- ( -- clusters.addKey c.name #() -- ) for p=1 to points.count do ( local thisPoint = points[p] local closestCentroid = undefined local closestDist = 99999 for c=1 to centroids.count do ( local dist = (distance thisPoint centroids[c].centroid) if dist < closestDist then ( closestDist = dist closestCentroid = c ) ) _pointClusterList[p] = closestCentroid ) return _pointClusterList ), fn UpdateMean centroids items = ( for c = 1 to centroids.count do ( local centroid = centroids[c] centroid = (centroid*(items.count-1)+items[i])/(n as float) ) return mean ), --points reference centroids --find closest centroid --find all points that reference one centroid --update the centroid position to match the avg point pos --loop until maxiterations or nothing to move fn DistCompare v1 v2 = ( case of ( (v1.dist>v2.dist):1 (v1.dist 0) then ( ipItem.ArchetypeName = (modelNameElems.Item 0).InnerText ) local positionElem = thisItem.Item "Position" if (positionElem != undefined) then ( ipItem.Pos = [_islandPositionOffset.x + ((positionElem.GetAttribute "x") as Float), _islandPositionOffset.y + ((positionElem.GetAttribute "y") as Float), _islandPositionOffset.z + ((positionElem.GetAttribute "z") as Float)] ) local rotationElem = thisItem.Item "Rotation" if (rotationElem != undefined) then ( ipItem.Rot = [((rotationElem.GetAttribute "x") as Float), ((rotationElem.GetAttribute "y") as Float), ((rotationElem.GetAttribute "z") as Float)] ) local scaleElem = thisItem.Item "Scale" if (scaleElem != undefined) then ( ipItem.Scale = [((scaleElem.GetAttribute "x") as Float), ((scaleElem.GetAttribute "y") as Float), ((scaleElem.GetAttribute "z") as Float)] ) append _ipInstances ipItem ) ), fn GetContainerRefForInstances = ( --set container ref for ipItem in _ipInstances do ( case of ( ((ipItem.Pos.x <= _medianPlacementPoint.x) and (ipItem.Pos.y <= _medianPlacementPoint.y)): ( ipItem.ContainerRef = "sw_placement" ) ((ipItem.Pos.x <= _medianPlacementPoint.x) and (ipItem.Pos.y >= _medianPlacementPoint.y)): ( ipItem.ContainerRef = "nw_placement" ) ((ipItem.Pos.x >= _medianPlacementPoint.x) and (ipItem.Pos.y >= _medianPlacementPoint.y)): ( ipItem.ContainerRef = "ne_placement" ) ((ipItem.Pos.x >= _medianPlacementPoint.x) and (ipItem.Pos.y <= _medianPlacementPoint.y)): ( ipItem.ContainerRef = "se_placement" ) ) ) ), fn CreateRefsFromIpMeta = ( gRsPerforce.Sync _metaPath local xmlIO = rsta_xml_io() xmlIO.xmlFile = _metaPath xmlIO.load() local itemNodes = xmlIO.root.GetElementsByTagName "Item" local itemsEnumerator = itemNodes.GetEnumerator() while itemsEnumerator.MoveNext() do ( local thisItem = itemsEnumerator.Current local ipItem = IpInstance() local modelNameElems = thisitem.GetElementsByTagName "ModelName" if (modelNameElems.Count > 0) then ( ipItem.ArchetypeName = (modelNameElems.Item 0).InnerText ) local positionElem = thisItem.Item "Position" if (positionElem != undefined) then ( ipItem.Pos = [_islandPositionOffset.x + ((positionElem.GetAttribute "x") as Float), _islandPositionOffset.y + ((positionElem.GetAttribute "y") as Float), _islandPositionOffset.z + ((positionElem.GetAttribute "z") as Float)] ) --set container ref case of ( ((ipItem.Pos.x <= _medianPlacementPoint.x) and (ipItem.Pos.y <= _medianPlacementPoint.y)): ( ipItem.ContainerRef = "sw_placement" ) ((ipItem.Pos.x <= _medianPlacementPoint.x) and (ipItem.Pos.y >= _medianPlacementPoint.y)): ( ipItem.ContainerRef = "nw_placement" ) ((ipItem.Pos.x >= _medianPlacementPoint.x) and (ipItem.Pos.y >= _medianPlacementPoint.y)): ( ipItem.ContainerRef = "ne_placement" ) ((ipItem.Pos.x >= _medianPlacementPoint.x) and (ipItem.Pos.y <= _medianPlacementPoint.y)): ( ipItem.ContainerRef = "se_placement" ) ) local rotationElem = thisItem.Item "Rotation" if (rotationElem != undefined) then ( ipItem.Rot = [((rotationElem.GetAttribute "x") as Float), ((rotationElem.GetAttribute "y") as Float), ((rotationElem.GetAttribute "z") as Float)] ) local scaleElem = thisItem.Item "Scale" if (scaleElem != undefined) then ( ipItem.Scale = [((scaleElem.GetAttribute "x") as Float), ((scaleElem.GetAttribute "y") as Float), ((scaleElem.GetAttribute "z") as Float)] ) append _ipInstances ipItem ) --now find which container instances belong to and set that property for each GetContainerRefForInstances() --Now get all the instances for each container and sort them into proximate ipl groups --sw_placement local sw_placementPoints = for item in _ipInstances where (item.ContainerRef == "sw_placement") collect item KMeansConverge _swIplInitialCentroids sw_placementPoints --nw_placement local nw_placementPoints = for item in _ipInstances where (item.ContainerRef == "nw_placement") collect item KMeansConverge _nwIplInitialCentroids nw_placementPoints --ne_placement local ne_placementPoints = for item in _ipInstances where (item.ContainerRef == "ne_placement") collect item KMeansConverge _neIplInitialCentroids ne_placementPoints --se_placement local se_placementPoints = for item in _ipInstances where (item.ContainerRef == "se_placement") collect item KMeansConverge _seIplInitialCentroids se_placementPoints ), fn DebugPositionMesh objname positions:unsupplied = ( local em = Plane name:objName widthSegs:1 lengthSegs:1 converttomesh em if (positions == unsupplied) then ( positions = for item in _ipInstances collect item.Pos ) setmesh em vertices:positions faces:#() update em ), fn _SetNodeWorldRotation theNode theRot = ( in coordsys (transmatrix theNode.transform.pos) theNode.rotation = theRot ), fn _RemoveContainerNodes contHelper nodes = ( with undo off ( for item in nodes do ( contHelper.RemoveNodeFromContent item true if (isValidNode item) do delete item ) ) ), fn _ImportContainers = ( --check if they are already loaded if (_sw_placementCont == undefined) do ( _sw_placementCont = (Containers.CreateInheritedContainer @"X:\gta5_dlc\mpPacks\mpHeist4\Art\ng\Models\mph4_Island\mph4_island_sw_placement.maxc") _sw_placementCont.SetEditInPlace True local nodes = #() _sw_placementCont.GetContentNodes false &nodes _RemoveContainerNodes _sw_placementCont nodes ) if (_nw_placementCont == undefined) do ( _nw_placementCont = (Containers.CreateInheritedContainer @"X:\gta5_dlc\mpPacks\mpHeist4\Art\ng\Models\mph4_Island\mph4_island_nw_placement.maxc") _nw_placementCont.SetEditInPlace True local nodes = #() _nw_placementCont.GetContentNodes false &nodes _RemoveContainerNodes _nw_placementCont nodes ) if (_ne_placementCont == undefined) do ( _ne_placementCont = (Containers.CreateInheritedContainer @"X:\gta5_dlc\mpPacks\mpHeist4\Art\ng\Models\mph4_Island\mph4_island_ne_placement.maxc") _ne_placementCont.SetEditInPlace True local nodes = #() _ne_placementCont.GetContentNodes false &nodes _RemoveContainerNodes _ne_placementCont nodes ) if (_se_placementCont == undefined) do ( _se_placementCont = (Containers.CreateInheritedContainer @"X:\gta5_dlc\mpPacks\mpHeist4\Art\ng\Models\mph4_Island\mph4_island_se_placement.maxc") _se_placementCont.SetEditInPlace True local nodes = #() _se_placementCont.GetContentNodes false &nodes _RemoveContainerNodes _se_placementCont nodes ) ), fn _GetContainerRef ipItem = ( case ipItem.ContainerRef of ( "sw_placement": return _sw_placementCont "nw_placement": return _nw_placementCont "ne_placement": return _ne_placementCont "se_placement": return _se_placementCont ) return undefined ), fn CreateRsRefinstancesFromIpInstances = ( RSrefAllowUpdate = false _ImportContainers() local swIplCount = 0 local swIplName = "h4_sw_ipl_" + (formattedPrint swIplCount format:"02i") local nwIplCount = 0 local nwIplName = "h4_nw_ipl_" + (formattedPrint nwIplCount format:"02i") local neIplCount = 0 local neIplName = "h4_ne_ipl_" + (formattedPrint neIplCount format:"02i") local seIplCount = 0 local seIplName = "h4_se_ipl_" + (formattedPrint seIplCount format:"02i") local sw_instanceCounter = 0 local nw_instanceCounter = 0 local ne_instanceCounter = 0 local se_instanceCounter = 0 with undo off ( ProgressStart "Creating IP RsRefs..." local progressCounter = 0.0 for ipItem in _ipInstances while (ProgressUpdate (100.0 * (progressCounter / _ipInstances.count))) do ( local newRef = RsrefObject objectname:ipItem.ArchetypeName newRef.pos = ipItem.Pos _SetNodeWorldRotation newRef (eulerangles ipItem.Rot.x ipItem.Rot.y ipItem.Rot.z) Scale newRef ipItem.Scale --add to the relevant container based on its position local containerRef = _GetContainerRef ipItem containerRef.AddNodeToContent newRef case containerRef.name of ( "sw_placement": ( swIplName = "h4_sw_ipl_" + (formattedPrint ipItem.IplGroup format:"02i") setattr newRef _IPL_Attr swIplName setattr newRef _NEWDLC_ATTR true ) "nw_placement": ( nwIplName = "h4_nw_ipl_" + (formattedPrint ipItem.IplGroup format:"02i") setattr newRef _IPL_Attr nwIplName setattr newRef _NEWDLC_ATTR true ) "ne_placement": ( neIplName = "h4_ne_ipl_" + (formattedPrint ipItem.IplGroup format:"02i") setattr newRef _IPL_Attr neIplName setattr newRef _NEWDLC_ATTR true ) "se_placement": ( seIplName = "h4_se_ipl_" + (formattedPrint ipItem.IplGroup format:"02i") setattr newRef _IPL_Attr seIplName setattr newRef _NEWDLC_ATTR true ) ) progressCounter += 1.0 ) ProgressEnd() ) ), fn Run = ( this.CreateRefsFromIpMeta() -- this._metaPath this.CreateRsRefinstancesFromIpInstances() ), on create do ( for item in _swIplInitialCentroids do ( _iplGroupDefs.AddKey item.name item ) for item in _nwIplInitialCentroids do ( _iplGroupDefs.AddKey item.name item ) for item in _seIplInitialCentroids do ( _iplGroupDefs.AddKey item.name item ) for item in _neIplInitialCentroids do ( _iplGroupDefs.AddKey item.name item ) ) ) -- global gIpProc = IpInstanceProcessor() -- gIpProc.Run() try(destroydialog RSTA_IPIPLToolUI)catch() rollout RSTA_IPIPLToolUI "IP IPL Tool" width:300 ( local ipProc = IpInstanceProcessor() local IPLSELECTIONMAX = 925 struct IplMeta ( name, dist, pos, count ) button btnCreateRefs "Create Refs from Meta" tooltip:"Create rsrefs from placement meta" width:280 button btnSelectionToIPL "Selection to closest IPL" tooltip:"Assign the items in the selection to the closest ipl group" width:280 dropdownlist ddlIPLNames "IPL Names" items:(ipProc._iplGroupDefs.keys()) button btnIplCounter "Item Count" tooltip:"Count the number of items in each ipl group" width:280 edittext edtIPLCount "Count:" readonly:true bold:true text:"-" progressbar prgIPLCount "IPL Capacity" button btnGetIPLFromSelection "Get IPL From Selected" tooltip:"Set the ipl group name from the first selected" width:280 button btnSelectIPLContents "Select IPL Items" tooltip:"Select Items for chosen IPL" width:280 button btnAssignSelToIPL "Assign selection to chosen IPL" width:280 fn UpdateIPLCounter iplname = ( local iplCount = ipProc.IPLItemCount iplname local idx = finditem ddlIPLNames.items iplname ddlIPLNames.selection = idx if (iplCount > 950) then ( prgIPLCount.color = red ) else ( prgIPLCount.color = green ) prgIPLCount.value = (100 * (iplCount / IPLSELECTIONMAX as float)) edtIPLCount.text = iplCount as string ) on btnAssignSelToIPL pressed do ( local items = GetCurrentSelection() items = for item in items where (classof item == Rsrefobject) and ((ipProc.GetIPLName item) != ddlIPLNames.selected) collect item if (items.count == 0) do ( messagebox "Nothing valid selected" title:"Bad Selection" return false ) --Check we have capacity to assign this many items to this ipl group local iplCount = ipProc.IPLItemCount ddlIPLNames.selected if (items.count > (IPLSELECTIONMAX - iplCount)) then ( messagebox "Adding this many items will bust the limit on the chosen IPL group" title:"OVERLOAD" return false ) undo label:("Assign Items to " + ddlIPLNames.selected) on ( print "Assiging items to ipl" for item in items do ( SetAttr item ipProc._IPL_Attr ddlIPLNames.selected ) ) UpdateIPLCounter ddlIPLNames.selected ) on btnGetIPLFromSelection pressed do ( if (classof selection[1] == RsrefObject) then ( local iplname = ipProc.GetIPLName selection[1] local idx = finditem ddlIPLNames.items iplname if (idx == 0) then ( messagebox ("Ipl name: " + iplname + " not found!") title:"Not Found" return false ) else ( ddlIPLNames.selection = idx ) ) else ( messagebox "Pick an RsRef object please." title:"Bad Selection" return false ) ) on btnSelectIPLContents pressed do ( ipProc.SelectIPLContent ddlIPLNames.selected ) on btnIplCounter pressed do ( UpdateIPLCounter ddlIPLNames.selected ) on btnCreateRefs pressed do ( ipProc.Run() ) on btnSelectionToIPL pressed do ( --get ipl counts local iplGroups = Dictionary() for item in objects where (classof item == RSrefObject) do ( local iplGroup = getAttr item ipProc._IPL_Attr if (iplGroup != undefined) or (iplGroup != "undefined") then ( if (not iplGroups.hasKey iplGroup) then ( iplGroups.addKey iplGroup #(item) ) else ( local currentItems = iplGroups.getValue iplGroup append currentItems item iplGroups.setValue iplGroup currentItems ) ) ) --get ipl groups centres local iplCenters = Dictionary() for key in iplGroups.keys() do ( --get the values and find the avg center from the contents positions local items = iplGroups.getValue key local commonCenter = [0,0,0] for obj in items do ( commonCenter += obj.pos ) commonCenter /= items.count iplCenters.addKey key (iplMeta name:key pos:commonCenter count:items.count) ) for key in iplCenters.keys() do print (iplCenters.getValue key) --find the closest ipl group that has capacity to take another for item in (GetCurrentSelection()) where (classof item == RsrefObject) do ( local closestIplRank = #() for key in iplCenters.keys() do ( local iplData = iplCenters.getValue key local dist = distance item.pos iplData.pos --find the array index that is closer than this closest distance append closestIplRank (DataPair dist:dist data:iplData) ) --sort them by distance qsort closestIplRank ipProc.DistCompare local iplWithRoom = false local iplGroupName = undefined for ipl in closestIplRank while not iplWithRoom do ( if (ipl.data.count < IPLSELECTIONMAX) and (ipl.data.name != "undefined") then ( iplGroupName = ipl.data.name ipl.data.count += 1 iplWithRoom = true ) ) if (iplGroupName != undefined) then ( format "iplGroupName: %\n" iplGroupName setAttr item ipProc._IPL_Attr iplGroupName ) else ( print "no iplGroupName to assign" ) ) ) ) createDialog RSTA_IPIPLToolUI /* if FALSE do ( nwPos = for item in gIpProc._ipInstances where item.ContainerRef == "nw_placement" collect item.pos nw_clusters = gIpProc.ClusterPointAroundIPLGroups gIpProc._nwIplInitialCentroids nwPos --for key in nw_Clusters.keys() do gIpProc.DebugPositionMesh key positions:(nw_Clusters.getValue key) success = gIpProc.KMeansConverge gIpProc._nwIplInitialCentroids nwPos for i=1 to 10 do ( local iplPos = for idx=1 to gIpProc._pointClusterList.count where gIpProc._pointClusterList[idx]==i collect nwPos[idx] gIpProc.DebugPositionMesh ("ipl_" + i as string) positions:iplPos ) for item in $selection do centerpivot item nwmesh = Plane name:"nw" widthSegs:1 lengthSegs:1 converttomesh nwmesh setmesh nwmesh vertices:nwPos faces:#() update nwmesh select nwmesh ipls = #() for i=1 to 10 do ( ipls[i] = for x=1 to gIpProc._pointClusterList.count where gIpProc._pointClusterList[x] == i collect nwPos[x] ) for i=1 to ipls.count do print ipls[i].count for item in gIpProc._nwIplInitialCentroids do Point name:item.name pos:item.centroid color:red --spline paths for cluster centres for i=1 to gIpProc._pointClusterPaths.count do ( local new_spline = splineShape name:("PointCluster_" + i as string) select new_spline addNewSpline new_spline for p =1 to gIpProc._pointClusterPaths[i].count do ( addKnot new_spline 1 #corner #line gIpProc._pointClusterPaths[i][p] ) ) )*/