-- Uniconnector.ms -- By Nikolay Litvinov (gniklit@gmail.com) -- Created On: 2014-05-10 -- Modified: 2016-03-18 -- Fixed: 2024 - Undo stability & FlowConnect fix macroScript FlowConnectSwitcher_macro Category:"Niklit Scripts" Tooltip:"Uconnector FlowConnect Switcher" icon:#("SubObjectIcons",22) ( global FlowConnectSwitcher if FlowConnectSwitcher == undefined do FlowConnectSwitcher=true on isEnabled return selection.count>0 on ischecked return FlowConnectSwitcher on Execute do FlowConnectSwitcher = if FlowConnectSwitcher != true then true else false ) macroScript RefineConnectSwitcher_macro Category:"Niklit Scripts" Tooltip:"Uconnector RefineConnect Switcher" icon:#("Max_Edit_Modifiers",15) ( global RefineConnectSwitcher if RefineConnectSwitcher == undefined do RefineConnectSwitcher=false on isEnabled return selection.count>0 on ischecked return RefineConnectSwitcher on Execute do RefineConnectSwitcher = if RefineConnectSwitcher != true then true else false ) macroScript UniConnector Category:"Niklit Scripts" Tooltip:"UniConnector" ( ------------------------- Functions ------------------------- global NewSpline, num_CurO, Es_, gco, isEditPoly, FlowConnectSwitcher -- Safe undo function using theHold API fn safeUndo_fn = ( if theHold.Holding() then ( theHold.Cancel() ) else ( try(max undo)catch() ) ) -- Begin undo record fn beginUndo_fn label = ( if not theHold.Holding() do theHold.Begin() ) -- Accept undo record fn acceptUndo_fn label = ( if theHold.Holding() do theHold.Accept label ) -- Cancel undo record fn cancelUndo_fn = ( if theHold.Holding() do theHold.Cancel() ) fn isEditPolyClass fco = (classOf fco == Edit_Poly) fn getNode_fn node_i = selection[node_i] fn getNumFaces_fn fco node_i = ( if isEditPolyClass fco then fco.GetNumFaces node:(getNode_fn node_i) else fco.getNumFaces() ) fn getNumEdges_fn fco node_i = ( if isEditPolyClass fco then fco.GetNumEdges node:(getNode_fn node_i) else fco.getNumEdges() ) fn getSelection_fn fco selType node_i = ( if isEditPolyClass fco then fco.GetSelection selType node:(getNode_fn node_i) else fco.getSelection selType ) fn setSelection_fn fco selType selData node_i = ( if isEditPolyClass fco then ( fco.SetSelection selType #{} node:(getNode_fn node_i) case selType of ( #Vertex: setVertSelection (getNode_fn node_i) fco selData keep:on #Edge: setEdgeSelection (getNode_fn node_i) fco selData keep:on #Face: setFaceSelection (getNode_fn node_i) fco selData keep:on ) ) else fco.SetSelection selType selData ) fn getVertex_fn fco vertIdx node_i = ( if isEditPolyClass fco then fco.GetVertex vertIdx node:(getNode_fn node_i) else fco.GetVertex vertIdx ) fn getEdgeVertex_fn fco edgeIdx vertNum node_i = ( if isEditPolyClass fco then fco.GetEdgeVertex edgeIdx vertNum node:(getNode_fn node_i) else fco.GetEdgeVertex edgeIdx vertNum ) fn getVertsUsingEdge_fn fco edgeIdx node_i = ( if isEditPolyClass fco then fco.GetVertsUsingEdge edgeIdx node:(getNode_fn node_i) else polyop.getVertsUsingEdge (getNode_fn node_i) edgeIdx ) fn getEdgesUsingVert_fn fco vertIdx node_i = ( if isEditPolyClass fco then fco.GetEdgesUsingVert vertIdx node:(getNode_fn node_i) else polyop.getEdgesUsingVert (getNode_fn node_i) vertIdx ) fn getEdgesUsingFace_fn fco faceIdx node_i = ( if isEditPolyClass fco then fco.GetEdgesUsingFace faceIdx node:(getNode_fn node_i) else polyop.getEdgesUsingFace (getNode_fn node_i) faceIdx ) fn getFacesUsingVert_fn fco vertIdx node_i = ( if isEditPolyClass fco then fco.GetFacesUsingVert vertIdx node:(getNode_fn node_i) else polyop.getFacesUsingVert (getNode_fn node_i) vertIdx ) fn getFacesUsingEdge_fn fco edgeIdx node_i = ( if isEditPolyClass fco then fco.GetFacesUsingEdge edgeIdx node:(getNode_fn node_i) else polyop.getFacesUsingEdge (getNode_fn node_i) edgeIdx ) fn createVert_fn fco pos node_i = ( if isEditPolyClass fco then fco.CreateVertex pos node:(getNode_fn node_i) else polyop.createVert (getNode_fn node_i) pos ) fn createFace_fn fco vertArray node_i = ( if isEditPolyClass fco then fco.CreateFace vertArray node:(getNode_fn node_i) else fco.CreateFace vertArray ) fn createPolygon_fn fco vertArray node_i = ( if isEditPolyClass fco then fco.CreateFace vertArray node:(getNode_fn node_i) else polyop.createPolygon (getNode_fn node_i) vertArray ) fn commit_fn fco node_i = ( if isEditPolyClass fco then try(fco.Commit())catch() ) fn setVertSelection_fn fco verts node_i = ( if isEditPolyClass fco then ( fco.SetSelection #Vertex #{} node:(getNode_fn node_i) setVertSelection (getNode_fn node_i) fco verts keep:on ) else polyop.setVertSelection (getNode_fn node_i) verts ) fn setEdgeSelection_fn fco edges node_i = ( if isEditPolyClass fco then ( fco.SetSelection #Edge #{} node:(getNode_fn node_i) setEdgeSelection (getNode_fn node_i) fco edges keep:on ) else polyop.setEdgeSelection (getNode_fn node_i) edges ) fn getVertSelection_fn fco node_i = ( if isEditPolyClass fco then fco.GetSelection #Vertex node:(getNode_fn node_i) else polyop.getVertSelection (getNode_fn node_i) ) fn getEdgeSelection_fn fco node_i = ( if isEditPolyClass fco then fco.GetSelection #Edge node:(getNode_fn node_i) else polyop.getEdgeSelection (getNode_fn node_i) ) fn remove_fn fco selLevel node_i = ( if isEditPolyClass fco then ( fco.ButtonOp #RemoveVertex fco.Commit() ) else fco.EditablePoly.Remove selLevel:#Vertex flag:1 ) global Pos_AllAxisProportional_Rollout fn GabaritAssigner_fn= ( if Pos_AllAxisProportional_Rollout==undefined do Pos_AllAxisProportional_Rollout=mouse.screenpos rollout AllAxisProportional_Rollout "GabaritAssigner" width:99 height:57 ( checkButton ckb1 "Proportional" pos:[10,11] width:78 height:35 on AllAxisProportional_Rollout moved pos do Pos_AllAxisProportional_Rollout=pos ) createdialog AllAxisProportional_Rollout pos: Pos_AllAxisProportional_Rollout isSelMode=getToolbtnState #select oStart = pickobject() oEnd = pickobject() if oStart!=undefined and oEnd!=undefined and (not keyboard.escPressed) then ( oStartVector=oStart.max-oStart.min oEndVector=oEnd.max-oEnd.min Nx=oEndVector.x/oStartVector.x Ny=oEndVector.y/oStartVector.y Nz=oEndVector.z/oStartVector.z if isSelMode then scale oStart [Nx,Ny,Nz] else case toolmode.axisConstraints of ( #x: (if AllAxisProportional_Rollout.ckb1.state==on then scale oStart [Nx,Nx,Nx] else scale oStart [Nx,1,1]) #y: (if AllAxisProportional_Rollout.ckb1.state==on then scale oStart [Ny,Ny,Ny] else scale oStart [1,Ny,1]) #z: (if AllAxisProportional_Rollout.ckb1.state==on then scale oStart [Nz,Nz,Nz] else scale oStart [1,1,Nz]) #XY: scale oStart [Nx,Ny,1] #YZ: scale oStart [1,Ny,Nz] #ZX: scale oStart [Nx,1,Nz] Default: scale oStart [Nx,Ny,Nz] ) DestroyDialog AllAxisProportional_Rollout ) else DestroyDialog AllAxisProportional_Rollout ) fn MultiAttach_Spl_fn= ( spl_ar=for i in selection where IsShapeObject i collect (convertToSplineShape i); for i=2 to spl_ar.count do addandweld spl_ar[1] spl_ar[i] 0.002 ) fn WeldThreshold_fn = ( maxWindow_hwnd=(windows.getChildrenHWND (windows.getDesktopHWND())) WeldThreshold_hwnd=(for i=1 to maxWindow_hwnd.count where maxWindow_hwnd[i][5]=="Weld" do exit with maxWindow_hwnd[(i+1)][1]) WM_SETFOCUS = 0x007 VK_RETURN=0x000D WM_CHAR=0x0102 SendKeys=dotNetClass "System.Windows.Forms.SendKeys" windows.sendMessage WeldThreshold_hwnd WM_SETFOCUS 0 0 SendKeys.SendWait "0,002" windows.sendMessage WeldThreshold_hwnd WM_CHAR VK_RETURN 0 SendKeys.SendWait "{up}" UIAccessor.PressButton (for i in maxWindow_hwnd where i[5]=="Weld" do exit with i[1]) ) fn DrawLineBetweenTwoPoints_fn = ( undo "DrawLine" on ( slp_kn_pos=#(); for spl=1 to numSplines $ do for kn=1 to (getKnotSelection $ spl).count do append slp_kn_pos #(spl,(getKnotSelection $ spl)[kn],(getKnotPoint $ spl ((getKnotSelection $ spl)[kn]))); slp_kn_pos if classof $==splineShape or classof $==line do ( if (numSplines $)==1 then (close $ 1; updateshape $) else ( kn=addNewSpline $ kn_A=addKnot $ kn #corner #line slp_kn_pos[1][3] kn_B=addKnot $ kn #corner #line slp_kn_pos[2][3] try updateShape $ catch () setKnotSelection $ kn #(kn_A,kn_B) keep:true setKnotSelection $ slp_kn_pos[1][1] #(slp_kn_pos[1][2]) keep:true setKnotSelection $ slp_kn_pos[2][1] #(slp_kn_pos[2][2]) keep:true splineOps.weld $ updateShape $ redrawviews() ) ) ) ) fn ShapeConn_fn fco= ( fco = $ case subobjectlevel of ( 1: if not ( numselKnots = for i=1 to (numSplines selection[1]) collect getKnotSelection selection[1] i numKn=0; for i in numselKnots where i.count>0 do numKn+=i.count; numKn==2 ) then ( global RefineConnectSwitcher if RefineConnectSwitcher == undefined do RefineConnectSwitcher=false if subobjectlevel!=2 then if RefineConnectSwitcher then (max modify mode; subobjectlevel=1; splineOps.startRefineConnect $) else (max modify mode; subobjectlevel=1; splineOps.startRefine $) ) else DrawLineBetweenTwoPoints_fn() 2: splineOps.divide fco 3: (spl_index_int=(getSplineSelection fco)[1] splCount=numSegments fco spl_index_int arSpl=for i=1 to splCount collect i for i=1 to numSplines fco do setSegSelection fco i #() setSegSelection fco spl_index_int arSpl keep: on subObjectLevel=2 splineOps.divide fco subObjectLevel=3) ) ) fn TriFace_fn fco=( local NumF1 = getNumFaces_fn fco num_CurO local vertArray = (getSelection_fn fco #Vertex num_CurO) as array createFace_fn fco vertArray num_CurO commit_fn fco num_CurO local NumF2 = getNumFaces_fn fco num_CurO NumF1 != NumF2 ) fn NEXTvettoconnect_fn fco = ( local theNode = getNode_fn num_CurO local vertSel = getVertSelection_fn fco num_CurO local FuV = (getFacesUsingVert_fn fco vertSel num_CurO) as array local HInumberset = #(#{},0) for i=1 to FuV.count do ( local edgesUsingFace = getEdgesUsingFace_fn fco FuV[i] num_CurO if edgesUsingFace.numberset > HInumberset[1].numberset do ( HInumberset[1] = edgesUsingFace HInumberset[2] = FuV[i] ) ) setSelection_fn fco #Face #{HInumberset[2]} num_CurO local NumF1 = getNumFaces_fn fco num_CurO local aF = (getSelection_fn fco #Face num_CurO) as Array local baEuF = getEdgesUsingFace_fn fco aF[1] num_CurO local baV = (getVertSelection_fn fco num_CurO) as Array local baEuV1 = getEdgesUsingVert_fn fco baV[1] num_CurO local baEuV2 = getEdgesUsingVert_fn fco baV[2] num_CurO local baE0 = baEuV1 * baEuV2 local aE1 = (baEuF * (baEuV1 + baEuV2) - baE0) as Array local baV1 = (for i in aE1 collect ( local vertsUsingEdge = getVertsUsingEdge_fn fco i num_CurO ((vertsUsingEdge - (baV as BitArray)) as Array)[1] )) as BitArray setSelection_fn fco #Vertex baV1 num_CurO commit_fn fco num_CurO fco.ButtonOp #ConnectVertices commit_fn fco num_CurO local baF1 = getFacesUsingEdge_fn fco (baE0 as Array)[1] num_CurO local baFcur = getSelection_fn fco #Face num_CurO local baFset = baFcur - baF1 setSelection_fn fco #Face baFset num_CurO local NumF2 = getNumFaces_fn fco num_CurO NumF1 != NumF2 ) fn BridgeBorder_fn fco =( local NumF1 = getNumFaces_fn fco num_CurO fco.ButtonOp #BridgeBorder commit_fn fco num_CurO local NumF2 = getNumFaces_fn fco num_CurO NumF1 != NumF2 ) fn BridgePolygon_fn fco =( local NumF1 = getNumFaces_fn fco num_CurO fco.ButtonOp #BridgePolygon commit_fn fco num_CurO local NumF2 = getNumFaces_fn fco num_CurO NumF1 != NumF2 ) fn FilterOpenVert_fn fco=( local usOpenVert = (getSelection_fn fco #Vertex num_CurO) as Array local OpenE = #() local resultType = openEdges.Check currentTime (getNode_fn num_CurO) &OpenE local allOpnVert = for e in OpenE collect getEdgeVertex_fn fco e 1 num_CurO local FilterOpenVert = #() for i=1 to usOpenVert.count do if findItem allOpnVert usOpenVert[i] != 0 do appendIfUnique FilterOpenVert usOpenVert[i] FilterOpenVert.count > 2 ) fn PostWeld_fn fco = ( if isEditPolyClass fco then ( -- For Edit_Poly modifier - use direct polyOp on node local theNode = getNode_fn num_CurO local selVerts = fco.GetSelection #Vertex node:theNode if not selVerts.isEmpty do ( try ( polyOp.weldVertsByThreshold theNode selVerts 0.002 ) catch ( -- Fallback: try button operation try ( fco.ButtonOp #WeldSelected fco.Commit() ) catch () ) ) ) else ( -- For Editable_Poly try ( fco.weldThreshold = 0.002 fco.buttonOp #WeldSelected ) catch ( try ( fco.weldVertexThreshold = 0.002 fco.buttonOp #WeldVertex ) catch () ) ) ) fn VertConnect_ThroughE_fn fco =( local Sys0 = getRefCoordSys() toolMode.coordsys #local local NumF1 = getNumFaces_fn fco num_CurO local arVs = for i=1 to selection.count collect (getSelection_fn fco #Vertex i) as Array local startVertex_id = arVs[num_CurO][1] global startPosition_p3 startPosition_p3 = getVertex_fn fco arVs[num_CurO][1] num_CurO global endPosition_p3 endPosition_p3 = getVertex_fn fco arVs[num_CurO][2] num_CurO local viewDirWorld = (inverse(getViewTM())).row3 local objTM = (getNode_fn num_CurO).transform local objRotMatrix = copy objTM objRotMatrix.row4 = [0,0,0] local viewDirection_p3 = normalize (viewDirWorld * (inverse objRotMatrix)) if isEditPolyClass fco then ( fco.StartCut #Vertex startVertex_id endPosition_p3 viewDirection_p3 node:(getNode_fn num_CurO) fco.Commit() ) else ( try (fco.cutVertices startVertex_id endPosition_p3 viewDirection_p3) catch () ) setRefCoordSys Sys0 PostWeld_fn fco local NumF2 = getNumFaces_fn fco num_CurO NumF1 != NumF2 ) fn EdgeConnect_ThroughE_fn fco =( local arEs = for i=1 to selection.count collect (getEdgeSelection_fn fco i) as Array if arEs[num_CurO].count==2 do ( local startEdge_id = arEs[num_CurO][1] local EndEdge_id = arEs[num_CurO][2] local viewDirWorld = (inverse(getViewTM())).row3 local objTM = (getNode_fn num_CurO).transform local objRotMatrix = copy objTM objRotMatrix.row4 = [0,0,0] local viewDirection_p3 = normalize (viewDirWorld * (inverse objRotMatrix)) if isEditPolyClass fco then ( setVertSelection_fn fco #{} num_CurO for e in arEs[num_CurO] do fco.DivideEdge e .5 node:(getNode_fn num_CurO) fco.Commit() local arVs = for i=1 to selection.count collect (getSelection_fn fco #Vertex i) as Array local startVertex_id = arVs[num_CurO][1] local endPosition_p3 = getVertex_fn fco arVs[num_CurO][2] num_CurO setEdgeSelection_fn fco #{} num_CurO fco.StartCut #Vertex startVertex_id endPosition_p3 viewDirection_p3 node:(getNode_fn num_CurO) fco.Commit() ) else ( fco.cutEdges startEdge_id .5 EndEdge_id .5 viewDirection_p3 local arEs2 = fco.getSelection #Edge fco.setSelection #Edge arEs2 ) ) ) fn VertConn_fn fco =( local NumF1 = getNumFaces_fn fco num_CurO fco.ButtonOp #ConnectVertices commit_fn fco num_CurO local NumF2 = getNumFaces_fn fco num_CurO NumF1 != NumF2 ) fn HasDisconnectedVertPair_fn fco = ( local selVerts = try ((getSelection_fn fco #Vertex num_CurO) as array) catch #() if selVerts.count < 3 do return false for i = 1 to selVerts.count do ( for j = i+1 to selVerts.count do ( local edgesV1 = getEdgesUsingVert_fn fco selVerts[i] num_CurO local edgesV2 = getEdgesUsingVert_fn fco selVerts[j] num_CurO if edgesV1 != undefined and edgesV2 != undefined do ( if (edgesV1 * edgesV2).isEmpty do return true ) ) ) false ) fn GetVertPos_fn fco vertIdx = ( getVertex_fn fco vertIdx num_CurO ) fn SortVertsForPolygon_fn fco vertsArray = ( if vertsArray.count < 3 do return vertsArray local positions = for v in vertsArray collect (GetVertPos_fn fco v) local center = [0,0,0] for p in positions do center += p center /= positions.count local normal = [0,0,0] for i = 1 to positions.count do ( local j = mod i positions.count + 1 normal += cross (positions[i] - center) (positions[j] - center) ) normal = normalize normal local viewDir = (inverse(getViewTM())).row3 if (dot normal viewDir) > 0 do normal = -normal local axisX = normalize (positions[1] - center) local axisY = normalize (cross normal axisX) local vertAngles = #() for i = 1 to vertsArray.count do ( local dir = positions[i] - center local angle = atan2 (dot dir axisY) (dot dir axisX) append vertAngles #(vertsArray[i], angle) ) for i = 1 to vertAngles.count do ( for j = 1 to vertAngles.count - i do ( if vertAngles[j][2] > vertAngles[j+1][2] do ( local temp = vertAngles[j] vertAngles[j] = vertAngles[j+1] vertAngles[j+1] = temp ) ) ) for va in vertAngles collect va[1] ) fn CreatePolyFromDisconnectedVerts_fn fco = ( if not HasDisconnectedVertPair_fn fco do return false local selVerts = (getSelection_fn fco #Vertex num_CurO) as array local sortedVerts = SortVertsForPolygon_fn fco selVerts local NumF1 = getNumFaces_fn fco num_CurO createFace_fn fco sortedVerts num_CurO commit_fn fco num_CurO local NumF2 = getNumFaces_fn fco num_CurO NumF1 != NumF2 ) fn so1_operation_fn fco = ( if CreatePolyFromDisconnectedVerts_fn fco then ( max select return true ) if not VertConn_fn fco then if not (VertConnect_ThroughE_fn fco) then try NEXTvettoconnect_fn fco catch() if FilterOpenVert_fn fco then TriFace_fn fco max select ) fn DivideEdg_fn fco = ( local edgeSel = (getEdgeSelection_fn fco num_CurO) as array if edgeSel.count > 0 do ( if isEditPolyClass fco then ( fco.DivideEdge edgeSel[1] 0.5 node:(getNode_fn num_CurO) fco.Commit() ) else ( $.selectedVerts = #{(fco.DivideEdge edgeSel[1] 0.5)} ) ) ) fn FaceBy2EdgVectors fco = try ( local NumF1 = getNumFaces_fn fco num_CurO local OE = #() openEdges.Check currentTime (getNode_fn num_CurO) &OE local edg0 = getSelection_fn fco #Edge num_CurO if not (edg0 * (OE as BitArray)).isEmpty then ( local VuE = for i in edg0 collect getVertsUsingEdge_fn fco i num_CurO local v0 = ((VuE[1] * VuE[2]) as array)[1] local v12 = ((VuE[1] - VuE[2]) + (VuE[2] - VuE[1])) as array local v0_pos = getVertex_fn fco v0 num_CurO local v12pos = for i in v12 collect getVertex_fn fco i num_CurO local newVpos = (v12pos[1] + (v12pos[2] - v0_pos)) local newV if isEditPolyClass fco then ( newV = fco.CreateVertex (newVpos * (getNode_fn num_CurO).transform) node:(getNode_fn num_CurO) fco.CreateFace #(v12[1], newV, v12[2], v0) node:(getNode_fn num_CurO) fco.Commit() ) else ( newV = polyop.createVert fco newVpos polyop.createPolygon fco #(v12[1], newV, v12[2], v0) ) setSelection_fn fco #Vertex #{newV} num_CurO ) local NumF2 = getNumFaces_fn fco num_CurO NumF1 != NumF2 ) catch false fn EdgConnect_fn fco =( local NumF01 = getNumFaces_fn fco num_CurO fco.connectEdgeSegments = 1 fco.ButtonOp #ConnectEdges commit_fn fco num_CurO local NumF02 = getNumFaces_fn fco num_CurO NumF01 != NumF02 ) fn conn2Edg_fn fco =( local NumF1 = getNumFaces_fn fco num_CurO EdgConnect_fn fco local NumF2 = getNumFaces_fn fco num_CurO NumF1 != NumF2 ) fn BridgeEdge_fn fco =( local NumF1 = getNumFaces_fn fco num_CurO if isEditPolyClass fco then ( local theNode = getNode_fn num_CurO local edgeSel = fco.GetSelection #Edge node:theNode if not edgeSel.isEmpty do ( fco.SetSelection #Edge #{} node:theNode setEdgeSelection theNode fco edgeSel keep:on ) ) fco.ButtonOp #BridgeEdge commit_fn fco num_CurO local NumF2 = getNumFaces_fn fco num_CurO NumF1 != NumF2 ) fn ConnectVertByEdges_fn fco = ( local success = false local originalEdges = Es_ as bitarray try ( local baE = (getEdgeSelection_fn fco num_CurO) as Array -- Проверка наличия ровно двух рёбер if baE.count != 2 do return false -- Проверка валидности индексов local maxEdge = getNumEdges_fn fco num_CurO if baE[1] < 1 or baE[1] > maxEdge or baE[2] < 1 or baE[2] > maxEdge do return false -- Проверяем, имеют ли рёбра общую грань (если нет - это случай для Bridge, не для нас) local facesE1 = getFacesUsingEdge_fn fco baE[1] num_CurO local facesE2 = getFacesUsingEdge_fn fco baE[2] num_CurO if (facesE1 * facesE2).isEmpty do return false local VuE1 = try ((getVertsUsingEdge_fn fco baE[1] num_CurO) as Array) catch #() local VuE2 = try ((getVertsUsingEdge_fn fco baE[2] num_CurO) as Array) catch #() -- Проверка, что мы получили вершины if VuE1.count < 2 or VuE2.count < 2 do return false -- Проверяем, что вершины рёбер не совпадают (рёбра не смежные по вершине) local sharedVerts = (VuE1 as bitarray) * (VuE2 as bitarray) if not sharedVerts.isEmpty do return false with redraw off ( local NumF1 = getNumFaces_fn fco num_CurO fco.connectEdgeSegments = 1 fco.ConnectEdges () commit_fn fco num_CurO local baE1 = getEdgeSelection_fn fco num_CurO setVertSelection_fn fco (#{VuE1[1]} + #{VuE2[1]}) num_CurO fco.ButtonOp #ConnectVertices commit_fn fco num_CurO local testF1 = getNumFaces_fn fco num_CurO setVertSelection_fn fco (#{VuE1[2]} + #{VuE2[2]}) num_CurO fco.ButtonOp #ConnectVertices commit_fn fco num_CurO local testF2 = getNumFaces_fn fco num_CurO local needAlternative = (testF1 == testF2) if needAlternative do ( setVertSelection_fn fco (#{VuE1[1]} + #{VuE2[2]}) num_CurO fco.ButtonOp #ConnectVertices commit_fn fco num_CurO setVertSelection_fn fco (#{VuE1[2]} + #{VuE2[1]}) num_CurO fco.ButtonOp #ConnectVertices commit_fn fco num_CurO ) setSelection_fn fco #Edge #{} num_CurO setEdgeSelection_fn fco baE1 num_CurO if isEditPolyClass fco then ( fco.ButtonOp #RemoveEdge fco.Commit() ) else ( fco.EditablePoly.Remove () ) -- Безопасная очистка вершин с 2 рёбрами try ( local currentMaxEdge = getNumEdges_fn fco num_CurO local validEdges = for e in baE where (e >= 1 and e <= currentMaxEdge) collect e if validEdges.count > 0 do ( local VuEend = try ((getVertsUsingEdge_fn fco (validEdges as bitarray) num_CurO) as Array) catch #() if VuEend.count > 0 do ( local VertClear2 = (for i in VuEend where ( try ((getEdgesUsingVert_fn fco i num_CurO).numberset == 2) catch false ) collect i) as BitArray if not VertClear2.isEmpty do ( setVertSelection_fn fco VertClear2 num_CurO remove_fn fco #Vertex num_CurO ) ) ) ) catch () -- Пытаемся выбрать оставшиеся валидные рёбра try ( local finalMaxEdge = getNumEdges_fn fco num_CurO local finalValidEdges = for e in baE where (e >= 1 and e <= finalMaxEdge) collect e if finalValidEdges.count > 0 then ( setEdgeSelection_fn fco (finalValidEdges as bitarray) num_CurO ) else ( setEdgeSelection_fn fco #{} num_CurO ) ) catch () local NumF2 = getNumFaces_fn fco num_CurO success = (NumF1 != NumF2) ) ) catch ( success = false ) -- Восстанавливаем исходное выделение при неудаче if not success do ( try ( local currentMaxEdge = getNumEdges_fn fco num_CurO local validOriginalEdges = for e in originalEdges where (e >= 1 and e <= currentMaxEdge) collect e if validOriginalEdges.count > 0 then ( setEdgeSelection_fn fco (validOriginalEdges as bitarray) num_CurO ) ) catch () ) success ) -- Fixed: FlowConnect now works properly with the switcher fn so2_operation_fn fco =( -- Initialize FlowConnectSwitcher if needed if FlowConnectSwitcher == undefined do FlowConnectSwitcher = false if Es_.numberset==1 then ( DivideEdg_fn fco ) else if Es_.numberset==2 then ( -- For 2 edges: try various connection methods if not ConnectVertByEdges_fn fco then if (getNumEdges_fn fco num_CurO) <= 4 then ( -- Check FlowConnect for small edge count too if FlowConnectSwitcher then ( macros.run "PolyTools" "FlowConnect" ) else ( EdgConnect_fn fco ) ) else ( if not BridgeEdge_fn fco then if not conn2Edg_fn fco then if not FaceBy2EdgVectors fco then EdgeConnect_ThroughE_fn fco ) ) else ( -- For more than 2 edges if not BridgeEdge_fn fco then ( -- FlowConnect switcher check - FIXED: Now properly triggers FlowConnect if FlowConnectSwitcher then ( macros.run "PolyTools" "FlowConnect" ) else ( EdgConnect_fn fco ) ) ) ) fn so4_operation_fn fco =( if (getSelection_fn fco #Face num_CurO).numberset > 1 then if BridgePolygon_fn fco == off do (fco.ToggleCommandMode #QuickSlice) else ( try fco.tesselateBy = 1 catch fco.tessellateByFace = 1 fco.buttonop #Tessellate commit_fn fco num_CurO max select ) ) fn vSETi_sel_fn fco = ( local i_sel = 1 local Vet_ar = #() -- Use our wrapper function that handles both Editable_Poly and Edit_Poly for i=1 to selection.count do ( local vertSel = try (getVertSelection_fn fco i) catch #{} if vertSel != undefined then ( append Vet_ar vertSel ) else ( append Vet_ar #{} ) ) -- Find first non-empty selection while i_sel <= Vet_ar.count and (Vet_ar[i_sel] == undefined or Vet_ar[i_sel].numberset == 0) do i_sel += 1 -- Safety check if i_sel > Vet_ar.count do i_sel = 1 i_sel ) fn eSETi_sel_fn fco = ( local i_sel = 1 local Edge_ar = #() for i=1 to selection.count do ( local edgeSel = try (getEdgeSelection_fn fco i) catch #{} if edgeSel != undefined then ( append Edge_ar edgeSel ) else ( append Edge_ar #{} ) ) while i_sel <= Edge_ar.count and (Edge_ar[i_sel] == undefined or Edge_ar[i_sel].numberset == 0) do i_sel += 1 if i_sel > Edge_ar.count do i_sel = 1 i_sel ) fn getRandomPos = ( local mmax = 0.1 local mmin = 0.001 [random mmax mmin, random mmax mmin, random mmax mmin] ) fn InsertFormSphere_fn = ( sel = selection as array if sel.count == 2 then ( a = sel[1] b = sel[2] if a.parent == b then ( avarPos = (a.pos + b.pos) / 2.0 avarPos += getRandomPos() c = copy b c.pos = avarPos try ( c.scale = (a.scale + b.scale) / 2.0 ) catch() a.parent = c c.parent = b c.wirecolor = (a.wirecolor + b.wirecolor) / 2.0 try ( c.radius = (a.radius + b.radius) / 2.0 ) catch() try ( c.radius += 0.001 ) catch() select c ) else if b.parent == a then ( avarPos = (a.pos + b.pos) / 2 avarPos += getRandomPos() c = copy a c.pos = avarPos try ( c.scale = (a.scale + b.scale) / 2.0 ) catch() b.parent = c c.parent = a c.wirecolor = (a.wirecolor + b.wirecolor) / 2.0 try ( c.radius = (a.radius + b.radius) / 2.0 ) catch() try ( c.radius += 0.001 ) catch() select c ) else ( messageBox "The selected Form Spheres are not in the same branch." ) ) else ( messageBox "Select two Form Spheres." ) ) fn AppendFormSphere_fn = ( if selection.count != 0 then ( aftersel = #() for i in selection do ( maxmin = i.max - i.min widx = maxmin[1] / 1.5 widy = maxmin[2] / 1.5 widz = maxmin[3] / 1.5 wirecol = i.wirecolor if keyboard.shiftpressed then obj2 = instance i else obj2 = copy i append aftersel obj2 obj2.wirecolor = wirecol obj2.parent = i camview = Inverse(getViewTM()) camdir = -camview.row3 if getRefCoordsys() == #local then ( seldir = obj2.objecttransform dirs = #(acos (dot camdir seldir.row1), acos (dot camdir -seldir.row1), acos (dot camdir seldir.row2), acos (dot camdir -seldir.row2), \ acos (dot camdir seldir.row3), acos (dot camdir -seldir.row3)) eldodir = finditem dirs (amin dirs) movesarr = #([0,-widy,0], [0,widy,0], [widx,0,0], [-widx,0,0], [0,0,-widz], [0,0,widz]) in coordsys local move obj2 movesarr[eldodir] ) else ( dirs = #(acos (dot camdir [1,0,0]), acos (dot camdir [-1,0,0]), acos (dot camdir [0,1,0]), acos (dot camdir [0,-1,0]), \ acos (dot camdir [0,0,1]), acos (dot camdir [0,0,-1])) eldodir = finditem dirs (amin dirs) movesarr = #([0,-widy,0], [0,widy,0], [widx,0,0], [-widx,0,0], [0,0,-widz], [0,0,widz]) move obj2 movesarr[eldodir] ) ) aftersel.pos += getRandomPos() select aftersel ) else ( messageBox "Select one Form Sphere." ) ) ------------------------- Script ------------------------- -- Main execution wrapped in undo block for stability undo "UniConnector" on ( case classof selection[1] of ( Form_Sphere: (if selection.count==2 then InsertFormSphere_fn() else if selection.count !=0 then AppendFormSphere_fn()) default: ( if selection.count==0 then GabaritAssigner_fn() else if (subObjectLevel==0 or getCommandPanelTaskMode()!=#modify) then modPanel.addModToSelection (Edit_Poly ()) ui:on else if (gco = modPanel.getCurrentObject(); iskindof gco Editable_poly or iskindof gco Edit_poly) then ( num_CurO = 1 case subobjectlevel of ( 1: ( local selected_arr = #() for i=1 to selection.count do ( local vertSel = try (getVertSelection_fn gco i) catch #{} if vertSel != undefined and not vertSel.isEmpty do append selected_arr vertSel ) if selected_arr.count>0 and selected_arr[1].numberset>1 then ( num_CurO = vSETi_sel_fn gco so1_operation_fn gco ) else case selected_arr.count of ( 0: ( try (UIAccessor.PressButton (for i in windows.getChildrenHWND #max where i[5]=="Target Weld" do exit with i[1])) catch UIAccessor.PressButton (for i in (windows.getChildrenHWND (windows.getDesktopHWND())) where i[5]=="Target Weld" do exit with i[1]) ) 1: ( fn RemoveArr_fn = ( vertAr_cgl = #() global vertsBitArr1 = #() global FirstVert = #() ) ( RemoveArr_fn() unregisterredrawviewscallback orderVertSel_fn fn orderVertSel_fn = ( if selection.count>0 and classof gco==Editable_Poly and subobjectlevel==1 do ( try( vertsBitArr1 = gco.GetSelection #Vertex as array toConnectVerts=((vertsBitArr1 as bitarray)-(FirstVert as bitarray)) as array if vertsBitArr1.count==1 do FirstVert[1]=vertsBitArr1[1] if keyboard.ShiftPressed then undo "VertConnect" on ( if toConnectVerts.count<3 then for i=1 to toConnectVerts.count do ( polyop.weldVerts $ toConnectVerts[i] FirstVert[1] (polyop.getVert $ toConnectVerts[i]) ) else for i=1 to toConnectVerts.count do ( gco.SetSelection #Vertex ((FirstVert as bitarray) + #{(toConnectVerts[i])}) macros.run "Editable Polygon Object" "EPoly_Connect" ) unregisterredrawviewscallback orderVertSel_fn RemoveArr_fn() ) ) Catch() ) ) registerredrawviewscallback orderVertSel_fn ) ) ) ) 2: ( local selected_arr = #() for i=1 to selection.count do ( local edgeSel = try (getEdgeSelection_fn gco i) catch #{} if edgeSel != undefined and not edgeSel.isEmpty do append selected_arr edgeSel ) if selected_arr.count==0 do try (UIAccessor.PressButton (for i in windows.getChildrenHWND #max where i[5]=="Cut" do exit with i[1])) catch UIAccessor.PressButton (for i in (windows.getChildrenHWND (windows.getDesktopHWND())) where i[5]=="Cut" do exit with i[1]) if selected_arr.count>0 do ( num_CurO = eSETi_sel_fn gco Es_ = getEdgeSelection_fn gco num_CurO so2_operation_fn gco ) ) 3: if not (max select; BridgeBorder_fn gco) do macros.run "Editable Polygon Object" "EPoly_Cap" 4: ( if (getSelection_fn gco #Face num_CurO).numberset == 0 then try gco.ToggleCommandMode #CutFace catch gco.ToggleCommandMode #Cut else so4_operation_fn gco ) 5: gco.ToggleCommandMode #QuickSlice ) ) else if superclassof gco == shape do try (ShapeConn_fn gco; max select) catch() ) ) ) redrawviews() )