Macro to optimize object placement to minimal BoundBox volume

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
User avatar
flachyjoe
Veteran
Posts: 1891
Joined: Sat Mar 31, 2012 12:00 pm
Location: Limoges, France

Macro to optimize object placement to minimal BoundBox volume

Post by flachyjoe »

Hi,
here is an object placement optimizer relative to its bound-box volume.
It creates a simple copy of the object in the best position and outputs information in the report view.

Code: Select all

# -*- coding: utf-8 -*-

#Stop conditions
EPSILON = 1e-4 #mm² : minimum gain for face area optimization
MAXITER = 1e2 #maximum count for each loop

def BBVol(bb):
	'''returns the given BoundBox volume'''
	return bb.XLength*bb.YLength*bb.ZLength

def BBSize(bb):
	'''returns a string representation of a BoundBox'''
	return "size (x,y,z) : %g %g %g volume : %g mm³" % (bb.XLength, bb.YLength, bb.ZLength, BBVol(bb) )

if len(Gui.Selection.getSelection()) != 1:
	raise ValueError("Please select ONE object")

s=Gui.Selection.getSelection()[0].Shape.copy() #simple copy of the selected object
Msg("initial %s %s\n" %  (s.BoundBox, BBSize(s.BoundBox)))

UPSILON = s.Volume/1e6 # minimum gain for volume optimization
Msg("volume gain stop condition : %g mm³\n" % UPSILON)

# main loop initialization
angleX = angleY = angleZ = 0
gItter = 0
gDeltaAngle = 45 

initVol = minVol = lastVol = gDelta = BBVol(s.BoundBox)

# main loop which minimize boundbox volume
while abs(gDelta) > UPSILON and gItter < MAXITER:
	
	#sub-loops minimize boundbox face area in the rotation axis direction
	
	#Z axis, XY face
	deltaAngle = gDeltaAngle
	minArea = lastArea  =  deltaXY = s.BoundBox.XLength *  s.BoundBox.YLength
	itter = 0
	while abs(deltaXY) > EPSILON and itter < MAXITER:
		angleZ += deltaAngle
		s.Placement.Rotation = App.Rotation(angleZ, angleY, angleX)
		newArea = s.BoundBox.XLength * s.BoundBox.ZLength
		deltaXY = lastArea - newArea
		if newArea >= minArea:	 #if this step increases the face area
			deltaAngle = - deltaAngle/2 #switch rotation direction and reduce movement
		else:
			minArea  = newArea	#keep rotation direction and amount until volume increase
		lastArea  = newArea
		itter += 1

	#Y axis, XZ face
	deltaAngle = gDeltaAngle
	minArea = lastArea  =  s.BoundBox.XLength *  s.BoundBox.ZLength
	deltaXZ = lastArea
	itter = 0
	while abs(deltaXZ) > EPSILON and itter < MAXITER:
		angleY += deltaAngle
		s.Placement.Rotation = App.Rotation(angleZ, angleY, angleX)
		newArea = s.BoundBox.XLength * s.BoundBox.ZLength
		deltaXZ = lastArea - newArea
		if newArea >= minArea:
			deltaAngle = - deltaAngle/2
		else:
			minArea  = newArea
		lastArea  = newArea
		itter += 1

	#X axis, YZ face
	deltaAngle = gDeltaAngle
	minArea = lastArea  = s.BoundBox.YLength *  s.BoundBox.ZLength
	deltaYZ =lastArea
	itter = 0
	while abs(deltaYZ) > EPSILON and itter < MAXITER:
		angleX += deltaAngle
		s.Placement.Rotation = App.Rotation(angleZ, angleY, angleX)
		newArea = s.BoundBox.YLength *  s.BoundBox.ZLength
		deltaYZ = lastArea - newArea
		if newArea >= minArea:
			deltaAngle = - deltaAngle/2
		else:
			minArea  = newArea
		lastArea  = newArea
		itter += 1

	#compute stop criterion
	newVol =  BBVol(s.BoundBox)
	gDelta = lastVol - newVol

	if newVol >= minVol:	#if this step increases the boundbox volume
		gDeltaAngle = -gDeltaAngle/2 #switch rotation direction and reduce movement
	else:
		minVol  = newVol	#keep rotation direction and amount until volume increase
	lastVol = newVol
	gItter += 1

Msg("nb itter : %i, last delta  : %g mm³, angleX :  %g, angleY :  %g\n" % (gItter ,gDelta, angleX, angleY))
Msg("%s %s\n" % (s.BoundBox, BBSize(s.BoundBox)))
Msg("gain : %g %%\n" % ((initVol - BBVol(s.BoundBox))/initVol * 100))
Part.show(s, "BBOptimized")
Initial french post : https://forum.freecadweb.org/viewtopic. ... 88#p505188
- Flachy Joe -
Image
Post Reply