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()