Hi @Cairyn, thanks for your reply.
I think I conceptually understand what you said, but I don't have a clue about how to implement it...
I wrote a little example as an exercise, where I create a tube, select a couple polygons and translate the selected polygons based on the average normals. After that, I'd like to flatten out the selection... At some point I calculated the average points to see if that could be where the flattening might be applied (which is close to the modelling axis, when orientation is set to 'normals'), but I think I'd like to flatten where the selection starts, as shown in red on the following image:
I've also added a couple colored nulls where the average normals (yellow) and average points (cyan) are, just for reference.
import c4d
from c4d import utils
# -----------------------------------------------------------------------------------------------------------------------
# Main function
# -----------------------------------------------------------------------------------------------------------------------
def main():
doc = c4d.documents.GetActiveDocument()
#create an object and make it editable (use clone when using csto)
obj = c4d.BaseObject(c4d.Otube)
obj[c4d.PRIM_TUBE_IRAD] = 50 # inner rad
obj[c4d.PRIM_TUBE_ORAD] = 100 # outer rad
obj[c4d.PRIM_TUBE_HEIGHT] = 20 # height
obj[c4d.PRIM_TUBE_SEG] = 12 # rotation segments
obj[c4d.PRIM_TUBE_CSUB] = 1 # cap segments
obj[c4d.PRIM_TUBE_HSUB] = 1 # height segments
obj[c4d.PRIM_AXIS] = 2 # axis (2 = +Y)
result = utils.SendModelingCommand(
command=c4d.MCOMMAND_CURRENTSTATETOOBJECT,
list=[obj.GetClone()],
mode=c4d.MODELINGCOMMANDMODE_ALL,
doc=doc)
obj = result[0]
# optimize the obj
bc = c4d.BaseContainer() # contain settings
# set optimize (default c4d)
bc.SetFloat(c4d.MDATA_OPTIMIZE_TOLERANCE, .01)
bc.SetBool(c4d.MDATA_OPTIMIZE_POINTS, True)
bc.SetBool(c4d.MDATA_OPTIMIZE_POLYGONS, True)
bc.SetBool(c4d.MDATA_OPTIMIZE_UNUSEDPOINTS, True)
result = c4d.utils.SendModelingCommand( # optimize, result returns Boolean
command=c4d.MCOMMAND_OPTIMIZE,
list=[obj],
doc=doc,
bc=bc)
obj.Message(c4d.MSG_UPDATE) # update object
doc.InsertObject(obj) # add to scene
selected_polys = obj.GetPolygonS() #get selected polys/base select
#select arbitrary polys
selected_polys.Select(9)
selected_polys.Select(13)
selected_points = [] #to hold point vectors
selected_points_indexes = [] #to hold points indexes
selected_polygons_indexes = [] #to hold polygons indexes
polys_count = obj.GetPolygonCount() #get number of polys
polys_list = selected_polys.GetAll(polys_count) #list of poly indexes holding boolean values: 1: selected, 0: not selected
points = obj.GetAllPoints() # get all object points
normals_avg = 0 #store average of normals
#loop through polys, process if selected (true)
for i in xrange(polys_count):
if polys_list[i]:
polygon = obj.GetPolygon(i) #get polygon
selected_polygons_indexes.append(i)
a, b, c, d = points[polygon.a], points[polygon.b], points[polygon.c], points[polygon.d]
#there could be shared points on neighbour polys, this checks they're not added multiple times
if a not in selected_points:
selected_points.append(a)
selected_points_indexes.append(polygon.a)
if b not in selected_points:
selected_points.append(b)
selected_points_indexes.append(polygon.b)
if c not in selected_points:
selected_points.append(c)
selected_points_indexes.append(polygon.c)
if d not in selected_points:
selected_points.append(d)
selected_points_indexes.append(polygon.d)
#cross product
cross = (b - a).Cross(d - a).GetNormalized()
#add to avg
normals_avg += cross
#calculate avg
normals_avg = normals_avg / selected_polys.GetCount()
doc.InsertObject(createColorNull(normals_avg, "normals_avg", 255, 255, 0), parent=None)#display a yellow null where normals_avg is
#translate points
offset = 30 # arbitrary amount in cm
for i in xrange(len(selected_points)):
obj.SetPoint(selected_points_indexes[i], selected_points[i] + normals_avg * offset)
#update selected points
points = obj.GetAllPoints()
points_avg = 0 # average pos of selected points
selected_points = [] #reset
for i in xrange(len(selected_polygons_indexes)):
polygon = obj.GetPolygon(selected_polygons_indexes[i]) # get polygon
a, b, c, d = points[polygon.a], points[polygon.b], points[polygon.c], points[polygon.d]
if a not in selected_points:
selected_points.append(a)
if b not in selected_points:
selected_points.append(b)
if c not in selected_points:
selected_points.append(c)
if d not in selected_points:
selected_points.append(d)
print("sel points", selected_points)
#calculate avg points
for i in xrange(len(selected_points)):
points_avg += selected_points[i]
points_avg = points_avg / len(selected_points)
doc.InsertObject(createColorNull(points_avg, "points_avg", 0, 255, 255), parent=None) #display a cyan null where points_avg is
obj.Message(c4d.MSG_UPDATE) # update object
doc.Message(c4d.MSG_UPDATE) # update doc
c4d.EventAdd() # refresh viewport
# -----------------------------------------------------------------------------------------------------------------------
# Creates a color null/point
# -----------------------------------------------------------------------------------------------------------------------
def createColorNull(pos, name, red, green, blue):
null = c4d.BaseObject(c4d.Onull)
if null is None:
sys.exit('Could not create parent Null object!')
null.SetName(name)
null.SetRelPos(pos)
null[c4d.NULLOBJECT_DISPLAY] = 1
null[c4d.NULLOBJECT_RADIUS] = 3
null[c4d.NULLOBJECT_ASPECTRATIO] = 1
null[c4d.NULLOBJECT_ORIENTATION] = 1
null[c4d.ID_BASEOBJECT_USECOLOR] = 2
null[c4d.NULLOBJECT_ICONCOL] = True
r = c4d.utils.RangeMap(value=red, mininput=0, maxinput=255, minoutput=0, maxoutput=1, clampval=True)
g = c4d.utils.RangeMap(value=green, mininput=0, maxinput=255, minoutput=0, maxoutput=1, clampval=True)
b = c4d.utils.RangeMap(value=blue, mininput=0, maxinput=255, minoutput=0, maxoutput=1, clampval=True)
null[c4d.ID_BASEOBJECT_COLOR] = c4d.Vector(r, g, b)
return null
# -----------------------------------------------------------------------------------------------------------------------
# Execute main()
# -----------------------------------------------------------------------------------------------------------------------
if __name__ == '__main__':
main()
Any inputs on how should I continue to be able to flatten? Please keep in mind I'm kinda slow on this things 🙂
Thanks!
Jon