Hi,
It would take me longer to dissect your script and make the answer both readable and correct, so I wrote some piece of narrative code instead. Start reading in the main
function and then move on to part 1, 2 and 3.
Cheers,
zipit
""" Run this in the Script Manager.
You will need an editable polygon object selected with a point selection to make this
script work. The script has three parts, which have to be commented and uncommented
in the main() function, all dealing with different aspects and levels of
understanding of point manipulation.
"""
import c4d
import math
def part_1():
""" Deals with the basics.
"""
# Points in a PointObject (and by inheritance also in a PolygonObject)
# are defined in local coordinates. Unless you want to relate the points
# to something in world space, there is no need to convert them.
points = op.GetAllPoints()
# We move all points point_index * 2.0 units up on the y-axis, so
# the point with the index 0 is not being moved at all and the
# point with the index 10 for example is being moved by 20 units on
# the y axis.
points = [p + c4d.Vector(0, i * 2., 0) for i, p in enumerate(points)]
# The same could also be written as:
# for i, p in enumerate(points):
# points[i] += c4d.Vector(0, i * 2., 0)
# Write the points back, update the object and tell Cinema that we have
# modified something in the scene graph.
op.SetAllPoints(points)
op.Message(c4d.MSG_UPDATE)
c4d.EventAdd()
def part_2():
""" Deals with matrices.
"""
# Shoving around points manually can be useful, but has its limits when
# it comes to rotating and scaling points. Here is where matrices come
# into play.
# We can construct our matrices manually using the cross product (which
# is helpful when constructing a matrix for some kind of normal), but for
# now we will just use one of the convenience methods provided by
# c4d.utils.
# MatrixRotY returns a rotation Matrix that rotates around the y-axis.
# The input is in radians, so QUARTER_PI would be 45°.
transform = c4d.utils.MatrixRotY(math.pi * .25)
# This is just a normal matrix, so we can pile some more stuff onto it.
# Also translate the points by 100 units on the y axis.
transform.off = c4d.Vector(0, 100, 0)
# And scale them by a factor of .5 on the z-axis
transform.v3 *= .5
# Now we just apply our transform by multiplying each point by the
# matrix. Applying the inverse of a matrix will do the inverse of
# each aspect of the transform. So ~transform would scale z by the
# factor 2, translate by -100 on the y axis and rotate by -45° on
# the y-axis. From which follows that p * transform * ~transform = p
points = [p * transform for p in op.GetAllPoints()]
# And we write our points back just like the last time.
op.SetAllPoints(points)
op.Message(c4d.MSG_UPDATE)
c4d.EventAdd()
def part_3():
""" Deals with selections.
"""
# A transform we will apply and the points.
transform = c4d.utils.MatrixRotY(math.pi * .25)
points = op.GetAllPoints()
# Selections, i.e. BaseSelect are a bit weird in Cinema as they do not store
# only the selected elements, but also the not selected elements and are
# also boundless.
# Get the BaseSelect for all selected points in our object.
selection = op.GetPointS()
# The documentation is not correct on GetAll(), it will not return all
# selected elements, but all elements, selected or not, as booleans.
state = selection.GetAll(len(points))
print "selection state:", state
# Now we can sort out our selected ids. We go over all values in
# state and only keep the indices of elements where the state is ``True``.
selected = [i for i, value in enumerate(state) if value]
print "selected point ids:", selected
# With these applying our transform only to a subset of points is
# trivial.
for index in selected:
points[index] *= transform
# And we write our points back just like the last time.
op.SetAllPoints(points)
op.Message(c4d.MSG_UPDATE)
c4d.EventAdd()
def main():
"""
"""
# Terminate the script when the active object is not a PolygonObject
# the variable op is predefined as the selected/active objects in a
# Script Manager script.
if not isinstance(op, c4d.PolygonObject):
msg = "Requires a PolygonObject. Received: {type}"
raise TypeError(msg.format(type=type(op)))
# Once you have red and executed one part, comment the current one
# and uncomment the next one.
part_1()
# part_2()
# part_3()
if __name__ == "__main__":
main()