(
try destroydialog ::RO_BLUR_SELECTION catch()
rollout RO_BLUR_SELECTION "v0.4 - PolyTools3D" width:166 height:258
(
groupbox grp1 "Grid Count:" pos:[ 8, 8] width:150 height:44
spinner sp_count_x "X:" pos:[16, 28] fieldwidth:40 range:[3, 50, 9] type:#integer
spinner sp_count_y "Y:" pos:[88, 28] fieldwidth:40 range:[3, 50, 21] type:#integer
groupbox grp2 "Grid Spacing:" pos:[ 8, 60] width:150 height:44
spinner sp_space_x "X:" pos:[16, 80] fieldwidth:40 range:[0, 100, 5] type:#float
spinner sp_space_y "Y:" pos:[88, 80] fieldwidth:40 range:[0, 100, 5] type:#float
spinner sp_node_scale "Node Scale: " pos:[23,112] fieldwidth:63 range:[ 0.1, 1.0, 0.25] type:#float scale:0.01
spinner sp_pos_z "Grid Position Z: " pos:[ 8,136] fieldwidth:63 range:[-1E4, 1E2, 0.0 ] type:#float
spinner sp_drag_dist "Drag Distance: " pos:[ 9,160] fieldwidth:63 range:[ 0, 100, 1.0 ] type:#float
button bt_create_grid "Create Grid" pos:[ 8,186] width:150 height:32
button bt_reset_z_pos "Rese Z Pos" pos:[ 8,218] width:150 height:32
local pAllNodes = #() -- Array of all the grid nodes.
local pNeighborsLUT = #() -- LUT of each node neighbors.
local pLimitsLUT = #() -- LUT of each node Z limits.
local pGridPosZ = 0.0 -- Grid Z positoin "floor"
local pGridCountX = 15 -- Amount of nodes in X
local pGridCountY = 15 -- Amount of nodes in Y
local pSpacingX = 5.0 -- X Distance between nodes
local pSpacingY = 5.0 -- Y Distance between nodes
local pDragDistance = 1.0 -- The distance at which the neighbors start moving
local pNodeScale = 0.5 -- The scale of each point node
local pColorDefault = [130,230, 50]
local pColorTop = [230, 50, 50]
local pColorBottom = [ 40,130,240]
-- FUNCTIONS
local CallbackUpdateNeighborsPos
local CallbackUpdateColors
local CallbackSelectionChanged
local CallbackNodeDeleted
function CreateGridHelpers = with undo off
(
pAllNodes = #()
grid_offset_x = ((pGridCountX-1)*pSpacingX) * 0.5
grid_offset_y = ((pGridCountY-1)*pSpacingY) * 0.5
tmp = point box:on axistripod:off cross:off size:(pSpacingX*2)
for y = 0 to pGridCountY-1 do
(
for x = 0 to pGridCountX-1 do
(
x_pos = x*pSpacingX - grid_offset_x
y_pos = y*pSpacingY - grid_offset_y
obj = instance tmp pos:[x_pos, y_pos, pGridPosZ] wirecolor:pColorDefault
append pAllNodes obj
)
)
delete tmp
settransformlockflags pAllNodes #{1,2,4..9}
pAllNodes.scale = [1, 1, 1] * pNodeScale
for j = 1 to pAllNodes.count do pAllNodes[j].name = j as string
)
/*
This will build a LUT of neighbor rings. Note that it will add some neighbors that will never be affected
by a given node, and so the real-time performance will later be worse. For small grids, it doesn't represent
a big issue, but for large grids, a better algorithm should be used to find out only the affected neighbors.
This function is roughly 2-3 X faster than the previous version
*/
fn BuildNeighborsLUT =
(
maxRings = int (amax pGridCountX pGridCountY)
maxDepth = int (maxRings / 2)
maxDepthX = (amin maxRings pGridCountX) / 2
maxDepthY = (amin maxRings pGridCountY) / 2
pNeighborsLUT = #()
delta = if pGridCountX < pGridCountY then amin pGridCountX pGridCountY else amax pGridCountX pGridCountY
for idx = 1 to pGridCountX*pGridCountY do
(
node_x = int (mod (idx-1) delta + 1)
node_y = int ( (idx-1) / delta + 1)
if node_x <= maxDepthX then
(
left = int (node_x - 1)
right = maxDepthX
)else(
left = maxDepthX
right = int (abs (pGridCountX-node_x))
)
if node_y <= maxDepthY then
(
top = int (node_y - 1)
bottom = maxDepthY
)else(
top = maxDepthY
bottom = int (abs (pGridCountY-node_y))
)
sx = int (node_x - left )
ex = int (node_x + right )
sy = int (node_y - top )
ey = int (node_y + bottom)
neighbors = for j = 1 to maxDepth collect #()
for y = sy to ey do
(
y2 = (y-1)*pGridCountX
absy = abs (y-node_y)
for x = sx to ex do
(
level = amax 1 (abs (x-node_x)) absy
append neighbors[level] pAllNodes[y2+x]
)
)
for j = 1 to neighbors[1].count where neighbors[1][j] == pAllNodes[idx] do deleteitem neighbors[1] j
pNeighborsLUT[idx] = neighbors
limit = amin node_x node_y (pGridCountX+1-node_x) (pGridCountY+1-node_y)
top = limit * pDragDistance
bottom = -limit * pDragDistance
pLimitsLUT[idx] = [top, bottom]
)
)
fn PaintNodes obj selectedOnly:true = with undo on
(
if not obj.isselected or not selectedOnly do
(
idx = obj.name as integer
top = pLimitsLUT[idx][1] + pGridPosZ
bottom = pLimitsLUT[idx][2] + pGridPosZ
if obj.pos.z <= bottom then
(
obj.wirecolor = pColorBottom
)
else if obj.pos.z >= top then
(
obj.wirecolor = pColorTop
)
else
(
obj.wirecolor = pColorDefault
)
)
)
fn CallbackUpdateColors obj = PaintNodes obj
fn CallbackSelectionChanged = with undo off
(
st=timestamp()
deleteallchangehandlers id:#ID_0X7BAD0113
-- We could keep track of the selected nodes and only update those
for obj in pAllNodes do PaintNodes obj selectedOnly:false
for obj in selection where finditem pAllNodes obj != 0 do
(
when transform obj changes id:#ID_0X7BAD0113 obj do CallbackUpdateNeighborsPos obj
)
-- Let's keep the heap low
gc light:on
)
-- This callback process "unnecessarily" the same nodes more than once when having multiple selection
fn CallbackUpdateNeighborsPos node =
(
idx = node.name as integer
neighbors = pNeighborsLUT[idx]
limits = pLimitsLUT[idx]
top = limits[1] + pGridPosZ
bottom = limits[2] + pGridPosZ
if node.pos.z >= top then
(
node.pos.z = top
)
else if node.pos.z <= bottom then
(
node.pos.z = bottom
)
nodez = node.pos.z
done = false
for level = 1 to neighbors.count while not done do
(
delta = level * pDragDistance
done = true
for obj in neighbors[level] where not obj.isselected do
(
-- Limit Z Position -------------------
if (obj.pos.z - nodez) > delta then
(
obj.pos.z = nodez + delta
done = false
)
else if (nodez - obj.pos.z) > delta then
(
obj.pos.z = nodez - delta
done = false
)
)
)
)
-- If we delete one node, then dewlete the whole grid and clear the arrays
fn CallbackNodeDeleted =
(
nodes = callbacks.notificationparam()
if finditem pAllNodes nodes[1] != 0 do pAllNodes = pNeighborsLUT = pLimitsLUT = #()
)
fn RemoveCallbacks =
(
deleteallchangehandlers id:#ID_0X7BAD0113
deleteallchangehandlers id:#ID_0X7BAD0114
callbacks.removescripts id:#ID_0X7BAD0113
)
/* ######################################################################################################################## */
/* ######################################################################################################################## */
/* ######################################################################################################################## */
on RO_BLUR_SELECTION oktoclose do
(
if pAllNodes.count > 0 do
(
answer = yesNoCancelBox "Would you like delete the grid nodes?"
if answer == #cancel do return false
try (pAllNodes.wirecolor = pColorDefault) catch()
RemoveCallbacks()
if answer == #yes do try (delete pAllNodes) catch()
return true
)
)
on RO_BLUR_SELECTION open do RemoveCallbacks()
on bt_create_grid pressed do
(
RemoveCallbacks()
if keyboard.controlPressed then delete pAllNodes else delete objects
gc()
completeredraw()
setwaitcursor()
pGridCountX = sp_count_x.value
pGridCountY = sp_count_y.value
pSpacingX = sp_space_x.value
pSpacingY = sp_space_y.value
pNodeScale = sp_node_scale.value
pGridPosZ = sp_pos_z.value
pDragDistance = sp_drag_dist.value
CreateGridHelpers()
completeredraw()
BuildNeighborsLUT()
callbacks.addscript #selectionSetChanged "::RO_BLUR_SELECTION.CallbackSelectionChanged()" id:#ID_0X7BAD0113
callbacks.addscript #selectedNodesPreDelete "::RO_BLUR_SELECTION.CallbackNodeDeleted()" id:#ID_0X7BAD0113
when transform pAllNodes changes handleAt:#redrawViews id:#ID_0X7BAD0114 obj do CallbackUpdateColors obj
setarrowcursor()
completeredraw()
)
on sp_node_scale changed arg do with undo off
(
pNodeScale = arg
pAllNodes.scale = [1, 1, 1] * pNodeScale
redrawviews()
)
on sp_drag_dist changed arg do pDragDistance = arg
on sp_pos_z changed arg do with undo off
(
pGridPosZ = arg
pAllNodes.pos.z = pGridPosZ
redrawviews()
)
on bt_reset_z_pos pressed do with undo off
(
pAllNodes.pos.z = pGridPosZ
pAllNodes.wirecolor = pColorDefault
redrawviews()
)
)
createdialog RO_BLUR_SELECTION
)