Notifications
Clear all

[Closed] Challenge! and Fun 🙂

“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!! 🙁 )

1 Reply
(@polytools3d)
Joined: 10 months ago

Posts: 0

Max <= 2016
time:207

Max 2017
time:429

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
	)
)

i didn’t do memory optimization

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 )

2 Replies
(@polytools3d)
Joined: 10 months ago

Posts: 0

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.

(@polytools3d)
Joined: 10 months ago

Posts: 0

There are a few errors in your code:

  1. You use getshapelength(), which I guess is a custom DLX, so need to replace with curvelength()
  2. 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

2 Replies
(@polytools3d)
Joined: 10 months ago

Posts: 0

I though you wanted it to run fast. There is a HUGE difference using [B]lengthinterp/B and [B]interpcurve3d/B Also lengthinterp() isn’t accurate enought with very small values, so you need to dynamically adjust the steps.

(@denist)
Joined: 10 months ago

Posts: 0

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?

1 Reply
(@denist)
Joined: 10 months ago

Posts: 0

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

Page 2 / 9