@m_adam Thank you very much for your reply. But while I was waiting for a reply, I experimented and realized that the Python tag suits me much better than the xpresso system, and rewrote the whole scene overnight). Have a great day.
Best posts made by ll2pakll
-
RE: Python Xpresso Node
-
RE: Python tag doesn't work when rendering
@ferdinand Your solution of replacing the
doc = c4d.documents.GetActiveDocument()
line withdoc: c4d.documents.BaseDocument = op.GetDocument()
really worked. From my point of view, as an ordinary person, your level of understanding of the code seems to be something beyond the realm of possibility. I wish you a good day and thank you again for your help.
Latest posts made by ll2pakll
-
The cloner is calculated before the Python tag
Good afternoon, friends. I have a spline formula in my scene, which is used as an object with clones on its vertices. This spline is modified by a formula which is regulated by an expression in a python tag.
from typing import Optional import c4d import math def main() -> None: doc: c4d.documents.BaseDocument = op.GetDocument() wave_obj = doc.SearchObject('Wave 1') wave_obj[c4d.PRIM_FORMULA_TMAX] = 33 velocity_factor = doc.GetTime().Get() * 100 wave_obj[c4d.PRIM_FORMULA_Y] = f"{str(20)}*Sin((t-{str(math.radians(velocity_factor))})*{str(0.8)})" # Update the scene c4d.EventAdd()
The problem is that the cloner executes before the expression, and for this reason, the clones are placed not on the current vertices of the spline, but on the positions of vertices from the previous frame. If you stop the animation, then at the next update of the scene clones become on the right places, but during the animation they still lag a frame. I've tried changing this by decreasing or increasing the tag priority, but it has no effect. How can I solve this problem?
-
RE: How do you manipulate the points on a spline?
I found the answer on my own. It turned out that the methods of point management are in the parent class
c4d.SplineObject
-c4d.PointObject
. It is also necessary to call the.Message(c4d.MSG_UPDATE)
method, otherwise the spline is not updated. -
How do you manipulate the points on a spline?
Greetings. Is there any way to manage specific points on a spline? Create, move and delete them?
I have created a spline with a certain shape, and I want it to be visible not the whole spline, but only a part of it. I want to change the end visible points over time, which will make it look like it is moving in space. I have done this with a "formula", it has parameters for the endpoints and I can change them over time. But using the formula I can't create a spline of the shape I want, and if I convert the formula to a spline and edit it, I find that I can't implement the same movement logic, because I don't understand how I can access the endpoints to create, delete and control their parameters.It's a basic script. The second script must be in the file located at the address specified in line 171. It contains the logic of Python tags.
import c4d import math import sys doc: c4d.documents.BaseDocument def open_script_for_python_tag(file_path: str, replacement_dictionary: dict = None): """ Эта функция возвращает открытый файл в котором заменены указанные в replacement_dictionary строки. :param file_path: путь к файлу :param replacement_dictionary: передаётся словарь в котором ключ это строка которую нужно заменить, а значение это строка на которую нужно заменить. :return: """ # Открываем файл для чтения if replacement_dictionary: with open(file_path, "r", encoding="utf8") as file: # Читаем все строки файла в список lines lines = file.readlines() # Создаем пустую строку result result = "" # Проходим по каждой строке в списке lines for line in lines: for sours, target in replacement_dictionary.items(): # Если строка совпадает с ключом, то она заменяется на значение ключа if line == sours: result += target # Иначе оставляем строку без изменений else: result += line # Возвращаем результат return result with open(file_path, "r", encoding="utf8") as file: text = file.read() return text def get_all_objects() -> list[c4d.BaseObject]: """ # функция, которая возвращает все объекты в сцене. :return: """ # Создаем пустой список для хранения имен объектов objects = [] # Получаем корневой объект сцены root = doc.GetFirstObject() # Если сцена не пуста if root: # Создаем стек для обхода дерева объектов stack = [root] # Пока стек не пуст while stack: # Извлекаем верхний элемент стека obj = stack.pop() # Добавляем его имя в список objects.append(obj) # Если у объекта есть дочерние объекты if obj.GetDown(): # Добавляем их в стек stack.append(obj.GetDown()) # Если у объекта есть соседние объекты if obj.GetNext(): # Добавляем их в стек stack.append(obj.GetNext()) # Возвращаем список имен объектов return objects def get_all_objects_names(): """ Получает имена всех объектов в сцене. :return: """ object_names = [] for object_c4d in get_all_objects(): object_names.append(object_c4d.GetName()) return object_names def find_all_single_waves() -> list: """ Ищет все объекты одиночных волны в сцене. :return: """ # Get the first object in the scene obj = doc.GetFirstObject() waves = [] # Loop through all objects in the scene while obj: # Get the object name name = obj.GetName() # Check if the name matches the pattern "Wave" followed by a number if name.startswith("Wave") and name[5:].isdigit(): # Print the object name and its global position waves.append(obj) # Get the next object in the scene obj = obj.GetNext() return waves class WaveMove: def __init__(self, length: (float, int) = 304, amplitude: (float, int) = 100, velocity: (float, int) = 30, phase: int = 0, name: str = None): self.__length = length self.__amplitude = amplitude self.__velocity = velocity self.__phase = phase # в градусах от 0 до 360 self.__start_point = 0 self.__end_point = 10 self._python_tag = None # создаём сплайн формула, который будет представлять волну self.wave_obj = c4d.BaseObject(c4d.Osplineformula) # если было передано имя волны то сразу называем её указанным именем if name: self.wave_obj.SetName(name) else: # если имя не было передано, то смотрим сколько волн в сцене и исходя из этого нумеруем волну. count_waves_in_scene = len(find_all_single_waves()) self.wave_obj.SetName('Wave ' + str(count_waves_in_scene)) # меняем плоскость расположения волны self.wave_obj[c4d.PRIM_PLANE] = c4d.PRIM_PLANE_ZY if name == 'Result wave': self.wave_obj[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = c4d.OBJECT_OFF self.wave_obj[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = c4d.OBJECT_OFF # добавляем волну в сцену doc.InsertObject(self.wave_obj) # создаём пользовательские данные которые будут определять параметры волны # создаём дневник с названиями и значениями полей пользовательских данных self.ud_dict = {"Length wave": self.__length, "Amplitude wave": self.__amplitude, "Velocity wave": self.__velocity, "Phase wave": self.__phase} for ud_name, ud_value in self.ud_dict.items(): self.create_user_data(ud_name, ud_value) # устанавливаем точки начала и конца волны self.set_start_point(self.__start_point) self.set_end_point(self.__end_point) # устанавливаем количество точек в сплайне self.wave_obj[c4d.PRIM_FORMULA_SAMPLES] = 100 # включаем кубическую интерполяцию self.wave_obj[c4d.PRIM_FORMULA_CUBIC] = True # промежуточные точки = равномерно self.wave_obj[c4d.SPLINEOBJECT_INTERPOLATION] = c4d.SPLINEOBJECT_INTERPOLATION_UNIFORM # задаём формулу для поля X(t) которое будет определять длину волны self.formula_xt = f"100*t" self.set_Xt(self.formula_xt) # задаём формулу для поля Y(t) которое будет определять амплитуду волны self.formula_yt = f"{str(self.__amplitude)}*Sin((t+{str(math.radians(phase))})*{str(self.__length)})" self.set_Yt(self.formula_yt) # создаём словарь в котором будут содержаться строки, которые нужно заменить в скрипте тега self.replacement_dictionary = { " wave_obj: c4d.BaseObject =\n": f" wave_obj: c4d.BaseObject = doc.SearchObject('{self.wave_obj.GetName()}')\n" } self.script_for_tag_path = r'F:\Work_area\YouTube\c4d\scripts\9_script_for_teg_single_wave.py' self.create_tag_python() # self.create_volume() def create_user_data(self, name: str, value: int): """ Создаёт параметр в пользовательских данных :param value: :param name: :return: """ ud = c4d.GetCustomDataTypeDefault(c4d.DTYPE_LONG) ud[c4d.DESC_NAME] = name self.wave_obj[self.wave_obj.AddUserData(ud)] = value def create_tag_python(self): """ Создаёт тег Python в котором будет описываться поведение волны и надевает его на объект формула сплайн волны. :return: """ # создаём тег и вешаем его self._python_tag: c4d.BaseTag = self.wave_obj.MakeTag(c4d.Tpython) # помещаем скрипт в тело тега Python и передаём в него вводные данные self._python_tag[c4d.TPYTHON_CODE] = open_script_for_python_tag( replacement_dictionary=self.replacement_dictionary, file_path=self.script_for_tag_path ) def create_volume(self): """ Создаёт sweep и circle что бы появился объём. :return: """ sweep = c4d.BaseObject(c4d.Osweep) sweep.SetName(f"Sweep {self.get_name()}") # создаём окружность circle = c4d.BaseObject(c4d.Osplinecircle) circle[c4d.PRIM_CIRCLE_RADIUS] = 4 circle[c4d.SPLINEOBJECT_SUB] = 2 circle.SetName(f'Circle {self.get_name()}') # вставляем пружину и спираль в sweep self.wave_obj.InsertUnder(sweep) circle.InsertUnder(sweep) doc.InsertObject(sweep) def set_Xt(self, xt: str): """ устанавливаем поле Xt для формулы :param xt: :return: """ self.wave_obj[c4d.PRIM_FORMULA_X] = xt def set_Yt(self, yt: str): """ устанавливаем поле Yt для формулы :param yt: :return: """ self.wave_obj[c4d.PRIM_FORMULA_Y] = yt def set_start_point(self, value: float): """ Устанавливает точку начала волны :param value: :return: """ self.wave_obj[c4d.PRIM_FORMULA_TMIN] = value def set_end_point(self, value: float): """ Устанавливает точку окончания волны :param value: :return: """ self.wave_obj[c4d.PRIM_FORMULA_TMAX] = value def get_name(self): """ Возвращает имя объекта волны. :return: """ return self.wave_obj.GetName() def get_Yt(self): """ Возвращает формулу из поля Yt. :return: """ return self.wave_obj[c4d.PRIM_FORMULA_Y] if __name__ == '__main__': WaveMove() c4d.EventAdd()
from typing import Optional import c4d import math import sys def main() -> None: doc: c4d.documents.BaseDocument = op.GetDocument() wave_obj: c4d.BaseObject = # рассчитываем фактор скорости velocity_factor = doc.GetTime().Get() * wave_obj[c4d.ID_USERDATA, 3] * 0.1 # устанавливаем связи между пользовательскими данными и состоянием волны wave_obj[c4d.PRIM_FORMULA_Y] = f"{str(wave_obj[c4d.ID_USERDATA, 2])}*Sin((t+{str(math.radians(wave_obj[c4d.ID_USERDATA, 4]))})*{str(wave_obj[c4d.ID_USERDATA, 1] * 0.01)})" wave_obj[c4d.PRIM_FORMULA_TMIN] = velocity_factor wave_obj[c4d.PRIM_FORMULA_TMAX] = velocity_factor + 10 # Update the scene c4d.EventAdd()
-
RE: How to increase the number of calculations in a Python tag?
@ferdinand Thank you for your answer, I didn’t think that the solution would be so difficult. From this point of view, unfortunately, the use of the python tag, in a sense, loses its relevance in the case of complex calculations. I tried the option with ExecutePasses, but it completely hangs the entire program. As a result, the only thing that more or less works, without completely reworking the code, is increasing the FPS of the scene from 25 to 100, this is exactly what I wanted - increasing the number of calculations per second. Unfortunately, this does a lot of unnecessary work related to calculating graphics for frames, which I will reduce to 25 per second anyway. Perhaps my comment will help someone who encounters a similar problem, and who, like me, is not a professional programmer. As always, I thank you and wish you a good mood.
-
RE: Time Shift. Python tag.
@ferdinand Thank you very much for your reply. I only need to animate one parameter and I have already written something similar to this parameter cache myself, you saw it in a previous question and advised me to change the location of the cache for more reliability. I will take your advice in the future to avoid reinventing the wheel. Thanks again for your help.
-
How to increase the number of calculations in a Python tag?
Windows 10
Cinema 4d 2023.2.1Good afternoon. I've encountered a problem where the calculation of object behavior written in the Python tag is either not synchronized with the scene frame rate, or otherwise does not have time to calculate some parameters. Because of this, when the amplitude of movements is large, the springs that connect the cubes in the scene look in the wrong direction. In the parameters of the python tag, I have not found a setting for the calculation frequency. Is there any way to increase the number of calculations per frame?
P.S. I made a video in viewport, but when rendering the picture does not change.
-
RE: Python tag doesn't work when rendering
@ferdinand Your solution of replacing the
doc = c4d.documents.GetActiveDocument()
line withdoc: c4d.documents.BaseDocument = op.GetDocument()
really worked. From my point of view, as an ordinary person, your level of understanding of the code seems to be something beyond the realm of possibility. I wish you a good day and thank you again for your help. -
RE: Python tag doesn't work when rendering
@mogh I just tried it and it didn't make any difference. I don't have many tags in my scene, besides the Python tag there are few targets. I set Python to priority 1, the others to 10, everything stayed the same.
-
RE: Python tag doesn't work when rendering
Apparently I've found a solution, but it doesn't look very convenient. In order for the animation to work in the renderer, you need to bake all objects that are affected by the python code. If there is a more elegant solution to the problem, I will be very grateful to someone who will share it.
-
Python tag doesn't work when rendering
Windows 10
Cinema 2023.2.1Good evening. I am faced with a strange situation. Almost all the behavior in my scene is written using Python tag. In the viewport everything works exactly as it should, but when I try to render the scene only the manually animated object moves, and everything that adjusts the Python tag doesn't work. What could be the problem?
.
To understand more about the problem, I made a short video
Here is the code written in the tag (I apologize for the part of the description written in Russian. I didn't think that this code would be intended for anyone but me.):from typing import Optional import c4d import math # определяем глобальную переменную для истории history = [] def rad_to_deg(rad): """ переводит радианы в градусы :param rad: :return: """ # Проверяем, является ли rad числом if isinstance(rad, (int, float)): # Вычисляем градусы по формуле deg = rad * 180 / pi deg = rad * 180 / math.pi # Возвращаем результат return deg else: # Выводим сообщение об ошибке, если rad не число print("Неверный тип аргумента. Ожидается число.") def deg_to_rad(deg): """ Переводит градусы в радианы :param deg: :return: """ # Проверяем, является ли deg числом if isinstance(deg, (int, float)): # Вычисляем радианы по формуле rad = deg * pi / 180 rad = deg * math.pi / 180 # Возвращаем результат return rad else: # Выводим сообщение об ошибке, если deg не число print("Неверный тип аргумента. Ожидается число.") def spring_length_calculation(spring_target: c4d.BaseObject, helix: c4d.BaseObject) -> float: """ Вычисляет длину пружины. В качестве аргументов получает helix и цель для пружины. :param spring_target: :param helix: :return: """ katet_1 = spring_target[c4d.ID_BASEOBJECT_GLOBAL_POSITION, c4d.VECTOR_Y] - helix[ c4d.ID_BASEOBJECT_GLOBAL_POSITION, c4d.VECTOR_Y] katet_2 = spring_target[c4d.ID_BASEOBJECT_GLOBAL_POSITION, c4d.VECTOR_Z] - helix[ c4d.ID_BASEOBJECT_GLOBAL_POSITION, c4d.VECTOR_Z] kvadrat_giopotenuzy = katet_1 ** 2 + katet_2 ** 2 return kvadrat_giopotenuzy ** 0.5 def spring_direction_calculation(spring_direction: c4d.BaseObject) -> float: """ Рассчитывает угол направления пружины. Из за того что тег цель работает не корректно, пружина отражается каждый раз при пересечении рубежа в 90 градусов, из за этого приходится делать рассчёты её положения таким образом что бы не учитывался разворот. :param spring_direction: принимает объект :return: """ # получаем значение направления направляющей в радианах rad_spring_direction = spring_direction[c4d.ID_BASEOBJECT_REL_ROTATION, c4d.VECTOR_Y] # получаем значение вращения пружины в радианах rad_spring_rotation = spring_direction[c4d.ID_BASEOBJECT_REL_ROTATION, c4d.VECTOR_X] # переводим радианы в градусы deg_spring_rotation = rad_to_deg(rad_spring_rotation) # смотрим если градус разворота направляющей меньше 10 то направления пружины и направляющей должны быть одинаковыми # в ином случае рассчитываем новое направление. Перед тем как вернуть значение переводим его обратно в радианы if deg_spring_rotation < 10: return rad_spring_direction else: deg_spring_direction = rad_to_deg(rad_spring_direction) d = 180 - deg_spring_direction return deg_to_rad(d) def main() -> None: count_oscillators = 15 doc = c4d.documents.GetActiveDocument() for i in range(count_oscillators - 1): spring_target = doc.SearchObject(f"spring target {i}") helix = doc.SearchObject(f"helix {i}") spring = doc.SearchObject(f"spring {i}") spring_direction = doc.SearchObject(f"spring direction {i}") # изменяем длину пружины в соответсвии с положением цели helix[c4d.PRIM_HELIX_HEIGHT] = spring_length_calculation(spring_target, helix) # изменяем направление пружины в соответсвии с направляющей для пружины spring[c4d.ID_BASEOBJECT_REL_ROTATION, c4d.VECTOR_Y] = spring_direction_calculation(spring_direction) # создаём механику памяти # Get the document and the fps fps = doc.GetFps() # Это крайний куб вслед за которым будут двигаться все остальные кубы cube_main = doc.SearchObject(f"cube 0") pos_main = cube_main[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Z] # Declare the global variable history global history # Create a global variable to store the history of the first cube's position # If it doesn't exist, initialize it with the current position if not "history" in globals(): history = [pos_main] else: # Append the current position to the history list history.append(pos_main) # Get the current frame number frame = doc.GetTime().GetFrame(fps) # Check if the current frame is the first one if frame == 0: # Clear the history list history = [] for i in range(1, count_oscillators): # Get the cube by name cube = doc.SearchObject(f"cube {i}") # Set the initial position of the cube cube[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Z] = 0 # Повторяем движение для остальных кубов с задержкой for i in range(1, count_oscillators): # Get the cube by name cube = doc.SearchObject(f"cube {i}") # Calculate the time offset in seconds offset = i * 10 # Calculate the index in the history list index = frame - offset # If the index is valid, get the position from the history list if index >= 0 and index < len(history): pos = history[index] # Otherwise, use the initial position else: pos = cube[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Z] # Set the position of the cube cube[c4d.ID_BASEOBJECT_REL_POSITION,c4d.VECTOR_Z] = pos # Update the scene c4d.EventAdd()