“but it kinda is a pity that people don’t want to play…”
I would like to play but I’m on holidyas, have few time and… really don’t understand very well the challenge.
“it’s fair enough… but one problem is when some threads have the same length…”
In your template, that’s not possible. Each thread has a different length. Is it a possibility?
Questions:
- do we have to care about beads radius? Can they intersect each other in the same thread?
- Geometry will always be arc for threads and sphere for beads?
“Can you also put a reference time with this?”
time:255 on my new laptop. And yours? (I don’t know if my new machine is bad or very bad!! 🙁 )
Yes, it’s kind of confusing… I don’t think I get the whole picture.
If I understand it correctly, “fair” boils down to: threads get priority based on length.
In such a case, wouldn’t it be as simple as sorting an array of threads by their length, then looping through it longest-to-shortest repeatedly assigning a bead, until all beads are assigned? (Not the fastest way, but is that what it supposed to do?)
What are the constraints anyhow?
Are we assigning numbers of beads or actual bead-objects? Do the beads have to be in their original input order?
Do threads get “full” at some point and they have to be skipped?
Do we have all the beads at the start or do they sort of “keep arriving”?
[edit]Oh, this is about even distribution of objects? Not distributing tasks to “threads”? : )
For the first part, I think this distributes the beads in a “fair” way:
fn DistributePointsInShapes mShapes numPoints:0 =
(
fn SortByLength t1 t2 = t2[2] - t1[2]
totalLength = 0
result = for j in mShapes collect
(
length = curvelength j
totalLength += length
#(j, length)
)
qsort result SortByLength
if numPoints <= result.count then
(
result.count = numPoints
for j in result do j[2] = 1
)else(
numPoints -= result.count
done = 0
for j in result do
(
amount = int ((j[2] / (totalLength/numPoints)) + 0.5)
j[2] = amount + 1
done += amount
)
result[1][2] += (numPoints-done)
)
return result
)
For the second part (distributing the beads along the shapes), here is a simple function:
fn DistributePointsAlongShapes &mShapes =
(
for j in mShapes do
(
mmin = 1.0/(j[2]+1)
j[2] = for i = 1 to j[2] collect interpcurve3d j[1] 1 (i*mmin)
)
)
Test:
(
st=timestamp(); sh=heapfree
result = DistributePointsInShapes (shapes as array) numPoints:1000
DistributePointsAlongShapes &result
format "time:% heap:%
" (timestamp()-st) (sh-heapfree)
gc()
delete $bead*
for j in result do
(
for i in j[2] do geosphere pos:i radius:2 segs:1 name:"bead" wirecolor:green
)
)
try(destroydialog BeadsAndThreads) catch()
global BeadsAndThreadsPos = if BeadsAndThreadsPos == undefined then unsupplied else BeadsAndThreadsPos
global BeadsAndThreadsParams = if BeadsAndThreadsParams == undefined then #(0,1,20,5,10) else BeadsAndThreadsParams
rollout BeadsAndThreads "Beads And Threads" width:250
(
fn p_sort p1 p2 index:1 = if p1[index] < p2[index] then 1 else if p1[index] > p2[index] then -1 else 0
local xx = BeadsAndThreadsParams
local threads = #()
local thread_data = #()
local all_len
local min_len
spinner num_beads_sp "Beads: " type:#integer range:[0,1000000,xx[1]] fieldwidth:56 align:#right offset:[4,8]
spinner num_threads_sp "Threads: " type:#integer range:[1,100,xx[2]] fieldwidth:56 align:#right offset:[4,0]
group "Create: "
(
spinner min_radius_sp "Min Radius: " type:#integer range:[1,100,xx[3]] fieldwidth:56 align:#right offset:[4,0]
spinner bead_radius_sp "Bead Radius: " type:#integer range:[1,100,xx[4]] fieldwidth:56 align:#right offset:[4,0]
spinner threads_space_sp "Spacing: " type:#integer range:[1,100,xx[5]] fieldwidth:56 align:#right offset:[4,0]
button create_bt "Create" width:232 align:#center offset:[0,8]
)
group ""
(
button distribute_bt "Distribute" width:232 align:#center offset:[0,-6]
button distribute2_bt "Distribute 2" width:232 align:#center offset:[0,6]
)
local cc = #(num_beads_sp, num_threads_sp, min_radius_sp, bead_radius_sp, threads_space_sp)
fn updateParams =
(
for k=1 to cc.count do xx[k] = cc[k].value
)
fn createThreads =
(
delete objects
all_len = 0
min_len = 1e9
num_threads = num_threads_sp.value
thread_data = #()
for i = 1 to num_threads do
(
thread = arc name:"thread" radius:(min_radius_sp.value + threads_space_sp.value*(i-1)) from:180 to:0 wirecolor:orange
rotate thread (eulerangles 90 0 0)
converttosplineshape thread
len = getshapelength thread
if len < min_len do min_len = len
all_len += len
append thread_data [gethandlebyanim thread, len, 0, 0]
)
for i = 1 to num_threads do thread_data[i].y = thread_data[i].y / all_len
)
on create_bt pressed do undo "Create" on createThreads()
fn updateBeads = if thread_data.count == num_threads_sp.value do
(
delete (getnodebyname #bead all:on)
num_beads = num_beads_sp.value
num_threads = num_threads_sp.value
t = timestamp()
h = heapfree
count = 0
if num_beads > 0 do
(
qsort thread_data p_sort index:2
--psort thread_data index:2 dir:-1
for i = 1 to num_threads while i <= num_beads do
(
thread_data[i].z = 1
count += 1
)
beads = num_beads - count
if beads > 0 do
(
int_part = 0
for i=1 to num_threads do
(
n_beads = thread_data[i].y * beads
thread_data[i].w = modf n_beads &int_part
thread_data[i].z += int_part
)
qsort thread_data p_sort index:4
--psort thread_data index:4 dir:-1
count = 0
for i = 1 to num_threads do count += thread_data[i].z
for i = 1 to (num_beads - count) do thread_data[i].z += 1
)
count = 0
for i = 1 to num_threads do
(
thread = getanimbyhandle thread_data[i].x
seed (finditem (shapes as array) thread) --(getnodeindex shapes thread)
col = random white black
num = thread_data[i].z
for k = 1 to num do
(
b = geosphere name:"bead" radius:bead_radius_sp.value segments:2 wirecolor:col
p = k/(num + 1.0)
b.pos = interpCurve3D thread 1 p
count += 1
)
)
)
format "% >>>>>>>> % - % = time:% heap:%
" num_beads num_threads count (timestamp() - t) (h - heapfree)
)
on distribute_bt pressed do updateBeads()
--on num_threads_sp changed val do updateBeads()
--on min_radius_sp changed val do updateBeads()
--on bead_radius_sp changed val do updateBeads()
--on threads_space_sp changed val do updateBeads()
on BeadsAndThreads close do
(
updateParams()
BeadsAndThreadsPos = getdialogpos BeadsAndThreads
)
on BeadsAndThreads open do
(
threads = getnodebyname #thread all:on
)
fn DistributePointsInShapes mShapes numPoints:0 =
(
fn SortByLength t1 t2 = t2[2] - t1[2]
totalLength = 0
result = for j in mShapes collect
(
length = curvelength j
totalLength += length
#(j, length)
)
qsort result SortByLength
if numPoints <= result.count then
(
result.count = numPoints
for j in result do j[2] = 1
)else(
numPoints -= result.count
done = 0
for j in result do
(
amount = int ((j[2] / (totalLength/numPoints)) + 0.5)
j[2] = amount + 1
done += amount
)
result[1][2] += (numPoints-done)
)
return result
)
fn DistributePointsAlongShapes &mShapes =
(
for j in mShapes do
(
mmin = 1.0/(j[2]+1)
j[2] = for i = 1 to j[2] collect lengthinterp j[1] 1 (i*mmin)
)
)
on distribute2_bt pressed do
(
st = timestamp()
sh = heapfree
result = DistributePointsInShapes (shapes as array) numPoints:num_beads_sp.value
DistributePointsAlongShapes &result
format "time:% heap:%
" (timestamp()-st) (sh-heapfree)
gc()
delete $bead*
for j in result do
(
for i in j[2] do geosphere pos:i name:"bead" radius:bead_radius_sp.value segments:2 wirecolor:green
)
)
--on num_beads_sp changed val do distribute2_bt.pressed() --updateBeads()
on num_beads_sp changed val do updateBeads()
)
createdialog BeadsAndThreads pos:BeadsAndThreadsPos
i added Jorge’s algorithm to the code. But our algorithms are very similar, and both have the same problem.
as you can see in my distribution when we increase number of beads some threads can lose number of beads on them… (see 26 – 27, for example. first thread loses a bead)
same story in Jorge’s, but long threads are losing beads: (see 13 – 14, for example )
Need to update:
j[2] = for i = 1 to j[2] collect [B]lengthinterp [/B]j[1] 1 (i*mmin)
to use interpcurve3d()
j[2] = for i = 1 to j[2] collect [B]interpcurve3d [/B]j[1] 1 (i*mmin)
It is a lot faster an accurate.
There are a few errors in your code:
- You use getshapelength(), which I guess is a custom DLX, so need to replace with curvelength()
- modf() function is not included in the code. You posted it earlier, but may be good to add it to the complete code if other want to do a quick test.
i don’t really care about performance and memory now. i see we are pretty good here unless we are not using ‘a sorting after every bead distribution’. which can solve the problem, but it will make everything slow
i know… that’s why i use interpcurve3d. (actually interpSplineShape :))
(i’ve replaced all spline(shape) related methods to work with both node and baseobject)
try(destroydialog BeadsAndThreads) catch()
global BeadsAndThreadsPos = if BeadsAndThreadsPos == undefined then unsupplied else BeadsAndThreadsPos
global BeadsAndThreadsParams = if BeadsAndThreadsParams == undefined then #(0,1,20,5,10) else BeadsAndThreadsParams
rollout BeadsAndThreads "Beads And Threads" width:250
(
fn p_sort p1 p2 index:1 = if p1[index] < p2[index] then 1 else if p1[index] > p2[index] then -1 else 0
fn mod_f val &int_part =
(
int_part = val as integer
flt_part = val - int_part
)
local xx = BeadsAndThreadsParams
local threads = #()
local thread_data = #()
local all_len
local min_len
spinner num_beads_sp "Beads: " type:#integer range:[0,1000000,xx[1]] fieldwidth:56 align:#right offset:[4,8]
spinner num_threads_sp "Threads: " type:#integer range:[1,100,xx[2]] fieldwidth:56 align:#right offset:[4,0]
group "Create: "
(
spinner min_radius_sp "Min Radius: " type:#integer range:[1,100,xx[3]] fieldwidth:56 align:#right offset:[4,0]
spinner bead_radius_sp "Bead Radius: " type:#integer range:[1,100,xx[4]] fieldwidth:56 align:#right offset:[4,0]
spinner threads_space_sp "Spacing: " type:#integer range:[1,100,xx[5]] fieldwidth:56 align:#right offset:[4,0]
button create_bt "Create" width:232 align:#center offset:[0,8]
)
group ""
(
button distribute_bt "Distribute" width:232 align:#center offset:[0,-6]
button distribute2_bt "Distribute 2" width:232 align:#center offset:[0,6]
)
local cc = #(num_beads_sp, num_threads_sp, min_radius_sp, bead_radius_sp, threads_space_sp)
fn updateParams =
(
for k=1 to cc.count do xx[k] = cc[k].value
)
fn createThreads =
(
delete objects
all_len = 0
min_len = 1e9
num_threads = num_threads_sp.value
thread_data = #()
for i = 1 to num_threads do
(
thread = arc name:"thread" radius:(min_radius_sp.value + threads_space_sp.value*(i-1)) from:180 to:0 wirecolor:orange
rotate thread (eulerangles 90 0 0)
converttosplineshape thread
len = curvelength thread --getshapelength thread
if len < min_len do min_len = len
all_len += len
append thread_data [gethandlebyanim thread, len, 0, 0]
)
for i = 1 to num_threads do thread_data[i].y = thread_data[i].y / all_len
)
on create_bt pressed do undo "Create" on createThreads()
fn updateBeads = if thread_data.count == num_threads_sp.value do
(
delete (getnodebyname #bead all:on)
num_beads = num_beads_sp.value
num_threads = num_threads_sp.value
t = timestamp()
h = heapfree
count = 0
if num_beads > 0 do
(
qsort thread_data p_sort index:2
--psort thread_data index:2 dir:-1
for i = 1 to num_threads while i <= num_beads do
(
thread_data[i].z = 1
count += 1
)
beads = num_beads - count
if beads > 0 do
(
int_part = 0
for i=1 to num_threads do
(
n_beads = thread_data[i].y * beads
--thread_data[i].w = modf n_beads &int_part
thread_data[i].w = mod_f n_beads &int_part
thread_data[i].z += int_part
)
qsort thread_data p_sort index:4
--psort thread_data index:4 dir:-1
count = 0
for i = 1 to num_threads do count += thread_data[i].z
for i = 1 to (num_beads - count) do thread_data[i].z += 1
)
count = 0
for i = 1 to num_threads do
(
thread = getanimbyhandle thread_data[i].x
seed (finditem (shapes as array) thread) --(getnodeindex shapes thread)
col = random white black
num = thread_data[i].z
for k = 1 to num do
(
b = geosphere name:"bead" radius:bead_radius_sp.value segments:2 wirecolor:col
p = k/(num + 1.0)
b.pos = interpCurve3D thread 1 p
count += 1
)
)
)
format "% >>>>>>>> % - % = time:% heap:%
" num_beads num_threads count (timestamp() - t) (h - heapfree)
)
on distribute_bt pressed do updateBeads()
--on num_threads_sp changed val do updateBeads()
--on min_radius_sp changed val do updateBeads()
--on bead_radius_sp changed val do updateBeads()
--on threads_space_sp changed val do updateBeads()
on BeadsAndThreads close do
(
updateParams()
BeadsAndThreadsPos = getdialogpos BeadsAndThreads
)
on BeadsAndThreads open do
(
threads = getnodebyname #thread all:on
)
fn DistributePointsInShapes mShapes numPoints:0 =
(
fn SortByLength t1 t2 = t2[2] - t1[2]
totalLength = 0
result = for j in mShapes collect
(
length = curvelength j
totalLength += length
#(j, length)
)
qsort result SortByLength
if numPoints <= result.count then
(
result.count = numPoints
for j in result do j[2] = 1
)else(
numPoints -= result.count
done = 0
for j in result do
(
amount = int ((j[2] / (totalLength/numPoints)) + 0.5)
j[2] = amount + 1
done += amount
)
result[1][2] += (numPoints-done)
)
return result
)
fn DistributePointsAlongShapes &mShapes =
(
for j in mShapes do
(
mmin = 1.0/(j[2]+1)
j[2] = for i = 1 to j[2] collect interpCurve3D j[1] 1 (i*mmin)
)
)
on distribute2_bt pressed do
(
st = timestamp()
sh = heapfree
num_beads = num_beads_sp.value
result = DistributePointsInShapes (shapes as array) numPoints:num_beads
DistributePointsAlongShapes &result
count = 0
for j in result do count += j[2].count
format "% % >> time:% heap:%
" num_beads count (timestamp()-st) (sh-heapfree)
gc()
delete $bead*
for j in result do
(
for i in j[2] do
(
geosphere pos:i name:"bead" radius:bead_radius_sp.value segments:2 wirecolor:green
count += 1
)
)
)
--on num_beads_sp changed val do distribute2_bt.pressed() --updateBeads()
on num_beads_sp changed val do updateBeads()
)
createdialog BeadsAndThreads pos:BeadsAndThreadsPos
this is updated version… seems like all my extension functions are replaced now
I don’t understand what you mean.
How do I need to set it up and what should I look in order to see the lost beads?
see your result for 20-21, 26-27 beads and 10 threads. the last thread for 26 beads has 5 beads on, but for 27 only 3. this is not FAIR
un-comment second from the end line of my code to make the beads spinner works with your algorithm