def topa(l,r,a):
Q=q(l,r,a)
ac=acos((Q**2+l**2-r**2)/(2*Q*l))
if a%(2*pi)>pi : ac = -ac
return -ac
Pydriver выражение для RotX будет выглядеть вот так:
m.degrees(p.topa(1.542,0.655,ob('DriveShaftPart').RotX))/1 0.0
Впускной и выпускной клапаны управляются вращением их соответствующих распределительных валов. Очертание кулачка очень сложно, так что здесь мы используем не фактическую форму его контура, а аппроксимируем ее, она выглядит достаточно хорошо (то есть, открытый клапан в функции еще оживленное движение в правильном моменте). Следующая картинка показывает движение клапана как функцию от угла вращения:
Наконец, в pydrivers.py мы определяем функцию spike(), которая принимает угол поворота распределительного вала как аргумент и возвращает значение между 0.0 и 1.0 которое резко возрастает в районе нулевого угла:
def spike(angle):
t = (cos(angle)+1.0)/2.0
return t**4
Сейчас клапан движется линейно, но линия, по которой он следует, наклонена на 10 градусов (вперед для впускного клапана, назад для выпускного клапана), теперь нам придется управлять двумя каналами, LocZ и LocY, каждый нужно умножить на правильное значение для создания наклонного движения. Поэтому мы определим две функции в pydrivers.py:
def valveZ(angle,tilt,travel,offset):
return cos(radians(tilt))*spike(angle)*travel+offset
def valveY(angle,tilt,travel,offset):
return sin(radians(tilt))*spike(angle)*travel+offset
Обе функции возвращают расстояние в зависимости от угла поворота управляющего объекта. Tilt (наклон) - наклон клапана (в градусах), travel — максимальная длина пути, по которому проходит клапан вдоль наклонной линии, а offset (компенсация) - значение, которое позволяет регулировать позицию клапана. Соответствующие pydriver-выражения для LocZ и LocY-каналов впускного клапана:
p.valveZ(ob('CamInlet').RotX+m.pi,-10.0,-0.1,6.55)
и
p.valveY(ob('CamInlet').RotX+m.pi,-10.0,-0.1,-0.03)
(Выражения для выпускного клапана аналогичны, но с положительным углом tilt.)
До сих пор, все IPO-каналы были каналами объекта, такими как расположение и вращение. Но также возможно управлять другими каналами, ведь нам нужно изменять энергию лампы, помещенной в свечу зажигания. В pydrivers.py мы для начала определим вспомогательную функцию topi(), которая, в качестве аргументов, кроме угла вращения движущегося объекта принимает угол h (в радианах) и интенсивность i. topi() возвращает эту интенсивность, если угол двигающегося объекта находится между 0 и h, и ноль, если угол выйдет за пределы этого ряда. Поскольку угол на входе функции, возможно больше, чем 2*pi (когда двигающийся объект пройдет больше чем полный круг), мы исправляем это выделенной операцией деления по модулю:
def topi(a,h,i):
m = a%(2*pi)
r=0.0
if m<h: r=i
return r
pydriver-выражение для канала энергии (называемый "Energ" в редакторе Кривых IPO), может быть выражено следующим образом:
p.topi(ob('DriveShaftPart').RotX/2+m.pi,0.3,0.5)
Как видно, это выражение запустит «огонь» в свече зажигания при первых 17 градусах (0.3 радиан), установив энергию для этого цикла в 0.5 .
Как только мы смоделировали один цилиндр и позаботились о управлении движением отдельных частей, нашим следующим шагом будет дублирование цилиндров, для создания мотора как на вводной иллюстрации этой главы. В принципе мы можем просто выделить все и продублировать, нажав Shift + D, отрегулировав время срабатывания каждого IPO-канала.
Но есть препятствие. Если мы используем Shift + D, вместо Alt + D мы получим одинаковые копии мешей объектов, вместо того чтобы просто воспользоваться ссылкой на первый объект. К тому же, мы ожидаем, что скопировали и остальные атрибуты объекта, такие как материалы, текстуры и IPO. Блендер, по-умолчанию, не дублирует вышеперечисленные категории, копируя только сам объект. Это получится неуклюже, так как изменение IPO первого поршня, к примеру, затронуло бы все остальные.
Мы могли бы сделать остальные копии уникальными впоследствии (нажав на поле количества пользователей этих кривых IPO, например, и подтвердив своё согласие со всплывающим вопросом make single user?), но было бы слишком утомительным повторять это для каждой копии отдельно.