RE: Idea for LUA script to create a knolling layout
2024-10-11, 7:03 (This post was last modified: 2024-10-11, 7:06 by Fredrik Hareide.)
2024-10-11, 7:03 (This post was last modified: 2024-10-11, 7:06 by Fredrik Hareide.)
Hi! I think this is a really interesting idea. I would love to help make this at some point but I do not have a lot of time on my hands right now. I did however try our latest helper (AI) to see if we could get a working prototype to work from. It actually worked fairly well.
This script can layout all selected parts but it does not do it in nested mode and it does not reset rotation. With some work that should be possible to change. I must admit that for small stuff like this AI is pretty cool.
This script can layout all selected parts but it does not do it in nested mode and it does not reset rotation. With some work that should be possible to change. I must admit that for small stuff like this AI is pretty cool.
Code:
-- Function to get the X, Y (min), and Z dimensions of a subfile using its bounding box
function getSubfileDimensions(subfile)
local minVec = subfile:getBoundingBoxMin()
local maxVec = subfile:getBoundingBoxMax()
return maxVec:getX() - minVec:getX(), minVec:getY(), maxVec:getZ() - minVec:getZ() -- Return width, minY, and depth
end
-- Function to sort subfiles by their combined XY size
function sortSubfilesBySizeXY(a, b)
local widthA, depthA = getSubfileDimensions(a)
local widthB, depthB = getSubfileDimensions(b)
local sizeA = widthA * depthA
local sizeB = widthB * depthB
return sizeA < sizeB
end
-- Function to layout parts in a knolling grid arrangement on the XY plane
function knollingLayout()
local ses = ldc.session()
if not ses:isLinked() then
ldc.dialog.runMessage('No active model.')
return
end
local sel = ses:getSelection()
local cnt = sel:getRefCount()
if cnt < 2 then
ldc.dialog.runMessage('At least two items should be selected.')
return
end
local subfiles = {}
-- Collect all subfiles (Parts in the selection)
for i = 1, cnt do
table.insert(subfiles, sel:getRef(i))
end
-- Sort subfiles by size (combined X and Z dimensions)
table.sort(subfiles, function(a, b)
return sortSubfilesBySizeXY(a:getSubfile(), b:getSubfile())
end)
-- Determine grid dimensions and spacing
local gridSize = 20 -- Minimum grid size, consider part size + desired spacing
local xOffset = 0
local zOffset = 0
local rowWidth = 0
for i, part in ipairs(subfiles) do
local subfile = part:getSubfile()
local width, minY, depth = getSubfileDimensions(subfile) -- X and Z dimensions, and minY for the Y position
-- Set the part's position
local pos = ldc.vector(xOffset, minY, zOffset)
part:setPos(pos)
-- Update offset for next part
zOffset = zOffset + depth + gridSize -- Move right to the next column
-- Keep track of the maximum width in the current row
rowWidth= math.max(rowWidth, width + gridSize)
-- Check if we need to start a new row
if zOffset + depth > gridSize * 100 then -- Assuming grid is 10 columns wide
xOffset = xOffset + rowWidth
zOffset = 0 -- Move down to the next row
rowWidth = 0 -- Reset row width for the new row
end
end
end
-- Register the macro for the knolling layout
function register()
local macro_lo = ldc.macro('Perform knolling layout')
macro_lo:setHint('Arrange elements in a knolling layout based on size in XY plane with Z = 0.')
macro_lo:setEvent('run', 'knollingLayout')
end
register()