[Closed] array sort objects by position
Hi guys,
Firstly, I’m new, so hi to everyone.
I’m trying to write a script that checks all objects in my selection and deletes all those that have a duplicate xyz position. I’m sure there is a script out there that already does this but that defeats the learning part of this.
I originally wrote
posarray = #()
for i in selection do
(
(
if findItem posarray i.pos == 0
then
append posarray i.pos
else
delete i
)
)
which is horribly slow, so I had a rethink and it seemed to me the easiest and most logical way to do this would be to collect all the objects and their positions in an array and then sort them by position. That way I only have to check each item and the few next to it in the array for duplicates till I get to a different value and repeat the process (if that makes sense).
Basically I have started to try and write this but I can’t sort my array by its position.
Can anyone point me in the right direction please?
Cheers in advance
As it’s a Friday.
--script selects identical objects in same position with a tolerance of:
Tolerance = 0.00001 -- change this value to adjust tolerance.
AR_temp = #()
for o in objects do
(
for p in objects do
(
if p != o then
(
a = p.center - o.center
if abs(a.x + a.y + a.z) < Tolerance then append AR_temp p
)
)
)
select AR_temp
--delete AR_temp -- uncomment this line to make it auto delete
thanks for your quick reply! … I’m somewhat jealous of how easily yo uanswered that.
I think it makes sense to me. The for loop inside another confuses me a little but the more I read over it and play with I get the idea behind it.
My next question is, how do I pick out all the unique objects out of AR_temp?
The script above does what you asked for originally, it deletes all objects that have identical xyz coordinates.
If you want to leave one object in each position you need to check whether the first object has already been marked for deletion.
--script selects identical objects in same position with a tolerance of:
Tolerance = 0.00001 -- change this value to adjust tolerance.
AR_temp = #()
for o in objects do
(
for p in objects do
(
if p != o and findItem AR_temp o == 0 then
(
l = length (p.center - o.center)
if abs l < Tolerance then append AR_temp p
)
)
)
select AR_temp
--delete AR_temp -- uncomment this line to make it auto delete
to be mathematically correct it’s:
a = p.center - o.center
if sqrt(a.x*a.x + a.y*a.y + a.z*a.z) < Tolerance
to be mxs effective it’s:
distance p.center o.center < Tolerance
Thanks guys, thats all very useful. I appreciate your help and speed with responding. Now to go and play with it and try learn a bit more.
but all methods above are VERY slow for really large amount of nodes to check.
the way to make it faster is to use 2D(or 3D) grid-buffer.
there is a sample that can be easily modified to do searching by node distance:
http://forums.cgsociety.org/showpost.php?p=7032076&postcount=23
seed 0
for k=1 to 10000 do box name:(k as string) pos:(random [-1,-1,-1] [1,1,1])
does anyone want to be challenged?
for this scene of 10000 nodes my algorithm finds all nodes placed in the same position with threshold of 0.01 for ~200ms…
if nobody is interested to beat me i will post a solution a little later…
Edit:
After a little optimization it takes ~160ms. Looks good enough for me to try beat myself
I am interested in seeing a faster solution but my mxs skills aren’t good enough for a challenge.
I am interested because I had a similar problem to solve a couple of months ago. I was given some Adobe Illustrator artwork to bring into Max. The imported splines were full of duplicate segments. The best I could come up with was a nested loop like the solution above. And yes, it was very slow ~ 5 – 10 minutes to clean each file.
Ok I had a think about this and this is the best I can do:
(
/*
seed 0
for k=1 to 10000 do box name:(k as string) pos:(random [-1,-1,-1] [1,1,1])
*/
clearselection()
fn sortByAxis ob1 ob2 =
(
if ob1.pos.x < ob2.pos.x then -1 else 1
)
local T1 = timeStamp()
local delObs = #()
local tolerance = 0.01
local obs = objects as array
local tObs = obs.count
qSort obs sortByAxis
for i = 1 to tObs-1 do
(
local c = i+1
while c<=tObs and abs (obs[i].pos.x - obs[c].pos.x) < tolerance do
(
if (distance obs[i].pos obs[c].pos) < tolerance then append delObs obs[i]
c+=1
)
)
select delObs
local T2 = timeStamp() - T1
)
It takes about 9 seconds to find 29 objects with a position within the tolerance in 10,000 objects. The nested loop at the top of the thread takes 566 seconds so mine over 50 times quicker but still over 50 times slower than denisT’s.
@denisT: Would you mind posting your solution please?
with redraw off
(
struct EasyNodeData (node, near = #())
global _nodes = for n in (objects as array) collect EasyNodeData node:n
gc()
/********** find close enough ******************/
select objects
t1 = timestamp()
m1 = heapfree
bmin = selection.min
bmax = selection.max
cell = pow (1.0*_nodes.count) (1./3)
cell_size = cell*[1,1,1]/(bmax - bmin)
threshold = 0.01
local buffer = #()
for n in _nodes do
(
dpos = 1 + (n.node.transform.pos - bmin)*cell_size
x = dpos.x
y = dpos.y
z = dpos.z
if buffer[x] == undefined do buffer[x] = #()
if buffer[x][y] == undefined do buffer[x][y] = #()
if buffer[x][y][z] == undefined do buffer[x][y][z] = #()
append buffer[x][y][z] n
)
t2 = timestamp()
m2 = heapfree
format "BUFFER... nodes:% time:% memory:% cell:%
" _nodes.count (t2 - t1) (m1 - m2) cell
local source, target
local done = #()
for x = 1 to buffer.count where buffer[x] != undefined do
for y = 1 to buffer[x].count where buffer[x][y] != undefined do
for z = 1 to buffer[x][y].count where buffer[x][y][z] != undefined do
(
for n=1 to buffer[x][y][z].count-1 do
(
source = buffer[x][y][z][n]
for k=n+1 to buffer[x][y][z].count do
(
target = buffer[x][y][z][k]
if (distance source.node.transform.pos target.node.transform.pos <= threshold) do
(
append source.near target
append target.near source
)
)
)
)
t3 = timestamp()
m3 = heapfree
format "COMPUTE... nodes:% time:% memory:% cell:%
" _nodes.count (t3 - t2) (m2 - m3) cell
format "COMPLETE... nodes:% time:% memory:%
" _nodes.count (t3 - t1) (m1 - m3)
intersected = for n in _nodes where n.near.count > 0 collect n.node
format " intersected:%
" intersected.count
clearselection()
)
/*
with undo off with redraw off
(
delete objects
seed 0
for k=1 to 10000 do box name:(k as string) width:1 length:1 height:1 pos:(random [-1,-1,-1] [1,1,1])
)
*/
but… there is some bug in my code… i don’t have time right now to look for it. so everyone is free to help me with it
Thanks for posting your solution Denis. I’ll have a look at it later. I’d have to understand it before I could have a go at getting it working.