Files
OmniLottie/lottie/objects/lottie_tokenize.py
T
2026-03-01 21:36:54 +08:00

6247 lines
266 KiB
Python

import torch
import re
import json
from typing import Union, List, Dict, Tuple, Optional, Any
import difflib
import numpy as np
class LottieTensor:
# Command type constants (添加新的命令常量)
tokenizer = None
CMD_ANIMATION = 0
CMD_LAYER = 1
CMD_TRANSFORM = 2
CMD_POSITION = 3
CMD_KEYFRAME = 4
CMD_POSITION_END = 5
CMD_SCALE = 6
CMD_SCALE_END = 7
CMD_ROTATION = 8
CMD_OPACITY = 9
CMD_OPACITY_END = 10
CMD_ANCHOR = 11
CMD_GROUP = 12
CMD_GROUP_END = 13
CMD_TRANSFORM_SHAPE = 14
CMD_PATH = 15
CMD_PATH_END = 16
CMD_POINT = 17
CMD_FILL = 18
CMD_GRADIENT_FILL = 19
CMD_GRADIENT_FILL_END = 20
CMD_START_POINT = 21
CMD_END_POINT = 22
CMD_GRADIENT_TYPE = 23
CMD_HIGHLIGHT_LENGTH = 24
CMD_HIGHLIGHT_ANGLE = 25
CMD_TRANSFORM_END = 26
CMD_LAYER_END = 27
CMD_PAD = 28
CMD_EOS = 29
CMD_SOS = 30
CMD_RECT = 31
CMD_RECT_END = 32
CMD_SIZE = 33
CMD_ROUNDED = 34
CMD_ELLIPSE = 35
CMD_ELLIPSE_END = 36
CMD_STROKE = 37
CMD_SKEW = 38
CMD_SKEW_AXIS = 39
CMD_ASSET = 40
CMD_ASSET_END = 41
CMD_PARENT = 42
CMD_NULL_LAYER = 43
CMD_NULL_LAYER_END = 44
CMD_PRECOMP_LAYER = 45
CMD_PRECOMP_LAYER_END = 46
CMD_REFERENCE_ID = 47
CMD_DIMENSIONS = 48
CMD_ROTATION_END = 49
CMD_STAR = 50
CMD_STAR_END = 51
CMD_INNER_RADIUS = 52
CMD_OUTER_RADIUS = 53
CMD_INNER_ROUNDNESS = 54
CMD_OUTER_ROUNDNESS = 55
CMD_POINTS = 56
CMD_STAR_ROTATION = 57
CMD_TRIM = 58
CMD_TRIM_END = 59
CMD_START = 60
CMD_END = 61
CMD_OFFSET = 62
CMD_MULTIPLE = 63
CMD_REPEATER = 64
CMD_REPEATER_END = 65
CMD_COPIES = 66
CMD_REPEATER_OFFSET = 67
CMD_COMPOSITE = 68
CMD_REPEATER_TRANSFORM = 69
CMD_REPEATER_TRANSFORM_END = 70
CMD_GRADIENT_STROKE = 71
CMD_GRADIENT_STROKE_END = 72
CMD_WIDTH = 73
CMD_LINE_CAP = 74
CMD_LINE_JOIN = 75
CMD_MITER_LIMIT = 76
CMD_MERGE = 77
CMD_MERGE_END = 78
CMD_MERGE_MODE = 79
CMD_ROUNDED_CORNERS = 80
CMD_ROUNDED_CORNERS_END = 81
CMD_RADIUS = 82
CMD_TWIST = 83
CMD_TWIST_END = 84
CMD_ANGLE = 85
CMD_CENTER = 86
CMD_BEZIER = 87
CMD_BEZIER_END = 88
CMD_TEXT_LAYER = 89
CMD_TEXT_LAYER_END = 90
CMD_TEXT_DATA = 91
CMD_TEXT_DATA_END = 92
CMD_DOCUMENT = 93
CMD_SOLID_LAYER = 94
CMD_SOLID_LAYER_END = 95
CMD_POSITION_X = 96
CMD_POSITION_Y = 97
CMD_POSITION_Z = 98
CMD_POSITION_X_END = 99
CMD_POSITION_Y_END = 100
CMD_POSITION_Z_END = 101
CMD_SCALE_X = 102
CMD_SCALE_Y = 103
CMD_SCALE_Z = 104
CMD_SCALE_X_END = 105
CMD_SCALE_Y_END = 106
CMD_SCALE_Z_END = 107
CMD_ROTATION_X = 108
CMD_ROTATION_Y = 109
CMD_ROTATION_Z = 110
CMD_ROTATION_X_END = 111
CMD_ROTATION_Y_END = 112
CMD_ROTATION_Z_END = 113
CMD_EFFECTS = 114
CMD_EFFECTS_END = 115
CMD_EFFECT = 116
CMD_EFFECT_END = 117
CMD_HAS_MASK = 118
CMD_MASKS_PROPERTIES = 119
CMD_CT = 120
CMD_EF = 121
CMD_TT = 122
CMD_TP = 123
CMD_TD = 124
CMD_HD = 125
CMD_CL = 126
CMD_LN = 127
CMD_AO = 128
CMD_ANCHOR_END = 129
CMD_OPACITY_FILL = 130
CMD_FILL_RULE = 131
CMD_COLOR_DIM = 132
CMD_DDD = 133
CMD_MARKERS = 134
CMD_PROPS = 135
CMD_ORIGINAL_COLORS = 136
CMD_COLOR_POINTS = 137
CMD_COLORS = 138
CMD_ML2 = 139
CMD_ML2_IX = 140
CMD_OFFSET_IX = 141
CMD_TR_P_IX = 142
CMD_TR_A_IX = 143
CMD_TR_SCALE = 144
CMD_TR_S_IX = 145
CMD_TR_R_IX = 146
CMD_TR_SO_IX = 147
CMD_TR_EO_IX = 148
CMD_KEYFRAME_END = 149
CMD_POSITION_EXPR = 150
CMD_SCALE_EXPR = 151
CMD_ROTATION_EXPR = 152
CMD_WIDTH_KEYFRAME = 153 # 新增
CMD_WIDTH_ANIMATED_END = 154 # 新增
CMD_FONTS = 155
CMD_FONTS_END = 156
CMD_FONT = 157
CMD_CHARS = 158
CMD_CHARS_END = 159
CMD_CHAR = 160
CMD_CHAR_END = 161
CMD_CHAR_SHAPES = 162
CMD_CHAR_SHAPES_END = 163
CMD_TEXT_KEYFRAMES = 164
CMD_TEXT_KEYFRAMES_END = 165
CMD_TEXT_KEYFRAME = 166
CMD_TEXT_DOC = 167
CMD_TEXT_DOC_END = 168
CMD_FONT_SIZE = 169
CMD_FONT_FAMILY = 170
CMD_TEXT = 171
CMD_CA = 172
CMD_JUSTIFY = 173
CMD_TRACKING = 174
CMD_LINE_HEIGHT = 175
CMD_LETTER_SPACING = 176
CMD_FILL_COLOR = 177
CMD_MORE_OPTIONS = 178
CMD_MORE_OPTIONS_END = 179
CMD_G = 180
CMD_ALIGNMENT = 181
CMD_ALIGNMENT_K = 182
CMD_ALIGNMENT_IX = 183
CMD_DROPDOWN = 184
CMD_IGNORED = 185
CMD_SLIDER = 186
CMD_COLOR = 187
CMD_OPACITY_ANIMATED = 188
CMD_OPACITY_KEYFRAME = 189
CMD_MASKS_PROPERTIES_END = 190
CMD_MASK = 191
CMD_MASK_END = 192
CMD_MASK_PT = 193
CMD_MASK_PT_END = 194
CMD_MASK_PT_K = 195
CMD_MASK_PT_K_END = 196
CMD_MASK_PT_K_I = 197
CMD_MASK_PT_K_O = 198
CMD_MASK_PT_K_V = 199
CMD_MASK_O = 200
CMD_MASK_X = 201
CMD_TM = 202
CMD_TM_END = 203
CMD_MASK_PT_K_ARRAY = 204
CMD_MASK_PT_K_ARRAY_END = 205
CMD_MASK_PT_KEYFRAME = 206
CMD_MASK_PT_KEYFRAME_END = 207
CMD_MASK_PT_KF_I = 208
CMD_MASK_PT_KF_O = 209
CMD_MASK_PT_KF_S = 210
CMD_MASK_PT_KF_S_END = 211
CMD_MASK_PT_KF_SHAPE = 212
CMD_MASK_PT_KF_SHAPE_END = 213
CMD_MASK_PT_KF_SHAPE_I = 214
CMD_MASK_PT_KF_SHAPE_O = 215
CMD_MASK_PT_KF_SHAPE_V = 216
CMD_VALUE = 217
CMD_VALUE_END = 218
CMD_TR_POSITION = 219
CMD_TR_ANCHOR = 220
CMD_TR_ROTATION = 221
CMD_TR_START_OPACITY = 222
CMD_TR_END_OPACITY = 223
CMD_ZIG_ZAG = 224
CMD_ZIG_ZAG_END = 225
CMD_FREQUENCY = 226
CMD_AMPLITUDE = 227
CMD_POINT_TYPE = 228
CMD_ANIMATORS = 229
CMD_ANIMATORS_END = 230
CMD_ANIMATOR = 231
CMD_ANIMATOR_END = 232
CMD_RANGE_SELECTOR = 233
CMD_RANGE_SELECTOR_END = 234
CMD_RANGE_START = 235
CMD_RANGE_START_END = 236
CMD_RANGE_START_KEYFRAME = 237
CMD_AMOUNT = 238
CMD_MAX_EASE = 239
CMD_MIN_EASE = 240
CMD_ANIMATOR_PROPERTIES = 241
CMD_ANIMATOR_PROPERTIES_END = 242
CMD_OPACITY_ANIMATED_END = 243
CMD_MASK_PT_K_C = 244
CMD_RANGE_END = 245
CMD_RANGE_END_END = 246
CMD_RANGE_END_KEYFRAME = 247
CMD_END_END = 248
CMD_START_END = 249
CMD_OFFSET_END = 250
CMD_POINTS_STAR = 251
CMD_RANGE_OFFSET = 252
CMD_RANGE_OFFSET_END = 253
CMD_RANGE_OFFSET_KEYFRAME = 254
CMD_S_M = 255
CMD_OPACITY_ANIMATORS = 256
CMD_SCALE_ANIMATORS = 257
CMD_SCALE_ANIMATORS_END = 258
CMD_ROTATION_ANIMATORS = 259
CMD_ROTATION_ANIMATORS_END = 260
CMD_POSITION_ANIMATORS = 261
CMD_POSITION_ANIMATORS_END = 262
CMD_TRACKING_ANIMATORS = 263
CMD_OPACITY_ANIMATORS_END = 264
CMD_COLOR_KEYFRAME = 265 # Add this constant
CMD_COLOR_ANIMATED_END = 266
CMD_DASHES = 267
CMD_DASHES_END = 268
CMD_DASH = 269
CMD_DASH_OFFSET = 270
CMD_LAYER_EFFECT = 271
CMD_NO_VALUE = 272
CMD_WIDTH_ANIMATED = 273 # Add this if it doesn't exist
CMD_SIZE_END = 274
CMD_RECT_SIZE = 275 # Add this new constant
CMD_ELLIPSE_SIZE = 276
CMD_RECT_ROUNDED = 277 # Add this new constant for animated rect_rounded
CMD_RECT_ROUNDED_END = 278
CMD_DASH_ANIMATED = 279 # New constant
CMD_DASH_KEYFRAME = 280 # New constant
CMD_DASH_ANIMATED_END = 281 # New constant
# Command names mapped to their numeric constants
COMMANDS = [
"animation", # 0
"layer", # 1
"transform", # 2
"position", # 3
"keyframe", # 4
"/position", # 5
"scale", # 6
"/scale", # 7
"rotation", # 8
"opacity", # 9
"/opacity", # 10
"anchor", # 11
"group", # 12
"/group", # 13
'"TransformShape"', # 14
"path", # 15
"/path", # 16
"point", # 17
"fill", # 18
"gradient_fill", # 19
"/gradient_fill", # 20
"start_point", # 21
"end_point", # 22
"gradient_type", # 23
"highlight_length", # 24
"highlight_angle", # 25
"/transform", # 26
"/layer", # 27
"PAD", # 28
"EOS", # 29
"SOS", # 30
"rect", # 31
"/rect", # 32
"size", # 33
"rounded", # 34
"ellipse", # 35
"/ellipse", # 36
"stroke", # 37
"skew", # 38
"skew_axis", # 39
"asset", # 40
"/asset", # 41
"parent", # 42
"null_layer", # 43
"/null_layer", # 44
"precomp_layer", # 45
"/precomp_layer", # 46
"reference_id", # 47
"dimensions", # 48
"/rotation", # 49
"star", # 50
"/star", # 51
"inner_radius", # 52
"outer_radius", # 53
"inner_roundness", # 54
"outer_roundness", # 55
"points", # 56
"star_rotation", # 57
"trim", # 58
"/trim", # 59
"start", # 60
"end", # 61
"offset", # 62
"multiple", # 63
"repeater", # 64
"/repeater", # 65
"copies", # 66
"repeater_offset", # 67
"composite", # 68
"repeater_transform", # 69
"/repeater_transform", # 70
"gradient_stroke", # 71
"/gradient_stroke", # 72
"width", # 73
"line_cap", # 74
"line_join", # 75
"miter_limit", # 76
"merge", # 77
"/merge", # 78
"merge_mode", # 79
"rounded_corners", # 80
"/rounded_corners", # 81
"radius", # 82
"twist", # 83
"/twist", # 84
"angle", # 85
"center", # 86
"bezier", # 87
"/bezier", # 88
"text_layer", # 89
"/text_layer", # 90
"text_data", # 91
"/text_data", # 92
"document", # 93
"solid_layer", # 94
"/solid_layer", # 95
"position_x", # 96
"position_y", # 97
"position_z", # 98
"/position_x", # 99
"/position_y", # 100
"/position_z", # 101
"scale_x", # 102
"scale_y", # 103
"scale_z", # 104
"/scale_x", # 105
"/scale_y", # 106
"/scale_z", # 107
"rotation_x", # 108
"rotation_y", # 109
"rotation_z", # 110
"/rotation_x", # 111
"/rotation_y", # 112
"/rotation_z", # 113
"effects", # 114
"/effects", # 115
"effect", # 116
"/effect", # 117
"hasMask", # 118
"masksProperties", # 119
"ct", # 120
"ef", # 121
"tt", # 122
"tp", # 123
"td", # 124
"hd", # 125
"cl", # 126
"ln", # 127
"ao", # 128
"/anchor", # 129
"opacity_fill", # 130
"fill_rule", # 131
"color_dim", # 132
"ddd", # 133
"markers", # 134
"props", # 135
"original_colors", # 136
"color_points", # 137
"colors", # 138
"ml2", # 139
"ml2_ix", # 140
"offset_ix", # 141
"tr_p_ix", # 142
"tr_a_ix", # 143
"tr_scale", # 144
"tr_s_ix", # 145
"tr_r_ix", # 146
"tr_so_ix", # 147
"tr_eo_ix", # 148
"/keyframe", # 149
"position_expr", # 150
"scale_expr", # 151
"rotation_expr", # 152
"width_keyframe", # 153 # 新增
"/width_animated", # 154 # 新增
"fonts", # 155
"/fonts", # 156
"font", # 157
"chars", # 158
"/chars", # 159
"char", # 160
"/char", #161
"char_shapes", # 162
"/char_shapes", # 163
"text_keyframes", # 164
"/text_keyframes", # 165
"text_keyframe", # 166
"text_doc", # 167
"/text_doc", # 168
"font_size", # 169
"font_family", # 170
"text", # 171
"ca", # 172
"justify", # 173
"tracking_animators", # 174
"line_height", # 175
"letter_spacing", # 176
"fill_color", # 177
"more_options", # 178
"/more_options", # 179
"g", # 180
"alignment", # 181
"alignment_k", # 182
"alignment_ix", # 183
"dropdown", # 184
"ignored", # 185
"slider", # 186
"color", # 187
"opacity_animated", # 188
"opacity_keyframe", # 189
"/masksProperties", # 190
"mask", # 191
"/mask", # 192
"mask_pt", # 193
"/mask_pt", # 194
"mask_pt_k", # 195
"/mask_pt_k", # 196
"mask_pt_k_i", # 197
"mask_pt_k_o", # 198
"mask_pt_k_v", # 199
"mask_o", # 200
"mask_x", # 201
"tm", # 202
"/tm", # 203
"mask_pt_k_array", # 204
"/mask_pt_k_array", # 205
"mask_pt_keyframe", # 206
"/mask_pt_keyframe", # 207
"mask_pt_kf_i", # 208
"mask_pt_kf_o", # 209
"mask_pt_kf_s", # 210
"/mask_pt_kf_s", # 211
"mask_pt_kf_shape", # 212
"/mask_pt_kf_shape", # 213
"mask_pt_kf_shape_i", # 214
"mask_pt_kf_shape_o", # 215
"mask_pt_kf_shape_v", # 216
"value", # 217
"/value", # 218
"tr_position", # 219
"tr_anchor", # 220
"tr_rotation", # 221
"tr_start_opacity", # 222
"tr_end_opacity", # 223
"zig_zag", # 224
"/zig_zag", # 225
"frequency", # 226
"amplitude", # 227
"point_type", # 228
"animators", # 229
"/animators", # 230
"animator", # 231
"/animator", # 232
"range_selector", # 233
"/range_selector", # 234
"range_start", # 235
"/range_start", # 236
"range_start_keyframe", # 237
"amount", # 238
"max_ease", # 239
"min_ease", # 240
"animator_properties", # 241
"/animator_properties", # 242
"/opacity_animated", # 243
"mask_pt_k_c", #244
"range_end", # 245
"/range_end", # 246
"range_end_keyframe", # 247
"/end", #248
"/start" , # 249
"/offset" , # 250
"points_star", #251
"range_offset", # 252
"/range_offset", # 253
"range_offset_keyframe", # 254
"s_m", # 255
"opacity_animators", # 256
"scale_animators", # 257
"/scale_animators", # 258
"rotation_animators", # 259
"/rotation_animators", # 260
"position_animators", # 261
"/position_animators", # 262
"tracking_animators", # 263
"/opacity_animators", #264
"color_keyframe", # 265
"/color_animated", #266
"dashes", # 267
"/dashes", # 268
"dash", # 269
"dash_offset", # 270
"layer_effect", # 271
"no_value", #272
"width_animated" , #273
"/size", #274
"rect_size", # 275 # Add this new command
"ellipse_size", # 276
"rect_rounded", # 277
"/rounded", #278
"dash_animated", # 279 # Add this
"dash_keyframe", # 280 # Add this
"/dash_animated", # 281 # Add this
]
# Command to index mapping
COMMAND_TO_IDX = {cmd: idx for idx, cmd in enumerate(COMMANDS)}
_OFFSET_CACHE = {}
# Parameter indices for each command type (添加新的Index定义)
class Index:
# Animation parameters
class Animation:
FR = 0
IP = 1
OP = 2
W = 3
H = 4
DDD = 5
class Layer:
INDEX = 0
IN_POINT = 1
OUT_POINT = 2
START_TIME = 3
DDD = 4
HD = 5
HAS_MASK = 6
AO = 7
TT = 8
TP = 9
TD = 10
CT = 11
CP = 12
class Value:
VALUE = 0
class Transform:
ANIMATED = 0
X = 1
Y = 2
Z = 3
class Keyframe:
T = 0
S1 = 1
S2 = 2
S3 = 3
I_X = 4 # 第一个值,或单值情况
I_Y = 5 # 第一个值,或单值情况
O_X = 6 # 第一个值,或单值情况
O_Y = 7 # 第一个值,或单值情况
TO1 = 8
TO2 = 9
TO3 = 10
TI1 = 11
TI2 = 12
TI3 = 13
# Multi-dimensional easing (for scale, position, anchor)
I_X2 = 14
I_X3 = 15
I_Y2 = 16
I_Y3 = 17
O_X2 = 18
O_X3 = 19
O_Y2 = 20
O_Y3 = 21
H_FLAG = 22
E1 = 23
E2 = 24
E3 = 25
class Tm:
A = 0
#IX = 1
class WidthKeyframe: # 新增
T = 0
S = 1
I_X = 2
I_Y = 3
O_X = 4
O_Y = 5
class Path:
IX = 0
IND = 1
KS_IX = 2
CLOSED = 3
HD = 4
ANIMATED = 5
class Point:
X = 0
Y = 1
IN_X = 2
IN_Y = 3
OUT_X = 4
OUT_Y = 5
class Fill:
R = 0
G = 1
B = 2
COLOR_DIM = 3
HAS_C_A = 4
HAS_C_IX = 5
C_IX = 6
BM = 7
FILL_RULE = 8
OPACITY = 9
COLOR_ANIMATED = 10 # New
OPACITY_ANIMATED = 11 # New
HAS_O_A = 12 # New
HAS_O_IX = 13 # New
O_IX = 14 # New
class TransformShape:
POSITION_X = 0
POSITION_Y = 1
SCALE_X = 2
SCALE_Y = 3
ROTATION = 4
OPACITY = 5
ANCHOR_X = 6
ANCHOR_Y = 7
SKEW = 8
SKEW_AXIS = 9
HD = 10
class Stroke:
R = 0
G = 1
B = 2
COLOR_DIM = 3
HAS_C_A = 4
HAS_C_IX = 5
C_IX = 6
BM = 7
LC = 8
LJ = 9
ML = 10
#WIDTH = 11
#OPACITY = 12
WIDTH_ANIMATED = 11 # 新增
COLOR_ANIMATED = 12 # Add this
A = 13 # Add alpha channel support
class Bezier:
CLOSED = 0
class Group:
IX = 0
CIX = 1
BM = 2
HD = 3
NP = 4
class Star:
D = 0
SY = 1
class StarValue: # 新增用于 star 的子命令
VALUE = 0
class Trim:
IX = 0
START = 1
END = 2
OFFSET = 3
MULTIPLE = 4
class TrimValue:
VALUE = 0
ANIMATED = 1
IX = 2
class Repeater:
IX = 0
COPIES = 1
REPEATER_OFFSET = 2
COMPOSITE = 3
TR_P_IX = 4
TR_A_IX = 5
TR_SCALE = 6
TR_S_IX = 7
TR_R_IX = 8
TR_SO_IX = 9
TR_EO_IX = 10
class Asset:
#ID = 0
FR = 0
ID_TOKEN_0 = 1
ID_TOKEN_1 = 2
ID_TOKEN_2 = 3
ID_TOKEN_3 = 4
ID_TOKEN_4 = 5
ID_TOKEN_5 = 6
ID_TOKEN_6 = 7
ID_TOKEN_7 = 8
ID_TOKEN_8 = 9
ID_TOKEN_9 = 10
ID_TOKEN_COUNT = 11 # Store count of tokens
class Rect:
HD = 0
D = 1
POSITION_X = 2
POSITION_Y = 3
SIZE_X = 4
SIZE_Y = 5
ROUNDED = 6
IX = 7
class Ellipse:
POSITION_X = 0
POSITION_Y = 1
SIZE_X = 2
SIZE_Y = 3
class SingleValue:
VALUE = 0
IX = 1
ANIMATED = 2
class TwoValues:
VALUE1 = 0
VALUE2 = 1
IX = 2
class ThreeValues:
VALUE1 = 0
VALUE2 = 1
VALUE3 = 2
class NullLayer:
INDEX = 0
IN_POINT = 1
OUT_POINT = 2
START_TIME = 3
CT = 4
#DDD = 5
HD = 5
HAS_MASK = 6
AO = 7
TT = 8
TP = 9
TD = 10
CP = 11
class PrecompLayer:
INDEX = 0
IN_POINT = 1
OUT_POINT = 2
START_TIME = 3
W = 4
H = 5
CT = 6 # 添加CT参数
HAS_MASK = 7
AO = 8
TT = 9
TP = 10
TD = 11
DDD =12
HD = 13
CP = 14
class SolidLayer:
INDEX = 0
IN_POINT = 1
OUT_POINT = 2
START_TIME = 3
WIDTH = 4
HEIGHT = 5
HAS_MASK = 6
COLOR_R = 7
COLOR_G = 8
COLOR_B = 9
COLOR_A = 10
class Parent:
PARENT_INDEX= 0
class ReferenceId: # 新增
ID_TOKEN_0 = 0
ID_TOKEN_1 = 1
ID_TOKEN_2 = 2
ID_TOKEN_3 = 3
ID_TOKEN_4 = 4
ID_TOKEN_5 = 5
ID_TOKEN_6 = 6
ID_TOKEN_7 = 7
ID_TOKEN_8 = 8
ID_TOKEN_9 = 9
ID_TOKEN_COUNT = 10 # Store count of tokens
class Dimensions: # 新增
WIDTH = 0
HEIGHT = 1
class Font:
ASCENT = 0
FAMILY_TOKEN_0 = 1
FAMILY_TOKEN_1 = 2
FAMILY_TOKEN_2 = 3
FAMILY_TOKEN_3 = 4
FAMILY_TOKEN_4 = 5
FAMILY_TOKEN_5 = 6
FAMILY_TOKEN_6 = 7
FAMILY_TOKEN_7 = 8
FAMILY_TOKEN_8 = 9
FAMILY_TOKEN_9 = 10
FAMILY_TOKEN_COUNT = 11
# Reserve slots for style tokens
STYLE_TOKEN_0 = 12
STYLE_TOKEN_1 = 13
STYLE_TOKEN_2 = 14
STYLE_TOKEN_3 = 15
STYLE_TOKEN_4 = 16
STYLE_TOKEN_5 = 17
STYLE_TOKEN_6 = 18
STYLE_TOKEN_7 = 19
STYLE_TOKEN_8 = 20
STYLE_TOKEN_9 = 21
STYLE_TOKEN_COUNT = 22
class Char:
SIZE = 0
W = 1
CH_TOKEN_0 = 2
CH_TOKEN_1 = 3
CH_TOKEN_2 = 4
CH_TOKEN_3 = 5
CH_TOKEN_4 = 6
CH_TOKEN_5 = 7
CH_TOKEN_6 = 8
CH_TOKEN_7 = 9
CH_TOKEN_8 = 10
CH_TOKEN_9 = 11
CH_TOKEN_COUNT = 12
# Reserve slots for style tokens
STYLE_TOKEN_0 = 13
STYLE_TOKEN_1 = 14
STYLE_TOKEN_2 = 15
STYLE_TOKEN_3 = 16
STYLE_TOKEN_4 = 17
STYLE_TOKEN_5 = 18
STYLE_TOKEN_6 = 19
STYLE_TOKEN_7 = 20
STYLE_TOKEN_8 = 21
STYLE_TOKEN_9 = 22
STYLE_TOKEN_COUNT = 23
# Reserve slots for family tokens
FAMILY_TOKEN_0 = 24
FAMILY_TOKEN_1 = 25
FAMILY_TOKEN_2 = 26
FAMILY_TOKEN_3 = 27
FAMILY_TOKEN_4 = 28
FAMILY_TOKEN_5 = 29
FAMILY_TOKEN_6 = 30
FAMILY_TOKEN_7 = 31
FAMILY_TOKEN_8 = 32
FAMILY_TOKEN_9 = 33
FAMILY_TOKEN_COUNT = 34
class TextLayer:
INDEX = 0
IN_POINT = 1
OUT_POINT = 2
START_TIME = 3
HAS_MASK = 4 # 新增
class TextKeyframe:
T = 0
STROKE_WIDTH = 1
OFFSET = 2
WRAP_POSITION_X = 3
WRAP_POSITION_Y = 4
WRAP_SIZE_X = 5
WRAP_SIZE_Y = 6
# Add numeric fields instead of string storage
FONT_SIZE = 7
CA = 8
JUSTIFY = 9
TRACKING = 10
LINE_HEIGHT = 11
LETTER_SPACING = 12
FILL_COLOR_R = 13
FILL_COLOR_G = 14
FILL_COLOR_B = 15
STROKE_COLOR_R = 16
STROKE_COLOR_G = 17
STROKE_COLOR_B = 18
HAS_STROKE_COLOR = 19 # Flag to indicate if stroke_color exists
FONT_FAMILY_TOKENS_START = 20 # Store up to 10 tokens for font_family
TEXT_TOKENS_START = 30 # Store up to 15 tokens for text
FONT_FAMILY_TOKEN_COUNT = 45 # Store the count of font_family tokens
TEXT_TOKEN_COUNT = 46 # Store the count of text tokens
class MoreOptions:
G = 0
ALIGNMENT_A = 1
ALIGNMENT_K1 = 2
ALIGNMENT_K2 = 3
ALIGNMENT_IX = 4
class OriginalColors:
# Support up to 18 color values
COLOR_0 = 0
COLOR_1 = 1
COLOR_2 = 2
COLOR_3 = 3
COLOR_4 = 4
COLOR_5 = 5
COLOR_6 = 6
COLOR_7 = 7
COLOR_8 = 8
COLOR_9 = 9
COLOR_10 = 10
COLOR_11 = 11
COLOR_12 = 12
COLOR_13 = 13
COLOR_14 = 14
COLOR_15 = 15
COLOR_16 = 16
COLOR_17 = 17
COLOR_18 = 18 # Added
COLOR_19 = 19 # Added
COLOR_20 = 20 # Added
COLOR_21 = 21 # Added
COLOR_22 = 22 # Added
COLOR_23 = 23 # Added
COLOR_24 = 24 # Added
COLOR_25 = 25 # Added
COLOR_26 = 26 # Added
COLOR_27 = 27 # Added
COLOR_28 = 28 # Added
COLOR_29 = 29 # Added
COLOR_30 = 30 # Added
COLOR_31 = 31 # Added
COLOR_32 = 32 # Added
COLOR_33 = 33 # Added
COLOR_34 = 34 # Added
COLOR_35 = 35 # Added
COLOR_36 = 36 # Added
COLOR_37 = 37 # Added
COLOR_38 = 38 # Added
COLOR_39 = 39 # Added
COLOR_40 = 40 # Added
COLOR_41 = 41 # Added
COLOR_42 = 42 # Added
COLOR_43 = 43 # Added
COLOR_44 = 44 # Added
COLOR_45 = 45 # Added
COLOR_46 = 46 # Added
COUNT = 47 # Store the count of colors
class FontSize:
SIZE = 0
class Text:
TEXT_TOKEN_0 = 0
TEXT_TOKEN_1 = 1
TEXT_TOKEN_2 = 2
TEXT_TOKEN_3 = 3
TEXT_TOKEN_4 = 4
TEXT_TOKEN_5 = 5
TEXT_TOKEN_6 = 6
TEXT_TOKEN_7 = 7
TEXT_TOKEN_8 = 8
TEXT_TOKEN_9 = 9
TEXT_TOKEN_COUNT = 10
class Ca:
VALUE = 0
class Justify:
VALUE = 0
class Tracking:
VALUE = 0
class LineHeight:
VALUE = 0
class LetterSpacing:
VALUE = 0
class FillColor:
R = 0
G = 1
B = 2
class G:
VALUE = 0
class Alignment:
A = 0
class AlignmentK:
VALUE1 = 0
VALUE2 = 1
class AlignmentIx:
VALUE = 0
class GradientFill:
OPACITY = 0
FILL_RULE = 1
START_POINT_X = 2
START_POINT_Y = 3
END_POINT_X = 4
END_POINT_Y = 5
GRADIENT_TYPE = 6
HIGHLIGHT_LENGTH = 7
HIGHLIGHT_ANGLE = 8
COLOR_POINTS = 9
# Original colors (up to 12 values for RGBA * 3 color stops)
ORIGINAL_COLOR_0 = 10
ORIGINAL_COLOR_1 = 11
ORIGINAL_COLOR_2 = 12
ORIGINAL_COLOR_3 = 13
ORIGINAL_COLOR_4 = 14
ORIGINAL_COLOR_5 = 15
ORIGINAL_COLOR_6 = 16
ORIGINAL_COLOR_7 = 17
ORIGINAL_COLOR_8 = 18
ORIGINAL_COLOR_9 = 19
ORIGINAL_COLOR_10 = 20
ORIGINAL_COLOR_11 = 21
ORIGINAL_COLOR_12 = 22 # Added
ORIGINAL_COLOR_13 = 23 # Added
ORIGINAL_COLOR_14 = 24 # Added
ORIGINAL_COLOR_15 = 25 # Added
ORIGINAL_COLOR_16 = 26 # Added
ORIGINAL_COLOR_17 = 27 # Added
ORIGINAL_COLOR_18 = 28 # Added
ORIGINAL_COLOR_19 = 29 # Added
ORIGINAL_COLOR_20 = 30 # Added
ORIGINAL_COLOR_21 = 31 # Added
ORIGINAL_COLOR_22 = 32 # Added
ORIGINAL_COLOR_23 = 33 # Added
class GradientStroke:
OPACITY = 0
WIDTH = 1
LINE_CAP = 2
LINE_JOIN = 3
MITER_LIMIT = 4
ML2 = 5
ML2_IX = 6
START_POINT_X = 7
START_POINT_Y = 8
END_POINT_X = 9
END_POINT_Y = 10
GRADIENT_TYPE = 11
HIGHLIGHT_LENGTH = 12
HIGHLIGHT_ANGLE = 13
COLOR_POINTS = 14
# Original colors (up to 18 values for RGBA * 4.5 color stops)
ORIGINAL_COLOR_0 = 15
ORIGINAL_COLOR_1 = 16
ORIGINAL_COLOR_2 = 17
ORIGINAL_COLOR_3 = 18
ORIGINAL_COLOR_4 = 19
ORIGINAL_COLOR_5 = 20
ORIGINAL_COLOR_6 = 21
ORIGINAL_COLOR_7 = 22
ORIGINAL_COLOR_8 = 23
ORIGINAL_COLOR_9 = 24
ORIGINAL_COLOR_10 = 25
ORIGINAL_COLOR_11 = 26
ORIGINAL_COLOR_12 = 27
ORIGINAL_COLOR_13 = 28
ORIGINAL_COLOR_14 = 29
ORIGINAL_COLOR_15 = 30
ORIGINAL_COLOR_16 = 31
ORIGINAL_COLOR_17 = 32
ORIGINAL_COLOR_18 = 33 # Added
ORIGINAL_COLOR_19 = 34 # Added
ORIGINAL_COLOR_20 = 35 # Added
ORIGINAL_COLOR_21 = 36 # Added
ORIGINAL_COLOR_22 = 37 # Added
ORIGINAL_COLOR_23 = 38 # Added
class StartPointCmd:
X = 0
Y = 1
class EndPointCmd:
X = 0
Y = 1
class OriginalColorsCmd:
COLOR_1 = 0
COLOR_2 = 1
COLOR_3 = 2
COLOR_4 = 3
COLOR_5 = 4
COLOR_6 = 5
COLOR_7 = 6
COLOR_8 = 7
COLOR_9 = 8
COLOR_10 = 9
COLOR_11 = 10
COLOR_12 = 11
class ColorPoints:
VALUE = 0
class Effect:
TYPE = 0
INDEX = 1
NP = 2
ENABLED = 3
class LayerEffect: # Add new Index class
INDEX = 0
VALUE = 1
class Dropdown:
INDEX = 0
VALUE = 1
class NO_VALUE:
INDEX = 0
VALUE = 1
class Ignored:
INDEX = 0
VALUE = 1
class Slider:
INDEX = 0
VALUE = 1
class Color:
NAME_INDEX = 0 # Using NAME_INDEX to avoid confusion with INDEX
INDEX = 1
R = 2
G = 3
B = 4
class Merge:
# merge命令的name会存储在string_params中
pass
class MergeMode:
MODE = 0
class Mask:
INDEX = 0
INV = 1
MODE = 2 # mode will be stored as string
# nm will be stored in string_params
class MaskPt:
A = 0
IX = 1
class MaskPtK:
C = 0 # closed
class MaskPtKValues: # For i, o, v
V1 = 0
V2 = 1
V3 = 2
V4 = 3
V5 = 4
V6 = 5
V7 = 6
V8 = 7
V9 = 8
V10 = 9
V11 = 10
V12 = 11
V13 = 12
V14 = 13
V15 = 14
V16 = 15
V17 = 16
V18 = 17
V19 = 18
V20 = 19
COUNT = 20
class MaskO: # For mask_o
A = 0
K = 1
IX = 2
class MaskX: # For mask_x
A = 0
K = 1
IX = 2
class MaskPtKeyframe:
INDEX = 0
T = 1
class MaskPtKfI:
X = 0
Y = 1
class MaskPtKfO:
X = 0
Y = 1
class MaskPtKfShape:
INDEX = 0
C = 1 # closed
class MaskPtKfShapeValues: # For shape_i, shape_o, shape_v
V1 = 0
V2 = 1
V3 = 2
V4 = 3
V5 = 4
V6 = 5
V7 = 6
V8 = 7
V9 = 8
V10 = 9
V11 = 10
V12 = 11
V13 = 12
V14 = 13
V15 = 14
V16 = 15
V17 = 16
V18 = 17
V19 = 18
V20 = 19
COUNT = 20 # Add this to store the count
class TrPosition:
X = 0
Y = 1
class TrAnchor:
X = 0
Y = 1
class TrRotation:
VALUE = 0
class TrStartOpacity:
VALUE = 0
class TrEndOpacity:
VALUE = 0
class ZigZag:
NAME_INDEX = 0 # Will store in string_params
IX = 1
class Frequency:
VALUE = 0
class Amplitude:
VALUE = 0
class PointType:
VALUE = 0
class Animator:
# nm will be stored in string_params
pass
class RangeSelector:
T = 0
R = 1
B = 2
SH = 3
RN = 4
class RangeStart:
A = 0
class RangeStartKeyframe:
T = 0
S = 1
I_X = 2
I_Y = 3
O_X = 4
O_Y = 5
class Amount:
A = 0
K = 1
IX = 2
class MaxEase:
A = 0
K = 1
IX = 2
class MinEase:
A = 0
K = 1
IX = 2
class Radius:
VALUE = 0
class RangeEnd:
A = 0
class RangeEndKeyframe:
T = 0
S = 1
I_X = 2
I_Y = 3
O_X = 4
O_Y = 5
#class RangeOffset:
# A = 0
class RangeOffsetKeyframe:
T = 0
S = 1
I_X = 2
I_Y = 3
O_X = 4
O_Y = 5
class SM:
A = 0
K = 1
IX = 2
class OpacityAnimators:
A = 0
K = 1
IX = 2
class ScaleAnimators:
A = 0
K_X = 1
K_Y = 2
K_Z = 3
IX = 4
class RotationAnimators:
A = 0
K = 1
IX = 2
class PositionAnimators:
A = 0
K_X = 1
K_Y = 2
K_Z = 3
IX = 4
class TrackingAnimators:
A = 0
K = 1
IX = 2
class Dashes:
# Container command, no parameters
pass
class Dash:
TYPE = 0 # Store type as numeric (0 for "d", 1 for "g", 2 for "o")
LENGTH = 1 # dash length
V_IX = 2 # v_ix parameter
class DashAnimated:
TYPE = 0 # Store type as numeric
V_IX = 1 # v_ix parameter
class DashKeyframe:
T = 0
S = 1
I_X = 2
I_Y = 3
O_X = 4
O_Y = 5
class DashOffset:
O = 0 # offset value
# Parameter dimension (fixed length for all commands)
PARAM_DIM = 50
PAD_VAL = -2001
def __init__(self, commands, params, seq_len=None, PAD_VAL=-2001, flattened_data=None):
"""Initialize LottieTensor"""
self.PAD_VAL = PAD_VAL
self.commands = commands.reshape(-1, 1).long()
self.params = params.float()
self.seq_len = torch.tensor(len(commands)) if seq_len is None else seq_len
self.sos_token = torch.tensor([LottieTensor.CMD_SOS]).unsqueeze(-1).long()
self.eos_token = self.pad_token = torch.tensor([LottieTensor.CMD_EOS]).unsqueeze(-1).long()
# Store original string values
self.string_params = {}
@staticmethod
def _parse_easing_value(value_str: str) -> int:
return NotImplementedError
@staticmethod
def _parse_multi_easing_values(value_str: str) -> List[int]:
return NotImplementedError
@staticmethod
def from_sequence(sequence: str) -> 'LottieTensor':
return NotImplementedError
@staticmethod
def _parse_attributes(attrs_str: str) -> Dict[str, str]:
"""Parse attribute string to dictionary - no change needed as it returns strings"""
attrs = {}
# First, handle special attributes with quotes that might contain special characters
# Handle ch attribute specially (for char command)
ch_match = re.search(r'ch="([^"]*)"', attrs_str)
if ch_match:
attrs['ch'] = ch_match.group(1)
# Remove the ch attribute from the string to avoid re-parsing
attrs_str = attrs_str[:ch_match.start()] + attrs_str[ch_match.end():]
# Handle name attribute specially if it contains quotes
name_match = re.search(r'name="([^"]*)"', attrs_str)
if name_match:
attrs['name'] = name_match.group(1)
# Remove the name attribute from the string to avoid re-parsing
attrs_str = attrs_str[:name_match.start()] + attrs_str[name_match.end():]
else:
# Try without quotes
name_match = re.search(r'name=([^\s]+)', attrs_str)
if name_match:
attrs['name'] = name_match.group(1)
attrs_str = attrs_str[:name_match.start()] + attrs_str[name_match.end():]
# Parse remaining attributes
# Pattern for key=value or key="value"
pattern = r'([^\s=]+)=(?:"([^"]*)"|([^\s]*))'
for match in re.finditer(pattern, attrs_str):
key, quoted_val, unquoted_val = match.groups()
if key not in ['name', 'ch']: # Skip if we already handled these
attrs[key] = quoted_val if quoted_val is not None else unquoted_val
return attrs
@staticmethod
def _extract_array_values(array_str: str, max_values: int) -> List[int]:
"""Extract values from array string and return as int list"""
values = [0] * max_values
if array_str:
# Handle quoted array
if array_str.startswith('"') and array_str.endswith('"'):
array_str = array_str[1:-1]
# Handle bracketed array
if array_str.startswith("[") and array_str.endswith("]"):
try:
array_str = array_str.strip('[]')
parts = array_str.split(',')
for i, part in enumerate(parts):
if i >= max_values:
break
values[i] = round(float(part.strip()))
except ValueError:
pass
# Handle space-separated values
else:
parts = array_str.split()
for i, part in enumerate(parts):
if i >= max_values:
break
try:
values[i] = round(float(part))
except ValueError:
pass
return values
@staticmethod
def _format_value(value, preserve_int=True):
"""Format value as integer with proper rounding"""
val = float(value)
# Handle special case for very small values
if abs(val) < 1e-10:
return 0
# Always round to integer
return val
def to_sequence(self) -> str:
"""Convert LottieTensor to sequence string"""
lines = []
current_context = None
string_params = getattr(self, 'string_params', {})
for i in range(self.seq_len.item()):
cmd_idx = int(self.commands[i].item())
# Skip padding and special tokens
if cmd_idx in [LottieTensor.CMD_PAD, LottieTensor.CMD_EOS, LottieTensor.CMD_SOS]:
continue
cmd = LottieTensor.COMMANDS[cmd_idx]
if not cmd: # Skip empty command entries
continue
cmd_key = f"{i}"
# Handle end tags
if cmd.startswith('/'):
lines.append(f"({cmd})")
# Reset context
if cmd in ["/position", "/scale", "/opacity", "/rotation", "/keyframe", "/anchor", "/path", "/width_animated",
"/range_start", "/range_end", "/range_offset", "/scale_animators", "/rotation_animators",
"/position_x", "/position_y", "/position_z", "/tm", "/start", "/end", "/offset", "/color_animated", "/size", "/rounded"]:
current_context = None
continue
# Update context
if cmd in ["position", "scale", "opacity", "rotation", "anchor"]:
current_context = cmd
elif cmd == "size":
# Check if size is animated
params = self.params[i].tolist()
if params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
current_context = "size"
elif cmd in ["ellipse_size", "rect_size"]:
# Check if size is animated
params = self.params[i].tolist()
if params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
current_context = "size"
elif cmd in ["position_x", "position_y", "position_z"]:
# Check if animated
params = self.params[i].tolist()
if params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
current_context = cmd
elif cmd == "path":
# Check if path is animated (would be stored in string_params)
if f"{cmd_key}_animated" in string_params:
current_context = "path"
elif cmd == "width_keyframe":
current_context = "width"
elif cmd == "start":
# Check if animated
params = self.params[i].tolist()
if params[LottieTensor.Index.SingleValue.ANIMATED] > 0.5:
current_context = "trim_start"
elif cmd == "end":
# Check if animated
params = self.params[i].tolist()
if params[LottieTensor.Index.SingleValue.ANIMATED] > 0.5:
current_context = "trim_end"
elif cmd == "offset":
# Check if animated
params = self.params[i].tolist()
if params[LottieTensor.Index.SingleValue.ANIMATED] > 0.5:
current_context = "trim_offset"
elif cmd == "mask_x":
# Check if animated
params = self.params[i].tolist()
if params[LottieTensor.Index.MaskX.A] > 0.5:
current_context = "mask_x"
elif cmd == "scale_animators":
params = self.params[i].tolist()
if params[LottieTensor.Index.ScaleAnimators.A] > 0.5:
current_context = "scale_animators"
elif cmd == "rotation_animators":
params = self.params[i].tolist()
if params[LottieTensor.Index.RotationAnimators.A] > 0.5:
current_context = "rotation_animators"
elif cmd == "opacity_animators":
params = self.params[i].tolist()
if params[LottieTensor.Index.OpacityAnimators.A] > 0.5:
current_context = "opacity_animators"
elif cmd == "position_animators":
params = self.params[i].tolist()
if params[LottieTensor.Index.PositionAnimators.A] > 0.5:
current_context = "position_animators"
elif cmd == "tracking_animators":
params = self.params[i].tolist()
if params[LottieTensor.Index.TrackingAnimators.A] > 0.5:
current_context = "tracking_animators"
elif cmd == "rect_rounded":
# Check if animated
params = self.params[i].tolist()
if params[LottieTensor.Index.SingleValue.ANIMATED] > 0.5:
current_context = "rect_rounded"
elif cmd in ["ellipse_size", "rect_size"]:
# Check if ellipse/rect size is animated
params = self.params[i].tolist()
if params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
current_context = "size"
# Extract parameters
params = self.params[i].tolist()
# Format line based on command type
if cmd_idx == LottieTensor.CMD_ANIMATION:
# Use stored string values if available
#v = string_params.get(f"{cmd_key}_v", "5.12.1")
v = "5.12.1"
#nm = string_params.get(f"{cmd_key}_nm", "Comp 1")
#markers = string_params.get(f"{cmd_key}_markers", "[]")
#props = string_params.get(f"{cmd_key}_props", "{}")
fr = LottieTensor._format_value(params[LottieTensor.Index.Animation.FR])
ip = LottieTensor._format_value(params[LottieTensor.Index.Animation.IP])
op = LottieTensor._format_value(params[LottieTensor.Index.Animation.OP])
w = LottieTensor._format_value(params[LottieTensor.Index.Animation.W])
h = LottieTensor._format_value(params[LottieTensor.Index.Animation.H])
ddd = int(params[LottieTensor.Index.Animation.DDD])
lines.append(f'({cmd} v="{v}" fr={fr} ip={ip} op={op} w={w} h={h} ddd={ddd})')
elif cmd_idx in [LottieTensor.CMD_FONTS, LottieTensor.CMD_FONTS_END, LottieTensor.CMD_CHARS,
LottieTensor.CMD_CHARS_END, LottieTensor.CMD_CHAR_SHAPES,
LottieTensor.CMD_CHAR_SHAPES_END, LottieTensor.CMD_TEXT_KEYFRAMES,
LottieTensor.CMD_TEXT_KEYFRAMES_END, LottieTensor.CMD_TEXT_DATA,
LottieTensor.CMD_TEXT_DATA_END, LottieTensor.CMD_OPACITY_ANIMATED_END,
LottieTensor.CMD_END_END, LottieTensor.CMD_START_END,
LottieTensor.CMD_OFFSET_END, LottieTensor.CMD_OPACITY_ANIMATORS_END]:
lines.append(f"({cmd})")
continue
elif cmd_idx in [LottieTensor.CMD_POSITION_X, LottieTensor.CMD_POSITION_Y, LottieTensor.CMD_POSITION_Z]:
if params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
lines.append(f"({cmd} animated=true)")
current_context = cmd # Set context
else:
val = LottieTensor._format_value(params[LottieTensor.Index.Transform.X])
lines.append(f"({cmd} {val})")
# line based on command type
# Keep all existing formatting logic but update text_keyframe
elif cmd_idx == LottieTensor.CMD_TEXT_KEYFRAME:
# Initialize tokenizer if not already done
if LottieTensor.tokenizer is None:
LottieTensor.init_tokenizer()
t = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.T])
# Retrieve all stored attributes
# Retrieve numeric values
font_size = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.FONT_SIZE])
ca = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.CA])
justify = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.JUSTIFY])
tracking = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.TRACKING])
line_height = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.LINE_HEIGHT])
letter_spacing = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.LETTER_SPACING])
# Retrieve fill_color from numeric params
fill_r = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.FILL_COLOR_R]/255)
fill_g = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.FILL_COLOR_G]/255)
fill_b = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.FILL_COLOR_B]/255)
fill_color = f"[{fill_r},{fill_g},{fill_b}]"
# Retrieve string values
#font_family = string_params.get(f"{cmd_key}_font_family", "")
#text = string_params.get(f"{cmd_key}_text", "")
# Decode font_family from tokens
font_family = ""
font_family_count = int(params[LottieTensor.Index.TextKeyframe.FONT_FAMILY_TOKEN_COUNT]) if params[LottieTensor.Index.TextKeyframe.FONT_FAMILY_TOKEN_COUNT] > -2000 else 0
if font_family_count > 0:
font_family_tokens = []
for i in range(min(font_family_count, 10)):
token_val = params[LottieTensor.Index.TextKeyframe.FONT_FAMILY_TOKENS_START + i]
if token_val > -2000:
font_family_tokens.append(int(token_val))
if font_family_tokens:
try:
font_family = LottieTensor.tokenizer.decode(font_family_tokens)
except:
font_family = ""
# Decode text from tokens
text = ""
text_count = int(params[LottieTensor.Index.TextKeyframe.TEXT_TOKEN_COUNT]) if params[LottieTensor.Index.TextKeyframe.TEXT_TOKEN_COUNT] > -2000 else 0
if text_count > 0:
text_tokens = []
for i in range(min(text_count, 15)):
token_val = params[LottieTensor.Index.TextKeyframe.TEXT_TOKENS_START + i]
if token_val > -2000:
text_tokens.append(int(token_val))
if text_tokens:
try:
text = LottieTensor.tokenizer.decode(text_tokens)
except:
text = ""
# Build the output line
line = f'({cmd} t={t} font_size={font_size} font_family="{font_family}" text="{text}" ca={ca} justify={justify} tracking={tracking} line_height={line_height} letter_spacing={letter_spacing} fill_color={fill_color}'
# Add stroke_color if present
if params[LottieTensor.Index.TextKeyframe.HAS_STROKE_COLOR] > 0.5:
stroke_r = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.STROKE_COLOR_R]/255)
stroke_g = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.STROKE_COLOR_G]/255)
stroke_b = LottieTensor._format_value(params[LottieTensor.Index.TextKeyframe.STROKE_COLOR_B]/255)
stroke_color = f"[{stroke_r},{stroke_g},{stroke_b}]"
line += f' stroke_color={stroke_color}'
# Add stroke_width if present and not zero
stroke_width = params[LottieTensor.Index.TextKeyframe.STROKE_WIDTH] if params[LottieTensor.Index.TextKeyframe.STROKE_WIDTH] > -2000 else 0
if abs(stroke_width) > 1e-6:
line += f' stroke_width={LottieTensor._format_value(stroke_width)}'
# Add offset if true
if params[LottieTensor.Index.TextKeyframe.OFFSET] > 0.5:
line += ' offset=true'
# Add wrap_position if present (新增)
wrap_pos_x = params[LottieTensor.Index.TextKeyframe.WRAP_POSITION_X]
wrap_pos_y = params[LottieTensor.Index.TextKeyframe.WRAP_POSITION_Y]
if wrap_pos_x > -2000 and wrap_pos_y > -2000:
line += f' wrap_position=[{LottieTensor._format_value(wrap_pos_x)},{LottieTensor._format_value(wrap_pos_y)}]'
# Add wrap_size if present (新增)
wrap_size_x = params[LottieTensor.Index.TextKeyframe.WRAP_SIZE_X]
wrap_size_y = params[LottieTensor.Index.TextKeyframe.WRAP_SIZE_Y]
if wrap_size_x > -2000 and wrap_size_y > -2000:
line += f' wrap_size=[{LottieTensor._format_value(wrap_size_x)},{LottieTensor._format_value(wrap_size_y)}]'
line += ')'
lines.append(line)
elif cmd_idx == LottieTensor.CMD_STAR:
#name = string_params.get(f"{cmd_key}_name", "None")
d = int(params[LottieTensor.Index.Star.D]) if params[LottieTensor.Index.Star.D] > -2000 else 1
sy = int(params[LottieTensor.Index.Star.SY]) if params[LottieTensor.Index.Star.SY] > -2000 else 1
lines.append(f'({cmd} d={d} sy={sy})')
elif cmd_idx == LottieTensor.CMD_INNER_RADIUS:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_OUTER_RADIUS:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_INNER_ROUNDNESS:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_OUTER_ROUNDNESS:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_POINTS_STAR:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'(points_star {val})')
elif cmd_idx == LottieTensor.CMD_STAR_ROTATION:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_MORE_OPTIONS:
g = int(params[LottieTensor.Index.MoreOptions.G]) if params[LottieTensor.Index.MoreOptions.G] > -2000 else 1
alignment_a = int(params[LottieTensor.Index.MoreOptions.ALIGNMENT_A]) if params[LottieTensor.Index.MoreOptions.ALIGNMENT_A] > -2000 else 0
alignment_k1 = int(params[LottieTensor.Index.MoreOptions.ALIGNMENT_K1]) if params[LottieTensor.Index.MoreOptions.ALIGNMENT_K1] > -2000 else 0
alignment_k2 = int(params[LottieTensor.Index.MoreOptions.ALIGNMENT_K2]) if params[LottieTensor.Index.MoreOptions.ALIGNMENT_K2] > -2000 else 0
alignment_ix = int(params[LottieTensor.Index.MoreOptions.ALIGNMENT_IX]) if params[LottieTensor.Index.MoreOptions.ALIGNMENT_IX] > -2000 else 2
lines.append(f'({cmd} g {g} alignment a={alignment_a} alignment_k {alignment_k1} {alignment_k2} alignment_ix {alignment_ix})')
elif cmd_idx == LottieTensor.CMD_LAYER:
index = LottieTensor._format_value(params[LottieTensor.Index.Layer.INDEX])
in_point = LottieTensor._format_value(params[LottieTensor.Index.Layer.IN_POINT])
out_point = LottieTensor._format_value(params[LottieTensor.Index.Layer.OUT_POINT])
start_time = LottieTensor._format_value(params[LottieTensor.Index.Layer.START_TIME])
line = f'({cmd} index={index} in_point={in_point} out_point={out_point} start_time={start_time}'
if params[LottieTensor.Index.Layer.DDD] > -2000:
ddd = int(params[LottieTensor.Index.Layer.DDD])
line += f' ddd={ddd}'
if params[LottieTensor.Index.Layer.HD] > -2000 and params[LottieTensor.Index.Layer.HD] > 0.5:
line += f' hd=true'
if params[LottieTensor.Index.Layer.CP] > -2000:
cp = "true" if params[LottieTensor.Index.Layer.CP] > 0.5 else "false"
line += f' cp={cp}'
if params[LottieTensor.Index.Layer.CT] > -2000:
ct = int(params[LottieTensor.Index.Layer.CT])
line += f' ct={ct}'
if params[LottieTensor.Index.Layer.HAS_MASK] > -2000:
hasMask = "true" if params[LottieTensor.Index.Layer.HAS_MASK] > 0.5 else "false"
line += f' hasMask={hasMask}'
masksProperties = string_params.get(f"{cmd_key}_masksProperties", "")
if masksProperties:
line += f' masksProperties={masksProperties}'
if params[LottieTensor.Index.Layer.AO] > -2000:
ao = int(params[LottieTensor.Index.Layer.AO])
line += f' ao={ao}'
if params[LottieTensor.Index.Layer.TT] > -2000:
tt = int(params[LottieTensor.Index.Layer.TT])
line += f' tt={tt}'
if params[LottieTensor.Index.Layer.TP] > -2000:
tp = int(params[LottieTensor.Index.Layer.TP])
line += f' tp={tp}'
if params[LottieTensor.Index.Layer.TD] > -2000:
td = int(params[LottieTensor.Index.Layer.TD])
line += f' td={td}'
line += ')'
lines.append(line)
elif cmd_idx == LottieTensor.CMD_NULL_LAYER:
index = LottieTensor._format_value(params[LottieTensor.Index.NullLayer.INDEX])
in_point = LottieTensor._format_value(params[LottieTensor.Index.NullLayer.IN_POINT])
out_point = LottieTensor._format_value(params[LottieTensor.Index.NullLayer.OUT_POINT])
start_time = LottieTensor._format_value(params[LottieTensor.Index.NullLayer.START_TIME])
line = f'({cmd} index={index} in_point={in_point} out_point={out_point} start_time={start_time}'
if params[LottieTensor.Index.PrecompLayer.HD] > -2000 and params[LottieTensor.Index.PrecompLayer.HD] > 0.5:
line += f' hd=true'
if params[LottieTensor.Index.PrecompLayer.CP] > -2000:
cp = "true" if params[LottieTensor.Index.PrecompLayer.CP] > 0.5 else "false"
line += f' cp={cp}'
if params[LottieTensor.Index.PrecompLayer.HAS_MASK] > -2000:
hasMask = "true" if params[LottieTensor.Index.PrecompLayer.HAS_MASK] > 0.5 else "false"
line += f' hasMask={hasMask}'
if params[LottieTensor.Index.PrecompLayer.AO] > -2000:
ao = int(params[LottieTensor.Index.PrecompLayer.AO])
line += f' ao={ao}'
if params[LottieTensor.Index.PrecompLayer.TT] > -2000:
tt = int(params[LottieTensor.Index.PrecompLayer.TT])
line += f' tt={tt}'
if params[LottieTensor.Index.PrecompLayer.TP] > -2000:
tp = int(params[LottieTensor.Index.PrecompLayer.TP])
line += f' tp={tp}'
if params[LottieTensor.Index.PrecompLayer.TD] > -2000:
td = int(params[LottieTensor.Index.PrecompLayer.TD])
line += f' td={td}'
line += ')'
lines.append(line)
elif cmd_idx == LottieTensor.CMD_PRECOMP_LAYER:
index = LottieTensor._format_value(params[LottieTensor.Index.PrecompLayer.INDEX])
in_point = LottieTensor._format_value(params[LottieTensor.Index.PrecompLayer.IN_POINT])
out_point = LottieTensor._format_value(params[LottieTensor.Index.PrecompLayer.OUT_POINT])
start_time = LottieTensor._format_value(params[LottieTensor.Index.PrecompLayer.START_TIME])
line = f'({cmd} index={index} in_point={in_point} out_point={out_point} start_time={start_time}'
if params[LottieTensor.Index.PrecompLayer.H] > -2000:
h = int(params[LottieTensor.Index.PrecompLayer.H])
line += f' h={h}'
if params[LottieTensor.Index.PrecompLayer.W] > -2000:
w = int(params[LottieTensor.Index.PrecompLayer.W])
line += f' w={w}'
if params[LottieTensor.Index.PrecompLayer.DDD] > -2000:
ddd = int(params[LottieTensor.Index.PrecompLayer.DDD])
line += f' ddd={ddd}'
if params[LottieTensor.Index.PrecompLayer.HD] > -2000 and params[LottieTensor.Index.PrecompLayer.HD] > 0.5:
line += f' hd=true'
if params[LottieTensor.Index.PrecompLayer.CP] > -2000:
cp = "true" if params[LottieTensor.Index.PrecompLayer.CP] > 0.5 else "false"
line += f' cp={cp}'
if params[LottieTensor.Index.PrecompLayer.CT] > -2000:
ct = int(params[LottieTensor.Index.PrecompLayer.CT])
line += f' ct={ct}'
if params[LottieTensor.Index.PrecompLayer.HAS_MASK] > -2000:
hasMask = "true" if params[LottieTensor.Index.PrecompLayer.HAS_MASK] > 0.5 else "false"
line += f' hasMask={hasMask}'
masksProperties = string_params.get(f"{cmd_key}_masksProperties", "")
if masksProperties:
line += f' masksProperties={masksProperties}'
if params[LottieTensor.Index.PrecompLayer.AO] > -2000:
ao = int(params[LottieTensor.Index.PrecompLayer.AO])
line += f' ao={ao}'
if params[LottieTensor.Index.PrecompLayer.TT] > -2000:
tt = int(params[LottieTensor.Index.PrecompLayer.TT])
line += f' tt={tt}'
if params[LottieTensor.Index.PrecompLayer.TP] > -2000:
tp = int(params[LottieTensor.Index.PrecompLayer.TP])
line += f' tp={tp}'
if params[LottieTensor.Index.PrecompLayer.TD] > -2000:
td = int(params[LottieTensor.Index.PrecompLayer.TD])
line += f' td={td}'
line += ')'
lines.append(line)
elif cmd_idx == LottieTensor.CMD_REFERENCE_ID:
tokenizer = LottieTensor.get_tokenizer()
id_count = int(params[LottieTensor.Index.ReferenceId.ID_TOKEN_COUNT]) if params[LottieTensor.Index.ReferenceId.ID_TOKEN_COUNT] > -2000 else 0
id_tokens = []
for i in range(id_count):
if params[LottieTensor.Index.ReferenceId.ID_TOKEN_0 + i] > -2000:
id_tokens.append(int(params[LottieTensor.Index.ReferenceId.ID_TOKEN_0 + i]))
reference_id = tokenizer.decode(id_tokens, skip_special_tokens=True) if id_tokens else "comp_0"
lines.append(f'({cmd} "{reference_id}")')
elif cmd_idx == LottieTensor.CMD_DIMENSIONS:
width = LottieTensor._format_value(params[LottieTensor.Index.Dimensions.WIDTH])
height = LottieTensor._format_value(params[LottieTensor.Index.Dimensions.HEIGHT])
lines.append(f'({cmd} width={width} height={height})')
elif cmd_idx == LottieTensor.CMD_STROKE:
color_animated = params[LottieTensor.Index.Stroke.COLOR_ANIMATED] > 0.5
line = f'({cmd}'
if color_animated:
line += ' color_animated=true'
else:
r = LottieTensor._format_value(params[LottieTensor.Index.Stroke.R] / 255)
g = LottieTensor._format_value(params[LottieTensor.Index.Stroke.G] / 255)
b = LottieTensor._format_value(params[LottieTensor.Index.Stroke.B] / 255)
a = LottieTensor._format_value(params[LottieTensor.Index.Stroke.A] / 255)
line += f' r={r} g={g} b={b} a={a}'
color_dim = int(params[LottieTensor.Index.Stroke.COLOR_DIM]) if params[LottieTensor.Index.Stroke.COLOR_DIM] > -2000 else 4
has_c_a = "True" if params[LottieTensor.Index.Stroke.HAS_C_A] > 0.5 else "False"
has_c_ix = "True" if params[LottieTensor.Index.Stroke.HAS_C_IX] > 0.5 else "False"
c_ix = int(params[LottieTensor.Index.Stroke.C_IX]) if params[LottieTensor.Index.Stroke.C_IX] > -2000 else 3
bm = int(params[LottieTensor.Index.Stroke.BM]) if params[LottieTensor.Index.Stroke.BM] > -2000 else 0
lc = int(params[LottieTensor.Index.Stroke.LC]) if params[LottieTensor.Index.Stroke.LC] > -2000 else 1
lj = int(params[LottieTensor.Index.Stroke.LJ]) if params[LottieTensor.Index.Stroke.LJ] > -2000 else 1
ml = int(params[LottieTensor.Index.Stroke.ML]) if params[LottieTensor.Index.Stroke.ML] > -2000 else 4
line += f' color_dim={color_dim} has_c_a={has_c_a} has_c_ix={has_c_ix}'
if not color_animated:
line += f' c_ix={c_ix}'
line += f' bm={bm} lc={lc} lj={lj} ml={ml}'
width_animated = params[LottieTensor.Index.Stroke.WIDTH_ANIMATED] > 0.5
if width_animated:
line += ' width_animated=true'
current_context = "width"
line += ')'
lines.append(line)
if width_animated:
lines.append('(width_animated true)')
if color_animated:
current_context = "stroke_color"
elif cmd_idx == LottieTensor.CMD_DASHES:
dashes_str = string_params.get(f"{cmd_key}_dashes", "")
if dashes_str:
lines.append(f'({cmd} {dashes_str})')
else:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_DASH:
type_map = {0: "d", 1: "g", 2: "o"}
type_val = int(params[LottieTensor.Index.Dash.TYPE]) if params[LottieTensor.Index.Dash.TYPE] > -2000 else 0
dash_type = type_map.get(type_val, "d")
# 除以100恢复原值
length = LottieTensor._format_value(params[LottieTensor.Index.Dash.LENGTH] / 10, preserve_int=False)
v_ix = int(params[LottieTensor.Index.Dash.V_IX]) if params[LottieTensor.Index.Dash.V_IX] > -2000 else 1
lines.append(f'({cmd} type="{dash_type}" length={length} v_ix={v_ix})')
elif cmd_idx == LottieTensor.CMD_DASH_ANIMATED:
type_map = {0: "d", 1: "g", 2: "o"}
type_val = int(params[LottieTensor.Index.DashAnimated.TYPE]) if params[LottieTensor.Index.DashAnimated.TYPE] > -2000 else 2
dash_type = type_map.get(type_val, "o")
v_ix = int(params[LottieTensor.Index.DashAnimated.V_IX]) if params[LottieTensor.Index.DashAnimated.V_IX] > -2000 else 7
name = string_params.get(f"{cmd_key}_name", "")
lines.append(f'({cmd} type="{dash_type}" name="{name}" v_ix={v_ix})')
current_context = "dash_animated"
elif cmd_idx == LottieTensor.CMD_DASH_KEYFRAME:
t = LottieTensor._format_value(params[LottieTensor.Index.DashKeyframe.T])
s = LottieTensor._format_value(params[LottieTensor.Index.DashKeyframe.S] / 10, preserve_int=False)
i_x = params[LottieTensor.Index.DashKeyframe.I_X]/100
i_y = params[LottieTensor.Index.DashKeyframe.I_Y]/100
o_x = params[LottieTensor.Index.DashKeyframe.O_X]/100
o_y = params[LottieTensor.Index.DashKeyframe.O_Y]/100
has_easing = (i_x > -2000 or i_y > -2000 or o_x > -2000 or o_y > -2000)
if has_easing:
i_x_val = LottieTensor._format_value(i_x, preserve_int=False) if i_x > -2000 else 0
i_y_val = LottieTensor._format_value(i_y, preserve_int=False) if i_y > -2000 else 0
o_x_val = LottieTensor._format_value(o_x, preserve_int=False) if o_x > -2000 else 0
o_y_val = LottieTensor._format_value(o_y, preserve_int=False) if o_y > -2000 else 0
lines.append(f'({cmd} t={t} s={s} i_x={i_x_val} i_y={i_y_val} o_x={o_x_val} o_y={o_y_val})')
else:
lines.append(f'({cmd} t={t} s={s})')
elif cmd_idx == LottieTensor.CMD_DASH_ANIMATED_END:
lines.append(f'({cmd})')
current_context = None
elif cmd_idx == LottieTensor.CMD_DASH_OFFSET:
o = LottieTensor._format_value(params[LottieTensor.Index.DashOffset.O] / 10, preserve_int=False)
lines.append(f'({cmd} {o})')
elif cmd_idx == LottieTensor.CMD_DASHES_END:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_DASH_ANIMATED_END:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_SIZE_END:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_COLOR_KEYFRAME:
t = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.T])
r = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.S1]/255)
g = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.S2]/255)
b = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.S3]/255)
a = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.E1]/255)
i_x = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.I_X]/100, preserve_int=False)
i_y = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.I_Y]/100, preserve_int=False)
o_x = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.O_X]/100, preserve_int=False)
o_y = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.O_Y]/100, preserve_int=False)
lines.append(f'({cmd} t={t} r={r} g={g} b={b} a={a} i_x={i_x} i_y={i_y} o_x={o_x} o_y={o_y})')
elif cmd_idx == LottieTensor.CMD_OPACITY_ANIMATED:
lines.append(f'({cmd} true)')
current_context = "opacity_animated"
elif cmd_idx == LottieTensor.CMD_OPACITY_KEYFRAME:
t = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.T])
keyframe_line = f'({cmd} t={t}'
if params[LottieTensor.Index.Keyframe.S1] > -2000:
s = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.S1])
keyframe_line += f' s={s}'
i_x = params[LottieTensor.Index.Keyframe.I_X]/100
i_y = params[LottieTensor.Index.Keyframe.I_Y]/100
o_x = params[LottieTensor.Index.Keyframe.O_X]/100
o_y = params[LottieTensor.Index.Keyframe.O_Y]/100
if i_x > -2000 or i_y > -2000 or o_x > -2000 or o_y > -2000:
i_x_val = LottieTensor._format_value(i_x, preserve_int=False) if i_x > -2000 else 0
i_y_val = LottieTensor._format_value(i_y, preserve_int=False) if i_y > -2000 else 0
o_x_val = LottieTensor._format_value(o_x, preserve_int=False) if o_x > -2000 else 0
o_y_val = LottieTensor._format_value(o_y, preserve_int=False) if o_y > -2000 else 0
keyframe_line += f' i_x={i_x_val} i_y={i_y_val} o_x={o_x_val} o_y={o_y_val}'
keyframe_line += ')'
lines.append(keyframe_line)
elif cmd_idx == LottieTensor.CMD_WIDTH_KEYFRAME:
t = LottieTensor._format_value(params[LottieTensor.Index.WidthKeyframe.T])
keyframe_line = f'({cmd} t={t}'
if params[LottieTensor.Index.WidthKeyframe.S] > -2000:
s = LottieTensor._format_value(params[LottieTensor.Index.WidthKeyframe.S] / 10, preserve_int=False)
keyframe_line += f' s={s}'
i_x = params[LottieTensor.Index.WidthKeyframe.I_X]/100
i_y = params[LottieTensor.Index.WidthKeyframe.I_Y]/100
o_x = params[LottieTensor.Index.WidthKeyframe.O_X]/100
o_y = params[LottieTensor.Index.WidthKeyframe.O_Y]/100
has_easing = (i_x > -2000 or i_y > -2000 or o_x > -2000 or o_y > -2000)
if has_easing:
i_x_val = LottieTensor._format_value(i_x, preserve_int=False) if i_x > -2000 else 0
i_y_val = LottieTensor._format_value(i_y, preserve_int=False) if i_y > -2000 else 0
o_x_val = LottieTensor._format_value(o_x, preserve_int=False) if o_x > -2000 else 0
o_y_val = LottieTensor._format_value(o_y, preserve_int=False) if o_y > -2000 else 0
keyframe_line += f' i_x={i_x_val} i_y={i_y_val} o_x={o_x_val} o_y={o_y_val}'
keyframe_line += ")"
lines.append(keyframe_line)
elif cmd_idx == LottieTensor.CMD_TRANSFORM:
lines.append(f"({cmd})")
elif cmd_idx == LottieTensor.CMD_POSITION:
if cmd == "position" and current_context not in ["position", "scale", "opacity", "rotation", "anchor"]:
x = LottieTensor._format_value(params[LottieTensor.Index.TwoValues.VALUE1])
y = LottieTensor._format_value(params[LottieTensor.Index.TwoValues.VALUE2])
lines.append(f"({cmd} {x} {y})")
else:
if params[LottieTensor.Index.Transform.ANIMATED] == 2.0:
lines.append(f"({cmd} separated=true)")
elif params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
lines.append(f"({cmd} animated=true)")
else:
x = LottieTensor._format_value(params[LottieTensor.Index.Transform.X])
y = LottieTensor._format_value(params[LottieTensor.Index.Transform.Y])
z = LottieTensor._format_value(params[LottieTensor.Index.Transform.Z])
if params[LottieTensor.Index.Transform.Z] > -2000 and abs(params[LottieTensor.Index.Transform.Z]) > 1e-6:
lines.append(f"({cmd} {x} {y} {z})")
else:
lines.append(f"({cmd} {x} {y})")
elif cmd_idx == LottieTensor.CMD_POSITION_X:
if params[LottieTensor.Index.SingleValue.VALUE] > -2000:
value = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f"({cmd} {value})")
else:
lines.append(f"({cmd})")
elif cmd_idx == LottieTensor.CMD_POSITION_Y:
if params[LottieTensor.Index.SingleValue.VALUE] > -2000:
value = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f"({cmd} {value})")
else:
lines.append(f"({cmd})")
elif cmd_idx == LottieTensor.CMD_POSITION_Z:
if params[LottieTensor.Index.SingleValue.VALUE] > -2000:
value = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f"({cmd} {value})")
else:
lines.append(f"({cmd})")
elif cmd_idx == LottieTensor.CMD_SCALE:
if params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
lines.append(f"({cmd} animated=true)")
else:
x = LottieTensor._format_value(params[LottieTensor.Index.Transform.X])
y = LottieTensor._format_value(params[LottieTensor.Index.Transform.Y])
z = LottieTensor._format_value(params[LottieTensor.Index.Transform.Z])
if params[LottieTensor.Index.Transform.Z] > -2000:
lines.append(f"({cmd} {x} {y} {z})")
else:
lines.append(f"({cmd} {x} {y})")
elif cmd_idx == LottieTensor.CMD_ROTATION:
if params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
lines.append(f"({cmd} animated=true)")
else:
angle = LottieTensor._format_value(params[LottieTensor.Index.Transform.X])
lines.append(f"({cmd} {angle})")
elif cmd_idx == LottieTensor.CMD_OPACITY:
if params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
lines.append(f"({cmd} animated=true)")
else:
val = LottieTensor._format_value(params[LottieTensor.Index.Transform.X])
lines.append(f"({cmd} {val})")
elif cmd_idx == LottieTensor.CMD_ANCHOR:
if params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
lines.append(f"({cmd} animated=true)")
else:
x = LottieTensor._format_value(params[LottieTensor.Index.Transform.X])
y = LottieTensor._format_value(params[LottieTensor.Index.Transform.Y])
z = LottieTensor._format_value(params[LottieTensor.Index.Transform.Z])
if params[LottieTensor.Index.Transform.Z] > -2000 and abs(params[LottieTensor.Index.Transform.Z]) > 1e-6:
lines.append(f"({cmd} {x} {y} {z})")
else:
lines.append(f"({cmd} {x} {y})")
elif cmd_idx == LottieTensor.CMD_TM:
a = int(params[LottieTensor.Index.Tm.A]) if params[LottieTensor.Index.Tm.A] > -2000 else 1
lines.append(f'({cmd} a={a})')
if a > 0.5:
current_context = "tm"
else:
current_context = "tm_static"
elif cmd_idx == LottieTensor.CMD_VALUE:
val = LottieTensor._format_value(params[LottieTensor.Index.Value.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_KEYFRAME:
t = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.T])
keyframe_line = f'({cmd} t={t}'
is_hold = params[LottieTensor.Index.Keyframe.H_FLAG] > 0.5
has_s = params[LottieTensor.Index.Keyframe.S1] > -2000
if has_s and current_context != "path":
if current_context in ["opacity", "rotation", "position_x", "position_y", "position_z", "tm", "width",
"trim_start", "trim_end", "trim_offset", "mask_x", "rotation_animators", "opacity_animators", "tracking_animators"]:
s = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.S1], preserve_int=False)
s_str = f'"{s}"'
else:
s1 = params[LottieTensor.Index.Keyframe.S1]
s2 = params[LottieTensor.Index.Keyframe.S2]
s3 = params[LottieTensor.Index.Keyframe.S3]
s_parts = []
s_parts.append(str(LottieTensor._format_value(s1, preserve_int=False)))
s_parts.append(str(LottieTensor._format_value(s2, preserve_int=False)))
if s3 > -2000 and abs(s3) > 1e-6:
s_parts.append(str(LottieTensor._format_value(s3, preserve_int=False)))
s_str = f'"{" ".join(s_parts)}"'
keyframe_line += f' s={s_str}'
has_e = params[LottieTensor.Index.Keyframe.E1] > -2000
if has_e: # Output e regardless of hold flag
if current_context in ["trim_start", "trim_end", "trim_offset"]:
e_val = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.E1], preserve_int=False)
keyframe_line += f' e="{e_val}"'
elif current_context == "rotation":
e_val = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.E1], preserve_int=False)
keyframe_line += f' e="[{e_val}]"'
elif current_context == "scale":
e1 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.E1], preserve_int=False) if params[LottieTensor.Index.Keyframe.E1] > -2000 else 0
e2 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.E2], preserve_int=False) if params[LottieTensor.Index.Keyframe.E2] > -2000 else 0
e3 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.E3], preserve_int=False) if params[LottieTensor.Index.Keyframe.E3] > -2000 else 0
keyframe_line += f' e="[{e1}, {e2}, {e3}]"'
if is_hold:
keyframe_line += ' h=1'
if current_context in ["position", "anchor", "scale_animators", "position_animators", "size"]:
has_multi_easing = (
params[LottieTensor.Index.Keyframe.I_X2] > -2000 or
params[LottieTensor.Index.Keyframe.I_Y2] > -2000 or
params[LottieTensor.Index.Keyframe.O_X2] > -2000 or
params[LottieTensor.Index.Keyframe.O_Y2] > -2000
)
if has_multi_easing:
i_x_vals = []
i_y_vals = []
o_x_vals = []
o_y_vals = []
i_x1 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.I_X] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.I_X] > -2000 else 0
i_x2 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.I_X2] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.I_X2] > -2000 else i_x1
i_x_vals = [i_x1, i_x2]
i_y1 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.I_Y] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.I_Y] > -2000 else 0
i_y2 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.I_Y2] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.I_Y2] > -2000 else i_y1
i_y_vals = [i_y1, i_y2]
o_x1 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.O_X] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.O_X] > -2000 else 0
o_x2 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.O_X2] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.O_X2] > -2000 else o_x1
o_x_vals = [o_x1, o_x2]
o_y1 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.O_Y] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.O_Y] > -2000 else 0
o_y2 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.O_Y2] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.O_Y2] > -2000 else o_y1
o_y_vals = [o_y1, o_y2]
i_x3 = params[LottieTensor.Index.Keyframe.I_X3]
i_y3 = params[LottieTensor.Index.Keyframe.I_Y3]
o_x3 = params[LottieTensor.Index.Keyframe.O_X3]
o_y3 = params[LottieTensor.Index.Keyframe.O_Y3]
has_third_dim = (
(i_x3 > -2000 and abs(i_x3) > 1e-6) or
(i_y3 > -2000 and abs(i_y3) > 1e-6) or
(o_x3 > -2000 and abs(o_x3) > 1e-6) or
(o_y3 > -2000 and abs(o_y3) > 1e-6)
)
if has_third_dim:
i_x_vals.append(LottieTensor._format_value(i_x3 / 100, preserve_int=False) if i_x3 > -2000 else i_x1)
i_y_vals.append(LottieTensor._format_value(i_y3 / 100, preserve_int=False) if i_y3 > -2000 else i_y1)
o_x_vals.append(LottieTensor._format_value(o_x3 / 100, preserve_int=False) if o_x3 > -2000 else o_x1)
o_y_vals.append(LottieTensor._format_value(o_y3 / 100, preserve_int=False) if o_y3 > -2000 else o_y1)
keyframe_line += f' i_x="{" ".join(str(v) for v in i_x_vals)}" i_y="{" ".join(str(v) for v in i_y_vals)}" o_x="{" ".join(str(v) for v in o_x_vals)}" o_y="{" ".join(str(v) for v in o_y_vals)}"'
elif params[LottieTensor.Index.Keyframe.I_X] > -2000 or params[LottieTensor.Index.Keyframe.I_Y] > -2000 or params[LottieTensor.Index.Keyframe.O_X] > -2000 or params[LottieTensor.Index.Keyframe.O_Y] > -2000:
# Fallback to single values if no multi-dimensional values found - DIVIDE BY 100
i_x_val = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.I_X] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.I_X] > -2000 else 0
i_y_val = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.I_Y] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.I_Y] > -2000 else 0
o_x_val = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.O_X] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.O_X] > -2000 else 0
o_y_val = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.O_Y] / 100, preserve_int=False) if params[LottieTensor.Index.Keyframe.O_Y] > -2000 else 0
keyframe_line += f' i_x={i_x_val} i_y={i_y_val} o_x={o_x_val} o_y={o_y_val}'
else:
# For single-dimensional properties, parse single easing values - DIVIDE BY 100
i_x = params[LottieTensor.Index.Keyframe.I_X]
i_y = params[LottieTensor.Index.Keyframe.I_Y]
o_x = params[LottieTensor.Index.Keyframe.O_X]
o_y = params[LottieTensor.Index.Keyframe.O_Y]
if i_x > -2000 or i_y > -2000 or o_x > -2000 or o_y > -2000:
i_x_val = LottieTensor._format_value(i_x / 100, preserve_int=False) if i_x > -2000 else 0
i_y_val = LottieTensor._format_value(i_y / 100, preserve_int=False) if i_y > -2000 else 0
o_x_val = LottieTensor._format_value(o_x / 100, preserve_int=False) if o_x > -2000 else 0
o_y_val = LottieTensor._format_value(o_y / 100, preserve_int=False) if o_y > -2000 else 0
keyframe_line += f' i_x={i_x_val} i_y={i_y_val} o_x={o_x_val} o_y={o_y_val}'
# Add to/ti parameters (they are separate from hold flag)
has_to = any(params[LottieTensor.Index.Keyframe.TO1 + j] > -2000 for j in range(3))
has_ti = any(params[LottieTensor.Index.Keyframe.TI1 + j] > -2000 for j in range(3))
if has_to:
to_values = []
for i in range(3):
val = params[LottieTensor.Index.Keyframe.TO1 + i]
if val > -2000:
to_values.append(LottieTensor._format_value(val, preserve_int=False))
else:
break # Stop at first padding value
# Only output non-zero values, but always include at least 2 dimensions if any exist
while len(to_values) > 2 and abs(to_values[-1]) < 1e-10:
to_values.pop() # Remove trailing zeros
# Ensure we have at least 2 values if we have any
while len(to_values) < 2:
to_values.append(0)
keyframe_line += f' to="[{", ".join(str(v) for v in to_values)}]"'
if has_ti:
ti_values = []
for i in range(3):
val = params[LottieTensor.Index.Keyframe.TI1 + i]
if val > -2000:
ti_values.append(LottieTensor._format_value(val, preserve_int=False))
else:
break # Stop at first padding value
# Only output non-zero values, but always include at least 2 dimensions if any exist
while len(ti_values) > 2 and abs(ti_values[-1]) < 1e-10:
ti_values.pop() # Remove trailing zeros
# Ensure we have at least 2 values if we have any
while len(ti_values) < 2:
ti_values.append(0)
keyframe_line += f' ti="[{", ".join(str(v) for v in ti_values)}]"'
# Check and output e parameter based on context
has_e = params[LottieTensor.Index.Keyframe.E1] > -2000
if has_e: # Output e regardless of hold flag
if current_context == "rotation":
# For rotation, output single e value
e_val = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.E1], preserve_int=False)
keyframe_line += f' e="[{e_val}]"'
elif current_context == "scale":
# For scale, output three e values
e1 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.E1], preserve_int=False) if params[LottieTensor.Index.Keyframe.E1] > -2000 else 0
e2 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.E2], preserve_int=False) if params[LottieTensor.Index.Keyframe.E2] > -2000 else 0
e3 = LottieTensor._format_value(params[LottieTensor.Index.Keyframe.E3], preserve_int=False) if params[LottieTensor.Index.Keyframe.E3] > -2000 else 0
keyframe_line += f' e="[{e1}, {e2}, {e3}]"'
# Add other contexts as needed
keyframe_line += ")"
lines.append(keyframe_line)
elif cmd_idx == LottieTensor.CMD_GROUP:
ix = int(params[LottieTensor.Index.Group.IX]) if params[LottieTensor.Index.Group.IX] > -2000 else 1
cix = int(params[LottieTensor.Index.Group.CIX]) if params[LottieTensor.Index.Group.CIX] > -2000 else 2
bm = int(params[LottieTensor.Index.Group.BM]) if params[LottieTensor.Index.Group.BM] > -2000 else 0
hd = "true" if params[LottieTensor.Index.Group.HD] > 0.5 else "false"
np = int(params[LottieTensor.Index.Group.NP]) if params[LottieTensor.Index.Group.NP] > -2000 else 0
lines.append(f'({cmd} ix={ix} cix={cix} bm={bm} hd={hd} np={np})')
elif cmd_idx == LottieTensor.CMD_PATH:
ix = int(params[LottieTensor.Index.Path.IX]) if params[LottieTensor.Index.Path.IX] > -2000 else 1
ind = int(params[LottieTensor.Index.Path.IND]) if params[LottieTensor.Index.Path.IND] > -2000 else 0
ks_ix = int(params[LottieTensor.Index.Path.KS_IX]) if params[LottieTensor.Index.Path.KS_IX] > -2000 else 2
closed = "true" if params[LottieTensor.Index.Path.CLOSED] > 0.5 else "false"
hd = "true" if params[LottieTensor.Index.Path.HD] > 0.5 else "false" # Add HD
if params[LottieTensor.Index.Path.ANIMATED] > 0.5:
lines.append(f'({cmd} ix={ix} ind={ind} ks_ix={ks_ix} animated="true" hd={hd})')
current_context = "path"
else:
lines.append(f'({cmd} ix={ix} ind={ind} ks_ix={ks_ix} closed={closed} hd={hd})')
elif cmd_idx == LottieTensor.CMD_POINT:
# Check if this is a valid point (not padding)
if params[LottieTensor.Index.Point.X] > -2000 and params[LottieTensor.Index.Point.Y] > -2000:
x = LottieTensor._format_value(params[LottieTensor.Index.Point.X])
y = LottieTensor._format_value(params[LottieTensor.Index.Point.Y])
in_x = LottieTensor._format_value(params[LottieTensor.Index.Point.IN_X])
in_y = LottieTensor._format_value(params[LottieTensor.Index.Point.IN_Y])
out_x = LottieTensor._format_value(params[LottieTensor.Index.Point.OUT_X])
out_y = LottieTensor._format_value(params[LottieTensor.Index.Point.OUT_Y])
lines.append(f"({cmd} x={x} y={y} in_x={in_x} in_y={in_y} out_x={out_x} out_y={out_y})")
# else: skip padding points
elif cmd_idx == LottieTensor.CMD_FILL:
#name = string_params.get(f"{cmd_key}_name", "Fill")
color_dim = int(params[LottieTensor.Index.Fill.COLOR_DIM]) if params[LottieTensor.Index.Fill.COLOR_DIM] > -2000 else 3
has_c_a = "True" if params[LottieTensor.Index.Fill.HAS_C_A] > 0.5 else "False"
has_c_ix = "True" if params[LottieTensor.Index.Fill.HAS_C_IX] > 0.5 else "False"
c_ix = int(params[LottieTensor.Index.Fill.C_IX]) if params[LottieTensor.Index.Fill.C_IX] > -2000 else 4
bm = int(params[LottieTensor.Index.Fill.BM]) if params[LottieTensor.Index.Fill.BM] > -2000 else 0
fill_rule = int(params[LottieTensor.Index.Fill.FILL_RULE]) if params[LottieTensor.Index.Fill.FILL_RULE] > -2000 else 1
has_o_a = "True" if params[LottieTensor.Index.Fill.HAS_O_A] > 0.5 else "False"
has_o_ix = "True" if params[LottieTensor.Index.Fill.HAS_O_IX] > 0.5 else "False"
o_ix = int(params[LottieTensor.Index.Fill.O_IX]) if params[LottieTensor.Index.Fill.O_IX] > -2000 else 5
color_animated = params[LottieTensor.Index.Fill.COLOR_ANIMATED] > 0.5
opacity_animated = params[LottieTensor.Index.Fill.OPACITY_ANIMATED] > 0.5
line_parts = [f'({cmd}']
# Handle color output
if color_animated:
# Output color keyframes with easing
color_keyframes_json = string_params.get(f"{cmd_key}_color_keyframes", "[]")
color_keyframes = json.loads(color_keyframes_json)
for i, kf in enumerate(color_keyframes):
line_parts.append(f' c_kf_{i}_t={LottieTensor._format_value(kf["t"])}')
line_parts.append(f' c_kf_{i}_r={LottieTensor._format_value(kf["r"]/255)}')
line_parts.append(f' c_kf_{i}_g={LottieTensor._format_value(kf["g"]/255)}')
line_parts.append(f' c_kf_{i}_b={LottieTensor._format_value(kf["b"]/255)}')
# Add easing parameters if they exist and are non-zero (divide by 100 for float output)
if "i_x" in kf and (abs(kf["i_x"]) > 1e-6 or abs(kf.get("i_y", 0)) > 1e-6 or
abs(kf.get("o_x", 0)) > 1e-6 or abs(kf.get("o_y", 0)) > 1e-6):
line_parts.append(f' c_kf_{i}_i_x={LottieTensor._format_value(kf["i_x"] / 100, preserve_int=False)}')
line_parts.append(f' c_kf_{i}_i_y={LottieTensor._format_value(kf.get("i_y", 0) / 100, preserve_int=False)}')
line_parts.append(f' c_kf_{i}_o_x={LottieTensor._format_value(kf.get("o_x", 0) / 100, preserve_int=False)}')
line_parts.append(f' c_kf_{i}_o_y={LottieTensor._format_value(kf.get("o_y", 0) / 100, preserve_int=False)}')
line_parts.append(f' c_kf_count={len(color_keyframes)}')
line_parts.append(' color_animated=true')
else:
# Output static color
r = LottieTensor._format_value(params[LottieTensor.Index.Fill.R]/255)
g = LottieTensor._format_value(params[LottieTensor.Index.Fill.G]/255)
b = LottieTensor._format_value(params[LottieTensor.Index.Fill.B]/255)
line_parts.append(f' r={r} g={g} b={b} color_animated=false')
# Add common color parameters
line_parts.append(f' color_dim={color_dim} has_c_a={has_c_a} has_c_ix={has_c_ix} c_ix={c_ix} bm={bm} fill_rule={fill_rule}')
# Handle opacity output
if opacity_animated:
# Output opacity keyframes (divide by 100 for float output)
opacity_keyframes_json = string_params.get(f"{cmd_key}_opacity_keyframes", "[]")
opacity_keyframes = json.loads(opacity_keyframes_json)
for i, kf in enumerate(opacity_keyframes):
line_parts.append(f' o_kf_{i}_t={LottieTensor._format_value(kf["t"])}')
line_parts.append(f' o_kf_{i}_s={LottieTensor._format_value(kf["s"])}')
line_parts.append(f' o_kf_{i}_i_x={LottieTensor._format_value(kf["i_x"] / 100, preserve_int=False)}')
line_parts.append(f' o_kf_{i}_i_y={LottieTensor._format_value(kf["i_y"] / 100, preserve_int=False)}')
line_parts.append(f' o_kf_{i}_o_x={LottieTensor._format_value(kf["o_x"] / 100, preserve_int=False)}')
line_parts.append(f' o_kf_{i}_o_y={LottieTensor._format_value(kf["o_y"] / 100, preserve_int=False)}')
line_parts.append(f' o_kf_count={len(opacity_keyframes)}')
line_parts.append(' opacity_animated=true')
else:
# Output static opacity
opacity = LottieTensor._format_value(params[LottieTensor.Index.Fill.OPACITY])
line_parts.append(f' opacity={opacity} opacity_animated=false')
# Add opacity-related parameters
line_parts.append(f' has_o_a={has_o_a} has_o_ix={has_o_ix} o_ix={o_ix})')
lines.append(''.join(line_parts))
elif cmd_idx == LottieTensor.CMD_BEZIER:
closed = "true" if params[LottieTensor.Index.Bezier.CLOSED] > 0.5 else "false"
lines.append(f'({cmd} closed={closed})')
elif cmd_idx == LottieTensor.CMD_ELLIPSE:
#name = string_params.get(f"{cmd_key}_name", "Ellipse Path 1")
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_SIZE:
# Check if size is animated - also check for PAD_VAL
if params[LottieTensor.Index.Transform.ANIMATED] > -2000 and params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
lines.append(f'({cmd} animated=true)')
current_context = "size"
else:
# 修改:使用 Transform.X 和 Transform.Y
x = LottieTensor._format_value(params[LottieTensor.Index.Transform.X])
y = LottieTensor._format_value(params[LottieTensor.Index.Transform.Y])
lines.append(f'({cmd} {x} {y})')
elif cmd_idx == LottieTensor.CMD_RECT:
#name = string_params.get(f"{cmd_key}_name", "Rectangle Path 1")
hd = "true" if params[LottieTensor.Index.Rect.HD] > 0.5 else "false"
d = int(params[LottieTensor.Index.Rect.D]) if params[LottieTensor.Index.Rect.D] > -2000 else 1
lines.append(f'({cmd} hd={hd} d={d})')
elif cmd_idx == LottieTensor.CMD_ROUNDED:
rounded = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
ix = int(params[LottieTensor.Index.SingleValue.IX]) if params[LottieTensor.Index.SingleValue.IX] > -2000 else 4
lines.append(f'({cmd} {rounded} ix={ix})')
elif cmd_idx == LottieTensor.CMD_TRIM:
#name = string_params.get(f"{cmd_key}_name", "Trim Paths 1")
ix = int(params[LottieTensor.Index.Trim.IX]) if params[LottieTensor.Index.Trim.IX] > -2000 else 1
lines.append(f'({cmd} ix={ix})')
elif cmd_idx == LottieTensor.CMD_END:
if params[LottieTensor.Index.SingleValue.ANIMATED] > 0.5:
lines.append(f'({cmd} animated=true)')
current_context = "trim_end" # Set context for keyframes
else:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_START:
if params[LottieTensor.Index.SingleValue.ANIMATED] > 0.5:
lines.append(f'({cmd} animated=true)')
current_context = "trim_start" # Set context for keyframes
else:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_OFFSET:
if params[LottieTensor.Index.SingleValue.ANIMATED] > 0.5:
lines.append(f'({cmd} animated=true)')
current_context = "trim_offset" # Set context for keyframes
else:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_MULTIPLE:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 1
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_REPEATER:
#name = string_params.get(f"{cmd_key}_name", "Repeater 1")
ix = int(params[LottieTensor.Index.Repeater.IX]) if params[LottieTensor.Index.Repeater.IX] > -2000 else 1
lines.append(f'({cmd} ix={ix})')
elif cmd_idx == LottieTensor.CMD_COPIES:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
ix = int(params[LottieTensor.Index.SingleValue.IX]) if params[LottieTensor.Index.SingleValue.IX] > -2000 else 1
lines.append(f'({cmd} {val} ix={ix})')
elif cmd_idx == LottieTensor.CMD_REPEATER_OFFSET:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
ix = int(params[LottieTensor.Index.SingleValue.IX]) if params[LottieTensor.Index.SingleValue.IX] > -2000 else 2
lines.append(f'({cmd} {val} ix={ix})')
elif cmd_idx == LottieTensor.CMD_COMPOSITE:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 1
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_REPEATER_TRANSFORM:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_TR_P_IX:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 2
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_TR_A_IX:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 1
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_TR_SCALE:
val1 = LottieTensor._format_value(params[LottieTensor.Index.TwoValues.VALUE1])
val2 = LottieTensor._format_value(params[LottieTensor.Index.TwoValues.VALUE2])
lines.append(f'({cmd} {val1} {val2})')
elif cmd_idx == LottieTensor.CMD_TR_S_IX:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 3
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_TR_R_IX:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 4
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_TR_SO_IX:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 5
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_TR_EO_IX:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 6
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_TRANSFORM_SHAPE:
#name = string_params.get(f"{cmd_key}_name", "Transform")
hd = "true" if params[LottieTensor.Index.TransformShape.HD] > 0.5 else "false"
position_x = LottieTensor._format_value(params[LottieTensor.Index.TransformShape.POSITION_X])
position_y = LottieTensor._format_value(params[LottieTensor.Index.TransformShape.POSITION_Y])
scale_x = LottieTensor._format_value(params[LottieTensor.Index.TransformShape.SCALE_X])
scale_y = LottieTensor._format_value(params[LottieTensor.Index.TransformShape.SCALE_Y])
rotation = LottieTensor._format_value(params[LottieTensor.Index.TransformShape.ROTATION])
opacity = LottieTensor._format_value(params[LottieTensor.Index.TransformShape.OPACITY])
anchor_x = LottieTensor._format_value(params[LottieTensor.Index.TransformShape.ANCHOR_X])
anchor_y = LottieTensor._format_value(params[LottieTensor.Index.TransformShape.ANCHOR_Y])
# Build the output line
line = f'({cmd} hd={hd} position="{position_x} {position_y}" scale="{scale_x} {scale_y}" rotation="{rotation}" opacity="{opacity}" anchor="{anchor_x} {anchor_y}"'
# Only add skew if it's not 0 or PAD_VAL
if params[LottieTensor.Index.TransformShape.SKEW] > -2000 and abs(params[LottieTensor.Index.TransformShape.SKEW]) > 1e-6:
skew = LottieTensor._format_value(params[LottieTensor.Index.TransformShape.SKEW])
line += f' skew="{skew}"'
# Only add skew_axis if it's not 0 or PAD_VAL
if params[LottieTensor.Index.TransformShape.SKEW_AXIS] > -2000 and abs(params[LottieTensor.Index.TransformShape.SKEW_AXIS]) > 1e-6:
skew_axis = LottieTensor._format_value(params[LottieTensor.Index.TransformShape.SKEW_AXIS])
line += f' skew_axis="{skew_axis}"'
line += ')'
lines.append(line)
elif cmd_idx == LottieTensor.CMD_PARENT:
parent_index = int(params[LottieTensor.Index.Parent.PARENT_INDEX]) if params[LottieTensor.Index.Parent.PARENT_INDEX] > -2000 else 0
lines.append(f'({cmd} {parent_index})')
elif cmd_idx == LottieTensor.CMD_ASSET:
# Get tokenizer
tokenizer = LottieTensor.get_tokenizer()
# Decode ID from tokens
id_count = int(params[LottieTensor.Index.Asset.ID_TOKEN_COUNT]) if params[LottieTensor.Index.Asset.ID_TOKEN_COUNT] > -2000 else 0
id_tokens = []
for i in range(id_count):
if params[LottieTensor.Index.Asset.ID_TOKEN_0 + i] > -2000:
id_tokens.append(int(params[LottieTensor.Index.Asset.ID_TOKEN_0 + i]))
asset_id = tokenizer.decode(id_tokens, skip_special_tokens=True) if id_tokens else "comp_0"
fr = LottieTensor._format_value(params[LottieTensor.Index.Asset.FR])
lines.append(f'({cmd} id="{asset_id}" fr={fr})')
elif cmd_idx == LottieTensor.CMD_FONT:
# Get tokenizer
tokenizer = LottieTensor.get_tokenizer()
# Decode family from tokens
family_count = int(params[LottieTensor.Index.Font.FAMILY_TOKEN_COUNT]) if params[LottieTensor.Index.Font.FAMILY_TOKEN_COUNT] > -2000 else 0
family_tokens = []
for i in range(family_count):
if params[LottieTensor.Index.Font.FAMILY_TOKEN_0 + i] > -2000:
family_tokens.append(int(params[LottieTensor.Index.Font.FAMILY_TOKEN_0 + i]))
# Decode style from tokens
style_count = int(params[LottieTensor.Index.Font.STYLE_TOKEN_COUNT]) if params[LottieTensor.Index.Font.STYLE_TOKEN_COUNT] > -2000 else 0
style_tokens = []
for i in range(style_count):
if params[LottieTensor.Index.Font.STYLE_TOKEN_0 + i] > -2000:
style_tokens.append(int(params[LottieTensor.Index.Font.STYLE_TOKEN_0 + i]))
family = tokenizer.decode(family_tokens, skip_special_tokens=True) if family_tokens else ""
style = tokenizer.decode(style_tokens, skip_special_tokens=True) if style_tokens else ""
ascent = LottieTensor._format_value(params[LottieTensor.Index.Font.ASCENT])
lines.append(f'({cmd} family="{family}" style="{style}" ascent={ascent})')
elif cmd_idx == LottieTensor.CMD_CHAR:
# Get tokenizer
tokenizer = LottieTensor.get_tokenizer()
# Decode ch from tokens
ch_count = int(params[LottieTensor.Index.Char.CH_TOKEN_COUNT]) if params[LottieTensor.Index.Char.CH_TOKEN_COUNT] > -2000 else 0
ch_tokens = []
for i in range(ch_count):
if params[LottieTensor.Index.Char.CH_TOKEN_0 + i] > -2000:
ch_tokens.append(int(params[LottieTensor.Index.Char.CH_TOKEN_0 + i]))
# Decode style from tokens
style_count = int(params[LottieTensor.Index.Char.STYLE_TOKEN_COUNT]) if params[LottieTensor.Index.Char.STYLE_TOKEN_COUNT] > -2000 else 0
style_tokens = []
for i in range(style_count):
if params[LottieTensor.Index.Char.STYLE_TOKEN_0 + i] > -2000:
style_tokens.append(int(params[LottieTensor.Index.Char.STYLE_TOKEN_0 + i]))
# Decode family from tokens
family_count = int(params[LottieTensor.Index.Char.FAMILY_TOKEN_COUNT]) if params[LottieTensor.Index.Char.FAMILY_TOKEN_COUNT] > -2000 else 0
family_tokens = []
for i in range(family_count):
if params[LottieTensor.Index.Char.FAMILY_TOKEN_0 + i] > -2000:
family_tokens.append(int(params[LottieTensor.Index.Char.FAMILY_TOKEN_0 + i]))
ch = tokenizer.decode(ch_tokens, skip_special_tokens=True) if ch_tokens else ""
style = tokenizer.decode(style_tokens, skip_special_tokens=True) if style_tokens else ""
family = tokenizer.decode(family_tokens, skip_special_tokens=True) if family_tokens else ""
size = LottieTensor._format_value(params[LottieTensor.Index.Char.SIZE])
w = LottieTensor._format_value(params[LottieTensor.Index.Char.W])
lines.append(f'({cmd} ch="{ch}" size={size} style="{style}" w={w} family="{family}")')
elif cmd_idx == LottieTensor.CMD_TEXT_LAYER:
#name = string_params.get(f"{cmd_key}_name", "Text Layer")
index = LottieTensor._format_value(params[LottieTensor.Index.TextLayer.INDEX])
in_point = LottieTensor._format_value(params[LottieTensor.Index.TextLayer.IN_POINT])
out_point = LottieTensor._format_value(params[LottieTensor.Index.TextLayer.OUT_POINT])
start_time = LottieTensor._format_value(params[LottieTensor.Index.TextLayer.START_TIME])
hasMask = "True" if params[LottieTensor.Index.TextLayer.HAS_MASK] > 0.5 else "False" # 新增
lines.append(f'({cmd} index={index} in_point={in_point} out_point={out_point} start_time={start_time} hasMask={hasMask})') # 修改
elif cmd_idx == LottieTensor.CMD_FONT_SIZE:
size = LottieTensor._format_value(params[LottieTensor.Index.FontSize.SIZE])
lines.append(f'({cmd} {size})')
elif cmd_idx == LottieTensor.CMD_FONT_FAMILY:
family = string_params.get(f"{cmd_key}_family", "")
lines.append(f'({cmd} "{family}")')
elif cmd_idx == LottieTensor.CMD_TEXT:
text = string_params.get(f"{cmd_key}_text", "")
lines.append(f'({cmd} "{text}")')
elif cmd_idx == LottieTensor.CMD_CA:
value = LottieTensor._format_value(params[LottieTensor.Index.Ca.VALUE])
lines.append(f'({cmd} {value})')
elif cmd_idx == LottieTensor.CMD_JUSTIFY:
value = LottieTensor._format_value(params[LottieTensor.Index.Justify.VALUE])
lines.append(f'({cmd} {value})')
elif cmd_idx == LottieTensor.CMD_TRACKING:
value = LottieTensor._format_value(params[LottieTensor.Index.Tracking.VALUE])
lines.append(f'({cmd} {value})')
elif cmd_idx == LottieTensor.CMD_LINE_HEIGHT:
value = LottieTensor._format_value(params[LottieTensor.Index.LineHeight.VALUE])
lines.append(f'({cmd} {value})')
elif cmd_idx == LottieTensor.CMD_LETTER_SPACING:
value = LottieTensor._format_value(params[LottieTensor.Index.LetterSpacing.VALUE])
lines.append(f'({cmd} {value})')
elif cmd_idx == LottieTensor.CMD_FILL_COLOR:
r = LottieTensor._format_value(params[LottieTensor.Index.FillColor.R]/255)
g = LottieTensor._format_value(params[LottieTensor.Index.FillColor.G]/255)
b = LottieTensor._format_value(params[LottieTensor.Index.FillColor.B]/255)
lines.append(f'({cmd} {r} {g} {b})')
elif cmd_idx == LottieTensor.CMD_G:
value = LottieTensor._format_value(params[LottieTensor.Index.G.VALUE])
lines.append(f'({cmd} {value})')
elif cmd_idx == LottieTensor.CMD_ALIGNMENT:
a = LottieTensor._format_value(params[LottieTensor.Index.Alignment.A])
lines.append(f'({cmd} a={a})')
elif cmd_idx == LottieTensor.CMD_ALIGNMENT_K:
val1 = LottieTensor._format_value(params[LottieTensor.Index.AlignmentK.VALUE1])
val2 = LottieTensor._format_value(params[LottieTensor.Index.AlignmentK.VALUE2])
lines.append(f'({cmd} {val1} {val2})')
elif cmd_idx == LottieTensor.CMD_ALIGNMENT_IX:
value = LottieTensor._format_value(params[LottieTensor.Index.AlignmentIx.VALUE])
lines.append(f'({cmd} {value})')
elif cmd_idx == LottieTensor.CMD_EFFECTS:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_EFFECT:
#name = string_params.get(f"{cmd_key}_name", "")
match_name = string_params.get(f"{cmd_key}_match_name", "")
type_val = int(params[LottieTensor.Index.Effect.TYPE]) if params[LottieTensor.Index.Effect.TYPE] > -2000 else 0
index = int(params[LottieTensor.Index.Effect.INDEX]) if params[LottieTensor.Index.Effect.INDEX] > -2000 else 1
np = int(params[LottieTensor.Index.Effect.NP]) if params[LottieTensor.Index.Effect.NP] > -2000 else 0
enabled = int(params[LottieTensor.Index.Effect.ENABLED]) if params[LottieTensor.Index.Effect.ENABLED] > -2000 else 1
line = f'({cmd} type={type_val} index={index}'
if np > 0: # Only output np if it's non-zero
line += f' np={np}'
line += f' match_name="{match_name}"'
if enabled != 1: # Only output enabled if it's not the default value
line += f' enabled={enabled}'
line += ')'
lines.append(line)
# Add CMD_LAYER_EFFECT output:
elif cmd_idx == LottieTensor.CMD_LAYER_EFFECT:
#name = string_params.get(f"{cmd_key}_name", "")
match_name = string_params.get(f"{cmd_key}_match_name", "")
index = int(params[LottieTensor.Index.LayerEffect.INDEX]) if params[LottieTensor.Index.LayerEffect.INDEX] > -2000 else 1
value = LottieTensor._format_value(params[LottieTensor.Index.LayerEffect.VALUE])
lines.append(f'({cmd} index={index} value={value} match_name="{match_name}")')
elif cmd_idx == LottieTensor.CMD_DROPDOWN:
#name = string_params.get(f"{cmd_key}_name", "")
index = int(params[LottieTensor.Index.Dropdown.INDEX]) if params[LottieTensor.Index.Dropdown.INDEX] > -2000 else 1
value = int(params[LottieTensor.Index.Dropdown.VALUE]) if params[LottieTensor.Index.Dropdown.VALUE] > -2000 else 0
lines.append(f'({cmd} index={index} value={value})')
elif cmd_idx == LottieTensor.CMD_NO_VALUE:
#@name = string_params.get(f"{cmd_key}_name", "")
index = int(params[LottieTensor.Index.NO_VALUE.INDEX]) if params[LottieTensor.Index.NO_VALUE.INDEX] > -2000 else 1
value = int(params[LottieTensor.Index.NO_VALUE.VALUE]) if params[LottieTensor.Index.NO_VALUE.VALUE] > -2000 else 0
lines.append(f'({cmd} index={index} value={value})')
elif cmd_idx == LottieTensor.CMD_IGNORED:
#name = string_params.get(f"{cmd_key}_name", "")
index = int(params[LottieTensor.Index.Ignored.INDEX]) if params[LottieTensor.Index.Ignored.INDEX] > -2000 else 1
value = LottieTensor._format_value(params[LottieTensor.Index.Ignored.VALUE])
lines.append(f'({cmd} index={index} value={value})')
elif cmd_idx == LottieTensor.CMD_SLIDER:
#name = string_params.get(f"{cmd_key}_name", "")
index = int(params[LottieTensor.Index.Slider.INDEX]) if params[LottieTensor.Index.Slider.INDEX] > -2000 else 1
value = LottieTensor._format_value(params[LottieTensor.Index.Slider.VALUE])
lines.append(f'({cmd} index={index} value={value})')
elif cmd_idx == LottieTensor.CMD_GRADIENT_FILL:
#name = string_params.get(f"{cmd_key}_name", "Gradient Fill 1")
lines.append(f'({cmd})')
current_context = "gradient_fill" # Set context for subsequent commands
elif cmd_idx == LottieTensor.CMD_OPACITY and current_context == "gradient_fill":
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_FILL_RULE:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 1
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_START_POINT:
x = LottieTensor._format_value(params[LottieTensor.Index.TwoValues.VALUE1])
y = LottieTensor._format_value(params[LottieTensor.Index.TwoValues.VALUE2])
lines.append(f'({cmd} {x} {y})')
elif cmd_idx == LottieTensor.CMD_END_POINT:
x = LottieTensor._format_value(params[LottieTensor.Index.TwoValues.VALUE1])
y = LottieTensor._format_value(params[LottieTensor.Index.TwoValues.VALUE2])
lines.append(f'({cmd} {x} {y})')
elif cmd_idx == LottieTensor.CMD_GRADIENT_TYPE:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 1
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_HIGHLIGHT_LENGTH:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_HIGHLIGHT_ANGLE:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_ORIGINAL_COLORS:
count = int(params[LottieTensor.Index.OriginalColors.COUNT]) if params[LottieTensor.Index.OriginalColors.COUNT] > -2000 else 0
color_values = []
for i in range(count):
if params[LottieTensor.Index.OriginalColors.COLOR_0 + i] > -2000:
color_values.append(LottieTensor._format_value(params[LottieTensor.Index.OriginalColors.COLOR_0 + i])/255)
colors_str = ", ".join(str(v) for v in color_values)
lines.append(f'({cmd} [{colors_str}])')
elif cmd_idx == LottieTensor.CMD_COLOR_POINTS:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 2
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_GRADIENT_FILL_END:
lines.append(f'({cmd})')
current_context = None # Reset context
elif cmd_idx == LottieTensor.CMD_GRADIENT_STROKE:
#name = string_params.get(f"{cmd_key}_name", "Gradient Stroke 1")
lines.append(f'({cmd})')
current_context = "gradient_stroke" # Set context for subsequent commands
elif cmd_idx == LottieTensor.CMD_OPACITY and current_context == "gradient_stroke":
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
current_context = "gradient_stroke" # Set context for subsequent commands
elif cmd_idx == LottieTensor.CMD_WIDTH:
if params[LottieTensor.Index.SingleValue.VALUE] > -2000:
# 除以100恢复原值
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE] / 10, preserve_int=False)
lines.append(f'({cmd} {val})')
#if current_context == "gradient_stroke":
# Check if value exists (not PAD_VAL)
# if params[LottieTensor.Index.SingleValue.VALUE] > -2000:
# val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
# lines.append(f'({cmd} {val})')
# else:
# No value, output just the command
# lines.append(f'({cmd})')
#else:
# Handle width in other contexts if needed
# lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_LINE_CAP:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 2
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_LINE_JOIN:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 2
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_MITER_LIMIT:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 0
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_GRADIENT_STROKE_END:
lines.append(f'({cmd})')
current_context = None # Reset context
elif cmd_idx == LottieTensor.CMD_COLOR:
#name = string_params.get(f"{cmd_key}_name", "Color")
index = int(params[LottieTensor.Index.Color.INDEX]) if params[LottieTensor.Index.Color.INDEX] > -2000 else 1
r = LottieTensor._format_value(params[LottieTensor.Index.Color.R]/255)
g = LottieTensor._format_value(params[LottieTensor.Index.Color.G]/255)
b = LottieTensor._format_value(params[LottieTensor.Index.Color.B]/255)
lines.append(f'({cmd} index={index} r={r} g={g} b={b})')
elif cmd_idx == LottieTensor.CMD_MERGE:
#name = string_params.get(f"{cmd_key}_name", "Merge Paths 1")
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_MERGE_MODE:
mode = int(params[LottieTensor.Index.MergeMode.MODE]) if params[LottieTensor.Index.MergeMode.MODE] > -2000 else 1
lines.append(f'({cmd} {mode})')
elif cmd_idx == LottieTensor.CMD_SOLID_LAYER:
#name = string_params.get(f"{cmd_key}_name", "Solid Layer")
#color = string_params.get(f"{cmd_key}_color", "#000000")
r = int(min(255, max(0, params[LottieTensor.Index.SolidLayer.COLOR_R])))
g = int(min(255, max(0, params[LottieTensor.Index.SolidLayer.COLOR_G])))
b = int(min(255, max(0, params[LottieTensor.Index.SolidLayer.COLOR_B])))
a = int(min(255, max(0, params[LottieTensor.Index.SolidLayer.COLOR_A])))
# You can use RGB values directly or convert back to hex if needed
color_rgb = (r, g, b, a)
# Or convert back to hex format if required:
color = f"#{r:02x}{g:02x}{b:02x}{a:02x}"
index = LottieTensor._format_value(params[LottieTensor.Index.SolidLayer.INDEX])
in_point = LottieTensor._format_value(params[LottieTensor.Index.SolidLayer.IN_POINT])
out_point = LottieTensor._format_value(params[LottieTensor.Index.SolidLayer.OUT_POINT])
start_time = LottieTensor._format_value(params[LottieTensor.Index.SolidLayer.START_TIME])
width = LottieTensor._format_value(params[LottieTensor.Index.SolidLayer.WIDTH])
height = LottieTensor._format_value(params[LottieTensor.Index.SolidLayer.HEIGHT])
hasMask = "True" if params[LottieTensor.Index.SolidLayer.HAS_MASK] > 0.5 else "False"
lines.append(f'({cmd} index={index} in_point={in_point} out_point={out_point} start_time={start_time} color="{color}" width={width} height={height} hasMask={hasMask})')
elif cmd_idx == LottieTensor.CMD_MASKS_PROPERTIES:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_MASK:
#nm = string_params.get(f"{cmd_key}_nm", "Mask 1")
index = int(params[LottieTensor.Index.Mask.INDEX]) if params[LottieTensor.Index.Mask.INDEX] > -2000 else 0
inv = "true" if params[LottieTensor.Index.Mask.INV] > 0.5 else "false"
# Convert mode value back to string
mode_val = int(params[LottieTensor.Index.Mask.MODE]) if params[LottieTensor.Index.Mask.MODE] > -2000 else 0
mode_map = {0: "a", 1: "s", 2: "i", 3: "n"}
mode = mode_map.get(mode_val, "a")
lines.append(f'({cmd} index={index} inv={inv} mode={mode})')
elif cmd_idx == LottieTensor.CMD_MASK_PT:
a = int(params[LottieTensor.Index.MaskPt.A]) if params[LottieTensor.Index.MaskPt.A] > -2000 else 1
ix = int(params[LottieTensor.Index.MaskPt.IX]) if params[LottieTensor.Index.MaskPt.IX] > -2000 else 1
lines.append(f'({cmd} a={a} ix={ix})')
elif cmd_idx == LottieTensor.CMD_MASK_PT_K_C:
c = "true" if params[LottieTensor.Index.MaskPtK.C] > 0.5 else "false"
lines.append(f'({cmd} {c})') # Changed from c={c} to just {c}
elif cmd_idx in [LottieTensor.CMD_MASK_PT_K_I, LottieTensor.CMD_MASK_PT_K_O, LottieTensor.CMD_MASK_PT_K_V]:
count = int(params[LottieTensor.Index.MaskPtKValues.COUNT]) if params[LottieTensor.Index.MaskPtKValues.COUNT] > -2000 else 0
if count == 0:
# Fallback: find last non-padding value
for i in range(19, -1, -1):
val = params[LottieTensor.Index.MaskPtKValues.V1 + i]
if val > -2000:
count = i + 1
break
values = []
for i in range(count):
val = params[LottieTensor.Index.MaskPtKValues.V1 + i]
if val > -2000:
values.append(LottieTensor._format_value(val))
else:
values.append(0.0)
lines.append(f'({cmd} {" ".join(str(v) for v in values)})')
elif cmd_idx == LottieTensor.CMD_MASK_O:
a = int(params[LottieTensor.Index.MaskO.A]) if params[LottieTensor.Index.MaskO.A] > -2000 else 0
k = LottieTensor._format_value(params[LottieTensor.Index.MaskO.K]) if params[LottieTensor.Index.MaskO.K] > -2000 else 100
ix = int(params[LottieTensor.Index.MaskO.IX]) if params[LottieTensor.Index.MaskO.IX] > -2000 else 3
lines.append(f'({cmd} a={a} k={k} ix={ix})')
elif cmd_idx == LottieTensor.CMD_MASK_X:
a = int(params[LottieTensor.Index.MaskX.A]) if params[LottieTensor.Index.MaskX.A] > -2000 else 0
k = LottieTensor._format_value(params[LottieTensor.Index.MaskX.K]) if params[LottieTensor.Index.MaskX.K] > -2000 else 0
ix = int(params[LottieTensor.Index.MaskX.IX]) if params[LottieTensor.Index.MaskX.IX] > -2000 else 4
lines.append(f'({cmd} a={a} k={k} ix={ix})')
elif cmd_idx == LottieTensor.CMD_MASK_END:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_MASK_PT_K:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_MASK_PT_END:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_MASK_PT_K_END:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_MASKS_PROPERTIES_END:
lines.append(f'({cmd})')
elif cmd_idx in [LottieTensor.CMD_MASK_PT_K_ARRAY, LottieTensor.CMD_MASK_PT_K_ARRAY_END,
LottieTensor.CMD_MASK_PT_KF_S, LottieTensor.CMD_MASK_PT_KF_S_END,
LottieTensor.CMD_MASK_PT_KF_SHAPE_END, LottieTensor.CMD_MASK_PT_KEYFRAME_END]:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_MASK_PT_KEYFRAME:
index = int(params[LottieTensor.Index.MaskPtKeyframe.INDEX]) if params[LottieTensor.Index.MaskPtKeyframe.INDEX] > -2000 else 0
t = LottieTensor._format_value(params[LottieTensor.Index.MaskPtKeyframe.T])
lines.append(f'({cmd} index={index} t={t})')
elif cmd_idx == LottieTensor.CMD_MASK_PT_KF_I:
x = LottieTensor._format_value(params[LottieTensor.Index.MaskPtKfI.X])
y = LottieTensor._format_value(params[LottieTensor.Index.MaskPtKfI.Y])
lines.append(f'({cmd} x={x} y={y})')
elif cmd_idx == LottieTensor.CMD_MASK_PT_KF_O:
x = LottieTensor._format_value(params[LottieTensor.Index.MaskPtKfO.X])
y = LottieTensor._format_value(params[LottieTensor.Index.MaskPtKfO.Y])
lines.append(f'({cmd} x={x} y={y})')
elif cmd_idx == LottieTensor.CMD_MASK_PT_KF_SHAPE:
index = int(params[LottieTensor.Index.MaskPtKfShape.INDEX]) if params[LottieTensor.Index.MaskPtKfShape.INDEX] > -2000 else 0
c = "true" if params[LottieTensor.Index.MaskPtKfShape.C] > 0.5 else "false"
lines.append(f'({cmd} index={index} c={c})')
elif cmd_idx in [LottieTensor.CMD_MASK_PT_KF_SHAPE_I, LottieTensor.CMD_MASK_PT_KF_SHAPE_O,
LottieTensor.CMD_MASK_PT_KF_SHAPE_V]:
# Get the count from params, not from string_params
count = int(params[LottieTensor.Index.MaskPtKfShapeValues.COUNT]) if params[LottieTensor.Index.MaskPtKfShapeValues.COUNT] > -2000 else 0
if count == 0:
# If no count stored, find the last non-padding value
for i in range(19, -1, -1): # Check V1 through V20
if i < LottieTensor.PARAM_DIM:
val = params[LottieTensor.Index.MaskPtKfShapeValues.V1 + i]
if val > -2000:
count = i + 1
break
if count == 0:
count = 8 # Default to 8 if no valid values found
# Output the exact number of values
values = []
for i in range(count):
if i < 20:
val = params[LottieTensor.Index.MaskPtKfShapeValues.V1 + i]
if val > -2000:
values.append(LottieTensor._format_value(val))
else:
values.append(0.0)
else:
values.append(0.0)
lines.append(f'({cmd} {" ".join(str(v) for v in values)})')
elif cmd_idx == LottieTensor.CMD_TR_POSITION:
x = LottieTensor._format_value(params[LottieTensor.Index.TrPosition.X])
y = LottieTensor._format_value(params[LottieTensor.Index.TrPosition.Y])
lines.append(f'({cmd} {x} {y})')
elif cmd_idx == LottieTensor.CMD_TR_ANCHOR:
x = LottieTensor._format_value(params[LottieTensor.Index.TrAnchor.X])
y = LottieTensor._format_value(params[LottieTensor.Index.TrAnchor.Y])
lines.append(f'({cmd} {x} {y})')
elif cmd_idx == LottieTensor.CMD_TR_ROTATION:
val = LottieTensor._format_value(params[LottieTensor.Index.TrRotation.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_TR_START_OPACITY:
val = LottieTensor._format_value(params[LottieTensor.Index.TrStartOpacity.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_TR_END_OPACITY:
if params[LottieTensor.Index.TrEndOpacity.VALUE] > -2000:
val = LottieTensor._format_value(params[LottieTensor.Index.TrEndOpacity.VALUE])
lines.append(f'({cmd} {val})')
else:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_ZIG_ZAG:
#name = string_params.get(f"{cmd_key}_name", "Zig Zag 1")
ix = int(params[LottieTensor.Index.ZigZag.IX]) if params[LottieTensor.Index.ZigZag.IX] > -2000 else 2
lines.append(f'({cmd} ix={ix})')
elif cmd_idx == LottieTensor.CMD_FREQUENCY:
value = LottieTensor._format_value(params[LottieTensor.Index.Frequency.VALUE])
lines.append(f'({cmd} {value})')
elif cmd_idx == LottieTensor.CMD_AMPLITUDE:
value = LottieTensor._format_value(params[LottieTensor.Index.Amplitude.VALUE])
lines.append(f'({cmd} {value})')
elif cmd_idx == LottieTensor.CMD_POINT_TYPE:
value = int(params[LottieTensor.Index.PointType.VALUE]) if params[LottieTensor.Index.PointType.VALUE] > -2000 else 2
lines.append(f'({cmd} {value})')
elif cmd_idx == LottieTensor.CMD_ZIG_ZAG_END:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_ANIMATORS:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_ANIMATOR:
#nm = string_params.get(f"{cmd_key}_nm", "Animator 1")
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_RANGE_SELECTOR:
t = int(params[LottieTensor.Index.RangeSelector.T]) if params[LottieTensor.Index.RangeSelector.T] > -2000 else 0
r = int(params[LottieTensor.Index.RangeSelector.R]) if params[LottieTensor.Index.RangeSelector.R] > -2000 else 1
b = int(params[LottieTensor.Index.RangeSelector.B]) if params[LottieTensor.Index.RangeSelector.B] > -2000 else 1
sh = int(params[LottieTensor.Index.RangeSelector.SH]) if params[LottieTensor.Index.RangeSelector.SH] > -2000 else 1
rn = int(params[LottieTensor.Index.RangeSelector.RN]) if params[LottieTensor.Index.RangeSelector.RN] > -2000 else 0
lines.append(f'({cmd} t={t} r={r} b={b} sh={sh} rn={rn})')
elif cmd_idx == LottieTensor.CMD_RANGE_START:
a = int(params[LottieTensor.Index.RangeStart.A]) if params[LottieTensor.Index.RangeStart.A] > -2000 else 0
lines.append(f'({cmd} a={a})')
if a > 0.5:
current_context = "range_start"
elif cmd_idx == LottieTensor.CMD_RANGE_START_KEYFRAME:
t = LottieTensor._format_value(params[LottieTensor.Index.RangeStartKeyframe.T])
s = LottieTensor._format_value(params[LottieTensor.Index.RangeStartKeyframe.S])
# Check if this is the last keyframe (no easing values or all zeros)
i_x = params[LottieTensor.Index.RangeStartKeyframe.I_X]/100
i_y = params[LottieTensor.Index.RangeStartKeyframe.I_Y]/100
o_x = params[LottieTensor.Index.RangeStartKeyframe.O_X]/100
o_y = params[LottieTensor.Index.RangeStartKeyframe.O_Y]/100
has_meaningful_easing = (
(i_x > -2000 and abs(i_x) > 1e-6) or
(i_y > -2000 and abs(i_y) > 1e-6) or
(o_x > -2000 and abs(o_x) > 1e-6) or
(o_y > -2000 and abs(o_y) > 1e-6)
)
if has_meaningful_easing:
i_x_val = LottieTensor._format_value(i_x, preserve_int=False) if i_x > -2000 else 0
i_y_val = LottieTensor._format_value(i_y, preserve_int=False) if i_y > -2000 else 0
o_x_val = LottieTensor._format_value(o_x, preserve_int=False) if o_x > -2000 else 0
o_y_val = LottieTensor._format_value(o_y, preserve_int=False) if o_y > -2000 else 0
lines.append(f'({cmd} t={t} s={s} i_x={i_x_val} i_y={i_y_val} o_x={o_x_val} o_y={o_y_val})')
else:
lines.append(f'({cmd} t={t} s={s})')
elif cmd_idx == LottieTensor.CMD_RANGE_START_END:
lines.append(f'({cmd})')
current_context = None
elif cmd_idx == LottieTensor.CMD_AMOUNT:
a = int(params[LottieTensor.Index.Amount.A]) if params[LottieTensor.Index.Amount.A] > -2000 else 0
k = LottieTensor._format_value(params[LottieTensor.Index.Amount.K]) if params[LottieTensor.Index.Amount.K] > -2000 else 100
ix = int(params[LottieTensor.Index.Amount.IX]) if params[LottieTensor.Index.Amount.IX] > -2000 else 4
lines.append(f'({cmd} a={a} k={k} ix={ix})')
elif cmd_idx == LottieTensor.CMD_MAX_EASE:
a = int(params[LottieTensor.Index.MaxEase.A]) if params[LottieTensor.Index.MaxEase.A] > -2000 else 0
k = LottieTensor._format_value(params[LottieTensor.Index.MaxEase.K]) if params[LottieTensor.Index.MaxEase.K] > -2000 else 0
ix = int(params[LottieTensor.Index.MaxEase.IX]) if params[LottieTensor.Index.MaxEase.IX] > -2000 else 7
lines.append(f'({cmd} a={a} k={k} ix={ix})')
elif cmd_idx == LottieTensor.CMD_MIN_EASE:
a = int(params[LottieTensor.Index.MinEase.A]) if params[LottieTensor.Index.MinEase.A] > -2000 else 0
k = LottieTensor._format_value(params[LottieTensor.Index.MinEase.K]) if params[LottieTensor.Index.MinEase.K] > -2000 else 0
ix = int(params[LottieTensor.Index.MinEase.IX]) if params[LottieTensor.Index.MinEase.IX] > -2000 else 8
lines.append(f'({cmd} a={a} k={k} ix={ix})')
elif cmd_idx == LottieTensor.CMD_ANIMATOR_PROPERTIES:
lines.append(f'({cmd})')
current_context = "animator_properties"
elif cmd_idx == LottieTensor.CMD_ANIMATOR_PROPERTIES_END:
lines.append(f'({cmd})')
current_context = None
elif cmd_idx == LottieTensor.CMD_OPACITY and current_context == "animator_properties":
# Special handling for opacity within animator_properties
a = int(params[LottieTensor.Index.Amount.A]) if params[LottieTensor.Index.Amount.A] > -2000 else 0
k = LottieTensor._format_value(params[LottieTensor.Index.Amount.K]) if params[LottieTensor.Index.Amount.K] > -2000 else 0
ix = int(params[LottieTensor.Index.Amount.IX]) if params[LottieTensor.Index.Amount.IX] > -2000 else 9
lines.append(f'({cmd} a={a} k={k} ix={ix})')
elif cmd_idx == LottieTensor.CMD_RANGE_SELECTOR_END:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_ANIMATOR_END:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_ANIMATORS_END:
lines.append(f'({cmd})')
elif cmd_idx == LottieTensor.CMD_RADIUS:
val = LottieTensor._format_value(params[LottieTensor.Index.Radius.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_RANGE_END:
a = int(params[LottieTensor.Index.RangeEnd.A]) if params[LottieTensor.Index.RangeEnd.A] > -2000 else 0
lines.append(f'({cmd} a={a})')
if a > 0.5:
current_context = "range_end"
elif cmd_idx == LottieTensor.CMD_RANGE_END_KEYFRAME:
t = LottieTensor._format_value(params[LottieTensor.Index.RangeEndKeyframe.T])
s = LottieTensor._format_value(params[LottieTensor.Index.RangeEndKeyframe.S])
# Check if this is the last keyframe (no easing values or all zeros)
i_x = params[LottieTensor.Index.RangeEndKeyframe.I_X]/100
i_y = params[LottieTensor.Index.RangeEndKeyframe.I_Y]/100
o_x = params[LottieTensor.Index.RangeEndKeyframe.O_X]/100
o_y = params[LottieTensor.Index.RangeEndKeyframe.O_Y]/100
has_meaningful_easing = (
(i_x > -2000 and abs(i_x) > 1e-6) or
(i_y > -2000 and abs(i_y) > 1e-6) or
(o_x > -2000 and abs(o_x) > 1e-6) or
(o_y > -2000 and abs(o_y) > 1e-6)
)
if has_meaningful_easing:
i_x_val = LottieTensor._format_value(i_x, preserve_int=False) if i_x > -2000 else 0
i_y_val = LottieTensor._format_value(i_y, preserve_int=False) if i_y > -2000 else 0
o_x_val = LottieTensor._format_value(o_x, preserve_int=False) if o_x > -2000 else 0
o_y_val = LottieTensor._format_value(o_y, preserve_int=False) if o_y > -2000 else 0
lines.append(f'({cmd} t={t} s={s} i_x={i_x_val} i_y={i_y_val} o_x={o_x_val} o_y={o_y_val})')
else:
lines.append(f'({cmd} t={t} s={s})')
elif cmd_idx == LottieTensor.CMD_RANGE_END_END:
lines.append(f'({cmd})')
current_context = None
# Fix position output in animator_properties context
elif cmd_idx == LottieTensor.CMD_POSITION and current_context == "animator_properties":
# Special handling for position within animator_properties
a = int(params[LottieTensor.Index.Amount.A]) if params[LottieTensor.Index.Amount.A] > -2000 else 0
ix = int(params[LottieTensor.Index.Amount.IX]) if params[LottieTensor.Index.Amount.IX] > -2000 else 2
# Check if we have array values stored
x = params[LottieTensor.Index.Transform.X]
y = params[LottieTensor.Index.Transform.Y]
z = params[LottieTensor.Index.Transform.Z]
if x > -2000 or y > -2000: # Changed condition - check x or y
# Format as array
x_val = LottieTensor._format_value(x) if x > -2000 else 0
y_val = LottieTensor._format_value(y) if y > -2000 else 0
# Only include z if it's meaningful
if z > -2000 and abs(z) > 1e-6:
lines.append(f'({cmd} a={a} k=[{x_val}, {y_val}, {LottieTensor._format_value(z)}] ix={ix})')
else:
lines.append(f'({cmd} a={a} k=[{x_val}, {y_val}, 0] ix={ix})')
else:
# Format as single value
k = LottieTensor._format_value(params[LottieTensor.Index.Amount.K]) if params[LottieTensor.Index.Amount.K] > -2000 else 0
lines.append(f'({cmd} a={a} k={k} ix={ix})')
elif cmd_idx == LottieTensor.CMD_ML2:
val = int(params[LottieTensor.Index.SingleValue.VALUE]) if params[LottieTensor.Index.SingleValue.VALUE] > -2000 else 4
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_RANGE_OFFSET_KEYFRAME:
t = LottieTensor._format_value(params[LottieTensor.Index.RangeOffsetKeyframe.T])
s = LottieTensor._format_value(params[LottieTensor.Index.RangeOffsetKeyframe.S])
# Check if this is the last keyframe (no easing values or all zeros)
i_x = params[LottieTensor.Index.RangeOffsetKeyframe.I_X]/100
i_y = params[LottieTensor.Index.RangeOffsetKeyframe.I_Y]/100
o_x = params[LottieTensor.Index.RangeOffsetKeyframe.O_X]/100
o_y = params[LottieTensor.Index.RangeOffsetKeyframe.O_Y]/100
has_meaningful_easing = (
(i_x > -2000 and abs(i_x) > 1e-6) or
(i_y > -2000 and abs(i_y) > 1e-6) or
(o_x > -2000 and abs(o_x) > 1e-6) or
(o_y > -2000 and abs(o_y) > 1e-6)
)
if has_meaningful_easing:
i_x_val = LottieTensor._format_value(i_x, preserve_int=False) if i_x > -2000 else 0
i_y_val = LottieTensor._format_value(i_y, preserve_int=False) if i_y > -2000 else 0
o_x_val = LottieTensor._format_value(o_x, preserve_int=False) if o_x > -2000 else 0
o_y_val = LottieTensor._format_value(o_y, preserve_int=False) if o_y > -2000 else 0
lines.append(f'({cmd} t={t} s={s} i_x={i_x_val} i_y={i_y_val} o_x={o_x_val} o_y={o_y_val})')
else:
lines.append(f'({cmd} t={t} s={s})')
elif cmd_idx == LottieTensor.CMD_RANGE_OFFSET_END:
lines.append(f'({cmd})')
current_context = None
elif cmd_idx == LottieTensor.CMD_S_M:
a = int(params[LottieTensor.Index.SM.A]) if params[LottieTensor.Index.SM.A] > -2000 else 0
k = LottieTensor._format_value(params[LottieTensor.Index.SM.K]) if params[LottieTensor.Index.SM.K] > -2000 else 100
ix = int(params[LottieTensor.Index.SM.IX]) if params[LottieTensor.Index.SM.IX] > -2000 else 6
lines.append(f'({cmd} a={a} k={k} ix={ix})')
# 修改 CMD_OPACITY_ANIMATORS 的输出:
elif cmd_idx == LottieTensor.CMD_OPACITY_ANIMATORS:
a = int(params[LottieTensor.Index.OpacityAnimators.A]) if params[LottieTensor.Index.OpacityAnimators.A] > -2000 else 0
if a > 0.5:
# Animated case - only output a
lines.append(f'({cmd} a={a})')
current_context = "opacity_animators"
else:
# Static case with k value
k = LottieTensor._format_value(params[LottieTensor.Index.OpacityAnimators.K]) if params[LottieTensor.Index.OpacityAnimators.K] > -2000 else 0
ix = int(params[LottieTensor.Index.OpacityAnimators.IX]) if params[LottieTensor.Index.OpacityAnimators.IX] > -2000 else 9
lines.append(f'({cmd} a={a} k={k} ix={ix})')
# 添加 CMD_POSITION_ANIMATORS 的输出:
elif cmd_idx == LottieTensor.CMD_POSITION_ANIMATORS:
a = int(params[LottieTensor.Index.PositionAnimators.A]) if params[LottieTensor.Index.PositionAnimators.A] > -2000 else 0
ix = int(params[LottieTensor.Index.PositionAnimators.IX]) if params[LottieTensor.Index.PositionAnimators.IX] > -2000 else 2
if a > 0.5:
# Animated case
lines.append(f'({cmd} a={a})')
current_context = "position_animators"
else:
# Static case with k value
k_x = params[LottieTensor.Index.PositionAnimators.K_X]
k_y = params[LottieTensor.Index.PositionAnimators.K_Y]
k_z = params[LottieTensor.Index.PositionAnimators.K_Z]
if k_x > -2000 and k_y > -2000:
# Format as array
x_val = LottieTensor._format_value(k_x) if k_x > -2000 else 0
y_val = LottieTensor._format_value(k_y) if k_y > -2000 else 0
z_val = LottieTensor._format_value(k_z) if k_z > -2000 else 0
lines.append(f'({cmd} a={a} k=[{x_val}, {y_val}, {z_val}] ix={ix})')
else:
lines.append(f'({cmd} a={a} k=[0.0, 0.0, 0.0] ix={ix})')
# 添加 CMD_TRACKING_ANIMATORS 的输出:
elif cmd_idx == LottieTensor.CMD_TRACKING_ANIMATORS:
a = int(params[LottieTensor.Index.TrackingAnimators.A]) if params[LottieTensor.Index.TrackingAnimators.A] > -2000 else 0
k = LottieTensor._format_value(params[LottieTensor.Index.TrackingAnimators.K]) if params[LottieTensor.Index.TrackingAnimators.K] > -2000 else 0
ix = int(params[LottieTensor.Index.TrackingAnimators.IX]) if params[LottieTensor.Index.TrackingAnimators.IX] > -2000 else 89
if a > 0.5:
# Animated case
lines.append(f'({cmd} a={a})')
current_context = "tracking_animators"
else:
# Static case
lines.append(f'({cmd} a={a} k={k} ix={ix})')
# 添加结束命令的输出:
elif cmd_idx == LottieTensor.CMD_POSITION_ANIMATORS_END:
lines.append(f'({cmd})')
current_context = None
# Add output formatting (after CMD_OPACITY_ANIMATORS, around line 5590)
elif cmd_idx == LottieTensor.CMD_SCALE_ANIMATORS:
a = int(params[LottieTensor.Index.ScaleAnimators.A]) if params[LottieTensor.Index.ScaleAnimators.A] > -2000 else 0
if a > 0.5:
# Animated case
lines.append(f'({cmd} a={a})')
current_context = "scale_animators"
else:
# Static case with k value
k_x = params[LottieTensor.Index.ScaleAnimators.K_X]
k_y = params[LottieTensor.Index.ScaleAnimators.K_Y]
k_z = params[LottieTensor.Index.ScaleAnimators.K_Z]
if k_x > -2000 and k_y > -2000 and k_z > -2000:
# Check if all values are the same
if abs(k_x - k_y) < 1e-6 and abs(k_y - k_z) < 1e-6:
# Output single value
lines.append(f'({cmd} a={a} k={LottieTensor._format_value(k_x)})')
else:
# Output array
lines.append(f'({cmd} a={a} k=[{LottieTensor._format_value(k_x)}, {LottieTensor._format_value(k_y)}, {LottieTensor._format_value(k_z)}])')
else:
lines.append(f'({cmd} a={a} k=100)')
elif cmd_idx == LottieTensor.CMD_SCALE_ANIMATORS_END:
lines.append(f'({cmd})')
current_context = None
elif cmd_idx == LottieTensor.CMD_ROTATION_ANIMATORS:
a = int(params[LottieTensor.Index.RotationAnimators.A]) if params[LottieTensor.Index.RotationAnimators.A] > -2000 else 0
if a > 0.5:
# Animated case
lines.append(f'({cmd} a={a})')
current_context = "rotation_animators"
else:
# Static case with k value
k = LottieTensor._format_value(params[LottieTensor.Index.RotationAnimators.K]) if params[LottieTensor.Index.RotationAnimators.K] > -2000 else 0
lines.append(f'({cmd} a={a} k={k})')
elif cmd_idx == LottieTensor.CMD_WIDTH_ANIMATED:
# This is a standalone width_animated command
# The context should already be set from the stroke command
# No parameters needed for this command
pass
elif cmd_idx == LottieTensor.CMD_RANGE_OFFSET:
# Use Amount indices for range_offset
a = int(params[LottieTensor.Index.Amount.A]) if params[LottieTensor.Index.Amount.A] > -2000 else 0
if a > 0.5: # This should be checking a, not params[LottieTensor.Index.Amount.A] again
# Animated case - only output a
lines.append(f'({cmd} a={a})')
current_context = "range_offset"
else:
# Static case - output a, k, and ix
k = LottieTensor._format_value(params[LottieTensor.Index.Amount.K]) if params[LottieTensor.Index.Amount.K] > -2000 else 0
ix = int(params[LottieTensor.Index.Amount.IX]) if params[LottieTensor.Index.Amount.IX] > -2000 else 3
lines.append(f'({cmd} a={a} k={k} ix={ix})')
elif cmd_idx == LottieTensor.CMD_ROTATION_ANIMATORS_END:
lines.append(f'({cmd})')
current_context = None
elif cmd_idx == LottieTensor.CMD_RECT_SIZE:
# Output rect_size with two values
if params[LottieTensor.Index.Transform.ANIMATED] > -2000 and params[LottieTensor.Index.Transform.ANIMATED] > 0.5:
lines.append(f'({cmd} animated=true)')
current_context = "size"
else:
# 修改:使用 Transform.X 和 Transform.Y
val1 = LottieTensor._format_value(params[LottieTensor.Index.Transform.X])
val2 = LottieTensor._format_value(params[LottieTensor.Index.Transform.Y])
lines.append(f'({cmd} {val1} {val2})')
elif cmd_idx == LottieTensor.CMD_ELLIPSE_SIZE:
# Output rect_size with two values
# 检查是否是动画
animated_val = params[LottieTensor.Index.Transform.ANIMATED]
if animated_val > -2000 and animated_val > 0.5:
lines.append(f'({cmd} animated=true)')
current_context = "size" # 确保设置上下文
else:
# 静态值 - 检查 X 和 Y 是否为有效值
x_val = params[LottieTensor.Index.Transform.X]
y_val = params[LottieTensor.Index.Transform.Y]
# 如果 X 和 Y 都是默认值 0 且 ANIMATED 未设置,可能是数据丢失
val1 = LottieTensor._format_value(x_val if x_val > -2000 else 0)
val2 = LottieTensor._format_value(y_val if y_val > -2000 else 0)
lines.append(f'({cmd} {val1} {val2})')
elif cmd_idx == LottieTensor.CMD_RECT_ROUNDED:
if params[LottieTensor.Index.SingleValue.ANIMATED] > 0.5:
lines.append(f'({cmd} animated=true)')
current_context = "rect_rounded" # Set context for keyframes
else:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_RECT_ROUNDED_END:
lines.append(f'({cmd})')
current_context = None
elif cmd_idx == LottieTensor.CMD_SKEW:
if params[LottieTensor.Index.SingleValue.VALUE] > -2000:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
elif cmd_idx == LottieTensor.CMD_SKEW_AXIS:
if params[LottieTensor.Index.SingleValue.VALUE] > -2000:
val = LottieTensor._format_value(params[LottieTensor.Index.SingleValue.VALUE])
lines.append(f'({cmd} {val})')
else:
# Default case for any unhandled commands
lines.append(f"({cmd})")
return '\n'.join(lines)
# Keep other methods unchanged
def to_tensor(self) -> torch.Tensor:
"""Convert LottieTensor to a single tensor"""
return torch.cat([self.commands.float(), self.params], dim=1)
@staticmethod
def from_tensor(tensor: torch.Tensor) -> 'LottieTensor':
"""Create LottieTensor from tensor"""
commands = tensor[:, 0:1].long()
params = tensor[:, 1:1+LottieTensor.PARAM_DIM].float()
return LottieTensor(commands, params, PAD_VAL=-2001)
def pad(self, seq_len: int):
"""Pad sequence to specified length"""
pad_len = max(seq_len - len(self.commands), 0)
if pad_len > 0:
pad_commands = torch.ones((pad_len, 1)) * LottieTensor.CMD_PAD
pad_params = torch.ones((pad_len, self.PARAM_DIM)) * self.PAD_VAL
self.commands = torch.cat([self.commands, pad_commands.long()])
self.params = torch.cat([self.params, pad_params])
return self
@staticmethod
def _clamp_value(value: float, min_val: float = -2000, max_val: float = 2000) -> float:
"""Clamp a value between min and max bounds"""
return max(min_val, min(max_val, value))
@staticmethod
def _index_clamp_value(value: float, min_val: float = -100, max_val: float = 100) -> float:
"""Clamp a value between min and max bounds"""
return max(min_val, min(max_val, value))
@classmethod
def init_tokenizer(cls, model_path=None):
"""Initialize tokenizer once for the class - 支持多路径fallback"""
if cls.tokenizer is None:
from transformers import AutoTokenizer
if model_path is None:
# 尝试多个可能的路径
possible_paths = [
'/mnt/jfs-test/Qwen2.5-VL-3B-Instruct',
'/data/models/Qwen2.5-VL-3B-Instruct',
'Qwen/Qwen2.5-VL-3B-Instruct', # HuggingFace Hub
]
for path in possible_paths:
try:
cls.tokenizer = AutoTokenizer.from_pretrained(path)
# 只在主进程打印一次
import os
if os.environ.get('RANK', '0') == '0':
print(f"Tokenizer loaded successfully from: {path}")
return
except Exception as e:
continue
raise ValueError(f"Failed to load tokenizer from any known path: {possible_paths}")
else:
cls.tokenizer = AutoTokenizer.from_pretrained(model_path)
@classmethod
def get_tokenizer(cls):
if cls.tokenizer is None:
from transformers import AutoTokenizer
cls.tokenizer = AutoTokenizer.from_pretrained('/mnt/jfs-test/Qwen2.5-VL-3B-Instruct')
return cls.tokenizer
@staticmethod
def get_param_offset(cmd_idx: int, param_idx: int) -> int:
"""
Get the offset for a parameter based on its command and parameter index.
Returns the offset to add to the parameter value.
"""
cache_key = (cmd_idx, param_idx)
if cache_key in LottieTensor._OFFSET_CACHE:
return LottieTensor._OFFSET_CACHE[cache_key]
TIME_OFFSET = 155000
SPACE_OFFSET = 159100
AMPLITUDE_OFFSET = 161200
ANCHOR_OFFSET = 161300
ANIMATED_OFFSET = 165400
H_FLAG_OFFSET = 165402
OFFSET_VAL_OFFSET = 165404
CA_OFFSET = 165406
JUSTIFY_OFFSET = 165409
TEXT_TRACKING_OFFSET = 165416
HAS_STROKE_COLOR_OFFSET = 166017
IX_OFFSET = 166019
BM_OFFSET = 167020
CLOSED_OFFSET = 167041
DIRECTION_OFFSET = 167043
STAR_TYPE_OFFSET = 167049
MULTIPLE_OFFSET = 167055
COMPOSITE_OFFSET = 167061
SKEW_OFFSET = 167067
SKEW_AXIS_OFFSET = 167118
SCALE_OFFSET = 167169
ROTATION_OFFSET = 170170
EASE_OFFSET = 171611
SMOOTH_OFFSET = 171812
TRACKING_OFFSET = 171913
INDEX_OFFSET = 172014
DDD_OFFSET = 173015
HD_OFFSET = 173017
CP_OFFSET = 173019
HAS_MASK_OFFSET = 173070
AO_OFFSET = 173072
TT_OFFSET = 173074
TP_OFFSET = 173080
TD_OFFSET = 173181
CT_OFFSET = 173184
NUMBER_OFFSET = 173186
DIM_OFFSET = 173687
HAS_C_A_OFFSET = 173698
HAS_C_IX_OFFSET = 173700
HAS_O_A_OFFSET = 173702
HAS_O_IX_OFFSET = 173704
FILL_RULE_OFFSET = 173706
TYPE_OFFSET = 173711
TEXT_RANGE_UNITS_OFFSET = 173752
INV_OFFSET = 173763
MODE_OFFSET = 173765
TEXT_SHAPE_TYPE_OFFSET = 173776
TEXT_RANDOM_OFFSET = 173787
COLOR_POINTS_OFFSET = 173789
ROUND_OFFSET = 173840
RADIUS_OFFSET = 174941
FREQUENCY_OFFSET = 175242
SPEED_OFFSET = 175393
FONT_OFFSET = 177394
COLOR_OFFSET = 179495
LINE_CAP_OFFSET = 179752
LINE_JOIN_OFFSET = 179757
MITER_LIMIT_OFFSET = 179760
EFFECT_OFFSET = 179861
OPACITY_OFFSET = 181112
WIDTH_VALUE_OFFSET = 181300
NO_OFFSET = 0
# Time dictionary parameters - all time-related values
time_params = {
LottieTensor.CMD_ANIMATION: [
LottieTensor.Index.Animation.IP,
LottieTensor.Index.Animation.OP
],
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.IN_POINT,
LottieTensor.Index.Layer.OUT_POINT,
LottieTensor.Index.Layer.START_TIME
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.IN_POINT,
LottieTensor.Index.NullLayer.OUT_POINT,
LottieTensor.Index.NullLayer.START_TIME
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.IN_POINT,
LottieTensor.Index.PrecompLayer.OUT_POINT,
LottieTensor.Index.PrecompLayer.START_TIME
],
LottieTensor.CMD_TEXT_LAYER: [
LottieTensor.Index.TextLayer.IN_POINT,
LottieTensor.Index.TextLayer.OUT_POINT,
LottieTensor.Index.TextLayer.START_TIME
],
LottieTensor.CMD_SOLID_LAYER: [
LottieTensor.Index.SolidLayer.IN_POINT,
LottieTensor.Index.SolidLayer.OUT_POINT,
LottieTensor.Index.SolidLayer.START_TIME
],
LottieTensor.CMD_KEYFRAME: [
LottieTensor.Index.Keyframe.T
],
LottieTensor.CMD_WIDTH_KEYFRAME: [
LottieTensor.Index.WidthKeyframe.T
],
LottieTensor.CMD_COLOR_KEYFRAME: [
LottieTensor.Index.Keyframe.T
],
LottieTensor.CMD_OPACITY_KEYFRAME: [
LottieTensor.Index.Keyframe.T
],
LottieTensor.CMD_TEXT_KEYFRAME: [
LottieTensor.Index.TextKeyframe.T
],
LottieTensor.CMD_MASK_PT_KEYFRAME: [
LottieTensor.Index.MaskPtKeyframe.T
],
LottieTensor.CMD_RANGE_START_KEYFRAME: [
LottieTensor.Index.RangeStartKeyframe.T
],
LottieTensor.CMD_RANGE_END_KEYFRAME: [
LottieTensor.Index.RangeEndKeyframe.T
],
LottieTensor.CMD_RANGE_OFFSET_KEYFRAME: [
LottieTensor.Index.RangeOffsetKeyframe.T
],
LottieTensor.CMD_DASH_KEYFRAME: [
LottieTensor.Index.DashKeyframe.T
],
}
# Space dictionary parameters - all spatial/positional values
space_params = {
LottieTensor.CMD_ANIMATION: [
LottieTensor.Index.Animation.W,
LottieTensor.Index.Animation.H
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.W,
LottieTensor.Index.PrecompLayer.H
],
LottieTensor.CMD_SOLID_LAYER: [
LottieTensor.Index.SolidLayer.WIDTH,
LottieTensor.Index.SolidLayer.HEIGHT
],
LottieTensor.CMD_DIMENSIONS: [
LottieTensor.Index.Dimensions.WIDTH,
LottieTensor.Index.Dimensions.HEIGHT
],
LottieTensor.CMD_TEXT_KEYFRAME: [
LottieTensor.Index.TextKeyframe.STROKE_WIDTH,
LottieTensor.Index.TextKeyframe.WRAP_POSITION_X,
LottieTensor.Index.TextKeyframe.WRAP_POSITION_Y,
LottieTensor.Index.TextKeyframe.WRAP_SIZE_X,
LottieTensor.Index.TextKeyframe.WRAP_SIZE_Y
],
LottieTensor.CMD_KEYFRAME: [
LottieTensor.Index.Keyframe.S1, LottieTensor.Index.Keyframe.S2, LottieTensor.Index.Keyframe.S3,
LottieTensor.Index.Keyframe.E1, LottieTensor.Index.Keyframe.E2, LottieTensor.Index.Keyframe.E3,
LottieTensor.Index.Keyframe.TO1, LottieTensor.Index.Keyframe.TO2, LottieTensor.Index.Keyframe.TO3,
LottieTensor.Index.Keyframe.TI1, LottieTensor.Index.Keyframe.TI2, LottieTensor.Index.Keyframe.TI3
],
#LottieTensor.CMD_WIDTH_KEYFRAME: [
# LottieTensor.Index.WidthKeyframe.S,
#],
LottieTensor.CMD_OPACITY_KEYFRAME: [
LottieTensor.Index.Keyframe.S1,
],
LottieTensor.CMD_POSITION: [
LottieTensor.Index.Transform.X, LottieTensor.Index.Transform.Y, LottieTensor.Index.Transform.Z,
LottieTensor.Index.TwoValues.VALUE1, LottieTensor.Index.TwoValues.VALUE2
],
LottieTensor.CMD_POSITION_X: [
LottieTensor.Index.Transform.X
],
LottieTensor.CMD_POSITION_Y: [
LottieTensor.Index.Transform.X
],
LottieTensor.CMD_POSITION_Z: [
LottieTensor.Index.Transform.X
],
LottieTensor.CMD_POINT: [
LottieTensor.Index.Point.X, LottieTensor.Index.Point.Y,
LottieTensor.Index.Point.IN_X, LottieTensor.Index.Point.IN_Y,
LottieTensor.Index.Point.OUT_X, LottieTensor.Index.Point.OUT_Y
],
LottieTensor.CMD_TRANSFORM_SHAPE: [
LottieTensor.Index.TransformShape.POSITION_X, LottieTensor.Index.TransformShape.POSITION_Y,
],
LottieTensor.CMD_SIZE: [
LottieTensor.Index.Transform.X, LottieTensor.Index.Transform.Y # 修改
],
LottieTensor.CMD_RECT_SIZE: [
LottieTensor.Index.Transform.X, LottieTensor.Index.Transform.Y # 修改
],
LottieTensor.CMD_ELLIPSE_SIZE: [
LottieTensor.Index.Transform.X, LottieTensor.Index.Transform.Y # 修改
],
LottieTensor.CMD_START_POINT: [
LottieTensor.Index.TwoValues.VALUE1, LottieTensor.Index.TwoValues.VALUE2
],
LottieTensor.CMD_END_POINT: [
LottieTensor.Index.TwoValues.VALUE1, LottieTensor.Index.TwoValues.VALUE2
],
LottieTensor.CMD_POINTS_STAR: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_START: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_END: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_OFFSET: [
LottieTensor.Index.SingleValue.VALUE
],
#LottieTensor.CMD_WIDTH: [
# LottieTensor.Index.SingleValue.VALUE
#],
#LottieTensor.CMD_DASH: [
# LottieTensor.Index.Dash.LENGTH
#],
#LottieTensor.CMD_DASH_OFFSET: [
# LottieTensor.Index.DashOffset.O
#],
#LottieTensor.CMD_DASH_KEYFRAME: [
# LottieTensor.Index.DashKeyframe.S,
# ],
LottieTensor.CMD_TR_POSITION: [
LottieTensor.Index.TrPosition.X, LottieTensor.Index.TrPosition.Y
],
LottieTensor.CMD_REPEATER_OFFSET: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_RANGE_START_KEYFRAME: [
LottieTensor.Index.RangeStartKeyframe.S,
],
LottieTensor.CMD_RANGE_END_KEYFRAME: [
LottieTensor.Index.RangeEndKeyframe.S,
],
LottieTensor.CMD_RANGE_OFFSET_KEYFRAME: [
LottieTensor.Index.RangeOffsetKeyframe.S,
],
LottieTensor.CMD_POSITION_ANIMATORS: [
LottieTensor.Index.PositionAnimators.K_X,
LottieTensor.Index.PositionAnimators.K_Y,
LottieTensor.Index.PositionAnimators.K_Z
],
LottieTensor.CMD_MASK_X: [
LottieTensor.Index.MaskX.K
],
LottieTensor.CMD_MASK_PT_K_I: list(range(20)),
LottieTensor.CMD_MASK_PT_K_O: list(range(20)),
LottieTensor.CMD_MASK_PT_K_V: list(range(20)),
LottieTensor.CMD_MASK_PT_KF_I: [
LottieTensor.Index.MaskPtKfI.X, LottieTensor.Index.MaskPtKfI.Y
],
LottieTensor.CMD_MASK_PT_KF_O: [
LottieTensor.Index.MaskPtKfO.X, LottieTensor.Index.MaskPtKfO.Y
],
LottieTensor.CMD_MASK_PT_KF_SHAPE_I: list(range(20)),
LottieTensor.CMD_MASK_PT_KF_SHAPE_O: list(range(20)),
LottieTensor.CMD_MASK_PT_KF_SHAPE_V: list(range(20)),
LottieTensor.CMD_VALUE: [
LottieTensor.Index.Value.VALUE
],
LottieTensor.CMD_MORE_OPTIONS: [
LottieTensor.Index.MoreOptions.ALIGNMENT_K1,
LottieTensor.Index.MoreOptions.ALIGNMENT_K2
],
LottieTensor.CMD_ALIGNMENT_K: [
LottieTensor.Index.AlignmentK.VALUE1,
LottieTensor.Index.AlignmentK.VALUE2
],
LottieTensor.CMD_CHAR: [
LottieTensor.Index.Char.W
],
LottieTensor.CMD_GRADIENT_FILL: [
LottieTensor.Index.GradientFill.START_POINT_X,
LottieTensor.Index.GradientFill.START_POINT_Y,
LottieTensor.Index.GradientFill.END_POINT_X,
LottieTensor.Index.GradientFill.END_POINT_Y,
LottieTensor.Index.GradientFill.HIGHLIGHT_LENGTH,
],
LottieTensor.CMD_GRADIENT_STROKE: [
LottieTensor.Index.GradientStroke.WIDTH,
LottieTensor.Index.GradientStroke.START_POINT_X,
LottieTensor.Index.GradientStroke.START_POINT_Y,
LottieTensor.Index.GradientStroke.END_POINT_X,
LottieTensor.Index.GradientStroke.END_POINT_Y,
LottieTensor.Index.GradientStroke.HIGHLIGHT_LENGTH,
],
LottieTensor.CMD_HIGHLIGHT_LENGTH: [
LottieTensor.Index.SingleValue.VALUE
],
}
width_value_params = {
LottieTensor.CMD_WIDTH: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_DASH: [
LottieTensor.Index.Dash.LENGTH
],
LottieTensor.CMD_DASH_OFFSET: [
LottieTensor.Index.DashOffset.O
],
LottieTensor.CMD_WIDTH_KEYFRAME: [
LottieTensor.Index.WidthKeyframe.S,
],
LottieTensor.CMD_DASH_KEYFRAME: [
LottieTensor.Index.DashKeyframe.S,
],
}
amplitude_params = {
LottieTensor.CMD_AMPLITUDE: [
LottieTensor.Index.Amplitude.VALUE
],
}
anchor_params = {
LottieTensor.CMD_TRANSFORM_SHAPE: [
LottieTensor.Index.TransformShape.ANCHOR_X, LottieTensor.Index.TransformShape.ANCHOR_Y,
],
LottieTensor.CMD_TR_ANCHOR: [
LottieTensor.Index.TrAnchor.X, LottieTensor.Index.TrAnchor.Y
],
LottieTensor.CMD_ANCHOR: [
LottieTensor.Index.Transform.X, LottieTensor.Index.Transform.Y, LottieTensor.Index.Transform.Z
],
}
animated_params = {
LottieTensor.CMD_MORE_OPTIONS: [
LottieTensor.Index.MoreOptions.ALIGNMENT_A,
],
LottieTensor.CMD_ELLIPSE_SIZE: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_RECT_SIZE: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_POSITION: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_POSITION_X: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_POSITION_Y: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_POSITION_Z: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_SCALE: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_ROTATION: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_OPACITY: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_ANCHOR: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_SIZE: [
LottieTensor.Index.Transform.ANIMATED
],
LottieTensor.CMD_PATH: [
LottieTensor.Index.Path.ANIMATED,
],
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.COLOR_ANIMATED,
LottieTensor.Index.Fill.OPACITY_ANIMATED,
],
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.WIDTH_ANIMATED,
LottieTensor.Index.Stroke.COLOR_ANIMATED
],
LottieTensor.CMD_RECT_ROUNDED: [
LottieTensor.Index.SingleValue.ANIMATED
],
LottieTensor.CMD_START: [
LottieTensor.Index.SingleValue.ANIMATED
],
LottieTensor.CMD_END: [
LottieTensor.Index.SingleValue.ANIMATED
],
LottieTensor.CMD_OFFSET: [
LottieTensor.Index.SingleValue.ANIMATED
],
LottieTensor.CMD_MASK_PT: [
LottieTensor.Index.MaskPt.A,
],
LottieTensor.CMD_MASK_O: [
LottieTensor.Index.MaskO.A,
],
LottieTensor.CMD_MASK_X: [
LottieTensor.Index.MaskX.A,
],
LottieTensor.CMD_TM: [
LottieTensor.Index.Tm.A
],
LottieTensor.CMD_RANGE_START: [
LottieTensor.Index.RangeStart.A
],
LottieTensor.CMD_RANGE_END: [
LottieTensor.Index.RangeEnd.A
],
LottieTensor.CMD_RANGE_OFFSET: [
LottieTensor.Index.Amount.A,
],
LottieTensor.CMD_AMOUNT: [
LottieTensor.Index.Amount.A,
],
LottieTensor.CMD_MAX_EASE: [
LottieTensor.Index.MaxEase.A,
],
LottieTensor.CMD_MIN_EASE: [
LottieTensor.Index.MinEase.A,
],
LottieTensor.CMD_S_M: [
LottieTensor.Index.SM.A,
],
LottieTensor.CMD_OPACITY_ANIMATORS: [
LottieTensor.Index.OpacityAnimators.A,
],
LottieTensor.CMD_POSITION_ANIMATORS: [
LottieTensor.Index.PositionAnimators.A,
],
LottieTensor.CMD_SCALE_ANIMATORS: [
LottieTensor.Index.ScaleAnimators.A,
],
LottieTensor.CMD_ROTATION_ANIMATORS: [
LottieTensor.Index.RotationAnimators.A,
],
LottieTensor.CMD_TRACKING_ANIMATORS: [
LottieTensor.Index.TrackingAnimators.A,
],
LottieTensor.CMD_ALIGNMENT: [
LottieTensor.Index.Alignment.A
],
}
h_flag_params = {
LottieTensor.CMD_KEYFRAME: [
LottieTensor.Index.Keyframe.H_FLAG
],
}
offset_val_params = {
LottieTensor.CMD_TEXT_KEYFRAME: [
LottieTensor.Index.TextKeyframe.OFFSET,
],
}
ca_params = {
LottieTensor.CMD_TEXT_KEYFRAME: [
LottieTensor.Index.TextKeyframe.CA,
],
LottieTensor.CMD_CA: [
LottieTensor.Index.Ca.VALUE
],
}
justify_params = {
LottieTensor.CMD_TEXT_KEYFRAME: [
LottieTensor.Index.TextKeyframe.JUSTIFY,
],
LottieTensor.CMD_JUSTIFY: [
LottieTensor.Index.Justify.VALUE
],
}
text_tracking_params = {
LottieTensor.CMD_TEXT_KEYFRAME: [
LottieTensor.Index.TextKeyframe.TRACKING,
],
LottieTensor.CMD_TRACKING: [
LottieTensor.Index.Tracking.VALUE
],
}
has_stroke_color_params = {
LottieTensor.CMD_TEXT_KEYFRAME: [
LottieTensor.Index.TextKeyframe.HAS_STROKE_COLOR,
],
}
ix_params = {
LottieTensor.CMD_PATH: [
LottieTensor.Index.Path.IX,
LottieTensor.Index.Path.KS_IX,
],
LottieTensor.CMD_GROUP: [
LottieTensor.Index.Group.IX,
LottieTensor.Index.Group.CIX,
],
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.C_IX,
LottieTensor.Index.Fill.O_IX,
],
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.C_IX,
],
LottieTensor.CMD_RECT: [
LottieTensor.Index.Rect.IX,
],
LottieTensor.CMD_ROUNDED: [
LottieTensor.Index.SingleValue.IX
],
LottieTensor.CMD_TRIM: [
LottieTensor.Index.Trim.IX
],
LottieTensor.CMD_REPEATER: [
LottieTensor.Index.Repeater.IX
],
LottieTensor.CMD_COPIES: [
LottieTensor.Index.SingleValue.IX
],
LottieTensor.CMD_REPEATER_OFFSET: [
LottieTensor.Index.SingleValue.IX
],
LottieTensor.CMD_TR_P_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_A_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_S_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_R_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_SO_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_EO_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_GRADIENT_STROKE: [
LottieTensor.Index.GradientStroke.ML2_IX,
],
LottieTensor.CMD_MASK_PT: [
LottieTensor.Index.MaskPt.IX,
],
LottieTensor.CMD_MASK_O: [
LottieTensor.Index.MaskO.IX,
],
LottieTensor.CMD_MASK_X: [
LottieTensor.Index.MaskX.IX,
],
LottieTensor.CMD_ZIG_ZAG: [
LottieTensor.Index.ZigZag.IX
],
LottieTensor.CMD_RANGE_OFFSET: [
LottieTensor.Index.Amount.IX
],
LottieTensor.CMD_AMOUNT: [
LottieTensor.Index.Amount.IX
],
LottieTensor.CMD_MAX_EASE: [
LottieTensor.Index.MaxEase.IX
],
LottieTensor.CMD_MIN_EASE: [
LottieTensor.Index.MinEase.IX
],
LottieTensor.CMD_S_M: [
LottieTensor.Index.SM.IX
],
LottieTensor.CMD_OPACITY_ANIMATORS: [
LottieTensor.Index.OpacityAnimators.IX
],
LottieTensor.CMD_POSITION_ANIMATORS: [
LottieTensor.Index.PositionAnimators.IX
],
LottieTensor.CMD_SCALE_ANIMATORS: [
LottieTensor.Index.ScaleAnimators.IX
],
LottieTensor.CMD_ROTATION_ANIMATORS: [
LottieTensor.Index.RotationAnimators.IX
],
LottieTensor.CMD_TRACKING_ANIMATORS: [
LottieTensor.Index.TrackingAnimators.IX
],
LottieTensor.CMD_ALIGNMENT_IX: [
LottieTensor.Index.AlignmentIx.VALUE
],
LottieTensor.CMD_MORE_OPTIONS: [
LottieTensor.Index.MoreOptions.ALIGNMENT_IX
],
LottieTensor.CMD_DASH: [
LottieTensor.Index.Dash.V_IX
],
LottieTensor.CMD_DASH_ANIMATED: [
LottieTensor.Index.DashAnimated.V_IX,
],
}
bm_params = {
LottieTensor.CMD_GROUP: [
LottieTensor.Index.Group.BM,
],
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.BM,
],
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.BM,
],
}
closed_params = {
LottieTensor.CMD_PATH: [
LottieTensor.Index.Path.CLOSED,
],
LottieTensor.CMD_BEZIER: [
LottieTensor.Index.Bezier.CLOSED
],
LottieTensor.CMD_MASK_PT_K_C: [
LottieTensor.Index.MaskPtK.C
],
LottieTensor.CMD_MASK_PT_KF_SHAPE: [
LottieTensor.Index.MaskPtKfShape.C,
],
}
direction_params = {
LottieTensor.CMD_RECT: [
LottieTensor.Index.Rect.D,
],
LottieTensor.CMD_STAR: [
LottieTensor.Index.Star.D,
],
}
star_type_params = {
LottieTensor.CMD_STAR: [
LottieTensor.Index.Star.SY
],
}
multiple_params = {
LottieTensor.CMD_MULTIPLE: [
LottieTensor.Index.SingleValue.VALUE
],
}
composite_params = {
LottieTensor.CMD_COMPOSITE: [
LottieTensor.Index.SingleValue.VALUE
],
}
skew_params = {
LottieTensor.CMD_TRANSFORM_SHAPE: [
LottieTensor.Index.TransformShape.SKEW,
],
LottieTensor.CMD_SKEW: [
LottieTensor.Index.SingleValue.VALUE
],
}
skew_axis_params = {
LottieTensor.CMD_TRANSFORM_SHAPE: [
LottieTensor.Index.TransformShape.SKEW_AXIS,
],
LottieTensor.CMD_SKEW_AXIS: [
LottieTensor.Index.SingleValue.VALUE
],
}
scale_params = {
LottieTensor.CMD_SCALE: [
LottieTensor.Index.Transform.X, LottieTensor.Index.Transform.Y, LottieTensor.Index.Transform.Z
],
LottieTensor.CMD_TRANSFORM_SHAPE: [
LottieTensor.Index.TransformShape.SCALE_X, LottieTensor.Index.TransformShape.SCALE_Y,
],
LottieTensor.CMD_TR_SCALE: [
LottieTensor.Index.TwoValues.VALUE1, LottieTensor.Index.TwoValues.VALUE2
],
LottieTensor.CMD_SCALE_ANIMATORS: [
LottieTensor.Index.ScaleAnimators.K_X,
LottieTensor.Index.ScaleAnimators.K_Y,
LottieTensor.Index.ScaleAnimators.K_Z
],
}
rotation_params = {
LottieTensor.CMD_ROTATION: [
LottieTensor.Index.Transform.X
],
LottieTensor.CMD_TRANSFORM_SHAPE: [
LottieTensor.Index.TransformShape.ROTATION
],
LottieTensor.CMD_STAR_ROTATION: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_ROTATION: [
LottieTensor.Index.TrRotation.VALUE
],
LottieTensor.CMD_ROTATION_ANIMATORS: [
LottieTensor.Index.RotationAnimators.K
],
LottieTensor.CMD_GRADIENT_FILL: [
LottieTensor.Index.GradientFill.HIGHLIGHT_ANGLE,
],
LottieTensor.CMD_GRADIENT_STROKE: [
LottieTensor.Index.GradientStroke.HIGHLIGHT_ANGLE
],
LottieTensor.CMD_HIGHLIGHT_ANGLE: [
LottieTensor.Index.SingleValue.VALUE
],
}
ease_params = {
LottieTensor.CMD_MAX_EASE: [
LottieTensor.Index.MaxEase.K
],
LottieTensor.CMD_MIN_EASE: [
LottieTensor.Index.MinEase.K
],
}
smooth_params = {
LottieTensor.CMD_S_M: [
LottieTensor.Index.SM.K
],
}
tracking_params = {
LottieTensor.CMD_TRACKING_ANIMATORS: [
LottieTensor.Index.TrackingAnimators.K
],
}
index_params = {
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.INDEX,
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.INDEX,
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.INDEX,
],
LottieTensor.CMD_TEXT_LAYER: [
LottieTensor.Index.TextLayer.INDEX,
],
LottieTensor.CMD_SOLID_LAYER: [
LottieTensor.Index.SolidLayer.INDEX,
],
LottieTensor.CMD_PARENT: [
LottieTensor.Index.Parent.PARENT_INDEX
],
LottieTensor.CMD_PATH: [
LottieTensor.Index.Path.IND,
],
LottieTensor.CMD_COLOR: [
LottieTensor.Index.Color.INDEX
],
LottieTensor.CMD_MASK: [
LottieTensor.Index.Mask.INDEX,
],
LottieTensor.CMD_MASK_PT_KEYFRAME: [
LottieTensor.Index.MaskPtKeyframe.INDEX
],
LottieTensor.CMD_MASK_PT_KF_SHAPE: [
LottieTensor.Index.MaskPtKfShape.INDEX,
],
LottieTensor.CMD_EFFECT: [
LottieTensor.Index.Effect.INDEX,
],
LottieTensor.CMD_LAYER_EFFECT: [
LottieTensor.Index.LayerEffect.INDEX,
],
LottieTensor.CMD_DROPDOWN: [
LottieTensor.Index.Dropdown.INDEX,
],
LottieTensor.CMD_NO_VALUE: [
LottieTensor.Index.NO_VALUE.INDEX,
],
LottieTensor.CMD_IGNORED: [
LottieTensor.Index.Ignored.INDEX,
],
LottieTensor.CMD_SLIDER: [
LottieTensor.Index.Slider.INDEX,
],
}
ddd_params = {
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.DDD,
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.DDD,
],
LottieTensor.CMD_ANIMATION: [
LottieTensor.Index.Animation.DDD
],
}
hd_params = {
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.HD,
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.HD,
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.HD,
],
LottieTensor.CMD_PATH: [
LottieTensor.Index.Path.HD,
],
LottieTensor.CMD_GROUP: [
LottieTensor.Index.Group.HD,
],
LottieTensor.CMD_RECT: [
LottieTensor.Index.Rect.HD,
],
LottieTensor.CMD_TRANSFORM_SHAPE: [
LottieTensor.Index.TransformShape.HD
],
}
cp_params = {
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.CP,
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.CP,
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.CP,
],
}
has_mask_params = {
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.HAS_MASK,
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.HAS_MASK,
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.HAS_MASK,
],
LottieTensor.CMD_TEXT_LAYER: [
LottieTensor.Index.TextLayer.HAS_MASK,
],
LottieTensor.CMD_SOLID_LAYER: [
LottieTensor.Index.SolidLayer.HAS_MASK
],
}
ao_params = {
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.AO,
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.AO,
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.AO,
],
}
tt_params = {
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.TT,
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.TT,
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.TT,
],
}
tp_params = {
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.TP,
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.TP,
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.TP,
],
}
td_params = {
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.TD,
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.TD,
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.TD,
],
}
ct_params = {
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.CT,
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.CT,
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.CT,
],
}
number_params = {
LottieTensor.CMD_COPIES: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_AMOUNT: [
LottieTensor.Index.Amount.K
],
LottieTensor.CMD_RANGE_OFFSET: [
LottieTensor.Index.Amount.K
],
LottieTensor.CMD_GROUP: [
LottieTensor.Index.Group.NP
],
LottieTensor.CMD_EFFECT: [
LottieTensor.Index.Effect.NP,
],
}
dim_params = {
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.COLOR_DIM,
],
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.COLOR_DIM,
],
}
has_c_a_params = {
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.HAS_C_A,
],
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.HAS_C_A,
],
}
has_c_ix_params = {
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.HAS_C_IX,
],
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.HAS_C_IX,
],
}
has_o_a_params = {
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.HAS_O_A,
],
}
has_o_ix_params = {
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.HAS_O_IX,
],
}
fill_rule_params = {
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.FILL_RULE,
],
LottieTensor.CMD_GRADIENT_FILL: [
LottieTensor.Index.GradientFill.FILL_RULE,
],
LottieTensor.CMD_FILL_RULE: [
LottieTensor.Index.SingleValue.VALUE
],
}
type_params = {
LottieTensor.CMD_GRADIENT_FILL: [
LottieTensor.Index.GradientFill.GRADIENT_TYPE,
],
LottieTensor.CMD_GRADIENT_STROKE: [
LottieTensor.Index.GradientStroke.GRADIENT_TYPE,
],
LottieTensor.CMD_GRADIENT_TYPE: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_POINT_TYPE: [
LottieTensor.Index.PointType.VALUE
],
LottieTensor.CMD_RANGE_SELECTOR: [
LottieTensor.Index.RangeSelector.T,
],
LottieTensor.CMD_DASH: [
LottieTensor.Index.Dash.TYPE,
],
LottieTensor.CMD_DASH_ANIMATED: [
LottieTensor.Index.DashAnimated.TYPE,
],
LottieTensor.CMD_EFFECT: [
LottieTensor.Index.Effect.TYPE,
],
}
text_range_units = {
LottieTensor.CMD_RANGE_SELECTOR: [
LottieTensor.Index.RangeSelector.R,
],
}
inv_params = {
LottieTensor.CMD_MASK: [
LottieTensor.Index.Mask.INV,
],
}
mode_params = {
LottieTensor.CMD_MERGE_MODE: [
LottieTensor.Index.MergeMode.MODE
],
LottieTensor.CMD_MASK: [
LottieTensor.Index.Mask.MODE,
],
LottieTensor.CMD_RANGE_SELECTOR: [
LottieTensor.Index.RangeSelector.B,
],
}
text_shape_type = {
LottieTensor.CMD_RANGE_SELECTOR: [
LottieTensor.Index.RangeSelector.SH,
],
}
text_random = {
LottieTensor.CMD_RANGE_SELECTOR: [
LottieTensor.Index.RangeSelector.RN
],
}
color_points_params = {
LottieTensor.CMD_GRADIENT_FILL: [
LottieTensor.Index.GradientFill.COLOR_POINTS
],
LottieTensor.CMD_GRADIENT_STROKE: [
LottieTensor.Index.GradientStroke.COLOR_POINTS
],
LottieTensor.CMD_COLOR_POINTS: [
LottieTensor.Index.SingleValue.VALUE
],
}
round_params = {
LottieTensor.CMD_ROUNDED: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_RECT_ROUNDED: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_INNER_ROUNDNESS: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_OUTER_ROUNDNESS: [
LottieTensor.Index.SingleValue.VALUE
],
}
radius_params = {
LottieTensor.CMD_INNER_RADIUS: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_OUTER_RADIUS: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_RADIUS: [
LottieTensor.Index.Radius.VALUE
],
}
frequency_params = {
LottieTensor.CMD_ANIMATION: [
LottieTensor.Index.Animation.FR,
],
LottieTensor.CMD_FREQUENCY: [
LottieTensor.Index.Frequency.VALUE
],
LottieTensor.CMD_ASSET: [
LottieTensor.Index.Asset.FR
],
}
speed_params = {
LottieTensor.CMD_KEYFRAME: [
LottieTensor.Index.Keyframe.I_X, LottieTensor.Index.Keyframe.I_Y,
LottieTensor.Index.Keyframe.O_X, LottieTensor.Index.Keyframe.O_Y,
LottieTensor.Index.Keyframe.I_X2, LottieTensor.Index.Keyframe.I_Y2,
LottieTensor.Index.Keyframe.O_X2, LottieTensor.Index.Keyframe.O_Y2,
LottieTensor.Index.Keyframe.I_X3, LottieTensor.Index.Keyframe.I_Y3,
LottieTensor.Index.Keyframe.O_X3, LottieTensor.Index.Keyframe.O_Y3,
],
LottieTensor.CMD_WIDTH_KEYFRAME: [
LottieTensor.Index.WidthKeyframe.I_X, LottieTensor.Index.WidthKeyframe.I_Y,
LottieTensor.Index.WidthKeyframe.O_X, LottieTensor.Index.WidthKeyframe.O_Y
],
LottieTensor.CMD_OPACITY_KEYFRAME: [
LottieTensor.Index.Keyframe.I_X, LottieTensor.Index.Keyframe.I_Y,
LottieTensor.Index.Keyframe.O_X, LottieTensor.Index.Keyframe.O_Y
],
LottieTensor.CMD_COLOR_KEYFRAME: [
LottieTensor.Index.Keyframe.I_X, LottieTensor.Index.Keyframe.I_Y,
LottieTensor.Index.Keyframe.O_X, LottieTensor.Index.Keyframe.O_Y
],
LottieTensor.CMD_DASH_KEYFRAME: [
LottieTensor.Index.DashKeyframe.I_X, LottieTensor.Index.DashKeyframe.I_Y,
LottieTensor.Index.DashKeyframe.O_X, LottieTensor.Index.DashKeyframe.O_Y
],
LottieTensor.CMD_RANGE_START_KEYFRAME: [
LottieTensor.Index.RangeStartKeyframe.I_X, LottieTensor.Index.RangeStartKeyframe.I_Y,
LottieTensor.Index.RangeStartKeyframe.O_X, LottieTensor.Index.RangeStartKeyframe.O_Y
],
LottieTensor.CMD_RANGE_END_KEYFRAME: [
LottieTensor.Index.RangeEndKeyframe.I_X, LottieTensor.Index.RangeEndKeyframe.I_Y,
LottieTensor.Index.RangeEndKeyframe.O_X, LottieTensor.Index.RangeEndKeyframe.O_Y
],
LottieTensor.CMD_RANGE_OFFSET_KEYFRAME: [
LottieTensor.Index.RangeOffsetKeyframe.I_X, LottieTensor.Index.RangeOffsetKeyframe.I_Y,
LottieTensor.Index.RangeOffsetKeyframe.O_X, LottieTensor.Index.RangeOffsetKeyframe.O_Y
],
}
font_params = {
LottieTensor.CMD_TEXT_KEYFRAME: [
LottieTensor.Index.TextKeyframe.FONT_SIZE,
LottieTensor.Index.TextKeyframe.LINE_HEIGHT,
LottieTensor.Index.TextKeyframe.LETTER_SPACING
],
LottieTensor.CMD_FONT_SIZE: [
LottieTensor.Index.FontSize.SIZE
],
LottieTensor.CMD_LINE_HEIGHT: [
LottieTensor.Index.LineHeight.VALUE
],
LottieTensor.CMD_LETTER_SPACING: [
LottieTensor.Index.LetterSpacing.VALUE
],
LottieTensor.CMD_FONT: [
LottieTensor.Index.Font.ASCENT
],
LottieTensor.CMD_CHAR: [
LottieTensor.Index.Char.SIZE
],
}
color_params = {
LottieTensor.CMD_TEXT_KEYFRAME: [
LottieTensor.Index.TextKeyframe.FILL_COLOR_R,
LottieTensor.Index.TextKeyframe.FILL_COLOR_G,
LottieTensor.Index.TextKeyframe.FILL_COLOR_B,
LottieTensor.Index.TextKeyframe.STROKE_COLOR_R,
LottieTensor.Index.TextKeyframe.STROKE_COLOR_G,
LottieTensor.Index.TextKeyframe.STROKE_COLOR_B
],
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.R,
LottieTensor.Index.Stroke.G,
LottieTensor.Index.Stroke.B,
LottieTensor.Index.Stroke.A
],
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.R,
LottieTensor.Index.Fill.G,
LottieTensor.Index.Fill.B,
],
LottieTensor.CMD_FILL_COLOR: [
LottieTensor.Index.FillColor.R,
LottieTensor.Index.FillColor.G,
LottieTensor.Index.FillColor.B
],
LottieTensor.CMD_SOLID_LAYER: [
LottieTensor.Index.SolidLayer.COLOR_R,
LottieTensor.Index.SolidLayer.COLOR_G,
LottieTensor.Index.SolidLayer.COLOR_B,
LottieTensor.Index.SolidLayer.COLOR_A
],
LottieTensor.CMD_COLOR: [
LottieTensor.Index.Color.R,
LottieTensor.Index.Color.G,
LottieTensor.Index.Color.B
],
LottieTensor.CMD_COLOR_KEYFRAME: [
LottieTensor.Index.Keyframe.S1,
LottieTensor.Index.Keyframe.S2,
LottieTensor.Index.Keyframe.S3,
LottieTensor.Index.Keyframe.E1
],
LottieTensor.CMD_ORIGINAL_COLORS: list(range(LottieTensor.Index.OriginalColors.COUNT)),
LottieTensor.CMD_GRADIENT_FILL: list(range(LottieTensor.Index.GradientFill.ORIGINAL_COLOR_0,
LottieTensor.Index.GradientFill.ORIGINAL_COLOR_23 + 1)),
LottieTensor.CMD_GRADIENT_STROKE: list(range(LottieTensor.Index.GradientStroke.ORIGINAL_COLOR_0,
LottieTensor.Index.GradientStroke.ORIGINAL_COLOR_23 + 1)),
}
line_cap_params = {
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.LC,
],
LottieTensor.CMD_LINE_CAP: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_GRADIENT_STROKE: [
LottieTensor.Index.GradientStroke.LINE_CAP,
],
}
line_join_params = {
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.LJ,
],
LottieTensor.CMD_LINE_JOIN: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_GRADIENT_STROKE: [
LottieTensor.Index.GradientStroke.LINE_JOIN,
],
}
miter_limit_params = {
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.ML
],
LottieTensor.CMD_MITER_LIMIT: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_ML2: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_GRADIENT_STROKE: [
LottieTensor.Index.GradientStroke.MITER_LIMIT,
LottieTensor.Index.GradientStroke.ML2
],
}
enabled_params = {
LottieTensor.CMD_EFFECT: [
LottieTensor.Index.Effect.ENABLED
],
}
effect_params = {
LottieTensor.CMD_LAYER_EFFECT: [
LottieTensor.Index.LayerEffect.VALUE
],
LottieTensor.CMD_DROPDOWN: [
LottieTensor.Index.Dropdown.VALUE
],
LottieTensor.CMD_NO_VALUE: [
LottieTensor.Index.NO_VALUE.VALUE
],
LottieTensor.CMD_IGNORED: [
LottieTensor.Index.Ignored.VALUE
],
LottieTensor.CMD_SLIDER: [
LottieTensor.Index.Slider.VALUE
],
}
opacity_params = {
LottieTensor.CMD_GRADIENT_FILL: [
LottieTensor.Index.GradientFill.OPACITY
],
LottieTensor.CMD_GRADIENT_STROKE: [
LottieTensor.Index.GradientStroke.OPACITY
],
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.OPACITY
],
LottieTensor.CMD_OPACITY: [
LottieTensor.Index.Transform.X
],
LottieTensor.CMD_TRANSFORM_SHAPE: [
LottieTensor.Index.TransformShape.OPACITY,
],
LottieTensor.CMD_TR_START_OPACITY: [
LottieTensor.Index.TrStartOpacity.VALUE
],
LottieTensor.CMD_TR_END_OPACITY: [
LottieTensor.Index.TrEndOpacity.VALUE
],
LottieTensor.CMD_OPACITY_ANIMATORS: [
LottieTensor.Index.OpacityAnimators.K
],
LottieTensor.CMD_MASK_O: [
LottieTensor.Index.MaskO.K
],
}
# Tokenizer parameters (no offset)
tokenizer_params = {
LottieTensor.CMD_TEXT_KEYFRAME: list(range(LottieTensor.Index.TextKeyframe.FONT_FAMILY_TOKENS_START,
LottieTensor.Index.TextKeyframe.TEXT_TOKEN_COUNT + 1)),
LottieTensor.CMD_ASSET: list(range(LottieTensor.Index.Asset.ID_TOKEN_0,
LottieTensor.Index.Asset.ID_TOKEN_COUNT + 1)),
LottieTensor.CMD_REFERENCE_ID: list(range(LottieTensor.Index.ReferenceId.ID_TOKEN_0,
LottieTensor.Index.ReferenceId.ID_TOKEN_COUNT + 1)),
LottieTensor.CMD_FONT: list(range(LottieTensor.Index.Font.FAMILY_TOKEN_0,
LottieTensor.Index.Font.STYLE_TOKEN_COUNT + 1)),
LottieTensor.CMD_CHAR: list(range(LottieTensor.Index.Char.CH_TOKEN_0,
LottieTensor.Index.Char.FAMILY_TOKEN_COUNT + 1)),
}
# 添加 width_value_params 的判断(在其他判断之前)
if cmd_idx in width_value_params and param_idx in width_value_params[cmd_idx]:
return WIDTH_VALUE_OFFSET
elif cmd_idx in tokenizer_params and param_idx in tokenizer_params[cmd_idx]:
return NO_OFFSET # No offset for tokenizer tokens
elif cmd_idx in time_params and param_idx in time_params[cmd_idx]:
return TIME_OFFSET
elif cmd_idx in space_params and param_idx in space_params[cmd_idx]:
return SPACE_OFFSET
elif cmd_idx in amplitude_params and param_idx in amplitude_params[cmd_idx]:
return AMPLITUDE_OFFSET
elif cmd_idx in anchor_params and param_idx in anchor_params[cmd_idx]:
return ANCHOR_OFFSET
elif cmd_idx in animated_params and param_idx in animated_params[cmd_idx]:
return ANIMATED_OFFSET
elif cmd_idx in h_flag_params and param_idx in h_flag_params[cmd_idx]:
return H_FLAG_OFFSET
elif cmd_idx in offset_val_params and param_idx in offset_val_params[cmd_idx]:
return OFFSET_VAL_OFFSET
elif cmd_idx in ca_params and param_idx in ca_params[cmd_idx]:
return CA_OFFSET
elif cmd_idx in justify_params and param_idx in justify_params[cmd_idx]:
return JUSTIFY_OFFSET
elif cmd_idx in text_tracking_params and param_idx in text_tracking_params[cmd_idx]:
return TEXT_TRACKING_OFFSET
elif cmd_idx in has_stroke_color_params and param_idx in has_stroke_color_params[cmd_idx]:
return HAS_STROKE_COLOR_OFFSET
elif cmd_idx in ix_params and param_idx in ix_params[cmd_idx]:
return IX_OFFSET
elif cmd_idx in bm_params and param_idx in bm_params[cmd_idx]:
return BM_OFFSET
elif cmd_idx in closed_params and param_idx in closed_params[cmd_idx]:
return CLOSED_OFFSET
elif cmd_idx in direction_params and param_idx in direction_params[cmd_idx]:
return DIRECTION_OFFSET
elif cmd_idx in star_type_params and param_idx in star_type_params[cmd_idx]:
return STAR_TYPE_OFFSET
elif cmd_idx in multiple_params and param_idx in multiple_params[cmd_idx]:
return MULTIPLE_OFFSET
elif cmd_idx in composite_params and param_idx in composite_params[cmd_idx]:
return COMPOSITE_OFFSET
elif cmd_idx in skew_params and param_idx in skew_params[cmd_idx]:
return SKEW_OFFSET
elif cmd_idx in skew_axis_params and param_idx in skew_axis_params[cmd_idx]:
return SKEW_AXIS_OFFSET
elif cmd_idx in scale_params and param_idx in scale_params[cmd_idx]:
return SCALE_OFFSET
elif cmd_idx in rotation_params and param_idx in rotation_params[cmd_idx]:
return ROTATION_OFFSET
elif cmd_idx in ease_params and param_idx in ease_params[cmd_idx]:
return EASE_OFFSET
elif cmd_idx in smooth_params and param_idx in smooth_params[cmd_idx]:
return SMOOTH_OFFSET
elif cmd_idx in tracking_params and param_idx in tracking_params[cmd_idx]:
return TRACKING_OFFSET
elif cmd_idx in index_params and param_idx in index_params[cmd_idx]:
return INDEX_OFFSET
elif cmd_idx in ddd_params and param_idx in ddd_params[cmd_idx]:
return DDD_OFFSET
elif cmd_idx in hd_params and param_idx in hd_params[cmd_idx]:
return HD_OFFSET
elif cmd_idx in cp_params and param_idx in cp_params[cmd_idx]:
return CP_OFFSET
elif cmd_idx in has_mask_params and param_idx in has_mask_params[cmd_idx]:
return HAS_MASK_OFFSET
elif cmd_idx in ao_params and param_idx in ao_params[cmd_idx]:
return AO_OFFSET
elif cmd_idx in tt_params and param_idx in tt_params[cmd_idx]:
return TT_OFFSET
elif cmd_idx in tp_params and param_idx in tp_params[cmd_idx]:
return TP_OFFSET
elif cmd_idx in td_params and param_idx in td_params[cmd_idx]:
return TD_OFFSET
elif cmd_idx in ct_params and param_idx in ct_params[cmd_idx]:
return CT_OFFSET
elif cmd_idx in number_params and param_idx in number_params[cmd_idx]:
return NUMBER_OFFSET
elif cmd_idx in dim_params and param_idx in dim_params[cmd_idx]:
return DIM_OFFSET
elif cmd_idx in has_c_a_params and param_idx in has_c_a_params[cmd_idx]:
return HAS_C_A_OFFSET
elif cmd_idx in has_c_ix_params and param_idx in has_c_ix_params[cmd_idx]:
return HAS_C_IX_OFFSET
elif cmd_idx in has_o_a_params and param_idx in has_o_a_params[cmd_idx]:
return HAS_O_A_OFFSET
elif cmd_idx in has_o_ix_params and param_idx in has_o_ix_params[cmd_idx]:
return HAS_O_IX_OFFSET
elif cmd_idx in fill_rule_params and param_idx in fill_rule_params[cmd_idx]:
return FILL_RULE_OFFSET
elif cmd_idx in type_params and param_idx in type_params[cmd_idx]:
return TYPE_OFFSET
elif cmd_idx in text_range_units and param_idx in text_range_units[cmd_idx]:
return TEXT_RANGE_UNITS_OFFSET
elif cmd_idx in inv_params and param_idx in inv_params[cmd_idx]:
return INV_OFFSET
elif cmd_idx in mode_params and param_idx in mode_params[cmd_idx]:
return MODE_OFFSET
elif cmd_idx in text_shape_type and param_idx in text_shape_type[cmd_idx]:
return TEXT_SHAPE_TYPE_OFFSET
elif cmd_idx in text_random and param_idx in text_random[cmd_idx]:
return TEXT_RANDOM_OFFSET
elif cmd_idx in color_points_params and param_idx in color_points_params[cmd_idx]:
return COLOR_POINTS_OFFSET
elif cmd_idx in round_params and param_idx in round_params[cmd_idx]:
return ROUND_OFFSET
elif cmd_idx in radius_params and param_idx in radius_params[cmd_idx]:
return RADIUS_OFFSET
elif cmd_idx in frequency_params and param_idx in frequency_params[cmd_idx]:
return FREQUENCY_OFFSET
elif cmd_idx in speed_params and param_idx in speed_params[cmd_idx]:
return SPEED_OFFSET
elif cmd_idx in font_params and param_idx in font_params[cmd_idx]:
return FONT_OFFSET
elif cmd_idx in color_params and param_idx in color_params[cmd_idx]:
return COLOR_OFFSET
elif cmd_idx in line_cap_params and param_idx in line_cap_params[cmd_idx]:
return LINE_CAP_OFFSET
elif cmd_idx in line_join_params and param_idx in line_join_params[cmd_idx]:
return LINE_JOIN_OFFSET
elif cmd_idx in miter_limit_params and param_idx in miter_limit_params[cmd_idx]:
return MITER_LIMIT_OFFSET
elif cmd_idx in effect_params and param_idx in effect_params[cmd_idx]:
return EFFECT_OFFSET
elif cmd_idx in opacity_params and param_idx in opacity_params[cmd_idx]:
return OPACITY_OFFSET
else:
return 0 # Default to no offset if not found
LottieTensor._OFFSET_CACHE[cache_key] = offset
return offset
@staticmethod
def get_command_param_indices(cmd_idx: int) -> List[int]:
"""
Get the list of parameter indices for a command in their fixed order.
Returns empty list for commands without parameters.
"""
param_orders = {
LottieTensor.CMD_ANIMATION: [
LottieTensor.Index.Animation.FR,
LottieTensor.Index.Animation.IP,
LottieTensor.Index.Animation.OP,
LottieTensor.Index.Animation.W,
LottieTensor.Index.Animation.H,
LottieTensor.Index.Animation.DDD
],
LottieTensor.CMD_LAYER: [
LottieTensor.Index.Layer.INDEX,
LottieTensor.Index.Layer.IN_POINT,
LottieTensor.Index.Layer.OUT_POINT,
LottieTensor.Index.Layer.START_TIME,
LottieTensor.Index.Layer.DDD,
LottieTensor.Index.Layer.HD,
LottieTensor.Index.Layer.CP,
LottieTensor.Index.Layer.HAS_MASK,
LottieTensor.Index.Layer.AO,
LottieTensor.Index.Layer.TT,
LottieTensor.Index.Layer.TP,
LottieTensor.Index.Layer.TD,
LottieTensor.Index.Layer.CT
],
LottieTensor.CMD_NULL_LAYER: [
LottieTensor.Index.NullLayer.INDEX,
LottieTensor.Index.NullLayer.IN_POINT,
LottieTensor.Index.NullLayer.OUT_POINT,
LottieTensor.Index.NullLayer.START_TIME,
LottieTensor.Index.NullLayer.CT,
LottieTensor.Index.NullLayer.HD,
LottieTensor.Index.NullLayer.HAS_MASK,
LottieTensor.Index.NullLayer.AO,
LottieTensor.Index.NullLayer.TT,
LottieTensor.Index.NullLayer.TP,
LottieTensor.Index.NullLayer.TD,
LottieTensor.Index.NullLayer.CP
],
LottieTensor.CMD_PRECOMP_LAYER: [
LottieTensor.Index.PrecompLayer.INDEX,
LottieTensor.Index.PrecompLayer.IN_POINT,
LottieTensor.Index.PrecompLayer.OUT_POINT,
LottieTensor.Index.PrecompLayer.START_TIME,
LottieTensor.Index.PrecompLayer.W,
LottieTensor.Index.PrecompLayer.H,
LottieTensor.Index.PrecompLayer.CT,
LottieTensor.Index.PrecompLayer.HAS_MASK,
LottieTensor.Index.PrecompLayer.AO,
LottieTensor.Index.PrecompLayer.TT,
LottieTensor.Index.PrecompLayer.TP,
LottieTensor.Index.PrecompLayer.TD,
LottieTensor.Index.PrecompLayer.DDD,
LottieTensor.Index.PrecompLayer.HD,
LottieTensor.Index.PrecompLayer.CP
],
LottieTensor.CMD_TEXT_LAYER: [
LottieTensor.Index.TextLayer.INDEX,
LottieTensor.Index.TextLayer.IN_POINT,
LottieTensor.Index.TextLayer.OUT_POINT,
LottieTensor.Index.TextLayer.START_TIME,
LottieTensor.Index.TextLayer.HAS_MASK
],
LottieTensor.CMD_SOLID_LAYER: [
LottieTensor.Index.SolidLayer.INDEX,
LottieTensor.Index.SolidLayer.IN_POINT,
LottieTensor.Index.SolidLayer.OUT_POINT,
LottieTensor.Index.SolidLayer.START_TIME,
LottieTensor.Index.SolidLayer.WIDTH,
LottieTensor.Index.SolidLayer.HEIGHT,
LottieTensor.Index.SolidLayer.HAS_MASK,
LottieTensor.Index.SolidLayer.COLOR_R,
LottieTensor.Index.SolidLayer.COLOR_G,
LottieTensor.Index.SolidLayer.COLOR_B,
LottieTensor.Index.SolidLayer.COLOR_A
],
LottieTensor.CMD_TRANSFORM: [],
LottieTensor.CMD_POSITION: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X,
LottieTensor.Index.Transform.Y,
LottieTensor.Index.Transform.Z
],
LottieTensor.CMD_POSITION_X: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X
],
LottieTensor.CMD_POSITION_Y: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X
],
LottieTensor.CMD_POSITION_Z: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X
],
LottieTensor.CMD_SCALE: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X,
LottieTensor.Index.Transform.Y,
LottieTensor.Index.Transform.Z
],
LottieTensor.CMD_ROTATION: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X
],
LottieTensor.CMD_OPACITY: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X
],
LottieTensor.CMD_ANCHOR: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X,
LottieTensor.Index.Transform.Y,
LottieTensor.Index.Transform.Z
],
LottieTensor.CMD_KEYFRAME: [
LottieTensor.Index.Keyframe.T,
LottieTensor.Index.Keyframe.S1,
LottieTensor.Index.Keyframe.S2,
LottieTensor.Index.Keyframe.S3,
LottieTensor.Index.Keyframe.I_X,
LottieTensor.Index.Keyframe.I_Y,
LottieTensor.Index.Keyframe.O_X,
LottieTensor.Index.Keyframe.O_Y,
LottieTensor.Index.Keyframe.TO1,
LottieTensor.Index.Keyframe.TO2,
LottieTensor.Index.Keyframe.TO3,
LottieTensor.Index.Keyframe.TI1,
LottieTensor.Index.Keyframe.TI2,
LottieTensor.Index.Keyframe.TI3,
LottieTensor.Index.Keyframe.I_X2,
LottieTensor.Index.Keyframe.I_X3,
LottieTensor.Index.Keyframe.I_Y2,
LottieTensor.Index.Keyframe.I_Y3,
LottieTensor.Index.Keyframe.O_X2,
LottieTensor.Index.Keyframe.O_X3,
LottieTensor.Index.Keyframe.O_Y2,
LottieTensor.Index.Keyframe.O_Y3,
LottieTensor.Index.Keyframe.H_FLAG,
LottieTensor.Index.Keyframe.E1,
LottieTensor.Index.Keyframe.E2,
LottieTensor.Index.Keyframe.E3
],
LottieTensor.CMD_GROUP: [
LottieTensor.Index.Group.IX,
LottieTensor.Index.Group.CIX,
LottieTensor.Index.Group.BM,
LottieTensor.Index.Group.HD,
LottieTensor.Index.Group.NP
],
LottieTensor.CMD_PATH: [
LottieTensor.Index.Path.IX,
LottieTensor.Index.Path.IND,
LottieTensor.Index.Path.KS_IX,
LottieTensor.Index.Path.CLOSED,
LottieTensor.Index.Path.HD,
LottieTensor.Index.Path.ANIMATED
],
LottieTensor.CMD_POINT: [
LottieTensor.Index.Point.X,
LottieTensor.Index.Point.Y,
LottieTensor.Index.Point.IN_X,
LottieTensor.Index.Point.IN_Y,
LottieTensor.Index.Point.OUT_X,
LottieTensor.Index.Point.OUT_Y
],
LottieTensor.CMD_FILL: [
LottieTensor.Index.Fill.R,
LottieTensor.Index.Fill.G,
LottieTensor.Index.Fill.B,
LottieTensor.Index.Fill.COLOR_DIM,
LottieTensor.Index.Fill.HAS_C_A,
LottieTensor.Index.Fill.HAS_C_IX,
LottieTensor.Index.Fill.C_IX,
LottieTensor.Index.Fill.BM,
LottieTensor.Index.Fill.FILL_RULE,
LottieTensor.Index.Fill.OPACITY,
LottieTensor.Index.Fill.COLOR_ANIMATED,
LottieTensor.Index.Fill.OPACITY_ANIMATED,
LottieTensor.Index.Fill.HAS_O_A,
LottieTensor.Index.Fill.HAS_O_IX,
LottieTensor.Index.Fill.O_IX
],
LottieTensor.CMD_STROKE: [
LottieTensor.Index.Stroke.R,
LottieTensor.Index.Stroke.G,
LottieTensor.Index.Stroke.B,
LottieTensor.Index.Stroke.COLOR_DIM,
LottieTensor.Index.Stroke.HAS_C_A,
LottieTensor.Index.Stroke.HAS_C_IX,
LottieTensor.Index.Stroke.C_IX,
LottieTensor.Index.Stroke.BM,
LottieTensor.Index.Stroke.LC,
LottieTensor.Index.Stroke.LJ,
LottieTensor.Index.Stroke.ML,
LottieTensor.Index.Stroke.WIDTH_ANIMATED,
LottieTensor.Index.Stroke.COLOR_ANIMATED,
LottieTensor.Index.Stroke.A
],
LottieTensor.CMD_TRANSFORM_SHAPE: [
LottieTensor.Index.TransformShape.POSITION_X,
LottieTensor.Index.TransformShape.POSITION_Y,
LottieTensor.Index.TransformShape.SCALE_X,
LottieTensor.Index.TransformShape.SCALE_Y,
LottieTensor.Index.TransformShape.ROTATION,
LottieTensor.Index.TransformShape.OPACITY,
LottieTensor.Index.TransformShape.ANCHOR_X,
LottieTensor.Index.TransformShape.ANCHOR_Y,
LottieTensor.Index.TransformShape.SKEW,
LottieTensor.Index.TransformShape.SKEW_AXIS,
LottieTensor.Index.TransformShape.HD
],
LottieTensor.CMD_RECT: [
LottieTensor.Index.Rect.HD,
LottieTensor.Index.Rect.D
],
LottieTensor.CMD_ELLIPSE: [],
LottieTensor.CMD_BEZIER: [
LottieTensor.Index.Bezier.CLOSED
],
LottieTensor.CMD_SIZE: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X, # 修改:从 TwoValues.VALUE1 改为 Transform.X
LottieTensor.Index.Transform.Y # 修改:从 TwoValues.VALUE2 改为 Transform.Y
],
LottieTensor.CMD_RECT_SIZE: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X, # 修改
LottieTensor.Index.Transform.Y # 修改
],
LottieTensor.CMD_ELLIPSE_SIZE: [
LottieTensor.Index.Transform.ANIMATED,
LottieTensor.Index.Transform.X, # 修改
LottieTensor.Index.Transform.Y # 修改
],
LottieTensor.CMD_ROUNDED: [
LottieTensor.Index.SingleValue.VALUE,
LottieTensor.Index.SingleValue.IX
],
LottieTensor.CMD_RECT_ROUNDED: [
LottieTensor.Index.SingleValue.ANIMATED,
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TRIM: [
LottieTensor.Index.Trim.IX
],
LottieTensor.CMD_START: [
LottieTensor.Index.SingleValue.ANIMATED,
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_END: [
LottieTensor.Index.SingleValue.ANIMATED,
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_OFFSET: [
LottieTensor.Index.SingleValue.ANIMATED,
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_PARENT: [
LottieTensor.Index.Parent.PARENT_INDEX
],
LottieTensor.CMD_REFERENCE_ID: list(range(11)), # 11 tokens
LottieTensor.CMD_DIMENSIONS: [
LottieTensor.Index.Dimensions.WIDTH,
LottieTensor.Index.Dimensions.HEIGHT
],
LottieTensor.CMD_ASSET: list(range(12)), # FR + 10 tokens + count
LottieTensor.CMD_TEXT_KEYFRAME: list(range(47)), # All text keyframe params
LottieTensor.CMD_FONT: list(range(23)), # All font params
LottieTensor.CMD_CHAR: list(range(35)), # All char params
LottieTensor.CMD_WIDTH_KEYFRAME: [
LottieTensor.Index.WidthKeyframe.T,
LottieTensor.Index.WidthKeyframe.S,
LottieTensor.Index.WidthKeyframe.I_X,
LottieTensor.Index.WidthKeyframe.I_Y,
LottieTensor.Index.WidthKeyframe.O_X,
LottieTensor.Index.WidthKeyframe.O_Y
],
LottieTensor.CMD_COLOR_KEYFRAME: [
LottieTensor.Index.Keyframe.T,
LottieTensor.Index.Keyframe.S1,
LottieTensor.Index.Keyframe.S2,
LottieTensor.Index.Keyframe.S3,
LottieTensor.Index.Keyframe.E1,
LottieTensor.Index.Keyframe.I_X,
LottieTensor.Index.Keyframe.I_Y,
LottieTensor.Index.Keyframe.O_X,
LottieTensor.Index.Keyframe.O_Y
],
LottieTensor.CMD_OPACITY_KEYFRAME: [
LottieTensor.Index.Keyframe.T,
LottieTensor.Index.Keyframe.S1,
LottieTensor.Index.Keyframe.I_X,
LottieTensor.Index.Keyframe.I_Y,
LottieTensor.Index.Keyframe.O_X,
LottieTensor.Index.Keyframe.O_Y
],
LottieTensor.CMD_OPACITY_ANIMATED: [],
LottieTensor.CMD_TM: [
LottieTensor.Index.Tm.A
],
LottieTensor.CMD_VALUE: [
LottieTensor.Index.Value.VALUE
],
LottieTensor.CMD_SKEW: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_SKEW_AXIS: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_STAR: [
LottieTensor.Index.Star.D,
LottieTensor.Index.Star.SY
],
LottieTensor.CMD_INNER_RADIUS: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_OUTER_RADIUS: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_INNER_ROUNDNESS: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_OUTER_ROUNDNESS: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_POINTS_STAR: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_STAR_ROTATION: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_MULTIPLE: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_REPEATER: [
LottieTensor.Index.Repeater.IX
],
LottieTensor.CMD_COPIES: [
LottieTensor.Index.SingleValue.VALUE,
LottieTensor.Index.SingleValue.IX
],
LottieTensor.CMD_REPEATER_OFFSET: [
LottieTensor.Index.SingleValue.VALUE,
LottieTensor.Index.SingleValue.IX
],
LottieTensor.CMD_COMPOSITE: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_REPEATER_TRANSFORM: [],
LottieTensor.CMD_TR_P_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_A_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_SCALE: [
LottieTensor.Index.TwoValues.VALUE1,
LottieTensor.Index.TwoValues.VALUE2
],
LottieTensor.CMD_TR_S_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_R_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_SO_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_TR_EO_IX: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_MORE_OPTIONS: [
LottieTensor.Index.MoreOptions.G,
LottieTensor.Index.MoreOptions.ALIGNMENT_A,
LottieTensor.Index.MoreOptions.ALIGNMENT_K1,
LottieTensor.Index.MoreOptions.ALIGNMENT_K2,
LottieTensor.Index.MoreOptions.ALIGNMENT_IX
],
LottieTensor.CMD_GRADIENT_FILL: [],
LottieTensor.CMD_START_POINT: [
LottieTensor.Index.TwoValues.VALUE1,
LottieTensor.Index.TwoValues.VALUE2
],
LottieTensor.CMD_END_POINT: [
LottieTensor.Index.TwoValues.VALUE1,
LottieTensor.Index.TwoValues.VALUE2
],
LottieTensor.CMD_GRADIENT_TYPE: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_HIGHLIGHT_LENGTH: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_HIGHLIGHT_ANGLE: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_ORIGINAL_COLORS: list(range(48)), # All color values + count
LottieTensor.CMD_COLOR_POINTS: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_GRADIENT_STROKE: [],
LottieTensor.CMD_WIDTH: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_LINE_CAP: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_LINE_JOIN: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_MITER_LIMIT: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_ML2: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_COLOR: [
LottieTensor.Index.Color.INDEX,
LottieTensor.Index.Color.R,
LottieTensor.Index.Color.G,
LottieTensor.Index.Color.B
],
LottieTensor.CMD_EFFECT: [
LottieTensor.Index.Effect.TYPE,
LottieTensor.Index.Effect.INDEX,
LottieTensor.Index.Effect.NP,
LottieTensor.Index.Effect.ENABLED
],
LottieTensor.CMD_LAYER_EFFECT: [
LottieTensor.Index.LayerEffect.INDEX,
LottieTensor.Index.LayerEffect.VALUE
],
LottieTensor.CMD_DROPDOWN: [
LottieTensor.Index.Dropdown.INDEX,
LottieTensor.Index.Dropdown.VALUE
],
LottieTensor.CMD_NO_VALUE: [
LottieTensor.Index.NO_VALUE.INDEX,
LottieTensor.Index.NO_VALUE.VALUE
],
LottieTensor.CMD_IGNORED: [
LottieTensor.Index.Ignored.INDEX,
LottieTensor.Index.Ignored.VALUE
],
LottieTensor.CMD_SLIDER: [
LottieTensor.Index.Slider.INDEX,
LottieTensor.Index.Slider.VALUE
],
LottieTensor.CMD_FILL_RULE: [
LottieTensor.Index.SingleValue.VALUE
],
LottieTensor.CMD_MERGE: [],
LottieTensor.CMD_MERGE_MODE: [
LottieTensor.Index.MergeMode.MODE
],
LottieTensor.CMD_MASKS_PROPERTIES: [],
LottieTensor.CMD_MASK: [
LottieTensor.Index.Mask.INDEX,
LottieTensor.Index.Mask.INV,
LottieTensor.Index.Mask.MODE
],
LottieTensor.CMD_MASK_PT: [
LottieTensor.Index.MaskPt.A,
LottieTensor.Index.MaskPt.IX
],
LottieTensor.CMD_MASK_PT_K: [],
LottieTensor.CMD_MASK_PT_K_C: [
LottieTensor.Index.MaskPtK.C
],
LottieTensor.CMD_MASK_PT_K_I: list(range(21)), # V1-V20 + COUNT
LottieTensor.CMD_MASK_PT_K_O: list(range(21)),
LottieTensor.CMD_MASK_PT_K_V: list(range(21)),
LottieTensor.CMD_MASK_O: [
LottieTensor.Index.MaskO.A,
LottieTensor.Index.MaskO.K,
LottieTensor.Index.MaskO.IX
],
LottieTensor.CMD_MASK_X: [
LottieTensor.Index.MaskX.A,
LottieTensor.Index.MaskX.K,
LottieTensor.Index.MaskX.IX
],
LottieTensor.CMD_MASK_PT_K_ARRAY: [],
LottieTensor.CMD_MASK_PT_KEYFRAME: [
LottieTensor.Index.MaskPtKeyframe.INDEX,
LottieTensor.Index.MaskPtKeyframe.T
],
LottieTensor.CMD_MASK_PT_KF_I: [
LottieTensor.Index.MaskPtKfI.X,
LottieTensor.Index.MaskPtKfI.Y
],
LottieTensor.CMD_MASK_PT_KF_O: [
LottieTensor.Index.MaskPtKfO.X,
LottieTensor.Index.MaskPtKfO.Y
],
LottieTensor.CMD_MASK_PT_KF_S: [],
LottieTensor.CMD_MASK_PT_KF_SHAPE: [
LottieTensor.Index.MaskPtKfShape.INDEX,
LottieTensor.Index.MaskPtKfShape.C
],
LottieTensor.CMD_MASK_PT_KF_SHAPE_I: list(range(21)),
LottieTensor.CMD_MASK_PT_KF_SHAPE_O: list(range(21)),
LottieTensor.CMD_MASK_PT_KF_SHAPE_V: list(range(21)),
LottieTensor.CMD_TR_POSITION: [
LottieTensor.Index.TrPosition.X,
LottieTensor.Index.TrPosition.Y
],
LottieTensor.CMD_TR_ANCHOR: [
LottieTensor.Index.TrAnchor.X,
LottieTensor.Index.TrAnchor.Y
],
LottieTensor.CMD_TR_ROTATION: [
LottieTensor.Index.TrRotation.VALUE
],
LottieTensor.CMD_TR_START_OPACITY: [
LottieTensor.Index.TrStartOpacity.VALUE
],
LottieTensor.CMD_TR_END_OPACITY: [
LottieTensor.Index.TrEndOpacity.VALUE
],
LottieTensor.CMD_ZIG_ZAG: [
LottieTensor.Index.ZigZag.IX
],
LottieTensor.CMD_FREQUENCY: [
LottieTensor.Index.Frequency.VALUE
],
LottieTensor.CMD_AMPLITUDE: [
LottieTensor.Index.Amplitude.VALUE
],
LottieTensor.CMD_POINT_TYPE: [
LottieTensor.Index.PointType.VALUE
],
LottieTensor.CMD_ANIMATORS: [],
LottieTensor.CMD_ANIMATOR: [],
LottieTensor.CMD_RANGE_SELECTOR: [
LottieTensor.Index.RangeSelector.T,
LottieTensor.Index.RangeSelector.R,
LottieTensor.Index.RangeSelector.B,
LottieTensor.Index.RangeSelector.SH,
LottieTensor.Index.RangeSelector.RN
],
LottieTensor.CMD_RANGE_START: [
LottieTensor.Index.RangeStart.A
],
LottieTensor.CMD_RANGE_START_KEYFRAME: [
LottieTensor.Index.RangeStartKeyframe.T,
LottieTensor.Index.RangeStartKeyframe.S,
LottieTensor.Index.RangeStartKeyframe.I_X,
LottieTensor.Index.RangeStartKeyframe.I_Y,
LottieTensor.Index.RangeStartKeyframe.O_X,
LottieTensor.Index.RangeStartKeyframe.O_Y
],
LottieTensor.CMD_AMOUNT: [
LottieTensor.Index.Amount.A,
LottieTensor.Index.Amount.K,
LottieTensor.Index.Amount.IX
],
LottieTensor.CMD_MAX_EASE: [
LottieTensor.Index.MaxEase.A,
LottieTensor.Index.MaxEase.K,
LottieTensor.Index.MaxEase.IX
],
LottieTensor.CMD_MIN_EASE: [
LottieTensor.Index.MinEase.A,
LottieTensor.Index.MinEase.K,
LottieTensor.Index.MinEase.IX
],
LottieTensor.CMD_ANIMATOR_PROPERTIES: [],
LottieTensor.CMD_RADIUS: [
LottieTensor.Index.Radius.VALUE
],
LottieTensor.CMD_RANGE_END: [
LottieTensor.Index.RangeEnd.A
],
LottieTensor.CMD_RANGE_END_KEYFRAME: [
LottieTensor.Index.RangeEndKeyframe.T,
LottieTensor.Index.RangeEndKeyframe.S,
LottieTensor.Index.RangeEndKeyframe.I_X,
LottieTensor.Index.RangeEndKeyframe.I_Y,
LottieTensor.Index.RangeEndKeyframe.O_X,
LottieTensor.Index.RangeEndKeyframe.O_Y
],
LottieTensor.CMD_RANGE_OFFSET: [
LottieTensor.Index.Amount.A,
LottieTensor.Index.Amount.K,
LottieTensor.Index.Amount.IX
],
LottieTensor.CMD_RANGE_OFFSET_KEYFRAME: [
LottieTensor.Index.RangeOffsetKeyframe.T,
LottieTensor.Index.RangeOffsetKeyframe.S,
LottieTensor.Index.RangeOffsetKeyframe.I_X,
LottieTensor.Index.RangeOffsetKeyframe.I_Y,
LottieTensor.Index.RangeOffsetKeyframe.O_X,
LottieTensor.Index.RangeOffsetKeyframe.O_Y
],
LottieTensor.CMD_S_M: [
LottieTensor.Index.SM.A,
LottieTensor.Index.SM.K,
LottieTensor.Index.SM.IX
],
LottieTensor.CMD_OPACITY_ANIMATORS: [
LottieTensor.Index.OpacityAnimators.A,
LottieTensor.Index.OpacityAnimators.K,
LottieTensor.Index.OpacityAnimators.IX
],
LottieTensor.CMD_SCALE_ANIMATORS: [
LottieTensor.Index.ScaleAnimators.A,
LottieTensor.Index.ScaleAnimators.K_X,
LottieTensor.Index.ScaleAnimators.K_Y,
LottieTensor.Index.ScaleAnimators.K_Z,
LottieTensor.Index.ScaleAnimators.IX
],
LottieTensor.CMD_ROTATION_ANIMATORS: [
LottieTensor.Index.RotationAnimators.A,
LottieTensor.Index.RotationAnimators.K,
LottieTensor.Index.RotationAnimators.IX
],
LottieTensor.CMD_POSITION_ANIMATORS: [
LottieTensor.Index.PositionAnimators.A,
LottieTensor.Index.PositionAnimators.K_X,
LottieTensor.Index.PositionAnimators.K_Y,
LottieTensor.Index.PositionAnimators.K_Z,
LottieTensor.Index.PositionAnimators.IX
],
LottieTensor.CMD_TRACKING_ANIMATORS: [
LottieTensor.Index.TrackingAnimators.A,
LottieTensor.Index.TrackingAnimators.K,
LottieTensor.Index.TrackingAnimators.IX
],
LottieTensor.CMD_DASHES: [],
LottieTensor.CMD_DASH: [
LottieTensor.Index.Dash.TYPE,
LottieTensor.Index.Dash.LENGTH,
LottieTensor.Index.Dash.V_IX
],
LottieTensor.CMD_DASH_ANIMATED: [
LottieTensor.Index.DashAnimated.TYPE,
LottieTensor.Index.DashAnimated.V_IX
],
LottieTensor.CMD_DASH_KEYFRAME: [
LottieTensor.Index.DashKeyframe.T,
LottieTensor.Index.DashKeyframe.S,
LottieTensor.Index.DashKeyframe.I_X,
LottieTensor.Index.DashKeyframe.I_Y,
LottieTensor.Index.DashKeyframe.O_X,
LottieTensor.Index.DashKeyframe.O_Y
],
LottieTensor.CMD_DASH_OFFSET: [
LottieTensor.Index.DashOffset.O
],
LottieTensor.CMD_WIDTH_ANIMATED: [],
# All end commands have empty param lists
LottieTensor.CMD_POSITION_END: [],
LottieTensor.CMD_SCALE_END: [],
LottieTensor.CMD_ROTATION_END: [],
LottieTensor.CMD_OPACITY_END: [],
LottieTensor.CMD_ANCHOR_END: [],
LottieTensor.CMD_GROUP_END: [],
LottieTensor.CMD_TRANSFORM_END: [],
LottieTensor.CMD_LAYER_END: [],
LottieTensor.CMD_PATH_END: [],
LottieTensor.CMD_RECT_END: [],
LottieTensor.CMD_ELLIPSE_END: [],
LottieTensor.CMD_STAR_END: [],
LottieTensor.CMD_TRIM_END: [],
LottieTensor.CMD_REPEATER_END: [],
LottieTensor.CMD_REPEATER_TRANSFORM_END: [],
LottieTensor.CMD_GRADIENT_FILL_END: [],
LottieTensor.CMD_GRADIENT_STROKE_END: [],
LottieTensor.CMD_MERGE_END: [],
LottieTensor.CMD_ROUNDED_CORNERS_END: [],
LottieTensor.CMD_TWIST_END: [],
LottieTensor.CMD_BEZIER_END: [],
LottieTensor.CMD_TEXT_LAYER_END: [],
LottieTensor.CMD_TEXT_DATA_END: [],
LottieTensor.CMD_SOLID_LAYER_END: [],
LottieTensor.CMD_NULL_LAYER_END: [],
LottieTensor.CMD_PRECOMP_LAYER_END: [],
LottieTensor.CMD_POSITION_X_END: [],
LottieTensor.CMD_POSITION_Y_END: [],
LottieTensor.CMD_POSITION_Z_END: [],
LottieTensor.CMD_SCALE_X_END: [],
LottieTensor.CMD_SCALE_Y_END: [],
LottieTensor.CMD_SCALE_Z_END: [],
LottieTensor.CMD_ROTATION_X_END: [],
LottieTensor.CMD_ROTATION_Y_END: [],
LottieTensor.CMD_ROTATION_Z_END: [],
LottieTensor.CMD_EFFECTS_END: [],
LottieTensor.CMD_EFFECT_END: [],
LottieTensor.CMD_KEYFRAME_END: [],
LottieTensor.CMD_WIDTH_ANIMATED_END: [],
LottieTensor.CMD_FONTS_END: [],
LottieTensor.CMD_CHARS_END: [],
LottieTensor.CMD_CHAR_END: [],
LottieTensor.CMD_CHAR_SHAPES_END: [],
LottieTensor.CMD_TEXT_KEYFRAMES_END: [],
LottieTensor.CMD_TEXT_DOC_END: [],
LottieTensor.CMD_MORE_OPTIONS_END: [],
LottieTensor.CMD_OPACITY_ANIMATED_END: [],
LottieTensor.CMD_MASKS_PROPERTIES_END: [],
LottieTensor.CMD_MASK_END: [],
LottieTensor.CMD_MASK_PT_END: [],
LottieTensor.CMD_MASK_PT_K_END: [],
LottieTensor.CMD_TM_END: [],
LottieTensor.CMD_MASK_PT_K_ARRAY_END: [],
LottieTensor.CMD_MASK_PT_KEYFRAME_END: [],
LottieTensor.CMD_MASK_PT_KF_S_END: [],
LottieTensor.CMD_MASK_PT_KF_SHAPE_END: [],
LottieTensor.CMD_VALUE_END: [],
LottieTensor.CMD_ZIG_ZAG_END: [],
LottieTensor.CMD_ANIMATORS_END: [],
LottieTensor.CMD_ANIMATOR_END: [],
LottieTensor.CMD_RANGE_SELECTOR_END: [],
LottieTensor.CMD_RANGE_START_END: [],
LottieTensor.CMD_RANGE_END_END: [],
LottieTensor.CMD_END_END: [],
LottieTensor.CMD_START_END: [],
LottieTensor.CMD_OFFSET_END: [],
LottieTensor.CMD_RANGE_OFFSET_END: [],
LottieTensor.CMD_SCALE_ANIMATORS_END: [],
LottieTensor.CMD_ROTATION_ANIMATORS_END: [],
LottieTensor.CMD_POSITION_ANIMATORS_END: [],
LottieTensor.CMD_OPACITY_ANIMATORS_END: [],
LottieTensor.CMD_COLOR_ANIMATED_END: [],
LottieTensor.CMD_DASHES_END: [],
LottieTensor.CMD_DASH_ANIMATED_END: [],
LottieTensor.CMD_SIZE_END: [],
LottieTensor.CMD_RECT_ROUNDED_END: [],
LottieTensor.CMD_ANIMATOR_PROPERTIES_END: [],
LottieTensor.CMD_ASSET_END: [],
LottieTensor.CMD_EFFECTS: [],
LottieTensor.CMD_FONTS: [],
LottieTensor.CMD_CHARS: [],
LottieTensor.CMD_CHAR_SHAPES: [],
LottieTensor.CMD_TEXT_KEYFRAMES: [],
LottieTensor.CMD_TEXT_DATA: [],
LottieTensor.CMD_DOCUMENT: [],
LottieTensor.CMD_TEXT_DOC: [],
}
# Commands without parameters
empty_param_cmds = {k for k, v in param_orders.items() if not v}
return param_orders.get(cmd_idx, [])
@staticmethod
def get_vocab_range_for_offset(offset: int) -> tuple:
return NotImplementedError
def flatten_to_list(lottie_tensor: 'LottieTensor', max_length: int = None) -> List[int]:
return NotImplementedError
@staticmethod
def from_list(flattened: List[int]) -> 'LottieTensor':
if LottieTensor.tokenizer is None:
try:
LottieTensor.init_tokenizer()
except Exception as e:
print(f"Warning: Failed to initialize tokenizer in from_list: {e}")
COMMAND_OFFSET = 151936
NUMBER_OFFSET = 173186
NUM_COMMANDS = len(LottieTensor.COMMANDS)
# SIZE类命令集合
SIZE_COMMANDS = {
LottieTensor.CMD_SIZE,
LottieTensor.CMD_ELLIPSE_SIZE,
LottieTensor.CMD_RECT_SIZE
}
ANIMATED_THRESHOLD = 0.5
PARAM_DEFAULTS = {}
for i in [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22]:
PARAM_DEFAULTS[(LottieTensor.CMD_KEYFRAME, i)] = 0.0
for i in range(2, 6):
PARAM_DEFAULTS[(LottieTensor.CMD_WIDTH_KEYFRAME, i)] = 0.0
for i in range(5, 9):
PARAM_DEFAULTS[(LottieTensor.CMD_COLOR_KEYFRAME, i)] = 0.0
for i in range(2, 6):
PARAM_DEFAULTS[(LottieTensor.CMD_OPACITY_KEYFRAME, i)] = 0.0
for i in range(2, 6):
PARAM_DEFAULTS[(LottieTensor.CMD_POINT, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_ANIMATION, 5)] = 0.0
for i in range(4, 13):
PARAM_DEFAULTS[(LottieTensor.CMD_LAYER, i)] = 0.0
for i in range(4, 12):
PARAM_DEFAULTS[(LottieTensor.CMD_NULL_LAYER, i)] = 0.0
for i in range(4, 15):
PARAM_DEFAULTS[(LottieTensor.CMD_PRECOMP_LAYER, i)] = 0.0
for i in range(3, 15):
PARAM_DEFAULTS[(LottieTensor.CMD_FILL, i)] = 0.0
for i in range(3, 14):
PARAM_DEFAULTS[(LottieTensor.CMD_STROKE, i)] = 0.0
for i in range(1, 5):
PARAM_DEFAULTS[(LottieTensor.CMD_GROUP, i)] = 0.0
for i in range(4, 6):
PARAM_DEFAULTS[(LottieTensor.CMD_PATH, i)] = 0.0
for i in range(8, 11):
PARAM_DEFAULTS[(LottieTensor.CMD_TRANSFORM_SHAPE, i)] = 0.0
for i in range(1, 4):
PARAM_DEFAULTS[(LottieTensor.CMD_POSITION, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_SCALE, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_ANCHOR, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_ROTATION, 1)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_OPACITY, 1)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_RECT, 1)] = 0.0
for i in range(21):
PARAM_DEFAULTS[(LottieTensor.CMD_MASK_PT_K_I, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_MASK_PT_K_O, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_MASK_PT_K_V, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_MASK_PT_KF_SHAPE_I, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_MASK_PT_KF_SHAPE_O, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_MASK_PT_KF_SHAPE_V, i)] = 0.0
for i in range(2, 6):
PARAM_DEFAULTS[(LottieTensor.CMD_DASH_KEYFRAME, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_RANGE_START_KEYFRAME, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_RANGE_END_KEYFRAME, i)] = 0.0
PARAM_DEFAULTS[(LottieTensor.CMD_RANGE_OFFSET_KEYFRAME, i)] = 0.0
TOKENIZER_COMMANDS = {
LottieTensor.CMD_FONT: {
'regular': [LottieTensor.Index.Font.ASCENT],
'token_groups': [
(LottieTensor.Index.Font.FAMILY_TOKEN_COUNT,
LottieTensor.Index.Font.FAMILY_TOKEN_0, 10),
(LottieTensor.Index.Font.STYLE_TOKEN_COUNT,
LottieTensor.Index.Font.STYLE_TOKEN_0, 10)
]
},
LottieTensor.CMD_CHAR: {
'regular': [
LottieTensor.Index.Char.SIZE,
LottieTensor.Index.Char.W
],
'token_groups': [
(LottieTensor.Index.Char.CH_TOKEN_COUNT,
LottieTensor.Index.Char.CH_TOKEN_0, 10),
(LottieTensor.Index.Char.STYLE_TOKEN_COUNT,
LottieTensor.Index.Char.STYLE_TOKEN_0, 10),
(LottieTensor.Index.Char.FAMILY_TOKEN_COUNT,
LottieTensor.Index.Char.FAMILY_TOKEN_0, 10)
]
},
LottieTensor.CMD_ASSET: {
'regular': [LottieTensor.Index.Asset.FR],
'token_groups': [
(LottieTensor.Index.Asset.ID_TOKEN_COUNT,
LottieTensor.Index.Asset.ID_TOKEN_0, 10)
]
},
LottieTensor.CMD_REFERENCE_ID: {
'regular': [],
'token_groups': [
(LottieTensor.Index.ReferenceId.ID_TOKEN_COUNT,
LottieTensor.Index.ReferenceId.ID_TOKEN_0, 10)
]
},
LottieTensor.CMD_TEXT_KEYFRAME: {
'regular': list(range(LottieTensor.Index.TextKeyframe.FONT_FAMILY_TOKENS_START)),
'token_groups': [
(LottieTensor.Index.TextKeyframe.FONT_FAMILY_TOKEN_COUNT,
LottieTensor.Index.TextKeyframe.FONT_FAMILY_TOKENS_START, 10),
(LottieTensor.Index.TextKeyframe.TEXT_TOKEN_COUNT,
LottieTensor.Index.TextKeyframe.TEXT_TOKENS_START, 15)
]
}
}
def is_command_token(token):
"""Check if a token is a command token."""
return COMMAND_OFFSET <= token < COMMAND_OFFSET + NUM_COMMANDS
def get_default_value(cmd_idx, param_pos, params):
"""Get default value for a parameter."""
# SIZE类命令的特殊处理
if cmd_idx in SIZE_COMMANDS:
animated_val = params[LottieTensor.Index.Transform.ANIMATED]
is_animated = animated_val != LottieTensor.PAD_VAL and animated_val >= ANIMATED_THRESHOLD
if param_pos in {1, 2}:
if is_animated:
return LottieTensor.PAD_VAL
else:
return 0.0
key = (cmd_idx, param_pos)
return PARAM_DEFAULTS.get(key, LottieTensor.PAD_VAL)
def has_following_keyframe(flattened_list, start_idx, cmd_idx):
SIZE_END_COMMANDS = {
LottieTensor.CMD_SIZE: LottieTensor.CMD_SIZE_END,
LottieTensor.CMD_ELLIPSE_SIZE: LottieTensor.CMD_SIZE_END,
LottieTensor.CMD_RECT_SIZE: LottieTensor.CMD_SIZE_END,
}
end_cmd = SIZE_END_COMMANDS.get(cmd_idx, LottieTensor.CMD_SIZE_END)
context_end_cmds = {
end_cmd,
LottieTensor.CMD_FILL,
LottieTensor.CMD_STROKE,
LottieTensor.CMD_GROUP_END,
LottieTensor.CMD_ELLIPSE_END,
LottieTensor.CMD_RECT_END,
}
for k in range(start_idx, len(flattened_list)):
if is_command_token(flattened_list[k]):
cmd = flattened_list[k] - COMMAND_OFFSET
if cmd in context_end_cmds:
return False
if cmd == LottieTensor.CMD_KEYFRAME:
return True
return False
commands = []
params_list = []
i = 0
while i < len(flattened):
if is_command_token(flattened[i]):
cmd_idx = flattened[i] - COMMAND_OFFSET
commands.append(cmd_idx)
cmd_start_i = i
i += 1
params = [LottieTensor.PAD_VAL] * LottieTensor.PARAM_DIM
if cmd_idx in TOKENIZER_COMMANDS:
cmd_info = TOKENIZER_COMMANDS[cmd_idx]
regular_params = cmd_info['regular']
for param_idx in regular_params:
if i < len(flattened) and not is_command_token(flattened[i]):
offset = LottieTensor.get_param_offset(cmd_idx, param_idx)
params[param_idx] = float(flattened[i] - offset)
i += 1
for count_idx, token_start, max_tokens in cmd_info['token_groups']:
if i < len(flattened) and not is_command_token(flattened[i]):
count = flattened[i] - NUMBER_OFFSET
params[count_idx] = float(count)
i += 1
for j in range(int(max(0, count))):
if i < len(flattened) and not is_command_token(flattened[i]):
if token_start + j < LottieTensor.PARAM_DIM:
params[token_start + j] = float(flattened[i])
i += 1
else:
break
else:
param_indices = LottieTensor.get_command_param_indices(cmd_idx)
param_pos = 0
while (param_pos < len(param_indices) and
i < len(flattened) and
not is_command_token(flattened[i])):
param_idx = param_indices[param_pos]
offset = LottieTensor.get_param_offset(cmd_idx, param_idx)
params[param_idx] = float(flattened[i] - offset)
param_pos += 1
i += 1
if cmd_idx in SIZE_COMMANDS:
animated_val = params[LottieTensor.Index.Transform.ANIMATED]
if param_pos == 1 and animated_val != LottieTensor.PAD_VAL and animated_val >= ANIMATED_THRESHOLD:
params[LottieTensor.Index.Transform.ANIMATED] = 1.0
elif animated_val == LottieTensor.PAD_VAL or animated_val < ANIMATED_THRESHOLD:
if has_following_keyframe(flattened, i, cmd_idx):
params[LottieTensor.Index.Transform.ANIMATED] = 1.0
elif param_pos >= 2:
params[LottieTensor.Index.Transform.ANIMATED] = 0.0
elif param_pos >= 2 and (animated_val == LottieTensor.PAD_VAL or animated_val < ANIMATED_THRESHOLD):
params[LottieTensor.Index.Transform.ANIMATED] = 0.0
# Fill in defaults for remaining parameters
for j in range(param_pos, len(param_indices)):
param_idx = param_indices[j]
default_val = get_default_value(cmd_idx, j, params)
if default_val != LottieTensor.PAD_VAL:
params[param_idx] = default_val
params_list.append(params)
else:
i += 1
if commands:
commands_tensor = torch.tensor(commands).reshape(-1, 1).long()
params_tensor = torch.tensor(params_list).float()
else:
commands_tensor = torch.zeros((0, 1)).long()
params_tensor = torch.zeros((0, LottieTensor.PARAM_DIM)).float()
return LottieTensor(commands_tensor, params_tensor)