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()
-
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. -
Hi @ll2pakll,
I am glad that you solved your issue.
Message(c4d.MSG_UPDATE)
is indeed required to finalize your spline editing. If you want a complete example on spline editing please take a look at geometry_splineobject.py example.Cheers,
Maxime.