[Closed] Randon element ID – not same ID for the neighbours
Please help me improve this code – I need to apply random ID for elements, but I don’t wants neighbor elements to have the same ID
This code is by Barigazy:
fn randomMID idRange:#(1,10) = if selection.count != 0 do
(
local pogeuf = polyOp.getElementsUsingFace, posfmid = polyop.setFaceMatID
with redraw off
(
for sel in selection where isKindOf sel Editable_Poly and sel.modifiers.count == 0 do
(
local fNumList = #{}, EuF = #{}
for fp = 1 to sel.numfaces where fNumList[fp] == false do
(
EuF = pogeuf sel fp
posfmid sel EuF (random idRange[1] idRange[2])
for f in EuF do fNumList[f] = true
)
)
)
)
randomMID idRange:#(1,6) -- change mtlID range array
It’s working excelent, but when I have tiles with more different textures, I have very often to change manually ID fot the tiles that are close and have the same texture!
Thx in advance!
pogeuf, posfmid
LOL… you could name them vfv5cjw3571gvbx and by90ue3245bf
but the idea to find a ‘neighbor’ element(s) sounds interesting. it will be cool to hear any ideas about what a neighbor element is. distance? intersection? …
I think a generic idea is not going to be something easy to develop. But, if for example it is intended to be used on tiled floors or walls, it could be easier to start thinking in a solution.
Assuming you cant do this by face instead of by element, and if you have a regular tiled surface (with rectangular tiles), one approach could be:
- Compute the bounding box for each tile (element)
- For each BB, Inflate it by a given threshold
- Get all the bounding boxes intersecting it. Those are neighbors.
so you are talking about bbox center distance and bbox intersection… the distance measuring is kinda … voluntary
I believe in case of square tiles measuring the distance should be enough and efficient.
In case of a tiled area build up with different rectangle sizes, the BB intersection would work better.
how is about to check a distance between bbox borders? doing that you can figure out closest neighbor elements
It could be too. I am not sure if that would be more efficient than moving the problem to 2D and doing a simple rectangle collision detection.
that’s exactly what i don’t want to do.
it’s really an interesting task – find a neighbor ‘cloud’
just imagine… we have clouds. we have random texture for them. but for sure we don’t want to have the same cloud repeated at the same place… does it make sense?
Is this a good idea:
1 – get the bbox of all clouds
2 – get a point(zeroPoint) ouside of the bbox(or use one of the bbox corners as zeroPoint)
3 – measure the distance of this point to the center/pivor/bboxCorner of each of the clouds
4 – sort the clouds by their distance from the zeroPoint
There is no way to determin the groups of neighbors when the clouds are sorted.
Just the faith that all neighbors of cloud X are at almost the same distance, or in front or in the back(left, right) of the cloud X. Then in the sorted clouds all neighbors of cloud X will be(in my prayers) before and after it in the sorted list. And when the textures are applied there will be no neighbor clouds with the same texture.
I used another approach in one of my scripts:
- get the cloud 1
- collect all neighbors in radius of 100(1000, 10000) units
- apply different textures to all of the collected clouds
- get each one of the collected clouds and for each of them search the neighbors in the givven distance excluding already collected clouds.
Creating 3d grid with all clouds in separate cell also can be used. I think that the size of each cell have to be as the size of the smallest cloud. Then bigger clouds will be placed at several cells, but…
Then I am not sure either that measuring distances from BB borders would be more efficient than doing a simple 3D box collision detection.
There could be several solutions, depending on what the goal is. For a simple flat floor or wall a 2D solution might be good enough. Now for clouds the problem is different and I think a 3D BB collision detection could be good enough.
I cant figure out what do you suggest by measuring the distances between BB borders. Do you mean you would have to compare the distance of each of the 12 borders against all other borders to find the neighbors?
Thank you for thinking about the solution!
I’ve made a little trick
local prev_rnd = 0, rnd = 1
while rnd == prev_rnd do
(
rnd = random idRange[1] idRange[2]
)
polyop.setFaceMatID sel EuF rnd
--posfmid sel EuF (random idRange[1] idRange[2])
for f in EuF do fNumList[f] = true
prev_rnd = rnd
it creates different ID for every next element => it’s working only on one direction, in other direction it happens to repeat 2 or more textures!
I guess, you’re right! We have to work with the distances!
Would you mind uploading a sample object?
The algorithm could vary a lot depending on what you need it for. For example:
- A wall of stones, where each stone has a different size and can be shifted (front/back) having a gap of 1 inch between stones
- A flat floor made of equal square tiles, being each tile made of 2 faces, sort of a grid, without any gap between the tiles.
here’s my idea… quick mockup… not finished though i have to go to bed also not sure how to replicate the “group a list of integers” in maxscript after that’s done, pretty much the script is done :buttrock:
By grouping the elements based on the distance from theCenter the desired effect could be achieved… soo you would have something like:
123
456
789
elements = #(#{1…3},#{3,5,7},#{6,8},#{9})
elements[1] gets some randoms id’s and store them previousID = elements[1] ID’s
elements[2] gets randoms id’s that are different from previousID’s
and so on :bounce:
fn randomElemID = (
fn getFacesCenter sel faces node: = (
local fCenter = [0,0,0]
local pgfc = polyOp.getFaceCenter
for i in faces do ( fCenter += pgfc sel i node:node )
fCenter / (if classOf faces == Array then faces.count else faces.numberSet)
)
fn distsort one two center: = ( if distance center one[3] < distance center two[3] then -1 else 1 )
local peuf = polyOp.getElementsUsingFace
local selbas = $.baseobject
local allFaces = #{1..(polyOp.getNumFaces selbas)}
local elements = (
local faces = allFaces
local elem = #()
while faces.numberSet != 0 and not keyboard.escpressed do (
local newElem = peuf selbas (faces as Array)[1]
append elem newElem
faces -= newElem
)
elem
)
local aElements = for g = 1 to elements.count collect #(g, elements[g], getFacesCenter selbas elements[g] node:$)
local faces = polyop.getFacesByFlag selbas 1
local theCenter = if not faces.isEmpty then ( getFacesCenter selbas faces ) else ( $.pos )
qsort aElements distsort center:theCenter
-- http://stackoverflow.com/questions/10016802/python-group-a-list-of-integer-with-nearest-values
-- ...
)
here is snippet that shows how to get neighbors by checking their bbox intersection.
i n my sample i use nodes and their min and max. it might be elements as well and i already showed on this forum how to calculate bbox of specified vertices.
struct minmax
(
bmin, bmax,
fn fromBBox bbox = if iskindof bbox Array do
(
bmin = bbox[1]
bmax = bbox[2]
),
fn fromNode node = if isvalidnode node do
(
bmin = node.min
bmax = node.max
),
fn intersects b space: = if iskindof b (classof this) do
(
if space != unsupplied do
(
b = copy b
b.bmin -= space
b.bmax += space
)
not (bmax.x < b.bmin.x) and
not (bmin.x > b.bmax.x) and
not (bmax.y < b.bmin.y) and
not (bmin.y > b.bmax.y) and
not (bmax.z < b.bmin.z) and
not (bmin.z > b.bmax.z)
)
)
fn doesIntersect node1 node2 space: =
(
bb1 = minmax node1.min node1.max
bb2 = minmax node2.min node2.max
bb1.intersects bb2 space:space
)
fn findNeighbours node list space: =
(
for n in list where node != n and doesIntersect node n space:space collect n
)
delete objects
--seed 0
colors =
#(
(color 255 0 0),
(color 0 255 0),
(color 0 0 255),
(color 255 255 0),
(color 255 127 0),
(color 30 160 150),
(color 150 30 153)
)
fn uniqueWirecolor obj list space: colors:colors =
(
cc = copy colors #nomap
nn = findNeighbours obj list space:space
for n in nn where (k = finditem cc n.wirecolor) != 0 do deleteitem cc k
--format "% > %
" cc.count cc
obj.wirecolor = (if cc.count == 0 then black else cc[random 1 cc.count])
)
with redraw off
(
dd = for k=1 to 300 collect
(
bb = random [1,1,1] [20,20,20]
box width:bb.x length:bb.y height:bb.z pos:(random [-50,-50,-50] [50,50,50]) wirecolor:white
)
for obj in dd do uniqueWirecolor obj dd space:5
)
after you run this code you should see boxes where every box has a unique wirecolor different from its neighbors.
if the algorithm couldn’t find a unique color is set BLACK
it’s just a method… and not optimized of course.
Thank you so much, Denis! Your approach is quite interesting! It can be more universal! Thank you so much for your efforts!
I get this error for now – sure there is a little mistake! I will take a look also!
– No “map” function for undefined
Appreciate!