diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..e14a8f8 --- /dev/null +++ b/404.html @@ -0,0 +1,1402 @@ + + + + + + + + + + + + + + + + + + + + + PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/client/communication/index.html b/api/client/communication/index.html new file mode 100644 index 0000000..6352956 --- /dev/null +++ b/api/client/communication/index.html @@ -0,0 +1,2096 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Communication - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Communication

+ +
+ + + + +
+ +

This modules handles the WebSocket client and automatically connect to the server running in TVPaint.

+

It also has crucial functions like send_cmd that send George commands and get the result.

+ + + +
+ + + + + + + + + + +
+ + + +

+ try_cmd(raise_exc: type[Exception] = GeorgeError, catch_exc: type[Exception] = GeorgeError, exception_msg: str | None = None) -> Callable[[T], T] + +

+ + +
+ +

Decorator that does a try/except with GeorgeError by default.

+

It raises the error with the custom exception message provided.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
raise_exc + type[Exception] + +
+

the exception to raise. Defaults to GeorgeError.

+
+
+ pytvpaint.george.exceptions.GeorgeError +
catch_exc + type[Exception] + +
+

the exception to catch. Defaults to GeorgeError.

+
+
+ pytvpaint.george.exceptions.GeorgeError +
exception_msg + str | None + +
+

Custom exception message. Defaults to None.

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ typing.Callable[[pytvpaint.george.client.T], pytvpaint.george.client.T] + +
+

the decorated function

+
+
+ +
+ Source code in pytvpaint/george/client/__init__.py +
70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
def try_cmd(
+    raise_exc: type[Exception] = GeorgeError,
+    catch_exc: type[Exception] = GeorgeError,
+    exception_msg: str | None = None,
+) -> Callable[[T], T]:
+    """Decorator that does a try/except with GeorgeError by default.
+
+    It raises the error with the custom exception message provided.
+
+    Args:
+        raise_exc: the exception to raise. Defaults to GeorgeError.
+        catch_exc: the exception to catch. Defaults to GeorgeError.
+        exception_msg: Custom exception message. Defaults to None.
+
+    Returns:
+        the decorated function
+    """
+
+    def decorate(func: T) -> T:
+        @functools.wraps(func)
+        def applicator(*args: Any, **kwargs: Any) -> Any:
+            try:
+                return func(*args, **kwargs)
+            except catch_exc as e:
+                raise raise_exc(exception_msg or e)
+
+        return cast(T, applicator)
+
+    return decorate
+
+
+
+ +
+ + +
+ + + +

+ send_cmd(command: str, *args: Any, error_values: list[Any] | None = None, handle_string: bool = True) -> str + +

+ + +
+ +

Send a George command with the provided arguments to TVPaint.

+

Catch basic ERROR XX errors returned from George, but you can provide your own error values.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
command + str + +
+

the George command to send

+
+
+ required +
*args + typing.Any + +
+

pass any arguments you want to that function

+
+
+ () +
error_values + list[typing.Any] | None + +
+

a list of error values to catch from George. Defaults to None.

+
+
+ None +
handle_string + bool + +
+

control the quote wrapping of string with spaces. Defaults to True.

+
+
+ True +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if we received ERROR XX or any of the custom error codes

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

the George return string

+
+
+ +
+ Source code in pytvpaint/george/client/__init__.py +
101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
def send_cmd(
+    command: str,
+    *args: Any,
+    error_values: list[Any] | None = None,
+    handle_string: bool = True,
+) -> str:
+    """Send a George command with the provided arguments to TVPaint.
+
+    Catch basic `ERROR XX` errors returned from George, but you can provide your own error values.
+
+    Args:
+        command: the George command to send
+        *args: pass any arguments you want to that function
+        error_values: a list of error values to catch from George. Defaults to None.
+        handle_string: control the quote wrapping of string with spaces. Defaults to True.
+
+    Raises:
+        GeorgeError: if we received `ERROR XX` or any of the custom error codes
+
+    Returns:
+        the George return string
+    """
+    tv_args = [
+        tv_handle_string(arg) if handle_string and isinstance(arg, str) else arg
+        for arg in args
+    ]
+    cmd_str = " ".join([str(arg) for arg in [command, *tv_args]])
+
+    is_undo_stack = command in [
+        "tv_UndoOpenStack",
+        "tv_UpdateUndo",
+        "tv_UndoCloseStack",
+    ]
+
+    if not is_undo_stack:
+        log.debug(f"[RPC] >> {cmd_str}")
+
+    response = rpc_client.execute_remote("execute_george", [cmd_str])
+    result = response["result"]
+
+    if not is_undo_stack:
+        log.debug(f"[RPC] << {result}")
+
+    # Test for basic ERROR X values and user provided custom errors
+    res_in_error_values = error_values and result in list(map(str, error_values))
+    if res_in_error_values or re.match(r"ERROR -?\d+", result, re.IGNORECASE):
+        msg = f"Received value: '{result}' considered as an error"
+        raise GeorgeError(msg, error_value=result)
+
+    return result
+
+
+
+ +
+ + +
+ + + +

+ run_script(script: Path | str) -> None + +

+ + +
+ +

Execute a George script from a .grg file.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
script + pathlib.Path | str + +
+

the path to the script

+
+
+ required +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if the script was not found

+
+
+ +
+ Source code in pytvpaint/george/client/__init__.py +
153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
def run_script(script: Path | str) -> None:
+    """Execute a George script from a .grg file.
+
+    Args:
+        script: the path to the script
+
+    Raises:
+        ValueError: if the script was not found
+    """
+    script = Path(script)
+    if not script.exists():
+        raise ValueError(f"Script not found at : {script.as_posix()}")
+    send_cmd("tv_RunScript", script.as_posix())
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/client/parsing/index.html b/api/client/parsing/index.html new file mode 100644 index 0000000..eb3d3db --- /dev/null +++ b/api/client/parsing/index.html @@ -0,0 +1,2883 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Parsing - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+ +
+ + + +
+
+ + + + + + + +

George parsing utilities

+ + +
+ + + + +
+ +

Parsing functions used to handle data coming from TVPaint and also preparing arguments for them to be sent.

+

The two main functions are tv_parse_dict and tv_parse_list which handle the return values of George functions.

+

George can either return a list of values or a list of key/value pairs which are consecutive.

+ + + +
+ + + + + + + + +
+ + + +

+ DataclassInstance + + +

+ + +
+

+ Bases: typing_extensions.Protocol

+ + +

Protocol that describes a Dataclass instance.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + + +

+ tv_handle_string(s: str) -> str + +

+ + +
+ +

String handling for George arguments. It wraps the string into quotes if it has spaces.

+

See an example here: https://www.tvpaint.com/doc/tvpaint-animation-11/george-commands#tv_projectnew

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
s + str + +
+

the input string

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

the "escaped" string

+
+
+ +
+ Source code in pytvpaint/george/client/parse.py +
33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
def tv_handle_string(s: str) -> str:
+    """String handling for George arguments. It wraps the string into quotes if it has spaces.
+
+    See an example here: https://www.tvpaint.com/doc/tvpaint-animation-11/george-commands#tv_projectnew
+
+    Args:
+        s (str): the input string
+
+    Returns:
+        the "escaped" string
+    """
+    if " " in s:
+        return f'"{s}"'
+
+    return s
+
+
+
+ +
+ + +
+ + + +

+ camel_to_pascal(s: str) -> str + +

+ + +
+ +

Convert a camel case string to pascal case.

+ +
+ Example +

this_is_a_text -> ThisIsAText

+
+ + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
s + str + +
+

the input string

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

the string in pascal case

+
+
+ +
+ Source code in pytvpaint/george/client/parse.py +
50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
def camel_to_pascal(s: str) -> str:
+    """Convert a camel case string to pascal case.
+
+    Example:
+        `this_is_a_text -> ThisIsAText`
+
+    Args:
+        s: the input string
+
+    Returns:
+        the string in pascal case
+    """
+    return "".join([c.capitalize() for c in s.split("_")])
+
+
+
+ +
+ + +
+ + + +

+ tv_cast_to_type(value: str, cast_type: type[T]) -> T + +

+ + +
+ +

Cast a value to the provided type using George's convention for values.

+ +
+ Note +

"1" and "on"/"ON" values are considered True when parsing a boolean

+
+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
value + str + +
+

the input value

+
+
+ required +
cast_type + type[pytvpaint.george.client.parse.T] + +
+

the type to cast to

+
+
+ required +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if given an enum, and it can't find the value or the enum index is invalid

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.client.parse.T + +
+

the value cast to the provided type

+
+
+ +
+ Source code in pytvpaint/george/client/parse.py +
 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
def tv_cast_to_type(value: str, cast_type: type[T]) -> T:
+    """Cast a value to the provided type using George's convention for values.
+
+    Note:
+        "1" and "on"/"ON" values are considered True when parsing a boolean
+
+    Args:
+        value: the input value
+        cast_type: the type to cast to
+
+    Raises:
+        ValueError: if given an enum, and it can't find the value or the enum index is invalid
+
+    Returns:
+        the value cast to the provided type
+    """
+    if issubclass(cast_type, Enum):
+        value = value.strip().strip('"')
+
+        # Find all enum members that matches the value (lower case)
+        matches = [m for m in cast_type if value.lower() == m.value.lower()]
+        try:
+            # If the unmodified value is in the enum, return that first
+            return cast(T, next(m for m in matches if value == m.value))
+        except StopIteration:
+            # Otherwise return the first match
+            if matches:
+                return cast(T, matches[0])
+
+        # It didn't work, it can be the enum index
+        try:
+            index = int(value)
+        except ValueError:
+            raise ValueError(
+                f"{value} is not a valid Enum index since it can't be parsed as int"
+            )
+
+        # We get the enum member at that index
+        enum_members = list(cast_type)
+        if index < len(enum_members):
+            return cast(T, enum_members[index])
+
+        raise ValueError(
+            f"Enum index {index} is out of bounds (max {len(enum_members) - 1})"
+        )
+
+    if get_origin(cast_type) is tuple:
+        # Split by space and convert each member to the right type
+        values_types = zip(value.split(" "), get_args(cast_type))
+        return cast(T, tuple(tv_cast_to_type(v, t) for v, t in values_types))
+
+    if cast_type == bool:
+        return cast(T, value.lower() in ["1", "on", "true"])
+
+    if cast_type == str:
+        return cast(T, value.strip().strip('"'))
+
+    return cast(T, cast_type(value))
+
+
+
+ +
+ + +
+ + + +

+ tv_parse_dict(input_text: str, with_fields: FieldTypes | type[DataclassInstance]) -> dict[str, Any] + +

+ + +
+ +

Parse a list of values as key value pairs returned from TVPaint commands.

+

Cast the values to a provided dataclass type or list of key/types pairs.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
input_text + str + +
+

the George string result

+
+
+ required +
with_fields + pytvpaint.george.client.parse.FieldTypes | type[pytvpaint.george.client.parse.DataclassInstance] + +
+

the field types (can be a dataclass)

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ dict[str, typing.Any] + +
+

a dict with the values cast to the given types

+
+
+ +
+ Source code in pytvpaint/george/client/parse.py +
150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
def tv_parse_dict(
+    input_text: str,
+    with_fields: FieldTypes | type[DataclassInstance],
+) -> dict[str, Any]:
+    """Parse a list of values as key value pairs returned from TVPaint commands.
+
+    Cast the values to a provided dataclass type or list of key/types pairs.
+
+    Args:
+        input_text: the George string result
+        with_fields: the field types (can be a dataclass)
+
+    Returns:
+        a dict with the values cast to the given types
+    """
+    # For dataclasses get the type hints and filter those with metadata
+    if is_dataclass(with_fields):
+        with_fields = _get_dataclass_fields(with_fields)
+    else:
+        # Explicitly cast because we are sure now
+        with_fields = cast(FieldTypes, with_fields)
+
+    output_dict: dict[str, Any] = {}
+    search_start = 0
+
+    for i, (field_name, field_type) in enumerate(with_fields):
+        current_key_pascal = camel_to_pascal(field_name)
+
+        # Search for the key from the end
+        search_text = input_text.lower()
+        try:
+            start = search_text.index(current_key_pascal.lower(), search_start)
+        except ValueError:
+            continue
+
+        if i < (len(with_fields) - 1):
+            # Search for the next key also from the end
+            next_key_pascal = camel_to_pascal(with_fields[i + 1][0])
+            end = search_text.rfind(" " + next_key_pascal.lower(), search_start)
+        else:
+            end = len(input_text)
+
+        # Get the "key value" substring
+        key_value = input_text[start:end]
+
+        # Extract the value
+        cut_at = len(current_key_pascal) + 1
+        value = key_value[cut_at:].strip()
+
+        # Cast it to the corresponding type
+        value = tv_cast_to_type(value, field_type)
+
+        output_dict[field_name] = value
+        search_start = end
+
+    return output_dict
+
+
+
+ +
+ + +
+ + + +

+ tv_parse_list(output: str, with_fields: FieldTypes | type[DataclassInstance], unused_indices: list[int] | None = None) -> dict[str, Any] + +

+ + +
+ +

Parse a list of values returned from TVPaint commands.

+

Cast the values to a provided dataclass type or list of key/types pairs.

+

You can specify unused indices to exclude positional values from being parsed. +This is useful because some George commands have unused return values.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
output + str + +
+

the input string

+
+
+ required +
with_fields + pytvpaint.george.client.parse.FieldTypes | type[pytvpaint.george.client.parse.DataclassInstance] + +
+

the field types (can be a dataclass)

+
+
+ required +
unused_indices + list[int] | None + +
+

Some George functions return positional arguments that are unused. Defaults to None.

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ dict[str, typing.Any] + +
+

a dict with the values cast to the given types

+
+
+ +
+ Source code in pytvpaint/george/client/parse.py +
208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
def tv_parse_list(
+    output: str,
+    with_fields: FieldTypes | type[DataclassInstance],
+    unused_indices: list[int] | None = None,
+) -> dict[str, Any]:
+    """Parse a list of values returned from TVPaint commands.
+
+    Cast the values to a provided dataclass type or list of key/types pairs.
+
+    You can specify unused indices to exclude positional values from being parsed.
+    This is useful because some George commands have unused return values.
+
+    Args:
+        output: the input string
+        with_fields: the field types (can be a dataclass)
+        unused_indices: Some George functions return positional arguments that are unused. Defaults to None.
+
+    Returns:
+        a dict with the values cast to the given types
+    """
+    start = 0
+    current = 0
+    string_open = False
+    tokens: list[str] = []
+
+    while current < len(output):
+        char = output[current]
+        is_quote = char == '"'
+        is_space = char == " "
+        last_char = current == len(output) - 1
+
+        if (is_space and not string_open) or last_char:
+            last_cut = current if not last_char else current + 1
+            token = output[start:last_cut]
+            if start != last_cut and token != " ":
+                tokens.append(token)
+            start = current = current + 1
+        elif is_quote:
+            if string_open:
+                tokens.append(output[start:current])
+
+            start = current = current + 1
+            string_open = not string_open
+        else:
+            current += 1
+
+    # Get type annotations from the dataclass fields
+    if is_dataclass(with_fields):
+        with_fields = _get_dataclass_fields(with_fields)
+    else:
+        # Explicitly cast because we are sure now
+        with_fields = cast(FieldTypes, with_fields)
+
+    # Remove any unused values
+    if unused_indices:
+        tokens = [t for i, t in enumerate(tokens) if i not in unused_indices]
+
+    # Cast each token to a type and construct the dict
+    tokens_dict: dict[str, Any] = {}
+    for token, (field_name, field_type) in zip(tokens, with_fields):
+        token = tv_cast_to_type(token, field_type)
+        tokens_dict[field_name] = token
+
+    return tokens_dict
+
+
+
+ +
+ + +
+ + + +

+ args_dict_to_list(args: dict[str, Any]) -> list[Any] + +

+ + +
+ +

Converts a dict of named arguments to a flat list of key/values.

+

It also filters pairs with None values

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
args + dict[str, typing.Any] + +
+

dict of arguments

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ list[typing.Any] + +
+

key/values list

+
+
+ +
+ Source code in pytvpaint/george/client/parse.py +
274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
def args_dict_to_list(args: dict[str, Any]) -> list[Any]:
+    """Converts a dict of named arguments to a flat list of key/values.
+
+    It also filters pairs with None values
+
+    Args:
+        args: dict of arguments
+
+    Returns:
+        key/values list
+    """
+    args_filter = {k: v for k, v in args.items() if v is not None}
+    # Flatten dictionnary key value pairs with zip
+    return [item for kv in args_filter.items() for item in kv]
+
+
+
+ +
+ + +
+ + + +

+ validate_args_list(optional_args: Sequence[Value | tuple[Value, ...]]) -> list[Any] + +

+ + +
+ +

Some George functions only accept a list of values and not key:value pairs, so to set the last positional argument for instance, you need to give all the previous ones.

+

This function allows you to give a list of argument or key:value pairs (as tuples) and check that they are not None.

+

For example, for tv_camerainfo [<iWidth> <iHeight> [<field_order>]] +you can't give [500, None, "upper"] because <iHeight> is not defined.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
optional_args + collections.abc.Sequence[pytvpaint.george.client.parse.Value | tuple[pytvpaint.george.client.parse.Value, ...]] + +
+

list of values or tuple of values (args block)

+
+
+ required +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if not all the parameters were given

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ list[typing.Any] + +
+

the list of parameters

+
+
+ +
+ Source code in pytvpaint/george/client/parse.py +
293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
def validate_args_list(optional_args: Sequence[Value | tuple[Value, ...]]) -> list[Any]:
+    """Some George functions only accept a list of values and not key:value pairs, so to set the last positional argument for instance, you need to give all the previous ones.
+
+    This function allows you to give a list of argument or key:value pairs (as tuples) and check that they are not None.
+
+    For example, for `tv_camerainfo [<iWidth> <iHeight> [<field_order>]]`
+    you can't give `[500, None, "upper"]` because `<iHeight>` is not defined.
+
+    Args:
+        optional_args: list of values or tuple of values (args block)
+
+    Raises:
+        ValueError: if not all the parameters were given
+
+    Returns:
+        the list of parameters
+    """
+    args: list[Any] = []
+
+    for arg in optional_args:
+        if arg is None or (isinstance(arg, tuple) and all(a is None for a in arg)):
+            break
+
+        # If it's a tuple they need to be defined
+        if isinstance(arg, tuple):
+            if any(a is None for a in arg):
+                raise ValueError(f"You must pass all the parameters: {arg}")
+            args.extend(arg)
+        else:
+            args.append(arg)
+
+    return args
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/client/rpc/index.html b/api/client/rpc/index.html new file mode 100644 index 0000000..b30ac58 --- /dev/null +++ b/api/client/rpc/index.html @@ -0,0 +1,2371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + JSON-RPC - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

JSON-RPC

+ + +
+ + + + +
+ +

JSON-RPC client and data models.

+ + + +
+ + + + + + + + +
+ + + +

+ JSONRPCPayload + + +

+ + +
+

+ Bases: typing_extensions.TypedDict

+ + +

A rpc call is represented by sending a Request object to a Server.

+

See: https://www.jsonrpc.org/specification#request_object

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ JSONRPCResponse + + +

+ + +
+

+ Bases: typing_extensions.TypedDict

+ + +

When a rpc call is made, the Server MUST reply with a Response.

+

See: https://www.jsonrpc.org/specification#response_object

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ JSONRPCError + + +

+ + +
+

+ Bases: typing_extensions.TypedDict

+ + +

When a rpc call encounters an error.

+

See: https://www.jsonrpc.org/specification#error_object

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ JSONRPCResponseError(error: JSONRPCError) + +

+ + +
+

+ Bases: Exception

+ + +

Exception used when a rpc call encounters an error.

+ +
+ Source code in pytvpaint/george/client/rpc.py +
58
+59
def __init__(self, error: JSONRPCError) -> None:
+    super().__init__(f"JSON-RPC Server error ({error['code']}): {error['message']}")
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ JSONRPCClient(url: str, timeout: int = 60, version: str = '2.0') + +

+ + +
+ + +

Simple JSON-RPC 2.0 client over websockets with automatic reconnection.

+

See: https://www.jsonrpc.org/specification#notification

+ +

Initialize a new JSON-RPC client with a WebSocket url endpoint.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
url + str + +
+

the WebSocket url endpoint

+
+
+ required +
timeout + int + +
+

the reconnection timeout

+
+
+ 60 +
version + str + +
+

The JSON-RPC version. Defaults to "2.0".

+
+
+ '2.0' +
+ +
+ Source code in pytvpaint/george/client/rpc.py +
68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
def __init__(self, url: str, timeout: int = 60, version: str = "2.0") -> None:
+    """Initialize a new JSON-RPC client with a WebSocket url endpoint.
+
+    Args:
+        url: the WebSocket url endpoint
+        timeout: the reconnection timeout
+        version: The JSON-RPC version. Defaults to "2.0".
+    """
+    self.ws_handle = WebSocket()
+    self.url = url
+    self.rpc_id = 0
+    self.timeout = timeout
+    self.jsonrpc_version = version
+
+    self.stop_ping = threading.Event()
+    self.run_forever = False
+    self.ping_thread: threading.Thread | None = None
+    self._ping_start_time: float = 0
+
+
+ + + +
+ + + + + + + +
+ + + +

+ is_connected: bool + + + property + + +

+ + +
+ +

Returns True if the client is connected.

+
+ +
+ + + + +
+ + + +

+ connect(timeout: float | None = None) -> None + +

+ + +
+ +

Connects to the WebSocket endpoint.

+ +
+ Source code in pytvpaint/george/client/rpc.py +
116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
def connect(self, timeout: float | None = None) -> None:
+    """Connects to the WebSocket endpoint."""
+    self.ws_handle.connect(self.url, timeout=timeout)
+
+    if not self.ping_thread:
+        self._ping_start_time = time()
+        self.ping_thread = threading.Thread(
+            target=self._auto_reconnect, daemon=True
+        )
+        self.run_forever = True
+        self.ping_thread.start()
+
+
+
+ +
+ + +
+ + + +

+ disconnect() -> None + +

+ + +
+ +

Disconnects from the server.

+ +
+ Source code in pytvpaint/george/client/rpc.py +
128
+129
+130
+131
+132
+133
+134
def disconnect(self) -> None:
+    """Disconnects from the server."""
+    self.run_forever = False
+    if self.ping_thread:
+        self.ping_thread.join()
+
+    self.ws_handle.close()
+
+
+
+ +
+ + +
+ + + +

+ increment_rpc_id() -> None + +

+ + +
+ +

Increments the internal RPC id until it reaches sys.maxsize.

+ +
+ Source code in pytvpaint/george/client/rpc.py +
136
+137
+138
def increment_rpc_id(self) -> None:
+    """Increments the internal RPC id until it reaches `sys.maxsize`."""
+    self.rpc_id = (self.rpc_id + 1) % sys.maxsize
+
+
+
+ +
+ + +
+ + + +

+ execute_remote(method: str, params: list[JSONValueType] | None = None) -> JSONRPCResponse + +

+ + +
+ +

Executes a remote procedure call.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
method + str + +
+

the name of the method to be invoked

+
+
+ required +
params + list[pytvpaint.george.client.rpc.JSONValueType] | None + +
+

the parameter values to be used during the invocation of the method. Defaults to None.

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+ ConnectionError + +
+

if the client is not connected

+
+
+ pytvpaint.george.client.rpc.JSONRPCResponseError + +
+

if there was an error server-side

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
JSONRPCResponse + pytvpaint.george.client.rpc.JSONRPCResponse + +
+

the JSON-RPC response payload

+
+
+ +
+ Source code in pytvpaint/george/client/rpc.py +
140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
def execute_remote(
+    self,
+    method: str,
+    params: list[JSONValueType] | None = None,
+) -> JSONRPCResponse:
+    """Executes a remote procedure call.
+
+    Args:
+        method: the name of the method to be invoked
+        params: the parameter values to be used during the invocation of the method. Defaults to None.
+
+    Raises:
+        ConnectionError: if the client is not connected
+        JSONRPCResponseError: if there was an error server-side
+
+    Returns:
+        JSONRPCResponse: the JSON-RPC response payload
+    """
+    if not self.is_connected:
+        raise ConnectionError(
+            f"Can't send rpc message because the client is not connected to {self.url}"
+        )
+
+    payload: JSONRPCPayload = {
+        "jsonrpc": self.jsonrpc_version,
+        "id": self.rpc_id,
+        "method": method,
+        "params": params or [],
+    }
+
+    self.ws_handle.send(json.dumps(payload))
+    self.increment_rpc_id()
+
+    result = self.ws_handle.recv()
+    response = cast(JSONRPCResponse, json.loads(result))
+
+    if "error" in response:
+        raise JSONRPCResponseError(response["error"])
+
+    return response
+
+
+
+ +
+ + + +
+ +
+ + +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/george/camera/index.html b/api/george/camera/index.html new file mode 100644 index 0000000..b49bce2 --- /dev/null +++ b/api/george/camera/index.html @@ -0,0 +1,2046 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Camera - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Camera related George functions

+ + +
+ + + + +
+ +

Camera related George functions.

+ + + +
+ + + + + + + + +
+ + + +

+ TVPCamera(width: int, height: int, field_order: FieldOrder, frame_rate: float, pixel_aspect_ratio: float, anti_aliasing: int) + + + dataclass + + +

+ + +
+ + +

TVPaint camera info values.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ TVPCameraPoint(x: float, y: float, angle: float, scale: float) + + + dataclass + + +

+ + +
+ + +

camera 2D point info.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + + +

+ tv_camera_info_get() -> TVPCamera + +

+ + +
+ +

Get the information of the camera.

+ +
+ Source code in pytvpaint/george/grg_camera.py +
37
+38
+39
def tv_camera_info_get() -> TVPCamera:
+    """Get the information of the camera."""
+    return TVPCamera(**tv_parse_list(send_cmd("tv_CameraInfo"), with_fields=TVPCamera))
+
+
+
+ +
+ + +
+ + + +

+ tv_camera_info_set(width: int | None = None, height: int | None = None, field_order: FieldOrder | None = None, frame_rate: float | None = None, pixel_aspect_ratio: float | None = None) -> TVPCamera + +

+ + +
+ +

Set the information of the camera.

+ +
+ Source code in pytvpaint/george/grg_camera.py +
42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
def tv_camera_info_set(
+    width: int | None = None,
+    height: int | None = None,
+    field_order: FieldOrder | None = None,
+    frame_rate: float | None = None,
+    pixel_aspect_ratio: float | None = None,
+) -> TVPCamera:
+    """Set the information of the camera."""
+    optional_args = [
+        (width, height),
+        field_order.value if field_order else None,
+        frame_rate,
+        pixel_aspect_ratio,
+    ]
+
+    args = validate_args_list(optional_args)
+
+    result = send_cmd("tv_CameraInfo", *args)
+    return TVPCamera(**tv_parse_list(result, with_fields=TVPCamera))
+
+
+
+ +
+ + +
+ + + +

+ tv_camera_enum_points(index: int) -> TVPCameraPoint + +

+ + +
+ +

Get the position/angle/scale values of the n-th point of the camera path.

+ +
+ Source code in pytvpaint/george/grg_camera.py +
63
+64
+65
+66
def tv_camera_enum_points(index: int) -> TVPCameraPoint:
+    """Get the position/angle/scale values of the n-th point of the camera path."""
+    res = send_cmd("tv_CameraEnumPoints", index, error_values=[GrgErrorValue.NONE])
+    return TVPCameraPoint(**tv_parse_list(res, with_fields=TVPCameraPoint))
+
+
+
+ +
+ + +
+ + + +

+ tv_camera_interpolation(position: float) -> TVPCameraPoint + +

+ + +
+ +

Get the position/angle/scale values at the given position on the camera path (between 0 and 1).

+ +
+ Source code in pytvpaint/george/grg_camera.py +
69
+70
+71
+72
+73
+74
+75
def tv_camera_interpolation(position: float) -> TVPCameraPoint:
+    """Get the position/angle/scale values at the given position on the camera path (between 0 and 1)."""
+    res = tv_parse_list(
+        send_cmd("tv_CameraInterpolation", position),
+        with_fields=TVPCameraPoint,
+    )
+    return TVPCameraPoint(**res)
+
+
+
+ +
+ + +
+ + + +

+ tv_camera_insert_point(index: int, x: float, y: float, angle: float, scale: float) -> None + +

+ + +
+ +

Add a point to the camera path before the given index.

+ +
+ Source code in pytvpaint/george/grg_camera.py +
78
+79
+80
+81
+82
+83
+84
+85
+86
def tv_camera_insert_point(
+    index: int,
+    x: float,
+    y: float,
+    angle: float,
+    scale: float,
+) -> None:
+    """Add a point to the camera path *before* the given index."""
+    send_cmd("tv_CameraInsertPoint", index, x, y, angle, scale)
+
+
+
+ +
+ + +
+ + + +

+ tv_camera_remove_point(index: int) -> None + +

+ + +
+ +

Remove a point at the given index.

+ +
+ Source code in pytvpaint/george/grg_camera.py +
89
+90
+91
def tv_camera_remove_point(index: int) -> None:
+    """Remove a point at the given index."""
+    send_cmd("tv_CameraRemovePoint", index)
+
+
+
+ +
+ + +
+ + + +

+ tv_camera_set_point(index: int, x: float, y: float, angle: float, scale: float) -> None + +

+ + +
+ +

Set position/angle/scale value of a point at the given index and make it current.

+ +
+ Source code in pytvpaint/george/grg_camera.py +
 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
def tv_camera_set_point(
+    index: int,
+    x: float,
+    y: float,
+    angle: float,
+    scale: float,
+) -> None:
+    """Set position/angle/scale value of a point at the given index and make it current."""
+    send_cmd("tv_CameraSetPoint", index, x, y, angle, scale)
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/george/clip/index.html b/api/george/clip/index.html new file mode 100644 index 0000000..ea59ecb --- /dev/null +++ b/api/george/clip/index.html @@ -0,0 +1,5504 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Clip - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Clip related George functions

+ + +
+ + + + +
+ +

Clip related George functions.

+ + + +
+ + + + + + + + +
+ + + +

+ TVPClip(id: int, name: str, is_current: bool, is_hidden: bool, is_selected: bool, storyboard_start_frame: int, first_frame: int, last_frame: int, frame_count: int, mark_in: int, mark_out: int, color_idx: int) + + + dataclass + + +

+ + +
+ + +

TVPaint clip info values.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ PSDSaveMode + + +

+ + +
+

+ Bases: enum.Enum

+ + +

PSD save modes.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ALL + +
+ +
+
IMAGE + +
+ +
+
MARKIN + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + + +

+ tv_clip_info(clip_id: int) -> TVPClip + +

+ + +
+ +

Get the information of the given clip.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid clip id

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid clip id",
+)
+def tv_clip_info(clip_id: int) -> TVPClip:
+    """Get the information of the given clip.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid clip id
+    """
+    result = send_cmd("tv_ClipInfo", clip_id, error_values=[GrgErrorValue.EMPTY])
+    clip = tv_parse_dict(result, with_fields=TVPClip)
+    clip["id"] = clip_id
+    return TVPClip(**clip)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_enum_id(scene_id: int, clip_position: int) -> int + +

+ + +
+ +

Get the id of the clip at the given position inside the given scene.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if given an invalid scene id or clip position or elements have been removed

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
@try_cmd(
+    exception_msg="Invalid scene id or clip position or elements have been removed"
+)
+def tv_clip_enum_id(scene_id: int, clip_position: int) -> int:
+    """Get the id of the clip at the given position inside the given scene.
+
+    Raises:
+        GeorgeError: if given an invalid scene id or clip position or elements have been removed
+    """
+    return int(
+        send_cmd(
+            "tv_ClipEnumId",
+            scene_id,
+            clip_position,
+            error_values=[GrgErrorValue.NONE],
+        )
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_current_id() -> int + +

+ + +
+ +

Get the id of the current clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
94
+95
+96
def tv_clip_current_id() -> int:
+    """Get the id of the current clip."""
+    return int(send_cmd("tv_ClipCurrentId"))
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_new(name: str) -> None + +

+ + +
+ +

Create a new clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
 99
+100
+101
def tv_clip_new(name: str) -> None:
+    """Create a new clip."""
+    send_cmd("tv_ClipNew", name, handle_string=False)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_duplicate(clip_id: int) -> None + +

+ + +
+ +

Duplicate the given clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
104
+105
+106
def tv_clip_duplicate(clip_id: int) -> None:
+    """Duplicate the given clip."""
+    send_cmd("tv_ClipDuplicate", clip_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_close(clip_id: int) -> None + +

+ + +
+ +

Remove the given clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
109
+110
+111
def tv_clip_close(clip_id: int) -> None:
+    """Remove the given clip."""
+    send_cmd("tv_ClipClose", clip_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_name_get(clip_id: int) -> str + +

+ + +
+ +

Get the clip name.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid clip id

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid clip id",
+)
+def tv_clip_name_get(clip_id: int) -> str:
+    """Get the clip name.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid clip id
+    """
+    return send_cmd("tv_ClipName", clip_id, error_values=[GrgErrorValue.EMPTY])
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_name_set(clip_id: int, name: str) -> None + +

+ + +
+ +

Set the clip name.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid clip id

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid clip id",
+)
+def tv_clip_name_set(clip_id: int, name: str) -> None:
+    """Set the clip name.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid clip id
+    """
+    send_cmd("tv_ClipName", clip_id, name, error_values=[GrgErrorValue.EMPTY])
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_move(clip_id: int, scene_id: int, position: int) -> None + +

+ + +
+ +

Manage clip position.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
140
+141
+142
def tv_clip_move(clip_id: int, scene_id: int, position: int) -> None:
+    """Manage clip position."""
+    send_cmd("tv_ClipMove", clip_id, scene_id, position)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_hidden_get(clip_id: int) -> bool + +

+ + +
+ +

Get clip visibility.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid clip id

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid clip id",
+)
+def tv_clip_hidden_get(clip_id: int) -> bool:
+    """Get clip visibility.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid clip id
+    """
+    res = send_cmd("tv_ClipHidden", clip_id, error_values=[GrgErrorValue.EMPTY])
+    return bool(int(res))
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_hidden_set(clip_id: int, new_state: bool) -> None + +

+ + +
+ +

Set clip visibility.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid clip id

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid clip id",
+)
+def tv_clip_hidden_set(clip_id: int, new_state: bool) -> None:
+    """Set clip visibility.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid clip id
+    """
+    send_cmd(
+        "tv_ClipHidden", clip_id, int(new_state), error_values=[GrgErrorValue.EMPTY]
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_select(clip_id: int) -> None + +

+ + +
+ +

Activate/Make current the given clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
174
+175
+176
def tv_clip_select(clip_id: int) -> None:
+    """Activate/Make current the given clip."""
+    send_cmd("tv_ClipSelect", clip_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_selection_get(clip_id: int) -> bool + +

+ + +
+ +

Get the clip's selection state.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid clip id

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid clip id",
+)
+def tv_clip_selection_get(clip_id: int) -> bool:
+    """Get the clip's selection state.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid clip id
+    """
+    res = send_cmd("tv_ClipSelection", clip_id, error_values=[-1])
+    return bool(int(res))
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_selection_set(clip_id: int, new_state: bool) -> None + +

+ + +
+ +

Set the clip's selection state.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid clip id

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid clip id",
+)
+def tv_clip_selection_set(clip_id: int, new_state: bool) -> None:
+    """Set the clip's selection state.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid clip id
+    """
+    send_cmd("tv_ClipSelection", clip_id, int(new_state), error_values=[-1])
+
+
+
+ +
+ + +
+ + + +

+ tv_first_image() -> int + +

+ + +
+ +

Get the first image of the clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
206
+207
+208
def tv_first_image() -> int:
+    """Get the first image of the clip."""
+    return int(send_cmd("tv_FirstImage"))
+
+
+
+ +
+ + +
+ + + +

+ tv_last_image() -> int + +

+ + +
+ +

Get the last image of the clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
211
+212
+213
def tv_last_image() -> int:
+    """Get the last image of the clip."""
+    return int(send_cmd("tv_LastImage"))
+
+
+
+ +
+ + +
+ + + +

+ tv_load_sequence(seq_path: Path | str, offset_count: tuple[int, int] | None = None, field_order: FieldOrder | None = None, stretch: bool = False, time_stretch: bool = False, preload: bool = False) -> int + +

+ + +
+ +

Load a sequence of images or movie in a new layer.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
seq_path + pathlib.Path | str + +
+

the first file of the sequence to load

+
+
+ required +
offset_count + tuple[int, int] | None + +
+

the start and number of images in the sequence to load. Defaults to None.

+
+
+ None +
field_order + pytvpaint.george.grg_base.FieldOrder | None + +
+

the field order. Defaults to None.

+
+
+ None +
stretch + bool + +
+

Stretch each image to the size of the layer. Defaults to None.

+
+
+ False +
time_stretch + bool + +
+

Once loaded, the layer will have a new number of images corresponding to the project framerate. Defaults to None.

+
+
+ False +
preload + bool + +
+

Load all the images in memory, no more reference on the files. Defaults to None.

+
+
+ False +
+ + + +

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+ FileNotFoundError + +
+

if the sequence file doesn't exist

+
+
+ pytvpaint.george.exceptions.GeorgeError + +
+

if the input file is in an invalid format

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

the number of images of the new layer

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
@try_cmd(exception_msg="Invalid format for sequence")
+def tv_load_sequence(
+    seq_path: Path | str,
+    offset_count: tuple[int, int] | None = None,
+    field_order: FieldOrder | None = None,
+    stretch: bool = False,
+    time_stretch: bool = False,
+    preload: bool = False,
+) -> int:
+    """Load a sequence of images or movie in a new layer.
+
+    Args:
+        seq_path: the first file of the sequence to load
+        offset_count: the start and number of images in the sequence to load. Defaults to None.
+        field_order: the field order. Defaults to None.
+        stretch: Stretch each image to the size of the layer. Defaults to None.
+        time_stretch: Once loaded, the layer will have a new number of images corresponding to the project framerate. Defaults to None.
+        preload: Load all the images in memory, no more reference on the files. Defaults to None.
+
+    Raises:
+        FileNotFoundError: if the sequence file doesn't exist
+        GeorgeError: if the input file is in an invalid format
+
+    Returns:
+        the number of images of the new layer
+    """
+    seq_path = Path(seq_path)
+
+    if not seq_path.exists():
+        raise FileNotFoundError(f"File not found at: {seq_path.as_posix()}")
+
+    args: list[int | str] = [seq_path.as_posix()]
+    if offset_count and len(offset_count) == 2:
+        args.extend(offset_count)
+    if field_order:
+        args.append(field_order.value)
+
+    extra_args = [
+        (stretch, "stretch"),
+        (time_stretch, "timestretch"),
+        (preload, "preload"),
+    ]
+    for param, param_name in extra_args:
+        if not param:
+            continue
+        args.append(param_name)
+
+    result = send_cmd(
+        "tv_LoadSequence",
+        *args,
+        error_values=[-1],
+    )
+
+    return int(result)
+
+
+
+ +
+ + +
+ + + +

+ tv_save_sequence(export_path: Path | str, mark_in: int | None = None, mark_out: int | None = None) -> None + +

+ + +
+ +

Save the current clip.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ NotADirectoryError + +
+

if the export directory doesn't exist

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
def tv_save_sequence(
+    export_path: Path | str,
+    mark_in: int | None = None,
+    mark_out: int | None = None,
+) -> None:
+    """Save the current clip.
+
+    Raises:
+        NotADirectoryError: if the export directory doesn't exist
+    """
+    export_path = Path(export_path).resolve()
+
+    if not export_path.parent.exists():
+        raise NotADirectoryError(
+            "Can't save the sequence because parent"
+            f"folder does not exist: {export_path.parent.as_posix()}"
+        )
+
+    args: list[Any] = [export_path.as_posix()]
+
+    if mark_in is not None and mark_out is not None:
+        args.extend([mark_in, mark_out])
+
+    send_cmd("tv_SaveSequence", *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_bookmarks_enum(position: int) -> int + +

+ + +
+ +

Get the frame (in the clip) corresponding to the bookmark at the given position.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if no bookmark found at provided position

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
298
+299
+300
+301
+302
+303
+304
+305
+306
+307
@try_cmd(exception_msg="No bookmark at provided position")
+def tv_bookmarks_enum(position: int) -> int:
+    """Get the frame (in the clip) corresponding to the bookmark at the given position.
+
+    Raises:
+        GeorgeError: if no bookmark found at provided position
+    """
+    return int(
+        send_cmd("tv_BookmarksEnum", position, error_values=[GrgErrorValue.NONE])
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_bookmark_set(frame: int) -> None + +

+ + +
+ +

Set a bookmark at the given frame.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
310
+311
+312
def tv_bookmark_set(frame: int) -> None:
+    """Set a bookmark at the given frame."""
+    send_cmd("tv_BookmarkSet", frame)
+
+
+
+ +
+ + +
+ + + +

+ tv_bookmark_clear(frame: int) -> None + +

+ + +
+ +

Remove a bookmark at the given frame.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
315
+316
+317
def tv_bookmark_clear(frame: int) -> None:
+    """Remove a bookmark at the given frame."""
+    send_cmd("tv_BookmarkClear", frame)
+
+
+
+ +
+ + +
+ + + +

+ tv_bookmark_next() -> None + +

+ + +
+ +

Go to the next bookmarked frame.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
320
+321
+322
def tv_bookmark_next() -> None:
+    """Go to the next bookmarked frame."""
+    send_cmd("tv_BookmarkNext")
+
+
+
+ +
+ + +
+ + + +

+ tv_bookmark_prev() -> None + +

+ + +
+ +

Go to the previous bookmarked frame.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
325
+326
+327
def tv_bookmark_prev() -> None:
+    """Go to the previous bookmarked frame."""
+    send_cmd("tv_BookmarkPrev")
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_color_get(clip_id: int) -> int + +

+ + +
+ +

Get the clip color.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
330
+331
+332
def tv_clip_color_get(clip_id: int) -> int:
+    """Get the clip color."""
+    return int(send_cmd("tv_ClipColor", clip_id, error_values=[GrgErrorValue.EMPTY]))
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_color_set(clip_id: int, color_index: int) -> None + +

+ + +
+ +

Set the clip color.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
335
+336
+337
def tv_clip_color_set(clip_id: int, color_index: int) -> None:
+    """Set the clip color."""
+    send_cmd("tv_ClipColor", clip_id, color_index, error_values=[GrgErrorValue.EMPTY])
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_action_get(clip_id: int) -> str + +

+ + +
+ +

Get the action text of the clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
340
+341
+342
+343
+344
+345
def tv_clip_action_get(clip_id: int) -> str:
+    """Get the action text of the clip."""
+    # We explicitly check if the clip exists because the error value is an empty string, and we can't determine if the
+    # action text is empty or the clip_id is invalid...
+    tv_clip_name_get(clip_id)
+    return send_cmd("tv_ClipAction", clip_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_action_set(clip_id: int, text: str) -> None + +

+ + +
+ +

Set the action text of the clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
348
+349
+350
+351
+352
def tv_clip_action_set(clip_id: int, text: str) -> None:
+    """Set the action text of the clip."""
+    # See tv_clip_action_get above
+    tv_clip_name_get(clip_id)
+    send_cmd("tv_ClipAction", clip_id, text)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_dialog_get(clip_id: int) -> str + +

+ + +
+ +

Get the dialog text of the clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
355
+356
+357
+358
+359
def tv_clip_dialog_get(clip_id: int) -> str:
+    """Get the dialog text of the clip."""
+    # See tv_clip_action_get above
+    tv_clip_name_get(clip_id)
+    return send_cmd("tv_ClipDialog", clip_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_dialog_set(clip_id: int, dialog: str) -> None + +

+ + +
+ +

Set the dialog text of the clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
362
+363
+364
+365
+366
def tv_clip_dialog_set(clip_id: int, dialog: str) -> None:
+    """Set the dialog text of the clip."""
+    # See tv_clip_action_get above
+    tv_clip_name_get(clip_id)
+    send_cmd("tv_ClipDialog", clip_id, dialog)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_note_get(clip_id: int) -> str + +

+ + +
+ +

Get the note text of the clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
369
+370
+371
+372
+373
def tv_clip_note_get(clip_id: int) -> str:
+    """Get the note text of the clip."""
+    # See tv_clip_action_get above
+    tv_clip_name_get(clip_id)
+    return send_cmd("tv_ClipNote", clip_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_note_set(clip_id: int, note: str) -> None + +

+ + +
+ +

Set the note text of the clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
376
+377
+378
+379
+380
def tv_clip_note_set(clip_id: int, note: str) -> None:
+    """Set the note text of the clip."""
+    # See tv_clip_action_get above
+    tv_clip_name_get(clip_id)
+    send_cmd("tv_ClipNote", clip_id, note)
+
+
+
+ +
+ + +
+ + + +

+ tv_save_clip(export_path: Path | str) -> None + +

+ + +
+ +

Save the current clip in .tvp format.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if file couldn't be saved

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
383
+384
+385
+386
+387
+388
+389
+390
+391
@try_cmd(exception_msg="Can't create file")
+def tv_save_clip(export_path: Path | str) -> None:
+    """Save the current clip in .tvp format.
+
+    Raises:
+        GeorgeError: if file couldn't be saved
+    """
+    export_path = Path(export_path)
+    send_cmd("tv_SaveClip", export_path.as_posix())
+
+
+
+ +
+ + +
+ + + +

+ tv_save_display(export_path: Path | str) -> None + +

+ + +
+ +

Save the display.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
394
+395
+396
+397
def tv_save_display(export_path: Path | str) -> None:
+    """Save the display."""
+    export_path = Path(export_path).resolve()
+    send_cmd("tv_SaveDisplay", export_path.as_posix())
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_save_structure_json(export_path: Path | str, file_format: SaveFormat, fill_background: bool = False, folder_pattern: str | None = None, file_pattern: str | None = None, visible_layers_only: bool = True, all_images: bool = False, ignore_duplicates: bool = False, exclude_names: list[str] | None = None) -> None + +

+ + +
+ +

Save the current clip structure in json.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

the JSON export path

+
+
+ required +
file_format + pytvpaint.george.grg_base.SaveFormat + +
+

file format to use for rendering

+
+
+ required +
fill_background + bool + +
+

add a background. Defaults to None.

+
+
+ False +
folder_pattern + str | None + +
+

the folder name pattern (%li: layer index, %ln: layer name, %fi: file index (added in 11.0.8)). Defaults to None.

+
+
+ None +
file_pattern + str | None + +
+

the file name pattern (%li: layer index, %ln: layer name, %ii: image index, %in: image name, %fi: file index (added in 11.0.8)). Defaults to None.

+
+
+ None +
visible_layers_only + bool + +
+

export only visible layers. Defaults to None.

+
+
+ True +
all_images + bool + +
+

export all images. Defaults to None.

+
+
+ False +
ignore_duplicates + bool + +
+

Ignore duplicates images. Defaults to None.

+
+
+ False +
exclude_names + list[str] | None + +
+

the instances names which won't be processed/exported. Defaults to None.

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

the parent folder doesn't exist

+
+
+ +
+ Source code in pytvpaint/george/grg_clip.py +
400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
def tv_clip_save_structure_json(
+    export_path: Path | str,
+    file_format: SaveFormat,
+    fill_background: bool = False,
+    folder_pattern: str | None = None,
+    file_pattern: str | None = None,
+    visible_layers_only: bool = True,
+    all_images: bool = False,
+    ignore_duplicates: bool = False,
+    exclude_names: list[str] | None = None,
+) -> None:
+    """Save the current clip structure in json.
+
+    Args:
+        export_path: the JSON export path
+        file_format: file format to use for rendering
+        fill_background: add a background. Defaults to None.
+        folder_pattern: the folder name pattern (%li: layer index, %ln: layer name, %fi: file index (added in 11.0.8)). Defaults to None.
+        file_pattern: the file name pattern (%li: layer index, %ln: layer name, %ii: image index, %in: image name, %fi: file index (added in 11.0.8)). Defaults to None.
+        visible_layers_only: export only visible layers. Defaults to None.
+        all_images: export all images. Defaults to None.
+        ignore_duplicates: Ignore duplicates images. Defaults to None.
+        exclude_names: the instances names which won't be processed/exported. Defaults to None.
+
+    Raises:
+        ValueError: the parent folder doesn't exist
+    """
+    export_path = Path(export_path).resolve()
+
+    if not export_path.parent.exists():
+        raise ValueError(
+            "Can't write file because the destination folder doesn't exist"
+        )
+
+    args = [export_path.as_posix(), "JSON"]
+
+    dict_args = {
+        "fileformat": file_format.value,
+        "background": int(fill_background) if fill_background else None,
+        "patternfolder": folder_pattern,
+        "patternfile": file_pattern,
+        "onlyvisiblelayers": int(visible_layers_only),
+        "allimages": int(all_images),
+        "ignoreduplicateimages": int(ignore_duplicates),
+        "excludenames": (";".join(exclude_names) if exclude_names else None),
+    }
+    args.extend(args_dict_to_list(dict_args))
+
+    send_cmd("tv_ClipSaveStructure", *args, error_values=[-1])
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_save_structure_psd(export_path: Path | str, mode: PSDSaveMode, image: int | None = None, mark_in: int | None = None, mark_out: int | None = None) -> None + +

+ + +
+ +

Save the current clip as a PSD.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

description

+
+
+ required +
mode + pytvpaint.george.grg_clip.PSDSaveMode + +
+

all will export all layers to a single PSD

+
+
+ required +
image + int | None + +
+

will only export the given image. Defaults to None.

+
+
+ None +
mark_in + int | None + +
+

start frame to render. Defaults to None.

+
+
+ None +
mark_out + int | None + +
+

end frame to render. Defaults to None.

+
+
+ None +
+ +
+ Source code in pytvpaint/george/grg_clip.py +
451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
def tv_clip_save_structure_psd(
+    export_path: Path | str,
+    mode: PSDSaveMode,
+    image: int | None = None,
+    mark_in: int | None = None,
+    mark_out: int | None = None,
+) -> None:
+    """Save the current clip as a PSD.
+
+    Args:
+        export_path: _description_
+        mode: all will export all layers to a single PSD
+        image: will only export the given image. Defaults to None.
+        mark_in: start frame to render. Defaults to None.
+        mark_out: end frame to render. Defaults to None.
+    """
+    export_path = Path(export_path)
+
+    if not export_path.parent.exists():
+        raise ValueError(
+            "Can't write file because the destination folder doesn't exist"
+        )
+
+    args_dict: dict[str, str | int | None]
+
+    if mode == PSDSaveMode.ALL:
+        args_dict = {"mode": "all"}
+    elif mode == PSDSaveMode.IMAGE:
+        if image is None:
+            raise ValueError("Image must be defined")
+        args_dict = {"image": image}
+    else:  # Markin
+        if mark_in is None or mark_out is None:
+            raise ValueError("mark_in and mark_out must be defined")
+        args_dict = {"markin": mark_in, "markout": mark_out}
+
+    args = args_dict_to_list(args_dict)
+
+    send_cmd(
+        "tv_ClipSaveStructure",
+        export_path.as_posix(),
+        "PSD",
+        *args,
+        error_values=[-1],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_save_structure_csv(export_path: Path | str, all_images: bool | None = None, exposure_label: str | None = None) -> None + +

+ + +
+ +

Save the current clip as a CSV.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

the .csv export path

+
+
+ required +
all_images + bool | None + +
+

export all images or only instances. Defaults to None.

+
+
+ None +
exposure_label + str | None + +
+

give a label when the image is an exposure. Defaults to None.

+
+
+ None +
+ +
+ Source code in pytvpaint/george/grg_clip.py +
498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
def tv_clip_save_structure_csv(
+    export_path: Path | str,
+    all_images: bool | None = None,
+    exposure_label: str | None = None,
+) -> None:
+    """Save the current clip as a CSV.
+
+    Args:
+        export_path: the .csv export path
+        all_images: export all images or only instances. Defaults to None.
+        exposure_label: give a label when the image is an exposure. Defaults to None.
+    """
+    export_path = Path(export_path)
+
+    args = args_dict_to_list(
+        {
+            "allimages": int(bool(all_images)),
+            "exposurelabel": exposure_label,
+        }
+    )
+
+    send_cmd(
+        "tv_ClipSaveStructure",
+        export_path.as_posix(),
+        "CSV",
+        *args,
+        error_values=[-1],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_save_structure_sprite(export_path: Path | str, layout: SpriteLayout | None = None, space: int | None = None) -> None + +

+ + +
+ +

Save the current clip as sprites in one image.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

the export path of the sprite image

+
+
+ required +
layout + pytvpaint.george.grg_base.SpriteLayout | None + +
+

the sprite layout. Defaults to None.

+
+
+ None +
space + int | None + +
+

the space between each sprite in the image. Defaults to None.

+
+
+ None +
+ +
+ Source code in pytvpaint/george/grg_clip.py +
528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
def tv_clip_save_structure_sprite(
+    export_path: Path | str,
+    layout: SpriteLayout | None = None,
+    space: int | None = None,
+) -> None:
+    """Save the current clip as sprites in one image.
+
+    Args:
+        export_path: the export path of the sprite image
+        layout: the sprite layout. Defaults to None.
+        space: the space between each sprite in the image. Defaults to None.
+    """
+    export_path = Path(export_path)
+
+    args = args_dict_to_list(
+        {
+            "layout": layout.value if layout is not None else None,
+            "space": space,
+        }
+    )
+
+    send_cmd(
+        "tv_ClipSaveStructure",
+        export_path.as_posix(),
+        "sprite",
+        *args,
+        error_values=[-1],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_clip_save_structure_flix(export_path: Path | str, mark_in: int | None = None, mark_out: int | None = None, parameters_import: str | None = None, parameters_file: str | None = None, send: bool | None = None, original_file: str | Path | None = None) -> None + +

+ + +
+ +

Save the current clip for Flix.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

the .xml export path

+
+
+ required +
mark_in + int | None + +
+

the start frame to render. Defaults to None.

+
+
+ None +
mark_out + int | None + +
+

the end frame to render. Defaults to None.

+
+
+ None +
parameters_import + str | None + +
+

the attribute(s) of the global tag (waitForSource/...). Defaults to None.

+
+
+ None +
parameters_file + str | None + +
+

the attribute(s) of each (file) tag (dialogue/...). Defaults to None.

+
+
+ None +
send + bool | None + +
+

open a browser with the prefilled url. Defaults to None.

+
+
+ None +
original_file + str | pathlib.Path | None + +
+

the original reference tvpp file path. Defaults to None.

+
+
+ None +
+ +
+ Source code in pytvpaint/george/grg_clip.py +
558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
def tv_clip_save_structure_flix(
+    export_path: Path | str,
+    mark_in: int | None = None,
+    mark_out: int | None = None,
+    parameters_import: str | None = None,
+    parameters_file: str | None = None,
+    send: bool | None = None,
+    original_file: str | Path | None = None,
+) -> None:
+    """Save the current clip for Flix.
+
+    Args:
+        export_path: the .xml export path
+        mark_in: the start frame to render. Defaults to None.
+        mark_out: the end frame to render. Defaults to None.
+        parameters_import: the attribute(s) of the global <flixImport> tag (waitForSource/...). Defaults to None.
+        parameters_file: the attribute(s) of each <image> (file) tag (dialogue/...). Defaults to None.
+        send: open a browser with the prefilled url. Defaults to None.
+        original_file: the original reference tvpp file path. Defaults to None.
+    """
+    export_path = Path(export_path)
+
+    args_dict = {
+        "markin": mark_in,
+        "markout": mark_out,
+        "parametersimport": parameters_import,
+        "parametersfile": parameters_file,
+        "send": int(send) if send else None,
+        "originalfile": Path(original_file).as_posix() if original_file else None,
+    }
+
+    args = args_dict_to_list(args_dict)
+
+    send_cmd(
+        "tv_ClipSaveStructure",
+        export_path.as_posix(),
+        "Flix",
+        *args,
+        error_values=[-1],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_sound_clip_info(clip_id: int, track_index: int) -> TVPSound + +

+ + +
+ +

Get information about a soundtrack.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
600
+601
+602
+603
+604
def tv_sound_clip_info(clip_id: int, track_index: int) -> TVPSound:
+    """Get information about a soundtrack."""
+    res = send_cmd("tv_SoundClipInfo", clip_id, track_index, error_values=[-1, -2, -3])
+    res_parse = tv_parse_list(res, with_fields=TVPSound)
+    return TVPSound(**res_parse)
+
+
+
+ +
+ + +
+ + + +

+ tv_sound_clip_new(sound_path: Path | str) -> None + +

+ + +
+ +

Add a new soundtrack.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
607
+608
+609
+610
+611
+612
def tv_sound_clip_new(sound_path: Path | str) -> None:
+    """Add a new soundtrack."""
+    path = Path(sound_path)
+    if not path.exists():
+        raise ValueError(f"Sound file not found at : {path.as_posix()}")
+    send_cmd("tv_SoundClipNew", path.as_posix(), error_values=[-1, -2, -3, -4])
+
+
+
+ +
+ + +
+ + + +

+ tv_sound_clip_remove(track_index: int) -> None + +

+ + +
+ +

Remove a soundtrack.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
615
+616
+617
def tv_sound_clip_remove(track_index: int) -> None:
+    """Remove a soundtrack."""
+    send_cmd("tv_SoundClipRemove", track_index, error_values=[-2])
+
+
+
+ +
+ + +
+ + + +

+ tv_sound_clip_reload(clip_id: int, track_index: int) -> None + +

+ + +
+ +

Reload a soundtrack from its file.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
clip_id + int + +
+

the clip id (only works with 0 being the current clip)

+
+
+ required +
track_index + int + +
+

the sound clip track index

+
+
+ required +
+ +
+ Warning +

this doesn't accept a proper clip id, only 0 seem to work for the current clip

+
+
+ Source code in pytvpaint/george/grg_clip.py +
620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
def tv_sound_clip_reload(clip_id: int, track_index: int) -> None:
+    """Reload a soundtrack from its file.
+
+    Args:
+        clip_id: the clip id (only works with `0` being the current clip)
+        track_index: the sound clip track index
+
+    Warning:
+        this doesn't accept a proper clip id, only `0` seem to work for the current clip
+    """
+    send_cmd("tv_SoundClipReload", clip_id, track_index, error_values=[-1, -2, -3])
+
+
+
+ +
+ + +
+ + + +

+ tv_sound_clip_adjust(track_index: int, mute: bool | None = None, volume: float | None = None, offset: float | None = None, fade_in_start: float | None = None, fade_in_stop: float | None = None, fade_out_start: float | None = None, fade_out_stop: float | None = None, color_index: int | None = None) -> None + +

+ + +
+ +

Change a soundtracks settings.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
def tv_sound_clip_adjust(
+    track_index: int,
+    mute: bool | None = None,
+    volume: float | None = None,
+    offset: float | None = None,
+    fade_in_start: float | None = None,
+    fade_in_stop: float | None = None,
+    fade_out_start: float | None = None,
+    fade_out_stop: float | None = None,
+    color_index: int | None = None,
+) -> None:
+    """Change a soundtracks settings."""
+    cur_options = tv_sound_clip_info(tv_clip_current_id(), track_index)
+    args: list[int | float | None] = []
+
+    optional_args = [
+        (int(mute) if mute is not None else None, int(cur_options.mute)),
+        (volume, cur_options.volume),
+        (offset, cur_options.offset),
+        (fade_in_start, cur_options.fade_in_start),
+        (fade_in_stop, cur_options.fade_in_stop),
+        (fade_out_start, cur_options.fade_out_start),
+        (fade_out_stop, cur_options.fade_out_stop),
+    ]
+    for arg, default_value in optional_args:
+        args.append(arg if arg is not None else default_value)
+
+    args.append(color_index)
+    send_cmd("tv_SoundClipAdjust", track_index, *args, error_values=[-2, -3])
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_image_get() -> int + +

+ + +
+ +

Get the current frame of the current clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
664
+665
+666
def tv_layer_image_get() -> int:
+    """Get the current frame of the current clip."""
+    return int(send_cmd("tv_LayerGetImage"))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_image(frame: int) -> None + +

+ + +
+ +

Set the current frame of the current clip.

+ +
+ Source code in pytvpaint/george/grg_clip.py +
669
+670
+671
def tv_layer_image(frame: int) -> None:
+    """Set the current frame of the current clip."""
+    send_cmd("tv_LayerImage", frame)
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/george/exceptions/index.html b/api/george/exceptions/index.html new file mode 100644 index 0000000..fa3731c --- /dev/null +++ b/api/george/exceptions/index.html @@ -0,0 +1,1675 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Exceptions - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

George Exceptions

+ + +
+ + + + +
+ +

Custom George exceptions.

+ + + +
+ + + + + + + + +
+ + + +

+ GeorgeError(message: str | None = None, error_value: Any | None = None) + +

+ + +
+

+ Bases: Exception

+ + +

George error exception.

+

Used for return values in the [ERROR] section of functions in TVPaint's documentation.

+ +
+ Source code in pytvpaint/george/exceptions.py +
14
+15
+16
+17
+18
def __init__(
+    self, message: str | None = None, error_value: Any | None = None
+) -> None:
+    super().__init__(f"{message}" if message else "")
+    self.error_value = error_value
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ NoObjectWithIdError(obj_id: int) + +

+ + +
+

+ Bases: pytvpaint.george.exceptions.GeorgeError

+ + +

Exception raised when a TVPaint was not found given its id.

+ +
+ Source code in pytvpaint/george/exceptions.py +
24
+25
def __init__(self, obj_id: int) -> None:
+    super().__init__(f"Can't find an object with id: {obj_id}")
+
+
+ + + +
+ + + + + + + + + + + +
+ +
+ + +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/george/layer/index.html b/api/george/layer/index.html new file mode 100644 index 0000000..75302dc --- /dev/null +++ b/api/george/layer/index.html @@ -0,0 +1,9543 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Layer - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Layer related George functions

+ + +
+ + + + +
+ +

Layer related George functions and enums.

+ + + +
+ + + + + + + + +
+ + + +

+ LayerColorAction + + +

+ + +
+

+ Bases: enum.Enum

+ + +

tv_layercolor actions.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
GETCOLOR + +
+ +
+
SETCOLOR + +
+ +
+
GET + +
+ +
+
SET + +
+ +
+
LOCK + +
+ +
+
UNLOCK + +
+ +
+
SHOW + +
+ +
+
HIDE + +
+ +
+
VISIBLE + +
+ +
+
SELECT + +
+ +
+
UNSELECT + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ LayerColorDisplayOpt + + +

+ + +
+

+ Bases: enum.Enum

+ + +

tv_layercolorshow display options.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
DISPLAY + +
+

Activate the layers to show them in the display

+
+
TIMELINE + +
+

Uncollpase layers from maximum collapse (2px height) in the timeline

+
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ InstanceNamingMode + + +

+ + +
+

+ Bases: enum.Enum

+ + +

tv_instancename naming modes.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ALL + +
+ +
+
SMART + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ InstanceNamingProcess + + +

+ + +
+

+ Bases: enum.Enum

+ + +

tv_instancename naming process.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
EMPTY + +
+ +
+
NUMBER + +
+ +
+
TEXT + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ LayerType + + +

+ + +
+

+ Bases: enum.Enum

+ + +

All the layer types.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
IMAGE + +
+ +
+
SEQUENCE + +
+ +
+
XSHEET + +
+ +
+
SCRIBBLES + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ StencilMode + + +

+ + +
+

+ Bases: enum.Enum

+ + +

All the stencil modes.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ON + +
+ +
+
OFF + +
+ +
+
NORMAL + +
+ +
+
INVERT + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ LayerBehavior + + +

+ + +
+

+ Bases: enum.Enum

+ + +

Layer behaviors on boundaries.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
NONE + +
+ +
+
REPEAT + +
+ +
+
PINGPONG + +
+ +
+
HOLD + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ LayerTransparency + + +

+ + +
+

+ Bases: enum.Enum

+ + +

Layer transparency values.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ON + +
+ +
+
OFF + +
+ +
+
MINUS_1 + +
+ +
+
NONE + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ InsertDirection + + +

+ + +
+

+ Bases: enum.Enum

+ + +

Instance insert direction.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
BEFORE + +
+ +
+
AFTER + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ TVPClipLayerColor(clip_id: int, color_index: int, color_r: int, color_g: int, color_b: int, name: str) + + + dataclass + + +

+ + +
+ + +

Clip layer color values.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ TVPLayer(id: int, visibility: bool, position: int, density: int, name: str, type: LayerType, first_frame: int, last_frame: int, selected: bool, editable: bool, stencil_state: StencilMode) + + + dataclass + + +

+ + +
+ + +

TVPaint layer info values.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + + +

+ tv_layer_current_id() -> int + +

+ + +
+ +

Get the id of the current layer.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
198
+199
+200
def tv_layer_current_id() -> int:
+    """Get the id of the current layer."""
+    return int(send_cmd("tv_LayerCurrentId"))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_get_id(position: int) -> int + +

+ + +
+ +

Get the id of the layer at the given position.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if no layer found at the provided position

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
203
+204
+205
+206
+207
+208
+209
+210
+211
@try_cmd(exception_msg="No layer at provided position")
+def tv_layer_get_id(position: int) -> int:
+    """Get the id of the layer at the given position.
+
+    Raises:
+        GeorgeError: if no layer found at the provided position
+    """
+    result = send_cmd("tv_LayerGetID", position, error_values=[GrgErrorValue.NONE])
+    return int(result)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_get_pos(layer_id: int) -> int + +

+ + +
+ +

Get the position of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_get_pos(layer_id: int) -> int:
+    """Get the position of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    return int(send_cmd("tv_LayerGetPos", layer_id, error_values=[GrgErrorValue.NONE]))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_info(layer_id: int) -> TVPLayer + +

+ + +
+ +

Get information of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_info(layer_id: int) -> TVPLayer:
+    """Get information of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    result = send_cmd("tv_LayerInfo", layer_id, error_values=[GrgErrorValue.EMPTY])
+    layer = tv_parse_list(result, with_fields=TVPLayer, unused_indices=[7, 8])
+    layer["id"] = layer_id
+    return TVPLayer(**layer)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_move(position: int) -> None + +

+ + +
+ +

Move the current layer to a new position in the layer stack.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if layer could not be moved

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
243
+244
+245
+246
+247
+248
+249
+250
@try_cmd(exception_msg="Couldn't move current layer to position")
+def tv_layer_move(position: int) -> None:
+    """Move the current layer to a new position in the layer stack.
+
+    Raises:
+        GeorgeError: if layer could not be moved
+    """
+    send_cmd("tv_LayerMove", position)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_set(layer_id: int) -> None + +

+ + +
+ +

Make the given layer the current one.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_set(layer_id: int) -> None:
+    """Make the given layer the current one.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerSet", layer_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_selection_get(layer_id: int) -> bool + +

+ + +
+ +

Get the selection state of a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
266
+267
+268
+269
+270
+271
+272
+273
+274
@try_cmd(raise_exc=NoObjectWithIdError, exception_msg="Invalid layer id")
+def tv_layer_selection_get(layer_id: int) -> bool:
+    """Get the selection state of a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd("tv_LayerSelection", layer_id, error_values=[-1])
+    return tv_cast_to_type(res, bool)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_selection_set(layer_id: int, new_state: bool) -> None + +

+ + +
+ +

Set the selection state of a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
277
+278
+279
+280
+281
+282
+283
+284
@try_cmd(raise_exc=NoObjectWithIdError, exception_msg="Invalid layer id")
+def tv_layer_selection_set(layer_id: int, new_state: bool) -> None:
+    """Set the selection state of a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerSelection", layer_id, int(new_state), error_values=[-1])
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_select(start_frame: int, frame_count: int) -> int + +

+ + +
+ +

Select frames in the current layer.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
start_frame + int + +
+

selection start

+
+
+ required +
frame_count + int + +
+

number of frames to select

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
int + int + +
+

number of frames selected

+
+
+ +
+ Note +

If the start position is before the beginning of the layer, the selection will only start at the beginning of +the layer, but its length will be measured from the start position. +This means that if you ask for a selection of 15 frames starting from position 0 in a layer that actually +starts at position 5, only the first 10 frames in the layer will be selected. +If the selection goes beyond the end of the layer, it will only include the frames between the start and end of +the layer. No frames will be selected if the start position is beyond the end of the layer

+
+
+ Source code in pytvpaint/george/grg_layer.py +
287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
def tv_layer_select(start_frame: int, frame_count: int) -> int:
+    """Select frames in the current layer.
+
+    Args:
+        start_frame: selection start
+        frame_count: number of frames to select
+
+    Returns:
+        int: number of frames selected
+
+    Note:
+        If the start position is before the beginning of the layer, the selection will only start at the beginning of
+        the layer, but its length will be measured from the start position.
+        This means that if you ask for a selection of 15 frames starting from position 0 in a layer that actually
+        starts at position 5, only the first 10 frames in the layer will be selected.
+        If the selection goes beyond the end of the layer, it will only include the frames between the start and end of
+        the layer. No frames will be selected if the start position is beyond the end of the layer
+    """
+    return int(send_cmd("tv_LayerSelect", start_frame, frame_count, error_values=[-1]))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_select_info(full: bool = False) -> tuple[int, int] + +

+ + +
+ +

Get Selected frames in a layer.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
full + bool + +
+

Always get the selection range, even on a non anim/ctg layer

+
+
+ False +
+ + + +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
frame + int + +
+

the start frame of the selection

+
+
count + int + +
+

the number of frames in the selection

+
+
+ +
+ Bug +

The official documentation states that this functions selects the layer frames, it does not, it simply +returns the frames selected. This will also return all frames in the layer even if they are not selected if the +argument full is set to True. We advise using tv_layer_select to select your frames and only using this +function to get the selected frames.

+
+
+ Source code in pytvpaint/george/grg_layer.py +
308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
def tv_layer_select_info(full: bool = False) -> tuple[int, int]:
+    """Get Selected frames in a layer.
+
+    Args:
+        full:  Always get the selection range, even on a non anim/ctg layer
+
+    Returns:
+        frame: the start frame of the selection
+        count: the number of frames in the selection
+
+    Bug:
+        The official documentation states that this functions selects the layer frames, it does not, it simply
+        returns the frames selected. This will also return all frames in the layer even if they are not selected if the
+        argument `full` is set to True. We advise using `tv_layer_select` to select your frames and only using this
+        function to get the selected frames.
+    """
+    args = ["full"] if full else []
+    res = send_cmd("tv_layerSelectInfo", *args)
+    frame, count = tuple(map(int, res.split(" ")))
+    return frame, count
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_create(name: str) -> int + +

+ + +
+ +

Create a new image layer with the given name.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
330
+331
+332
def tv_layer_create(name: str) -> int:
+    """Create a new image layer with the given name."""
+    return int(send_cmd("tv_LayerCreate", name, handle_string=False))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_duplicate(name: str) -> int + +

+ + +
+ +

Duplicate the current layer and make it the current one.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
335
+336
+337
def tv_layer_duplicate(name: str) -> int:
+    """Duplicate the current layer and make it the current one."""
+    return int(send_cmd("tv_LayerDuplicate", name, handle_string=False))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_rename(layer_id: int, name: str) -> None + +

+ + +
+ +

Rename a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_rename(layer_id: int, name: str) -> None:
+    """Rename a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerRename", layer_id, name)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_kill(layer_id: int) -> None + +

+ + +
+ +

Delete the layer with provided id.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_kill(layer_id: int) -> None:
+    """Delete the layer with provided id.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerKill", layer_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_density_get() -> int + +

+ + +
+ +

Get the current layer density (opacity).

+ +
+ Source code in pytvpaint/george/grg_layer.py +
366
+367
+368
def tv_layer_density_get() -> int:
+    """Get the current layer density (opacity)."""
+    return int(send_cmd("tv_LayerDensity"))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_density_set(new_density: int) -> None + +

+ + +
+ +

Set the current layer density (opacity ranging from 0 to 100).

+ +
+ Source code in pytvpaint/george/grg_layer.py +
371
+372
+373
def tv_layer_density_set(new_density: int) -> None:
+    """Set the current layer density (opacity ranging from 0 to 100)."""
+    send_cmd("tv_LayerDensity", new_density)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_display_get(layer_id: int) -> bool + +

+ + +
+ +

Get the visibility of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_display_get(layer_id: int) -> bool:
+    """Get the visibility of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd("tv_LayerDisplay", layer_id, error_values=[0])
+    return tv_cast_to_type(res.lower(), bool)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_display_set(layer_id: int, new_state: bool, light_table: bool = False) -> None + +

+ + +
+ +

Set the visibility of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_display_set(
+    layer_id: int, new_state: bool, light_table: bool = False
+) -> None:
+    """Set the visibility of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    args: list[Any] = [layer_id, int(new_state)]
+    if light_table:
+        args.insert(1, "lighttable")
+    send_cmd("tv_LayerDisplay", *args, error_values=[0])
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_lock_get(layer_id: int) -> bool + +

+ + +
+ +

Get the lock state of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_lock_get(layer_id: int) -> bool:
+    """Get the lock state of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd("tv_LayerLock", layer_id, error_values=[GrgErrorValue.ERROR])
+    return tv_cast_to_type(res.lower(), bool)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_lock_set(layer_id: int, new_state: bool) -> None + +

+ + +
+ +

Set the lock state of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_lock_set(layer_id: int, new_state: bool) -> None:
+    """Set the lock state of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd(
+        "tv_LayerLock",
+        layer_id,
+        int(new_state),
+        error_values=[GrgErrorValue.ERROR],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_collapse_get(layer_id: int) -> bool + +

+ + +
+ +

Get the collapse mode of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_collapse_get(layer_id: int) -> bool:
+    """Get the collapse mode of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    return bool(int(send_cmd("tv_LayerCollapse", layer_id, error_values=[-2])))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_collapse_set(layer_id: int, new_state: bool) -> None + +

+ + +
+ +

Set the collapse mode of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_collapse_set(layer_id: int, new_state: bool) -> None:
+    """Set the collapse mode of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerCollapse", layer_id, int(new_state), error_values=[-2])
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_blending_mode_get(layer_id: int) -> BlendingMode + +

+ + +
+ +

Get the blending mode of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_blending_mode_get(layer_id: int) -> BlendingMode:
+    """Get the blending mode of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd("tv_LayerBlendingMode", layer_id)
+    return tv_cast_to_type(res.lower(), BlendingMode)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_blending_mode_set(layer_id: int, mode: BlendingMode) -> None + +

+ + +
+ +

Set the blending mode of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_blending_mode_set(layer_id: int, mode: BlendingMode) -> None:
+    """Set the blending mode of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerBlendingMode", layer_id, mode.value)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_stencil_get(layer_id: int) -> StencilMode + +

+ + +
+ +

Get the stencil state and mode of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_stencil_get(layer_id: int) -> StencilMode:
+    """Get the stencil state and mode of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd("tv_LayerStencil", layer_id)
+    _, state, mode = res.split(" ")
+
+    if state == "off":
+        return StencilMode.OFF
+
+    return tv_cast_to_type(mode, StencilMode)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_stencil_set(layer_id: int, mode: StencilMode) -> None + +

+ + +
+ +

Set the stencil state and mode of the given layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_stencil_set(layer_id: int, mode: StencilMode) -> None:
+    """Set the stencil state and mode of the given layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    if mode == StencilMode.OFF:
+        args = ["off"]
+    elif mode == StencilMode.ON:
+        args = ["on"]
+    else:
+        args = ["on", mode.value]
+
+    send_cmd("tv_LayerStencil", layer_id, *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_show_thumbnails_get(layer_id: int) -> bool + +

+ + +
+ +

Get the show thumbnails state for a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_show_thumbnails_get(
+    layer_id: int,
+) -> bool:
+    """Get the show thumbnails state for a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd(
+        "tv_LayerShowThumbnails", layer_id, error_values=[GrgErrorValue.ERROR]
+    )
+    return res == "1"
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_show_thumbnails_set(layer_id: int, state: bool) -> None + +

+ + +
+ +

Set the show thumbnail state for a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_show_thumbnails_set(
+    layer_id: int,
+    state: bool,
+) -> None:
+    """Set the show thumbnail state for a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd(
+        "tv_LayerShowThumbnails",
+        layer_id,
+        int(state),
+        error_values=[GrgErrorValue.ERROR],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_auto_break_instance_get(layer_id: int) -> bool + +

+ + +
+ +

Get the layer auto break instance value for a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_auto_break_instance_get(layer_id: int) -> bool:
+    """Get the layer auto break instance value for a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd("tv_LayerAutoBreakInstance", layer_id, error_values=[-1, -2, -3])
+    return res == "1"
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_auto_break_instance_set(layer_id: int, state: bool) -> None + +

+ + +
+ +

Set the layer auto break instance value for a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_auto_break_instance_set(
+    layer_id: int,
+    state: bool,
+) -> None:
+    """Set the layer auto break instance value for a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd(
+        "tv_LayerAutoBreakInstance",
+        layer_id,
+        int(state),
+        error_values=[-1, -2, -3],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_auto_create_instance_get(layer_id: int) -> bool + +

+ + +
+ +

Get the layer auto create instance value for a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_auto_create_instance_get(
+    layer_id: int,
+) -> bool:
+    """Get the layer auto create instance value for a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd("tv_LayerAutoCreateInstance", layer_id, error_values=[-1, -2, -3])
+    return res == "1"
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_auto_create_instance_set(layer_id: int, state: bool) -> None + +

+ + +
+ +

Set the layer auto create instance value for a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_auto_create_instance_set(
+    layer_id: int,
+    state: bool,
+) -> None:
+    """Set the layer auto create instance value for a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd(
+        "tv_LayerAutoCreateInstance", layer_id, int(state), error_values=[-1, -2, -3]
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_pre_behavior_get(layer_id: int) -> LayerBehavior + +

+ + +
+ +

Get the pre-behavior value for a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_pre_behavior_get(layer_id: int) -> LayerBehavior:
+    """Get the pre-behavior value for a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd("tv_LayerPreBehavior", layer_id)
+    return tv_cast_to_type(res, LayerBehavior)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_pre_behavior_set(layer_id: int, behavior: LayerBehavior) -> None + +

+ + +
+ +

Set the pre-behavior value for a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_pre_behavior_set(layer_id: int, behavior: LayerBehavior) -> None:
+    """Set the pre-behavior value for a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerPreBehavior", layer_id, behavior.value)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_post_behavior_get(layer_id: int) -> LayerBehavior + +

+ + +
+ +

Get the post-behavior value for a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_post_behavior_get(layer_id: int) -> LayerBehavior:
+    """Get the post-behavior value for a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd("tv_LayerPostBehavior", layer_id)
+    return tv_cast_to_type(res, LayerBehavior)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_post_behavior_set(layer_id: int, behavior: LayerBehavior) -> None + +

+ + +
+ +

Set the post-behavior value for a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_post_behavior_set(layer_id: int, behavior: LayerBehavior) -> None:
+    """Set the post-behavior value for a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerPostBehavior", layer_id, behavior.value)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_lock_position_get(layer_id: int) -> bool + +

+ + +
+ +

Get the lock position state of a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_lock_position_get(layer_id: int) -> bool:
+    """Get the lock position state of a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd("tv_LayerLockPosition", layer_id)
+    return tv_cast_to_type(res, bool)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_lock_position_set(layer_id: int, state: bool) -> None + +

+ + +
+ +

Set the lock position state of a layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_lock_position_set(layer_id: int, state: bool) -> None:
+    """Set the lock position state of a layer.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerLockPosition", layer_id, int(state))
+
+
+
+ +
+ + +
+ + + +

+ tv_preserve_get() -> LayerTransparency + +

+ + +
+ +

Get the preserve transparency state of the current layer.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
721
+722
+723
+724
+725
def tv_preserve_get() -> LayerTransparency:
+    """Get the preserve transparency state of the current layer."""
+    res = send_cmd("tv_Preserve")
+    _, state = res.split(" ")
+    return tv_cast_to_type(state, LayerTransparency)
+
+
+
+ +
+ + +
+ + + +

+ tv_preserve_set(state: LayerTransparency) -> None + +

+ + +
+ +

Set the preserve transparency state of the current layer.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
728
+729
+730
def tv_preserve_set(state: LayerTransparency) -> None:
+    """Set the preserve transparency state of the current layer."""
+    send_cmd("tv_Preserve", "alpha", state.value)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_mark_get(layer_id: int, frame: int) -> int + +

+ + +
+ +

Get the mark color of a layer at a frame.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
layer_id + int + +
+

the layer id

+
+
+ required +
frame + int + +
+

the frame with a mark

+
+
+ required +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
int + int + +
+

the mark color index

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_mark_get(layer_id: int, frame: int) -> int:
+    """Get the mark color of a layer at a frame.
+
+    Args:
+        layer_id: the layer id
+        frame: the frame with a mark
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+
+    Returns:
+        int: the mark color index
+    """
+    return int(send_cmd("tv_LayerMarkGet", layer_id, frame))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_mark_set(layer_id: int, frame: int, color_index: int) -> None + +

+ + +
+ +

Set the mark of the layer's frame.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
layer_id + int + +
+

the layer id

+
+
+ required +
frame + int + +
+

the frame to set the mark (use 0 to remove it).

+
+
+ required +
color_index + int + +
+

the mark color

+
+
+ required +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_mark_set(layer_id: int, frame: int, color_index: int) -> None:
+    """Set the mark of the layer's frame.
+
+    Args:
+        layer_id: the layer id
+        frame: the frame to set the mark (use 0 to remove it).
+        color_index: the mark color
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerMarkSet", layer_id, frame, color_index)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_anim(layer_id: int) -> None + +

+ + +
+ +

Convert the layer to an anim layer.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
771
+772
+773
def tv_layer_anim(layer_id: int) -> None:
+    """Convert the layer to an anim layer."""
+    send_cmd("tv_LayerAnim", *([layer_id] if layer_id else []))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_copy() -> None + +

+ + +
+ +

Copy the current image or the selected ones.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
776
+777
+778
def tv_layer_copy() -> None:
+    """Copy the current image or the selected ones."""
+    send_cmd("tv_LayerCopy")
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_cut() -> None + +

+ + +
+ +

Cut the current image or the selected ones.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
781
+782
+783
def tv_layer_cut() -> None:
+    """Cut the current image or the selected ones."""
+    send_cmd("tv_LayerCut")
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_paste() -> None + +

+ + +
+ +

Paste the previously copied/cut images to the current layer.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
786
+787
+788
def tv_layer_paste() -> None:
+    """Paste the previously copied/cut images to the current layer."""
+    send_cmd("tv_LayerPaste")
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_insert_image(count: int = 1, direction: InsertDirection | None = None, duplicate: bool | None = None) -> None + +

+ + +
+ +

Add new image(s) before/after the current one and make it current.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
def tv_layer_insert_image(
+    count: int = 1,
+    direction: InsertDirection | None = None,
+    duplicate: bool | None = None,
+) -> None:
+    """Add new image(s) before/after the current one and make it current."""
+    if duplicate:
+        args = [0]
+    else:
+        args_dict = {
+            "count": count,
+            "direction": direction.value if direction is not None else None,
+        }
+        args = args_dict_to_list(args_dict)
+
+    send_cmd("tv_LayerInsertImage", *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_merge(layer_id: int, blending_mode: BlendingMode, stamp: bool = False, erase: bool = False, keep_color_grp: bool = True, keep_img_mark: bool = True, keep_instance_name: bool = True) -> None + +

+ + +
+ +

Merge the given layer with the current one.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
layer_id + int + +
+

the layer id

+
+
+ required +
blending_mode + pytvpaint.george.grg_base.BlendingMode + +
+

the blending mode to use

+
+
+ required +
stamp + bool + +
+

Use stamp mode

+
+
+ False +
erase + bool + +
+

Remove the source layer

+
+
+ False +
keep_color_grp + bool + +
+

Keep the color group

+
+
+ True +
keep_img_mark + bool + +
+

Keep the image mark

+
+
+ True +
keep_instance_name + bool + +
+

Keep the instance name

+
+
+ True +
+ +
+ Source code in pytvpaint/george/grg_layer.py +
809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
def tv_layer_merge(
+    layer_id: int,
+    blending_mode: BlendingMode,
+    stamp: bool = False,
+    erase: bool = False,
+    keep_color_grp: bool = True,
+    keep_img_mark: bool = True,
+    keep_instance_name: bool = True,
+) -> None:
+    """Merge the given layer with the current one.
+
+    Args:
+        layer_id: the layer id
+        blending_mode: the blending mode to use
+        stamp: Use stamp mode
+        erase: Remove the source layer
+        keep_color_grp: Keep the color group
+        keep_img_mark: Keep the image mark
+        keep_instance_name: Keep the instance name
+    """
+    args = [
+        layer_id,
+        blending_mode.value,
+    ]
+
+    if stamp:
+        args.append("stamp")
+    if erase:
+        args.append("erase")
+
+    args_dict = {
+        "keepcolorgroup": int(keep_color_grp),
+        "keepimagemark": int(keep_img_mark),
+        "keepinstancename": int(keep_instance_name),
+    }
+    args.extend(args_dict_to_list(args_dict))
+
+    send_cmd("tv_LayerMerge", layer_id, *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_merge_all(keep_color_grp: bool = True, keep_img_mark: bool = True, keep_instance_name: bool = True) -> None + +

+ + +
+ +

Merge all layers.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
keep_color_grp + bool + +
+

Keep the color group

+
+
+ True +
keep_img_mark + bool + +
+

Keep the image mark

+
+
+ True +
keep_instance_name + bool + +
+

Keep the instance name

+
+
+ True +
+ +
+ Source code in pytvpaint/george/grg_layer.py +
849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
def tv_layer_merge_all(
+    keep_color_grp: bool = True,
+    keep_img_mark: bool = True,
+    keep_instance_name: bool = True,
+) -> None:
+    """Merge all layers.
+
+    Args:
+        keep_color_grp: Keep the color group
+        keep_img_mark: Keep the image mark
+        keep_instance_name: Keep the instance name
+    """
+    args_dict = {
+        "keepcolorgroup": int(keep_color_grp),
+        "keepimagemark": int(keep_img_mark),
+        "keepinstancename": int(keep_instance_name),
+    }
+    send_cmd("tv_LayerMergeAll", *args_dict_to_list(args_dict))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_shift(layer_id: int, start: int) -> None + +

+ + +
+ +

Move the layer to a new frame.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
layer_id + int + +
+

layer id

+
+
+ required +
start + int + +
+

frame to shift layer to

+
+
+ required +
+ +
+ Source code in pytvpaint/george/grg_layer.py +
869
+870
+871
+872
+873
+874
+875
+876
def tv_layer_shift(layer_id: int, start: int) -> None:
+    """Move the layer to a new frame.
+
+    Args:
+        layer_id: layer id
+        start: frame to shift layer to
+    """
+    send_cmd("tv_LayerShift", layer_id, start)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_load_dependencies(layer_id: int) -> None + +

+ + +
+ +

Load all dependencies of the given layer in memory.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_load_dependencies(layer_id: int) -> None:
+    """Load all dependencies of the given layer in memory.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd("tv_LayerLoadDependencies", layer_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_get_color(clip_id: int, color_index: int) -> TVPClipLayerColor + +

+ + +
+ +

Get a specific colors information in the clips color list.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_color_get_color(clip_id: int, color_index: int) -> TVPClipLayerColor:
+    """Get a specific colors information in the clips color list.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    result = send_cmd(
+        "tv_LayerColor",
+        LayerColorAction.GETCOLOR.value,
+        clip_id,
+        color_index,
+        error_values=[GrgErrorValue.ERROR],
+    )
+    parsed = tv_parse_list(result, with_fields=TVPClipLayerColor)
+    return TVPClipLayerColor(**parsed)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_set_color(clip_id: int, color_index: int, color: RGBColor, name: str | None = None) -> None + +

+ + +
+ +

Set a specific colors information in the clips color list.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Note +

The color with index 0 is the "Default" color, and it can't be changed

+
+
+ Source code in pytvpaint/george/grg_layer.py +
913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_color_set_color(
+    clip_id: int,
+    color_index: int,
+    color: RGBColor,
+    name: str | None = None,
+) -> None:
+    """Set a specific colors information in the clips color list.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+
+    Note:
+        The color with index 0 is the "Default" color, and it can't be changed
+    """
+    args: list[Any] = [
+        LayerColorAction.SETCOLOR.value,
+        clip_id,
+        color_index,
+        color.r,
+        color.g,
+        color.b,
+    ]
+
+    if name:
+        args.append(name)
+
+    send_cmd("tv_LayerColor", *args, error_values=[GrgErrorValue.ERROR])
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_get(layer_id: int) -> int + +

+ + +
+ +

Get the layer's color index from the clips color list.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_color_get(layer_id: int) -> int:
+    """Get the layer's color index from the clips color list.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    res = send_cmd(
+        "tv_LayerColor",
+        LayerColorAction.GET.value,
+        layer_id,
+        error_values=[-1],
+    )
+    return int(res)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_set(layer_id: int, color_index: int) -> None + +

+ + +
+ +

Set the layer's color index from the clips color list.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
@try_cmd(raise_exc=NoObjectWithIdError)
+def tv_layer_color_set(layer_id: int, color_index: int) -> None:
+    """Set the layer's color index from the clips color list.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    send_cmd(
+        "tv_LayerColor",
+        LayerColorAction.SET.value,
+        layer_id,
+        color_index,
+        error_values=[-1],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_lock(color_index: int) -> int + +

+ + +
+ +

Lock all layers that use the given color index.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
color_index + int + +
+

the layer color index

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

the number of layers locked

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
981
+982
+983
+984
+985
+986
+987
+988
+989
+990
def tv_layer_color_lock(color_index: int) -> int:
+    """Lock all layers that use the given color index.
+
+    Args:
+        color_index: the layer color index
+
+    Returns:
+        the number of layers locked
+    """
+    return int(send_cmd("tv_LayerColor", LayerColorAction.LOCK.value, color_index))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_unlock(color_index: int) -> int + +

+ + +
+ +

Unlock all layers that use the given color index.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
color_index + int + +
+

the layer color index

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

the number of unlocked layers

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
 993
+ 994
+ 995
+ 996
+ 997
+ 998
+ 999
+1000
+1001
+1002
def tv_layer_color_unlock(color_index: int) -> int:
+    """Unlock all layers that use the given color index.
+
+    Args:
+        color_index: the layer color index
+
+    Returns:
+        the number of unlocked layers
+    """
+    return int(send_cmd("tv_LayerColor", LayerColorAction.UNLOCK.value, color_index))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_show(mode: LayerColorDisplayOpt, color_index: int) -> int + +

+ + +
+ +

Show all layers that use the given color index.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
mode + pytvpaint.george.grg_layer.LayerColorDisplayOpt + +
+

the display mode

+
+
+ required +
color_index + int + +
+

the layer color index

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

the number of unlocked layers

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
def tv_layer_color_show(mode: LayerColorDisplayOpt, color_index: int) -> int:
+    """Show all layers that use the given color index.
+
+    Args:
+        mode: the display mode
+        color_index: the layer color index
+
+    Returns:
+        the number of unlocked layers
+    """
+    res = send_cmd(
+        "tv_LayerColor",
+        LayerColorAction.SHOW.value,
+        mode.value,
+        color_index,
+        error_values=[GrgErrorValue.ERROR],
+    )
+    return int(res)
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_hide(mode: LayerColorDisplayOpt, color_index: int) -> int + +

+ + +
+ +

Hide all layers that use the given color index.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
mode + pytvpaint.george.grg_layer.LayerColorDisplayOpt + +
+

the display mode

+
+
+ required +
color_index + int + +
+

the layer color index

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

the number of unlocked layers

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
def tv_layer_color_hide(mode: LayerColorDisplayOpt, color_index: int) -> int:
+    """Hide all layers that use the given color index.
+
+    Args:
+        mode: the display mode
+        color_index: the layer color index
+
+    Returns:
+        the number of unlocked layers
+    """
+    return int(
+        send_cmd(
+            "tv_LayerColor",
+            LayerColorAction.HIDE.value,
+            mode.value,
+            color_index,
+            error_values=[GrgErrorValue.ERROR],
+        )
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_visible(color_index: int) -> bool + +

+ + +
+ +

Get the visibility of the color index (2px height) in the timeline.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id",
+)
+def tv_layer_color_visible(color_index: int) -> bool:
+    """Get the visibility of the color index (2px height) in the timeline.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    return bool(
+        send_cmd(
+            "tv_LayerColor",
+            LayerColorAction.VISIBLE.value,
+            color_index,
+            error_values=[-1],
+        )
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_select(color_index: int) -> int + +

+ + +
+ +

Select all layers that use the given color index.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid color index",
+)
+def tv_layer_color_select(color_index: int) -> int:
+    """Select all layers that use the given color index.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    return int(send_cmd("tv_LayerColor", LayerColorAction.SELECT.value, color_index))
+
+
+
+ +
+ + +
+ + + +

+ tv_layer_color_unselect(color_index: int) -> int + +

+ + +
+ +

Unselect all layers that use the given color index.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid color index",
+)
+def tv_layer_color_unselect(color_index: int) -> int:
+    """Unselect all layers that use the given color index.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id
+    """
+    return int(send_cmd("tv_LayerColor", LayerColorAction.UNSELECT.value, color_index))
+
+
+
+ +
+ + +
+ + + +

+ tv_instance_name(layer_id: int, mode: InstanceNamingMode, prefix: str | None = None, suffix: str | None = None, process: InstanceNamingProcess | None = None) -> None + +

+ + +
+ +

Rename all instances.

+ +
+ Note +

The suffix can only be added when using mode InstanceNamingMode.SMART

+
+
+ Bug +

Using a wrong layer_id causes a crash

+
+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
layer_id + int + +
+

the layer id

+
+
+ required +
mode + pytvpaint.george.grg_layer.InstanceNamingMode + +
+

the instance renaming mode

+
+
+ required +
prefix + str | None + +
+

the prefix to add to each name

+
+
+ None +
suffix + str | None + +
+

the suffix to add to each name

+
+
+ None +
process + pytvpaint.george.grg_layer.InstanceNamingProcess | None + +
+

the instance naming process

+
+
+ None +
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
def tv_instance_name(
+    layer_id: int,
+    mode: InstanceNamingMode,
+    prefix: str | None = None,
+    suffix: str | None = None,
+    process: InstanceNamingProcess | None = None,
+) -> None:
+    """Rename all instances.
+
+    Note:
+        The suffix can only be added when using mode InstanceNamingMode.SMART
+
+    Bug:
+        Using a wrong layer_id causes a crash
+
+    Args:
+        layer_id: the layer id
+        mode: the instance renaming mode
+        prefix: the prefix to add to each name
+        suffix: the suffix to add to each name
+        process: the instance naming process
+    """
+    args_dict: dict[str, Any] = {
+        "mode": mode.value,
+        "prefix": prefix,
+    }
+
+    if mode == InstanceNamingMode.SMART:
+        args_dict["suffix"] = suffix
+        args_dict["process"] = process.value if process else None
+
+    args = args_dict_to_list(args_dict)
+    send_cmd("tv_InstanceName", layer_id, *args, error_values=[-1, -2])
+
+
+
+ +
+ + +
+ + + +

+ tv_instance_get_name(layer_id: int, frame: int) -> str + +

+ + +
+ +

Get the name of an instance.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
layer_id + int + +
+

the layer id

+
+
+ required +
frame + int + +
+

the frame of the instance

+
+
+ required +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid layer id or an invalid instance frame

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

the instance name

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid layer id or the frame doesn't have an instance",
+)
+def tv_instance_get_name(layer_id: int, frame: int) -> str:
+    """Get the name of an instance.
+
+    Args:
+        layer_id: the layer id
+        frame: the frame of the instance
+
+    Raises:
+        NoObjectWithIdError: if given an invalid layer id or an invalid instance frame
+
+    Returns:
+        the instance name
+    """
+    return send_cmd("tv_InstanceGetName", layer_id, frame).strip('"')
+
+
+
+ +
+ + +
+ + + +

+ tv_instance_set_name(layer_id: int, frame: int, name: str) -> str + +

+ + +
+ +

Set the name of an instance.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if an invalid layer id was provided or no instance was found at the given frame

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
@try_cmd(exception_msg="Invalid layer id or no instance at given frame")
+def tv_instance_set_name(layer_id: int, frame: int, name: str) -> str:
+    """Set the name of an instance.
+
+    Raises:
+        GeorgeError: if an invalid layer id was provided or no instance was found at the given frame
+    """
+    return send_cmd("tv_InstanceSetName", layer_id, frame, name)
+
+
+
+ +
+ + +
+ + + +

+ tv_exposure_next() -> int + +

+ + +
+ +

Go to the next layer instance head.

+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

The next instances start frame

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1157
+1158
+1159
+1160
+1161
+1162
+1163
def tv_exposure_next() -> int:
+    """Go to the next layer instance head.
+
+    Returns:
+        The next instances start frame
+    """
+    return int(send_cmd("tv_ExposureNext"))
+
+
+
+ +
+ + +
+ + + +

+ tv_exposure_break(frame: int) -> None + +

+ + +
+ +

Break a layer instance/exposure at the given frame.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
frame + int + +
+

the split frame

+
+
+ required +
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1166
+1167
+1168
+1169
+1170
+1171
+1172
def tv_exposure_break(frame: int) -> None:
+    """Break a layer instance/exposure at the given frame.
+
+    Args:
+        frame: the split frame
+    """
+    send_cmd("tv_ExposureBreak", frame)
+
+
+
+ +
+ + +
+ + + +

+ tv_exposure_add(frame: int, count: int) -> None + +

+ + +
+ +

Add new frames to an existing layer instance/exposure.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
frame + int + +
+

the split frame

+
+
+ required +
count + int + +
+

the number of frames to add

+
+
+ required +
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
def tv_exposure_add(frame: int, count: int) -> None:
+    """Add new frames to an existing layer instance/exposure.
+
+    Args:
+        frame: the split frame
+        count: the number of frames to add
+    """
+    send_cmd("tv_ExposureAdd", frame, count)
+
+
+
+ +
+ + +
+ + + +

+ tv_exposure_set(frame: int, count: int) -> None + +

+ + +
+ +

Set the number frames of an existing layer instance/exposure.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
frame + int + +
+

the split frame

+
+
+ required +
count + int + +
+

the number of frames to add

+
+
+ required +
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
def tv_exposure_set(frame: int, count: int) -> None:
+    """Set the number frames of an existing layer instance/exposure.
+
+    Args:
+        frame: the split frame
+        count: the number of frames to add
+    """
+    send_cmd("tv_ExposureSet", frame, count)
+
+
+
+ +
+ + +
+ + + +

+ tv_exposure_prev() -> int + +

+ + +
+ +

Go to the previous layer instance head (before the current instance).

+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int + +
+

The previous instances start frame

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1195
+1196
+1197
+1198
+1199
+1200
+1201
def tv_exposure_prev() -> int:
+    """Go to the previous layer instance head (*before* the current instance).
+
+    Returns:
+        The previous instances start frame
+    """
+    return int(send_cmd("tv_ExposurePrev"))
+
+
+
+ +
+ + +
+ + + +

+ tv_save_image(export_path: Path | str) -> None + +

+ + +
+ +

Save the current image of the current layer.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if the file couldn't be saved or an invalid format was provided

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
@try_cmd(exception_msg="No file found or invalid format")
+def tv_save_image(export_path: Path | str) -> None:
+    """Save the current image of the current layer.
+
+    Raises:
+        GeorgeError: if the file couldn't be saved or an invalid format was provided
+    """
+    export_path = Path(export_path)
+    send_cmd("tv_SaveImage", export_path.as_posix())
+
+
+
+ +
+ + +
+ + + +

+ tv_load_image(img_path: Path | str, stretch: bool = False) -> None + +

+ + +
+ +

Load an image in the current image layer.

+ + + +

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+ FileNotFoundError + +
+

if the input file doesn't exist

+
+
+ pytvpaint.george.exceptions.GeorgeError + +
+

if the provided file is in an invalid format

+
+
+ +
+ Source code in pytvpaint/george/grg_layer.py +
1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
@try_cmd(exception_msg="Invalid image format")
+def tv_load_image(img_path: Path | str, stretch: bool = False) -> None:
+    """Load an image in the current image layer.
+
+    Raises:
+        FileNotFoundError: if the input file doesn't exist
+        GeorgeError: if the provided file is in an invalid format
+    """
+    img_path = Path(img_path)
+
+    if not img_path.exists():
+        raise FileNotFoundError(f"File not found at: {img_path.as_posix()}")
+
+    args: list[Any] = [img_path.as_posix()]
+    if stretch:
+        args.append("stretch")
+
+    send_cmd("tv_LoadImage", *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_clear(fill_b_pen: bool = False) -> None + +

+ + +
+ +

Clear (or fill with BPen) the current image (selection) of the current layer.

+ +
+ Source code in pytvpaint/george/grg_layer.py +
1235
+1236
+1237
def tv_clear(fill_b_pen: bool = False) -> None:
+    """Clear (or fill with BPen) the current image (selection) of the current layer."""
+    send_cmd("tv_Clear", int(fill_b_pen))
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/george/misc/index.html b/api/george/misc/index.html new file mode 100644 index 0000000..a91ee0d --- /dev/null +++ b/api/george/misc/index.html @@ -0,0 +1,8653 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Misc - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Miscellaneous George functions

+ + +
+ + + + +
+ +

All George values as enum and functions which are not specific to any TVPaint element.

+ + + +
+ + + + + + + + +
+ + + +

+ GrgErrorValue + + +

+ + +
+ + +

Common George error values.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
EMPTY + str + +
+

See tv_clipinfo for example

+
+
NONE + str + +
+

See tv_clipenumid for example

+
+
ERROR + str + +
+

See tv_setapen for example

+
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ GrgBoolState + + +

+ + +
+

+ Bases: enum.Enum

+ + +

George booleans.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ON + +
+ +
+
OFF + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ FieldOrder + + +

+ + +
+

+ Bases: enum.Enum

+ + +

Field order of the camera.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
NONE + +
+ +
+
LOWER + +
+ +
+
UPPER + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ MarkType + + +

+ + +
+

+ Bases: enum.Enum

+ + +

The mark command.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
MARKIN + +
+ +
+
MARKOUT + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ MarkReference + + +

+ + +
+

+ Bases: enum.Enum

+ + +

The object to mark.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
PROJECT + +
+ +
+
CLIP + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ MarkAction + + +

+ + +
+

+ Bases: enum.Enum

+ + +

The mark action.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
SET + +
+ +
+
CLEAR + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ RectButton + + +

+ + +
+

+ Bases: enum.Enum

+ + +

The rect button when drawing.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
LEFT + +
+ +
+
RIGHT + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ TVPShape + + +

+ + +
+

+ Bases: enum.Enum

+ + +

The shape tools.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
B_SPLINE + +
+ +
+
BEZIER + +
+ +
+
BEZIER_FILL + +
+ +
+
CAMERA + +
+ +
+
CIRCLE + +
+ +
+
CIRCLE_2PTS + +
+ +
+
CIRCLE_3PTS + +
+ +
+
CIRCLE_FILL + +
+ +
+
CIRCLE_2PTS_FILL + +
+ +
+
CIRCLE_3PTS_FILL + +
+ +
+
CROP + +
+ +
+
CUT_RECT + +
+ +
+
CUT_POLY + +
+ +
+
CUT_FREE_HAND + +
+ +
+
CUT_FLOOD + +
+ +
+
DOT + +
+ +
+
FLOOD + +
+ +
+
FREE_HAND_LINE + +
+ +
+
FREE_HAND_FILL + +
+ +
+
ELLIPSE + +
+ +
+
ELLIPSE_FILL + +
+ +
+
ELLIPSE_2PTS + +
+ +
+
ELLIPSE_2PTS_FILL + +
+ +
+
LINE + +
+ +
+
LINE_FILL + +
+ +
+
PLANNING + +
+ +
+
POSITION + +
+ +
+
RECTANGLE + +
+ +
+
RECTANGLE_FILL + +
+ +
+
SELECT_RECTANGLE + +
+ +
+
SELECT_ELLIPSE + +
+ +
+
SELECT_2PTS + +
+ +
+
SELECT_3PTS + +
+ +
+
SELECT_POLY + +
+ +
+
SELECT_FREE_HAND + +
+ +
+
SELECT_FLOOD + +
+ +
+
SELECT_COLOR + +
+ +
+
SELECT_BEZIER + +
+ +
+
SELECT_B_SPLINE + +
+ +
+
SINGLE_DOT + +
+ +
+
SPLIT_3PTS + +
+ +
+
SPLINE_FILL + +
+ +
+
WARP + +
+ +
+
WRAP + +
+ +
+
ZOOM_IN + +
+ +
+
ZOOM_OUT + +
+ +
+
ZOOM_HAND + +
+ +
+
ZOOM_RECT + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ ResizeOption + + +

+ + +
+

+ Bases: enum.Enum

+ + +

Resize options for projects.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
EMPTY + +
+ +
+
CROP + +
+ +
+
STRETCH + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ SpriteLayout + + +

+ + +
+

+ Bases: enum.Enum

+ + +

Sprite layout when exporting as sprites.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
RECTANGLE + +
+ +
+
HORIZONTAL + +
+ +
+
VERTICAL + +
+ +
+
DIAGONAL + +
+ +
+
ANTI_DIAGONAL + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ AlphaMode + + +

+ + +
+

+ Bases: enum.Enum

+ + +

The alpha load mode.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
PREMULTIPLY + +
+ +
+
NO_PREMULTIPLY + +
+ +
+
NO_ALPHA + +
+ +
+
ALPHA_ONLY + +
+ +
+
GUESS + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ AlphaSaveMode + + +

+ + +
+

+ Bases: enum.Enum

+ + +

The alpha save mode.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
PREMULTIPLY + +
+ +
+
NO_PREMULTIPLY + +
+ +
+
NO_ALPHA + +
+ +
+
ALPHA_ONLY + +
+ +
+
GUESS + +
+ +
+
ALPHA_BINARY + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ SaveFormat + + +

+ + +
+

+ Bases: enum.Enum

+ + +

All save formats.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
AVI + +
+ +
+
BMP + +
+ +
+
CINEON + +
+ +
+
DEEP + +
+ +
+
DPX + +
+ +
+
FLI + +
+ +
+
GIF + +
+ +
+
ILBM + +
+ +
+
JPG + +
+

jpeg

+
+
MKV + +
+

Mode=1017

+
+
MOV + +
+

Mode=1015

+
+
MP4 + +
+

Mode=1016

+
+
PCX + +
+ +
+
PDF + +
+ +
+
PNG + +
+ +
+
PSD + +
+ +
+
SGI + +
+

Mode=16

+
+
SOFTIMAGE + +
+

Mode=10

+
+
SUNRASTER + +
+

sun

+
+
TGA + +
+

tga

+
+
TIFF + +
+

Mode=15

+
+
VPB + +
+ +
+
WEBM + +
+

Mode=1018

+
+
+ + + + +
+ + + + + + + + + + +
+ + + +

+ from_extension(extension: str) -> SaveFormat + + + classmethod + + +

+ + +
+ +

Returns the correct tvpaint format value from a string extension.

+ +
+ Source code in pytvpaint/george/grg_base.py +
340
+341
+342
+343
+344
+345
+346
+347
+348
@classmethod
+def from_extension(cls, extension: str) -> SaveFormat:
+    """Returns the correct tvpaint format value from a string extension."""
+    extension = extension.replace(".", "").upper()
+    if not hasattr(SaveFormat, extension):
+        raise ValueError(
+            f"Could not find format ({extension}) in accepted formats ({SaveFormat})"
+        )
+    return cast(SaveFormat, getattr(cls, extension.upper()))
+
+
+
+ +
+ + +
+ + + +

+ is_image(extension: str) -> bool + + + classmethod + + +

+ + +
+ +

Returns True if the extension correspond to an image format.

+ +
+ Source code in pytvpaint/george/grg_base.py +
350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
@classmethod
+def is_image(cls, extension: str) -> bool:
+    """Returns True if the extension correspond to an image format."""
+    extension = extension.replace(".", "").lower()
+    image_formats = [
+        "bmp",
+        "cin",
+        "deep",
+        "dpx",
+        "ilbm",
+        "jpg",
+        "jpeg",
+        "pcx",
+        "png",
+        "psd",
+        "sgi",
+        "pic",
+        "ras",
+        "sun",
+        "tga",
+        "tiff",
+    ]
+    return extension in image_formats
+
+
+
+ +
+ + + +
+ +
+ + +
+ +
+ + + +

+ RGBColor(r: int, g: int, b: int) + + + dataclass + + +

+ + +
+ + +

RGB color with 0-255 range values.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ HSLColor(h: int, s: int, l: int) + + + dataclass + + +

+ + +
+ + +

HSL color. Maximum values are (360, 100, 100) for h, s, l.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ BlendingMode + + +

+ + +
+

+ Bases: enum.Enum

+ + +

All the blending modes.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
COLOR + +
+ +
+
BEHIND + +
+ +
+
ERASE + +
+ +
+
SHADE + +
+ +
+
LIGHT + +
+ +
+
COLORIZE + +
+ +
+
HUE + +
+ +
+
SATURATION + +
+ +
+
VALUE + +
+ +
+
ADD + +
+ +
+
SUB + +
+ +
+
MULTIPLY + +
+ +
+
SCREEN + +
+ +
+
REPLACE + +
+ +
+
COPY + +
+ +
+
DIFFERENCE + +
+ +
+
DIVIDE + +
+ +
+
OVERLAY + +
+ +
+
OVERLAY2 + +
+ +
+
LIGHT2 + +
+ +
+
SHADE2 + +
+ +
+
HARDLIGHT + +
+ +
+
SOFTLIGHT + +
+ +
+
GRAIN_EXTRACT + +
+ +
+
GRAIN_MERGE + +
+ +
+
SUB2 + +
+ +
+
DARKEN + +
+ +
+
LIGHTEN + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ DrawingMode + + +

+ + +
+

+ Bases: enum.Enum

+ + +

All the drawing modes.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
COLOR + +
+ +
+
BEHIND + +
+ +
+
ERASE + +
+ +
+
PANTO + +
+ +
+
MERGE + +
+ +
+
SHADE + +
+ +
+
LIGHT + +
+ +
+
COLORIZE + +
+ +
+
TINT + +
+ +
+
GRAIN + +
+ +
+
BLUR + +
+ +
+
NOISE + +
+ +
+
NEGATIVE + +
+ +
+
SHARP + +
+ +
+
EMBOSS + +
+ +
+
SOLARIZE + +
+ +
+
SATURATE + +
+ +
+
UNSATURATE + +
+ +
+
ADD + +
+ +
+
SUB + +
+ +
+
MULTIPLY + +
+ +
+
SCREEN + +
+ +
+
DIFF + +
+ +
+
HEALING + +
+ +
+
BURN + +
+ +
+
DODGE + +
+ +
+
DARKEN + +
+ +
+
LIGHTEN + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ MenuElement + + +

+ + +
+

+ Bases: enum.Enum

+ + +

All the TVPaint menu elements.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
SHOW_UI + +
+ +
+
HIDE_UI + +
+ +
+
RESIZE_UI + +
+ +
+
CENTER_DISPLAY + +
+ +
+
FIT_DISPLAY + +
+ +
+
FRONT + +
+ +
+
BACK + +
+ +
+
ASPECT_RATIO + +
+ +
+
CLIP + +
+ +
+
PROJECT + +
+ +
+
XSHEET + +
+ +
+
NOTES + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ FileMode + + +

+ + +
+

+ Bases: enum.Enum

+ + +

File mode save or load.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
SAVE + +
+ +
+
LOAD + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ TVPPenBrush(mode: DrawingMode, size: float, power: int, opacity: int, dry: bool, aaliasing: bool, gradient: bool, csize: str, cpower: str) + + + dataclass + + +

+ + +
+ + +

A TVPaint brush.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ TVPSound(offset: float, volume: float, mute: bool, fade_in_start: float, fade_in_stop: float, fade_out_start: float, fade_out_stop: float, path: Path, sound_in: float, sound_out: float, color_index: int) + + + dataclass + + +

+ + +
+ + +

A TVPaint sound (clip and project).

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + + +

+ undoable(func: T) -> T + +

+ + +
+ +

Decorator to register actions in the TVPaint undo stack.

+ +
+ Source code in pytvpaint/george/grg_base.py +
600
+601
+602
+603
+604
+605
+606
+607
+608
+609
def undoable(func: T) -> T:
+    """Decorator to register actions in the TVPaint undo stack."""
+
+    def wrapper(*args: Any, **kwargs: Any) -> T:
+        tv_undo_open_stack()
+        res = func(*args, **kwargs)
+        tv_undo_close_stack(func.__name__)
+        return cast(T, res)
+
+    return cast(T, wrapper)
+
+
+
+ +
+ + +
+ + + +

+ undoable_stack() -> Generator[None, None, None] + +

+ + +
+ +

Context manager that creates an undo stack. Useful to undo a sequence of George actions.

+ +
+ Source code in pytvpaint/george/grg_base.py +
612
+613
+614
+615
+616
+617
@contextlib.contextmanager
+def undoable_stack() -> Generator[None, None, None]:
+    """Context manager that creates an undo stack. Useful to undo a sequence of George actions."""
+    tv_undo_open_stack()
+    yield
+    tv_undo_close_stack()
+
+
+
+ +
+ + +
+ + + +

+ tv_warn(msg: str) -> None + +

+ + +
+ +

Display a warning message.

+ +
+ Source code in pytvpaint/george/grg_base.py +
620
+621
+622
def tv_warn(msg: str) -> None:
+    """Display a warning message."""
+    send_cmd("tv_Warn", msg)
+
+
+
+ +
+ + +
+ + + +

+ tv_version() -> tuple[str, str, str] + +

+ + +
+ +

Returns the software name, version and language.

+ +
+ Source code in pytvpaint/george/grg_base.py +
625
+626
+627
+628
+629
+630
+631
+632
+633
+634
def tv_version() -> tuple[str, str, str]:
+    """Returns the software name, version and language."""
+    cmd_fields = [
+        ("software_name", str),
+        ("version", str),
+        ("language", str),
+    ]
+    res = tv_parse_list(send_cmd("tv_Version"), with_fields=cmd_fields)
+    software_name, version, language = res.values()
+    return software_name, version, language
+
+
+
+ +
+ + +
+ + + +

+ tv_quit() -> None + +

+ + +
+ +

Closes the TVPaint instance.

+ +
+ Source code in pytvpaint/george/grg_base.py +
637
+638
+639
def tv_quit() -> None:
+    """Closes the TVPaint instance."""
+    send_cmd("tv_Quit")
+
+
+
+ +
+ + +
+ + + +

+ tv_host2back() -> None + +

+ + +
+ +

Minimize the TVPaint window.

+ +
+ Source code in pytvpaint/george/grg_base.py +
642
+643
+644
def tv_host2back() -> None:
+    """Minimize the TVPaint window."""
+    send_cmd("tv_Host2Back")
+
+
+
+ +
+ + +
+ + + +

+ tv_host2front() -> None + +

+ + +
+ +

Restore the TVPaint window after being minimized.

+ +
+ Source code in pytvpaint/george/grg_base.py +
647
+648
+649
def tv_host2front() -> None:
+    """Restore the TVPaint window after being minimized."""
+    send_cmd("tv_Host2Front")
+
+
+
+ +
+ + +
+ + + +

+ tv_menu_hide() -> None + +

+ + +
+ +

Switch to inlay view and hide all non-docking panels.

+ +
+ Source code in pytvpaint/george/grg_base.py +
652
+653
+654
def tv_menu_hide() -> None:
+    """Switch to inlay view and hide all non-docking panels."""
+    send_cmd("tv_MenuHide")
+
+
+
+ +
+ + +
+ + + +

+ add_some_magic(i_am_a_badass: bool = False, magic_number: int | None = None) -> None + +

+ + +
+ +

Don't use ! Will change your life forever...

+ +
+ Source code in pytvpaint/george/grg_base.py +
657
+658
+659
+660
+661
+662
+663
+664
+665
+666
def add_some_magic(
+    i_am_a_badass: bool = False, magic_number: int | None = None
+) -> None:
+    """Don't use ! Will change your life forever..."""
+    if not i_am_a_badass:
+        log.warning("Sorry, you're not enough of a badass for this function...")
+
+    magic_number = magic_number or 14
+    send_cmd("tv_MagicNumber", magic_number)
+    log.info("Totally worth it, right ? ^^")
+
+
+
+ +
+ + +
+ + + +

+ tv_menu_show(menu_element: MenuElement | None = None, *menu_options: Any, current: bool = False) -> None + +

+ + +
+ +

For the complete documentation, see: https://www.tvpaint.com/doc/tvpaint-animation-11/george-commands#tv_menushow.

+ +
+ Source code in pytvpaint/george/grg_base.py +
669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
def tv_menu_show(
+    menu_element: MenuElement | None = None, *menu_options: Any, current: bool = False
+) -> None:
+    """For the complete documentation, see: https://www.tvpaint.com/doc/tvpaint-animation-11/george-commands#tv_menushow."""
+    cmd_args: list[str] = []
+
+    if current:
+        cmd_args.append("current")
+
+    if menu_element:
+        cmd_args.append(menu_element.value)
+
+    send_cmd("tv_MenuShow", *cmd_args, *menu_options)
+
+
+
+ +
+ + +
+ + + +

+ tv_request(msg: str, confirm_text: str = 'Yes', cancel_text: str = 'No') -> bool + +

+ + +
+ +

Open a confirmation prompt with a message.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
msg + str + +
+

the message to display

+
+
+ required +
confirm_text + str + +
+

the confirm button text. Defaults to "Yes".

+
+
+ 'Yes' +
cancel_text + str + +
+

the cancel button text. Defaults to "No".

+
+
+ 'No' +
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
bool + bool + +
+

True if clicked on "Yes"

+
+
+ +
+ Source code in pytvpaint/george/grg_base.py +
684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
def tv_request(msg: str, confirm_text: str = "Yes", cancel_text: str = "No") -> bool:
+    """Open a confirmation prompt with a message.
+
+    Args:
+        msg: the message to display
+        confirm_text: the confirm button text. Defaults to "Yes".
+        cancel_text: the cancel button text. Defaults to "No".
+
+    Returns:
+        bool: True if clicked on "Yes"
+    """
+    return bool(int(send_cmd("tv_Request", msg, confirm_text, cancel_text)))
+
+
+
+ +
+ + +
+ + + +

+ tv_req_num(value: int, min: int, max: int, title: str = 'Enter Value') -> int | None + +

+ + +
+ +

Open a prompt to request an integer (within a range).

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
value + int + +
+

the initial value

+
+
+ required +
min + int + +
+

the minimum value

+
+
+ required +
max + int + +
+

the maximum value

+
+
+ required +
title + str + +
+

title of the prompt dialog. Defaults to "Enter Value".

+
+
+ 'Enter Value' +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ int | None + +
+

the value or None if cancelled

+
+
+ +
+ Source code in pytvpaint/george/grg_base.py +
698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
def tv_req_num(
+    value: int, min: int, max: int, title: str = "Enter Value"
+) -> int | None:
+    """Open a prompt to request an integer (within a range).
+
+    Args:
+        value: the initial value
+        min: the minimum value
+        max: the maximum value
+        title: title of the prompt dialog. Defaults to "Enter Value".
+
+    Returns:
+        the value or None if cancelled
+    """
+    res = send_cmd("tv_ReqNum", value, min, max, title, handle_string=False)
+    return None if res.lower() == "cancel" else int(res)
+
+
+
+ +
+ + +
+ + + +

+ tv_req_angle(value: float, min: float, max: float, title: str = 'Enter Value') -> float | None + +

+ + +
+ +

Open a prompt to request an angle (in degree).

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
value + float + +
+

the initial value

+
+
+ required +
min + float + +
+

the minimum value

+
+
+ required +
max + float + +
+

the maximum value

+
+
+ required +
title + str + +
+

title of the prompt. Defaults to "Enter Value".

+
+
+ 'Enter Value' +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ float | None + +
+

the value or None if cancelled

+
+
+ +
+ Source code in pytvpaint/george/grg_base.py +
716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
def tv_req_angle(
+    value: float, min: float, max: float, title: str = "Enter Value"
+) -> float | None:
+    """Open a prompt to request an angle (in degree).
+
+    Args:
+        value: the initial value
+        min: the minimum value
+        max: the maximum value
+        title: title of the prompt. Defaults to "Enter Value".
+
+    Returns:
+        the value or None if cancelled
+    """
+    res = send_cmd("tv_ReqAngle", value, min, max, title, handle_string=False)
+    return None if res.lower() == "cancel" else float(res)
+
+
+
+ +
+ + +
+ + + +

+ tv_req_float(value: float, min: float, max: float, title: str = 'Enter value') -> float | None + +

+ + +
+ +

Open a prompt to request a float.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
value + float + +
+

the initial value

+
+
+ required +
min + float + +
+

the minimum value

+
+
+ required +
max + float + +
+

the maximum value

+
+
+ required +
title + str + +
+

title of the prompt. Defaults to "Enter Value".

+
+
+ 'Enter value' +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ float | None + +
+

the value or None if cancelled

+
+
+ +
+ Source code in pytvpaint/george/grg_base.py +
734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
def tv_req_float(
+    value: float, min: float, max: float, title: str = "Enter value"
+) -> float | None:
+    """Open a prompt to request a float.
+
+    Args:
+        value: the initial value
+        min: the minimum value
+        max: the maximum value
+        title: title of the prompt. Defaults to "Enter Value".
+
+    Returns:
+        the value or None if cancelled
+    """
+    res = send_cmd("tv_ReqFloat", value, min, max, title, handle_string=False)
+    return None if res.lower() == "cancel" else float(res)
+
+
+
+ +
+ + +
+ + + +

+ tv_req_string(title: str, text: str) -> str | None + +

+ + +
+ +

Open a prompt to request a string.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
title + str + +
+

title of the requester. Defaults to "Enter Value".

+
+
+ required +
text + str + +
+

the initial value

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str | None + +
+

the value or None if cancelled

+
+
+ +
+ Source code in pytvpaint/george/grg_base.py +
752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
def tv_req_string(title: str, text: str) -> str | None:
+    """Open a prompt to request a string.
+
+    Args:
+        title: title of the requester. Defaults to "Enter Value".
+        text: the initial value
+
+    Returns:
+        the value or None if cancelled
+    """
+    cmd_args = ["|".join([title, text])]
+    if "\n" in text:
+        cmd_args.insert(0, "multiline")
+    res = send_cmd("tv_ReqString", *cmd_args, handle_string=False)
+    return None if res.lower() == "cancel" else res
+
+
+
+ +
+ + +
+ + + +

+ tv_list_request(entries: list[Entry]) -> tuple[int, str] + +

+ + +
+ +

Open a prompt to request a selection in a list.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
entries + list[pytvpaint.george.grg_base.Entry] + +
+

the list of entries (either a single entry or sub entries)

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ tuple[int, str] + +
+

the position, the entry

+
+
+ +
+ Source code in pytvpaint/george/grg_base.py +
781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
def tv_list_request(entries: list[Entry]) -> tuple[int, str]:
+    """Open a prompt to request a selection in a list.
+
+    Args:
+        entries: the list of entries (either a single entry or sub entries)
+
+    Returns:
+        the position, the entry
+    """
+    entries_str = "|".join(map(_entry_to_str, entries))
+    res = send_cmd("tv_ListRequest", entries_str, error_values=["-1 Cancel"])
+    res_obj = tv_parse_list(
+        res,
+        with_fields=[
+            ("index", int),
+            ("entry", str),
+        ],
+    )
+    index, entry = tuple(res_obj.values())
+    return int(index), entry
+
+
+
+ +
+ + +
+ + + +

+ tv_req_file(mode: FileMode, title: str = '', working_dir: Path | str | None = None, default_name: str | None = None, extension_filter: str | None = None) -> Path | None + +

+ + +
+ +

Open a prompt to request a file.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
mode + pytvpaint.george.grg_base.FileMode + +
+

save or load

+
+
+ required +
title + str + +
+

the title of the request

+
+
+ '' +
working_dir + pathlib.Path | str | None + +
+

the default folder to go. Defaults to None.

+
+
+ None +
default_name + str | None + +
+

the default name. Defaults to None.

+
+
+ None +
extension_filter + str | None + +
+

display the files with this extension. Defaults to None.

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ pathlib.Path | None + +
+

the choosen path or None if cancelled

+
+
+ +
+ Source code in pytvpaint/george/grg_base.py +
803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
def tv_req_file(
+    mode: FileMode,
+    title: str = "",
+    working_dir: Path | str | None = None,
+    default_name: str | None = None,
+    extension_filter: str | None = None,
+) -> Path | None:
+    """Open a prompt to request a file.
+
+    Args:
+        mode: save or load
+        title: the title of the request
+        working_dir: the default folder to go. Defaults to None.
+        default_name: the default name. Defaults to None.
+        extension_filter: display the files with this extension. Defaults to None.
+
+    Returns:
+        the choosen path or None if cancelled
+    """
+    cmd_args = [
+        title,
+        Path(working_dir).as_posix() if working_dir else None,
+        default_name,
+        extension_filter,
+    ]
+
+    arg_str = "|".join([v if v is not None else "" for v in cmd_args])
+    res = send_cmd("tv_ReqFile", f"{mode.value} {arg_str}", handle_string=False)
+
+    return None if res.lower() == "cancel" else Path(res)
+
+
+
+ +
+ + +
+ + + +

+ tv_undo() -> None + +

+ + +
+ +

Do an undo.

+ +
+ Source code in pytvpaint/george/grg_base.py +
835
+836
+837
def tv_undo() -> None:
+    """Do an undo."""
+    send_cmd("tv_Undo")
+
+
+
+ +
+ + +
+ + + +

+ tv_update_undo() -> None + +

+ + +
+ +

Copies the contents of the current image in the current layer into the buffer undo memory.

+

None of the draw commands described in this section updates this buffer memory. +If you click on the Undo button after executing a George program, everything that the program has drawn in your image will be deleted. +With this function you can update the undo buffer memory whenever you wish (for example at the beginning of the program).

+ +
+ Source code in pytvpaint/george/grg_base.py +
840
+841
+842
+843
+844
+845
+846
+847
def tv_update_undo() -> None:
+    """Copies the contents of the current image in the current layer into the buffer undo memory.
+
+    None of the draw commands described in this section updates this buffer memory.
+    If you click on the Undo button after executing a George program, everything that the program has drawn in your image will be deleted.
+    With this function you can update the undo buffer memory whenever you wish (for example at the beginning of the program).
+    """
+    send_cmd("tv_UpdateUndo")
+
+
+
+ +
+ + +
+ + + +

+ tv_undo_open_stack() -> None + +

+ + +
+ +

Open an 'undo' stack.

+

Surround a piece of code with tv_undoopenstack ... tv_undoclosestack, then multiple undo will be added to this stack, and closing this stack will undo everything inside. +(To be sure the script returns to the expected result use tv_updateundo before tv_undoopenstack)

+ +
+ Source code in pytvpaint/george/grg_base.py +
850
+851
+852
+853
+854
+855
+856
def tv_undo_open_stack() -> None:
+    """Open an 'undo' stack.
+
+    Surround a piece of code with tv_undoopenstack ... tv_undoclosestack, then multiple undo will be added to this stack, and closing this stack will undo everything inside.
+    (To be sure the script returns to the expected result use tv_updateundo before tv_undoopenstack)
+    """
+    send_cmd("tv_UndoOpenStack")
+
+
+
+ +
+ + +
+ + + +

+ tv_undo_close_stack(name: str = '') -> None + +

+ + +
+ +

Close an 'undo' stack (See tv_undo_open_stack).

+ +
+ Source code in pytvpaint/george/grg_base.py +
859
+860
+861
def tv_undo_close_stack(name: str = "") -> None:
+    """Close an 'undo' stack (See tv_undo_open_stack)."""
+    send_cmd("tv_UndoCloseStack", name)
+
+
+
+ +
+ + +
+ + + +

+ tv_save_mode_get() -> tuple[SaveFormat, list[str]] + +

+ + +
+ +

Get the saving alpha mode.

+ +
+ Source code in pytvpaint/george/grg_base.py +
864
+865
+866
+867
+868
+869
def tv_save_mode_get() -> tuple[SaveFormat, list[str]]:
+    """Get the saving alpha mode."""
+    res = send_cmd("tv_SaveMode")
+    res_split = res.split()
+    save_format = tv_cast_to_type(res_split.pop(0), SaveFormat)
+    return save_format, res_split
+
+
+
+ +
+ + +
+ + + +

+ tv_save_mode_set(save_format: SaveFormat, *format_options: str | int | float) -> None + +

+ + +
+ +

Set the saving alpha mode.

+ +
+ Source code in pytvpaint/george/grg_base.py +
872
+873
+874
+875
+876
def tv_save_mode_set(
+    save_format: SaveFormat, *format_options: str | int | float
+) -> None:
+    """Set the saving alpha mode."""
+    send_cmd("tv_SaveMode", save_format.value, *format_options)
+
+
+
+ +
+ + +
+ + + +

+ tv_alpha_load_mode_get() -> AlphaMode + +

+ + +
+ +

Set the loading alpha mode.

+ +
+ Source code in pytvpaint/george/grg_base.py +
879
+880
+881
+882
def tv_alpha_load_mode_get() -> AlphaMode:
+    """Set the loading alpha mode."""
+    res = send_cmd("tv_AlphaLoadMode")
+    return tv_cast_to_type(res, AlphaMode)
+
+
+
+ +
+ + +
+ + + +

+ tv_alpha_load_mode_set(mode: AlphaMode) -> None + +

+ + +
+ +

Get the loading alpha mode.

+ +
+ Source code in pytvpaint/george/grg_base.py +
885
+886
+887
def tv_alpha_load_mode_set(mode: AlphaMode) -> None:
+    """Get the loading alpha mode."""
+    send_cmd("tv_AlphaLoadMode", mode.value)
+
+
+
+ +
+ + +
+ + + +

+ tv_alpha_save_mode_get() -> AlphaSaveMode + +

+ + +
+ +

Get the saving alpha mode.

+ +
+ Source code in pytvpaint/george/grg_base.py +
890
+891
+892
+893
def tv_alpha_save_mode_get() -> AlphaSaveMode:
+    """Get the saving alpha mode."""
+    res = send_cmd("tv_AlphaSaveMode")
+    return tv_cast_to_type(res, AlphaSaveMode)
+
+
+
+ +
+ + +
+ + + +

+ tv_alpha_save_mode_set(mode: AlphaSaveMode) -> None + +

+ + +
+ +

Set the saving alpha mode.

+ +
+ Source code in pytvpaint/george/grg_base.py +
896
+897
+898
def tv_alpha_save_mode_set(mode: AlphaSaveMode) -> None:
+    """Set the saving alpha mode."""
+    send_cmd("tv_AlphaSaveMode", mode.value)
+
+
+
+ +
+ + +
+ + + +

+ tv_mark_in_get(reference: MarkReference) -> tuple[int, MarkAction] + +

+ + +
+ +

Get markin of the project / clip.

+ +
+ Source code in pytvpaint/george/grg_base.py +
901
+902
+903
+904
+905
def tv_mark_in_get(
+    reference: MarkReference,
+) -> tuple[int, MarkAction]:
+    """Get markin of the project / clip."""
+    return _tv_mark(MarkType.MARKIN, reference)
+
+
+
+ +
+ + +
+ + + +

+ tv_mark_in_set(reference: MarkReference, frame: int | None, action: MarkAction) -> tuple[int, MarkAction] + +

+ + +
+ +

Set markin of the project / clip.

+ +
+ Source code in pytvpaint/george/grg_base.py +
908
+909
+910
+911
+912
+913
+914
def tv_mark_in_set(
+    reference: MarkReference,
+    frame: int | None,
+    action: MarkAction,
+) -> tuple[int, MarkAction]:
+    """Set markin of the project / clip."""
+    return _tv_mark(MarkType.MARKIN, reference, frame, action)
+
+
+
+ +
+ + +
+ + + +

+ tv_mark_out_get(reference: MarkReference) -> tuple[int, MarkAction] + +

+ + +
+ +

Get markout of the project / clip.

+ +
+ Source code in pytvpaint/george/grg_base.py +
917
+918
+919
+920
+921
def tv_mark_out_get(
+    reference: MarkReference,
+) -> tuple[int, MarkAction]:
+    """Get markout of the project / clip."""
+    return _tv_mark(MarkType.MARKOUT, reference)
+
+
+
+ +
+ + +
+ + + +

+ tv_mark_out_set(reference: MarkReference, frame: int | None, action: MarkAction) -> tuple[int, MarkAction] + +

+ + +
+ +

Set markout of the project / clip.

+ +
+ Source code in pytvpaint/george/grg_base.py +
924
+925
+926
+927
+928
def tv_mark_out_set(
+    reference: MarkReference, frame: int | None, action: MarkAction
+) -> tuple[int, MarkAction]:
+    """Set markout of the project / clip."""
+    return _tv_mark(MarkType.MARKOUT, reference, frame, action)
+
+
+
+ +
+ + +
+ + + +

+ tv_get_active_shape() -> TVPShape + +

+ + +
+ +

Get the current shape.

+ +
+ Source code in pytvpaint/george/grg_base.py +
965
+966
+967
def tv_get_active_shape() -> TVPShape:
+    """Get the current shape."""
+    return tv_cast_to_type(send_cmd("tv_GetActiveShape"), TVPShape)
+
+
+
+ +
+ + +
+ + + +

+ tv_set_active_shape(shape: TVPShape, **shape_kwargs: Any) -> None + +

+ + +
+ +

Set the current shape and its tool parameters.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
shape + pytvpaint.george.grg_base.TVPShape + +
+

the shape to set

+
+
+ required +
**shape_kwargs + typing.Any + +
+

the shape specific parameters as keyword arguments

+
+
+ {} +
+ +
+ Source code in pytvpaint/george/grg_base.py +
970
+971
+972
+973
+974
+975
+976
+977
def tv_set_active_shape(shape: TVPShape, **shape_kwargs: Any) -> None:
+    """Set the current shape and its tool parameters.
+
+    Args:
+        shape: the shape to set
+        **shape_kwargs: the shape specific parameters as keyword arguments
+    """
+    send_cmd("tv_SetActiveShape", shape.value, *args_dict_to_list(shape_kwargs))
+
+
+
+ +
+ + +
+ + + +

+ tv_set_a_pen_rgba(color: RGBColor, alpha: int | None = None) -> RGBColor + +

+ + +
+ +

Set the APen RGBA color.

+ +
+ Source code in pytvpaint/george/grg_base.py +
1027
+1028
+1029
def tv_set_a_pen_rgba(color: RGBColor, alpha: int | None = None) -> RGBColor:
+    """Set the APen RGBA color."""
+    return _tv_set_ab_pen("a", color.r, color.g, color.b, "rgb", a=alpha)
+
+
+
+ +
+ + +
+ + + +

+ tv_set_a_pen_hsl(color: HSLColor) -> HSLColor + +

+ + +
+ +

Set the A Pen HSL color.

+ +
+ Source code in pytvpaint/george/grg_base.py +
1032
+1033
+1034
def tv_set_a_pen_hsl(color: HSLColor) -> HSLColor:
+    """Set the A Pen HSL color."""
+    return _tv_set_ab_pen("a", color.h, color.s, color.l, color_format="hsl")
+
+
+
+ +
+ + +
+ + + +

+ tv_set_b_pen_rgba(color: RGBColor, alpha: int | None = None) -> RGBColor + +

+ + +
+ +

Set the B Pen RGBA color.

+ +
+ Source code in pytvpaint/george/grg_base.py +
1037
+1038
+1039
def tv_set_b_pen_rgba(color: RGBColor, alpha: int | None = None) -> RGBColor:
+    """Set the B Pen RGBA color."""
+    return _tv_set_ab_pen("b", color.r, color.g, color.b, color_format="rgb", a=alpha)
+
+
+
+ +
+ + +
+ + + +

+ tv_set_b_pen_hsl(color: HSLColor) -> HSLColor + +

+ + +
+ +

Set the B Pen HSL color.

+ +
+ Source code in pytvpaint/george/grg_base.py +
1042
+1043
+1044
def tv_set_b_pen_hsl(color: HSLColor) -> HSLColor:
+    """Set the B Pen HSL color."""
+    return _tv_set_ab_pen("b", color.h, color.s, color.l, color_format="hsl")
+
+
+
+ +
+ + +
+ + + +

+ tv_pen(size: float) -> float + +

+ + +
+ +

Change current pen tool size. This function is most likely deprecated it is undocumented in the George reference but still works.

+ +
+ Source code in pytvpaint/george/grg_base.py +
1047
+1048
+1049
+1050
def tv_pen(size: float) -> float:
+    """Change current pen tool size. This function is most likely deprecated it is undocumented in the George reference but still works."""
+    res = tv_parse_dict(send_cmd("tv_Pen", size), with_fields=[("size", float)])
+    return cast(float, res["size"])
+
+
+
+ +
+ + +
+ + + +

+ tv_pen_brush_get(tool_mode: bool = False) -> TVPPenBrush + +

+ + +
+ +

Get pen brush parameters.

+ +
+ Source code in pytvpaint/george/grg_base.py +
1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
def tv_pen_brush_get(tool_mode: bool = False) -> TVPPenBrush:
+    """Get pen brush parameters."""
+    args = ("toolmode", "backup") if tool_mode else ("backup",)
+    result = send_cmd("tv_PenBrush", *args)
+
+    # Remove the first value which is tv_penbrush
+    result = result[len("tv_penbrush") + 1 :]
+
+    res = tv_parse_dict(result, with_fields=TVPPenBrush)
+    return TVPPenBrush(**res)
+
+
+
+ +
+ + +
+ + + +

+ tv_pen_brush_set(mode: DrawingMode | None = None, size: int | None = None, opacity: int | None = None, tool_mode: bool = False, reset: bool = False) -> TVPPenBrush + +

+ + +
+ +

Manage pen brush.

+ +
+ Source code in pytvpaint/george/grg_base.py +
1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
def tv_pen_brush_set(
+    mode: DrawingMode | None = None,
+    size: int | None = None,
+    opacity: int | None = None,
+    tool_mode: bool = False,
+    reset: bool = False,
+) -> TVPPenBrush:
+    """Manage pen brush."""
+    args = {
+        "mode": mode.value if mode else None,
+        "size": size,
+        "opacity": opacity,
+    }
+
+    args_list = args_dict_to_list(args)
+
+    if tool_mode:
+        args_list.append("toolmode")
+    if reset:
+        args_list.append("reset")
+
+    send_cmd("tv_PenBrush", *args_list)
+
+    # Since TVPaint is returning only the values that were modified
+    # this is almost impossible to parse so we call get
+    return tv_pen_brush_get()
+
+
+
+ +
+ + +
+ + + +

+ tv_line(xy1: tuple[int, int], xy2: tuple[int, int], right_click: bool = False, dry: bool = False) -> None + +

+ + +
+ +

Draw a line (with the current brush).

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
xy1 + tuple[int, int] + +
+

start position as (x, y)

+
+
+ required +
xy2 + tuple[int, int] + +
+

end position as (x, y)

+
+
+ required +
right_click + bool + +
+

True to emulate right click, False to emulate left click. Default is False

+
+
+ False +
dry + bool + +
+

True for dry mode

+
+
+ False +
+ +
+ Source code in pytvpaint/george/grg_base.py +
1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
def tv_line(
+    xy1: tuple[int, int],
+    xy2: tuple[int, int],
+    right_click: bool = False,
+    dry: bool = False,
+) -> None:
+    """Draw a line (with the current brush).
+
+    Args:
+        xy1: start position as (x, y)
+        xy2: end position as (x, y)
+        right_click: True to emulate right click, False to emulate left click. Default is False
+        dry: True for dry mode
+    """
+    args = [
+        *xy1,
+        *xy2,
+        bool(right_click),
+        bool(dry),
+    ]
+    send_cmd("tv_Line", *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_text(text: str, x: int, y: int, use_b_pen: bool = False) -> None + +

+ + +
+ +

Write text in a layer instance.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
text + str + +
+

text to write

+
+
+ required +
x + int + +
+

text x position

+
+
+ required +
y + int + +
+

text y position

+
+
+ required +
use_b_pen + bool + +
+

True will use b pen, False will use A pen

+
+
+ False +
+ +
+ Source code in pytvpaint/george/grg_base.py +
1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
def tv_text(text: str, x: int, y: int, use_b_pen: bool = False) -> None:
+    """Write text in a layer instance.
+
+    Args:
+        text: text to write
+        x: text x position
+        y: text y position
+        use_b_pen: True will use b pen, False will use A pen
+    """
+    send_cmd("tv_Text", x, y, int(use_b_pen), text)
+
+
+
+ +
+ + +
+ + + +

+ tv_text_brush(text: str) -> None + +

+ + +
+ +

Set the text for the text brush.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
text + str + +
+

text to write

+
+
+ required +
+ +
+ Source code in pytvpaint/george/grg_base.py +
1128
+1129
+1130
+1131
+1132
+1133
+1134
def tv_text_brush(text: str) -> None:
+    """Set the text for the text brush.
+
+    Args:
+        text: text to write
+    """
+    send_cmd("tv_TextBrush", text)
+
+
+
+ +
+ + +
+ + + +

+ tv_rect(tlx: float, tly: float, brx: float, bry: float, button: RectButton | None = None) -> None + +

+ + +
+ +

Draws an unfilled rectangle.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
tlx + float + +
+

top left x coordinate

+
+
+ required +
tly + float + +
+

top left y coordinate

+
+
+ required +
brx + float + +
+

bottom right x coordinate

+
+
+ required +
bry + float + +
+

bottom right y coordinate

+
+
+ required +
button + pytvpaint.george.grg_base.RectButton | None + +
+

use left or right click button (left draws, right erases)

+
+
+ None +
+ +
+ Source code in pytvpaint/george/grg_base.py +
1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
def tv_rect(
+    tlx: float,
+    tly: float,
+    brx: float,
+    bry: float,
+    button: RectButton | None = None,
+) -> None:
+    """Draws an unfilled rectangle.
+
+    Args:
+        tlx: top left x coordinate
+        tly: top left y coordinate
+        brx: bottom right x coordinate
+        bry: bottom right y coordinate
+        button: use left or right click button (left draws, right erases)
+    """
+    args: list[float] = [tlx, tly, brx, bry]
+    if button:
+        args.append(button.value)
+    send_cmd("tv_Rect", *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_rect_fill(tlx: float, tly: float, brx: float, bry: float, grx: float = 0, gry: float = 0, erase_mode: bool = False, tool_mode: bool = False) -> None + +

+ + +
+ +

Draws a filled rectangle.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
tlx + float + +
+

top left x coordinate

+
+
+ required +
tly + float + +
+

top left y coordinate

+
+
+ required +
brx + float + +
+

bottom right x coordinate

+
+
+ required +
bry + float + +
+

bottom right y coordinate

+
+
+ required +
grx + float + +
+

gradient vector x

+
+
+ 0 +
gry + float + +
+

gradient vector y

+
+
+ 0 +
erase_mode + bool + +
+

erase drawing mode

+
+
+ False +
tool_mode + bool + +
+

manage drawing mode

+
+
+ False +
+ +
+ Source code in pytvpaint/george/grg_base.py +
1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
def tv_rect_fill(
+    tlx: float,
+    tly: float,
+    brx: float,
+    bry: float,
+    grx: float = 0,
+    gry: float = 0,
+    erase_mode: bool = False,
+    tool_mode: bool = False,
+) -> None:
+    """Draws a filled rectangle.
+
+    Args:
+        tlx: top left x coordinate
+        tly: top left y coordinate
+        brx: bottom right x coordinate
+        bry: bottom right y coordinate
+        grx: gradient vector x
+        gry: gradient vector y
+        erase_mode: erase drawing mode
+        tool_mode: manage drawing mode
+    """
+    args: list[Any] = [tlx, tly, brx, bry, grx, gry, int(erase_mode)]
+    if tool_mode:
+        args.insert(0, "toolmode")
+    send_cmd("tv_RectFill", *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_fast_line(x1: float, y1: float, x2: float, y2: float, r: int = 255, b: int = 255, g: int = 0, a: int = 255) -> None + +

+ + +
+ +

Draw a line (1 pixel size and not antialiased).

+ +
+ Source code in pytvpaint/george/grg_base.py +
1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
def tv_fast_line(
+    x1: float,
+    y1: float,
+    x2: float,
+    y2: float,
+    r: int = 255,
+    b: int = 255,
+    g: int = 0,
+    a: int = 255,
+) -> None:
+    """Draw a line (1 pixel size and not antialiased)."""
+    send_cmd("tv_fastline", x1, y1, x2, y2, r, g, b, a)
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/george/project/index.html b/api/george/project/index.html new file mode 100644 index 0000000..8106c92 --- /dev/null +++ b/api/george/project/index.html @@ -0,0 +1,4700 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Project - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Project related George functions

+ + +
+ + + + +
+ +

Project related George functions and enums.

+ + + +
+ + + + + + + + +
+ + + +

+ TVPProject(id: str, path: Path, width: int, height: int, pixel_aspect_ratio: float, frame_rate: float, field_order: FieldOrder, start_frame: int) + + + dataclass + + +

+ + +
+ + +

TVPaint project info values.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ BackgroundMode + + +

+ + +
+

+ Bases: enum.Enum

+ + +

The project background mode.

+ + + +

Attributes:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
CHECK + +
+ +
+
COLOR + +
+ +
+
NONE + +
+ +
+
+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ + + +
+ + + +

+ tv_background_get() -> tuple[BackgroundMode, tuple[RGBColor, RGBColor] | RGBColor | None] + +

+ + +
+ +

Get the background mode of the project, and the color(s) if in color or check mode.

+ + + +

Returns:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
mode + pytvpaint.george.grg_project.BackgroundMode + +
+

the background mode

+
+
colors + tuple[pytvpaint.george.grg_base.RGBColor, pytvpaint.george.grg_base.RGBColor] | pytvpaint.george.grg_base.RGBColor | None + +
+

the background colors if any

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
def tv_background_get() -> (
+    tuple[BackgroundMode, tuple[RGBColor, RGBColor] | RGBColor | None]
+):
+    """Get the background mode of the project, and the color(s) if in `color` or `check` mode.
+
+    Returns:
+        mode: the background mode
+        colors: the background colors if any
+
+    """
+    res = send_cmd("tv_Background")
+
+    mode, *values = res.split(" ")
+
+    if mode == BackgroundMode.NONE.value:
+        return BackgroundMode.NONE, None
+
+    if mode == BackgroundMode.CHECK.value:
+        c1 = map(int, values[:3])
+        c2 = map(int, values[3:])
+        return BackgroundMode.CHECK, (RGBColor(*c1), RGBColor(*c2))
+
+    return BackgroundMode.COLOR, RGBColor(*map(int, values))
+
+
+
+ +
+ + +
+ + + +

+ tv_background_set(mode: BackgroundMode, color: tuple[RGBColor, RGBColor] | RGBColor | None = None) -> None + +

+ + +
+ +

Set the background mode of the project.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
mode + pytvpaint.george.grg_project.BackgroundMode + +
+

color mode (None, checker or one color)

+
+
+ required +
color + tuple[pytvpaint.george.grg_base.RGBColor, pytvpaint.george.grg_base.RGBColor] | pytvpaint.george.grg_base.RGBColor | None + +
+

None for None mode, RBGColor for one color, and tuple of RGBColors for checker

+
+
+ None +
+ +
+ Source code in pytvpaint/george/grg_project.py +
79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
def tv_background_set(
+    mode: BackgroundMode,
+    color: tuple[RGBColor, RGBColor] | RGBColor | None = None,
+) -> None:
+    """Set the background mode of the project.
+
+    Args:
+        mode: color mode (None, checker or one color)
+        color: None for None mode, RBGColor for one color, and tuple of RGBColors for checker
+    """
+    args = []
+
+    if mode == BackgroundMode.CHECK and isinstance(color, tuple):
+        c1, c2 = color
+        args = [c1.r, c1.g, c1.b, c2.r, c2.g, c2.b]
+    elif mode == BackgroundMode.COLOR and isinstance(color, RGBColor):
+        args = [color.r, color.g, color.b]
+
+    send_cmd("tv_Background", mode.value, *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_project_new(project_path: Path | str, width: int = 1920, height: int = 1080, pixel_aspect_ratio: float = 1.0, frame_rate: float = 24.0, field_order: FieldOrder = FieldOrder.NONE, start_frame: int = 1) -> str + +

+ + +
+ +

Create a new project.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if an error occurred during the project creation

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
@try_cmd(exception_msg="Project created but may be corrupted")
+def tv_project_new(
+    project_path: Path | str,
+    width: int = 1920,
+    height: int = 1080,
+    pixel_aspect_ratio: float = 1.0,
+    frame_rate: float = 24.0,
+    field_order: FieldOrder = FieldOrder.NONE,
+    start_frame: int = 1,
+) -> str:
+    """Create a new project.
+
+    Raises:
+        GeorgeError: if an error occurred during the project creation
+    """
+    return send_cmd(
+        "tv_ProjectNew",
+        Path(project_path).as_posix(),
+        width,
+        height,
+        pixel_aspect_ratio,
+        frame_rate,
+        field_order.value,
+        start_frame,
+        error_values=[GrgErrorValue.EMPTY],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_load_project(project_path: Path | str, silent: bool = False) -> str + +

+ + +
+ +

Load a file as a project if possible or open Import panel.

+ + + +

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+ FileNotFoundError + +
+

if the project file doesn't exist

+
+
+ pytvpaint.george.exceptions.GeorgeError + +
+

if the provided file is in an invalid format

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
@try_cmd(exception_msg="Invalid format")
+def tv_load_project(project_path: Path | str, silent: bool = False) -> str:
+    """Load a file as a project if possible or open Import panel.
+
+    Raises:
+        FileNotFoundError: if the project file doesn't exist
+        GeorgeError: if the provided file is in an invalid format
+    """
+    project_path = Path(project_path)
+
+    if not project_path.exists():
+        raise FileNotFoundError(f"Project not found at: {project_path.as_posix()}")
+
+    args: list[Any] = [project_path.as_posix()]
+
+    if silent:
+        args.extend(["silent", int(silent)])
+
+    return send_cmd("tv_LoadProject", *args, error_values=[-1])
+
+
+
+ +
+ + +
+ + + +

+ tv_save_project(project_path: Path | str) -> None + +

+ + +
+ +

Save the current project as tvpp.

+ +
+ Source code in pytvpaint/george/grg_project.py +
149
+150
+151
+152
+153
+154
+155
+156
+157
+158
def tv_save_project(project_path: Path | str) -> None:
+    """Save the current project as tvpp."""
+    project_path = Path(project_path)
+    parent = project_path.parent
+
+    if not parent.exists():
+        msg = f"Can't save because parent folder does not exist: {parent.as_posix()}"
+        raise ValueError(msg)
+
+    send_cmd("tv_SaveProject", project_path.as_posix())
+
+
+
+ +
+ + +
+ + + +

+ tv_project_duplicate() -> None + +

+ + +
+ +

Duplicate the current project.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if an error occurred during the project creation.

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
161
+162
+163
+164
+165
+166
+167
+168
@try_cmd(exception_msg="Can't duplicate the current project")
+def tv_project_duplicate() -> None:
+    """Duplicate the current project.
+
+    Raises:
+        GeorgeError: if an error occurred during the project creation.
+    """
+    send_cmd("tv_ProjectDuplicate", error_values=[0])
+
+
+
+ +
+ + +
+ + + +

+ tv_project_enum_id(position: int) -> str + +

+ + +
+ +

Get the id of the project at the given position.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if no project found at the provided position.

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
171
+172
+173
+174
+175
+176
+177
+178
@try_cmd(exception_msg="No project at provided position")
+def tv_project_enum_id(position: int) -> str:
+    """Get the id of the project at the given position.
+
+    Raises:
+        GeorgeError: if no project found at the provided position.
+    """
+    return send_cmd("tv_ProjectEnumId", position, error_values=[GrgErrorValue.NONE])
+
+
+
+ +
+ + +
+ + + +

+ tv_project_current_id() -> str + +

+ + +
+ +

Get the id of the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
181
+182
+183
def tv_project_current_id() -> str:
+    """Get the id of the current project."""
+    return send_cmd("tv_ProjectCurrentId")
+
+
+
+ +
+ + +
+ + + +

+ tv_project_info(project_id: str) -> TVPProject + +

+ + +
+ +

Get info of the given project.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid project id

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid project id",
+)
+def tv_project_info(project_id: str) -> TVPProject:
+    """Get info of the given project.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid project id
+    """
+    result = send_cmd("tv_ProjectInfo", project_id, error_values=[GrgErrorValue.EMPTY])
+    project = tv_parse_list(result, with_fields=TVPProject)
+    project["id"] = project_id
+    return TVPProject(**project)
+
+
+
+ +
+ + +
+ + + +

+ tv_get_project_name() -> str + +

+ + +
+ +

Returns the save path of the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
202
+203
+204
def tv_get_project_name() -> str:
+    """Returns the save path of the current project."""
+    return send_cmd("tv_GetProjectName")
+
+
+
+ +
+ + +
+ + + +

+ tv_project_select(project_id: str) -> str + +

+ + +
+ +

Make the given project current.

+ +
+ Source code in pytvpaint/george/grg_project.py +
207
+208
+209
def tv_project_select(project_id: str) -> str:
+    """Make the given project current."""
+    return send_cmd("tv_ProjectSelect", project_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_project_close(project_id: str) -> None + +

+ + +
+ +

Close the given project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
212
+213
+214
def tv_project_close(project_id: str) -> None:
+    """Close the given project."""
+    send_cmd("tv_ProjectClose", project_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_resize_project(width: int, height: int) -> None + +

+ + +
+ +

Resize the current project.

+ +
+ Note +

creates a resized copy of the project with a new id

+
+
+ Source code in pytvpaint/george/grg_project.py +
217
+218
+219
+220
+221
+222
+223
def tv_resize_project(width: int, height: int) -> None:
+    """Resize the current project.
+
+    Note:
+        creates a resized copy of the project with a new id
+    """
+    send_cmd("tv_ResizeProject", width, height)
+
+
+
+ +
+ + +
+ + + +

+ tv_resize_page(width: int, height: int, resize_opt: ResizeOption) -> None + +

+ + +
+ +

Create a new resized project and close the current one.

+ +
+ Source code in pytvpaint/george/grg_project.py +
226
+227
+228
def tv_resize_page(width: int, height: int, resize_opt: ResizeOption) -> None:
+    """Create a new resized project and close the current one."""
+    send_cmd("tv_ResizePage", width, height, resize_opt.value)
+
+
+
+ +
+ + +
+ + + +

+ tv_get_width() -> int + +

+ + +
+ +

Get the current project width.

+ +
+ Source code in pytvpaint/george/grg_project.py +
231
+232
+233
def tv_get_width() -> int:
+    """Get the current project width."""
+    return int(send_cmd("tv_GetWidth"))
+
+
+
+ +
+ + +
+ + + +

+ tv_get_height() -> int + +

+ + +
+ +

Get the current project height.

+ +
+ Source code in pytvpaint/george/grg_project.py +
236
+237
+238
def tv_get_height() -> int:
+    """Get the current project height."""
+    return int(send_cmd("tv_GetHeight"))
+
+
+
+ +
+ + +
+ + + +

+ tv_ratio() -> float + +

+ + +
+ +

Get the current project pixel aspect ratio.

+ +
+ Bug +

Doesn't work and always returns an empty string

+
+
+ Source code in pytvpaint/george/grg_project.py +
241
+242
+243
+244
+245
+246
+247
def tv_ratio() -> float:
+    """Get the current project pixel aspect ratio.
+
+    Bug:
+        Doesn't work and always returns an empty string
+    """
+    return float(send_cmd("tv_GetRatio", error_values=[GrgErrorValue.EMPTY]))
+
+
+
+ +
+ + +
+ + + +

+ tv_get_field() -> FieldOrder + +

+ + +
+ +

Get the current project field mode.

+ +
+ Source code in pytvpaint/george/grg_project.py +
250
+251
+252
def tv_get_field() -> FieldOrder:
+    """Get the current project field mode."""
+    return tv_cast_to_type(send_cmd("tv_GetField"), cast_type=FieldOrder)
+
+
+
+ +
+ + +
+ + + +

+ tv_project_save_sequence(export_path: Path | str, use_camera: bool = False, start: int | None = None, end: int | None = None) -> None + +

+ + +
+ +

Save the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
def tv_project_save_sequence(
+    export_path: Path | str,
+    use_camera: bool = False,
+    start: int | None = None,
+    end: int | None = None,
+) -> None:
+    """Save the current project."""
+    export_path = Path(export_path).resolve()
+    args: list[Any] = [export_path.as_posix()]
+
+    if use_camera:
+        args.append("camera")
+    if start is not None and end is not None:
+        args.extend((start, end))
+
+    send_cmd(
+        "tv_ProjectSaveSequence",
+        *args,
+        error_values=[-1],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_project_render_camera(project_id: str) -> str + +

+ + +
+ +

Render the given project's camera view to a new project.

+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ str + +
+

the new project id

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
def tv_project_render_camera(project_id: str) -> str:
+    """Render the given project's camera view to a new project.
+
+    Returns:
+        the new project id
+    """
+    return send_cmd(
+        "tv_ProjectRenderCamera",
+        project_id,
+        error_values=[GrgErrorValue.ERROR],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_frame_rate_get() -> tuple[float, float] + +

+ + +
+ +

Get the framerate of the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
def tv_frame_rate_get() -> tuple[float, float]:
+    """Get the framerate of the current project."""
+    parse = tv_parse_list(
+        send_cmd("tv_FrameRate", 1, "info"),
+        with_fields=[
+            ("project_fps", float),
+            ("playback_fps", float),
+        ],
+    )
+    project_fps, playback_fps = parse.values()
+    return project_fps, playback_fps
+
+
+
+ +
+ + +
+ + + +

+ tv_frame_rate_set(frame_rate: float, time_stretch: bool = False, preview: bool = False) -> None + +

+ + +
+ +

Get the framerate of the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
def tv_frame_rate_set(
+    frame_rate: float, time_stretch: bool = False, preview: bool = False
+) -> None:
+    """Get the framerate of the current project."""
+    args: list[Any] = []
+    if time_stretch:
+        args = ["timestretch"]
+    if preview:
+        args = ["preview"]
+    args.insert(0, frame_rate)
+    send_cmd("tv_FrameRate", *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_frame_rate_project_set(frame_rate: float, time_stretch: bool = False) -> None + +

+ + +
+ +

Set the framerate of the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
316
+317
+318
+319
+320
+321
def tv_frame_rate_project_set(frame_rate: float, time_stretch: bool = False) -> None:
+    """Set the framerate of the current project."""
+    args: list[Any] = [frame_rate]
+    if time_stretch:
+        args.append("timestretch")
+    send_cmd("tv_FrameRate", *args)
+
+
+
+ +
+ + +
+ + + +

+ tv_frame_rate_preview_set(frame_rate: float) -> None + +

+ + +
+ +

Set the framerate of the preview (playback).

+ +
+ Source code in pytvpaint/george/grg_project.py +
324
+325
+326
def tv_frame_rate_preview_set(frame_rate: float) -> None:
+    """Set the framerate of the preview (playback)."""
+    send_cmd("tv_FrameRate", frame_rate, "preview")
+
+
+
+ +
+ + +
+ + + +

+ tv_project_current_frame_get() -> int + +

+ + +
+ +

Get the current frame of the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
329
+330
+331
def tv_project_current_frame_get() -> int:
+    """Get the current frame of the current project."""
+    return int(send_cmd("tv_ProjectCurrentFrame"))
+
+
+
+ +
+ + +
+ + + +

+ tv_project_current_frame_set(frame: int) -> int + +

+ + +
+ +

Set the current frame of the current project.

+ +
+ Note +

this is relative to the current clip markin

+
+
+ Source code in pytvpaint/george/grg_project.py +
334
+335
+336
+337
+338
+339
+340
def tv_project_current_frame_set(frame: int) -> int:
+    """Set the current frame of the current project.
+
+    Note:
+        this is relative to the current clip markin
+    """
+    return int(send_cmd("tv_ProjectCurrentFrame", frame))
+
+
+
+ +
+ + +
+ + + +

+ tv_load_palette(palette_path: Path | str) -> None + +

+ + +
+ +

Load a palette(s) from a file/directory.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ FileNotFoundError + +
+

if palette was not found at the provided path

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
343
+344
+345
+346
+347
+348
+349
+350
+351
+352
def tv_load_palette(palette_path: Path | str) -> None:
+    """Load a palette(s) from a file/directory.
+
+    Raises:
+        FileNotFoundError: if palette was not found at the provided path
+    """
+    palette_path = Path(palette_path)
+    if not palette_path.exists():
+        raise FileNotFoundError(f"Palette not found at: {palette_path.as_posix()}")
+    send_cmd("tv_LoadPalette", palette_path.as_posix())
+
+
+
+ +
+ + +
+ + + +

+ tv_save_palette(palette_path: Path | str) -> None + +

+ + +
+ +

Save the current palette.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ FileNotFoundError + +
+

if palette save directory doesn't exist

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
def tv_save_palette(palette_path: Path | str) -> None:
+    """Save the current palette.
+
+    Raises:
+        FileNotFoundError: if palette save directory doesn't exist
+    """
+    palette_path = Path(palette_path)
+
+    if not palette_path.parent.exists():
+        parent_path = palette_path.parent.as_posix()
+        raise NotADirectoryError(
+            f"Can't save palette because parent folder doesn't exist: {parent_path}"
+        )
+
+    send_cmd("tv_SavePalette", palette_path.as_posix())
+
+
+
+ +
+ + +
+ + + +

+ tv_project_save_video_dependencies() -> None + +

+ + +
+ +

Saves current project video dependencies.

+ +
+ Source code in pytvpaint/george/grg_project.py +
372
+373
+374
def tv_project_save_video_dependencies() -> None:
+    """Saves current project video dependencies."""
+    send_cmd("tv_ProjectSaveVideoDependencies")
+
+
+
+ +
+ + +
+ + + +

+ tv_project_save_audio_dependencies() -> None + +

+ + +
+ +

Saves current project audio dependencies.

+ +
+ Source code in pytvpaint/george/grg_project.py +
377
+378
+379
def tv_project_save_audio_dependencies() -> None:
+    """Saves current project audio dependencies."""
+    send_cmd("tv_ProjectSaveAudioDependencies")
+
+
+
+ +
+ + +
+ + + +

+ tv_sound_project_info(project_id: str, track_index: int) -> TVPSound + +

+ + +
+ +

Get information about a project soundtrack.

+ +
+ Source code in pytvpaint/george/grg_project.py +
382
+383
+384
+385
+386
+387
+388
def tv_sound_project_info(project_id: str, track_index: int) -> TVPSound:
+    """Get information about a project soundtrack."""
+    res = send_cmd(
+        "tv_SoundProjectInfo", project_id, track_index, error_values=[-1, -2, -3]
+    )
+    res_parse = tv_parse_list(res, with_fields=TVPSound)
+    return TVPSound(**res_parse)
+
+
+
+ +
+ + +
+ + + +

+ tv_sound_project_new(sound_path: Path | str) -> None + +

+ + +
+ +

Add a new soundtrack to the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
391
+392
+393
+394
+395
+396
+397
def tv_sound_project_new(sound_path: Path | str) -> None:
+    """Add a new soundtrack to the current project."""
+    path = Path(sound_path)
+    if not path.exists():
+        raise ValueError(f"Sound file not found at : {path.as_posix()}")
+
+    send_cmd("tv_SoundProjectNew", path.as_posix(), error_values=[-1, -3, -4])
+
+
+
+ +
+ + +
+ + + +

+ tv_sound_project_remove(track_index: int) -> None + +

+ + +
+ +

Remove a soundtrack from the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
400
+401
+402
def tv_sound_project_remove(track_index: int) -> None:
+    """Remove a soundtrack from the current project."""
+    send_cmd("tv_SoundProjectRemove", track_index, error_values=[-2])
+
+
+
+ +
+ + +
+ + + +

+ tv_sound_project_reload(project_id: str, track_index: int) -> None + +

+ + +
+ +

Reload a project soundtracks file.

+ +
+ Source code in pytvpaint/george/grg_project.py +
405
+406
+407
+408
+409
+410
+411
+412
def tv_sound_project_reload(project_id: str, track_index: int) -> None:
+    """Reload a project soundtracks file."""
+    send_cmd(
+        "tv_SoundProjectReload",
+        project_id,
+        track_index,
+        error_values=[-1, -2, -3],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_sound_project_adjust(track_index: int, mute: bool | None = None, volume: float | None = None, offset: float | None = None, fade_in_start: float | None = None, fade_in_stop: float | None = None, fade_out_start: float | None = None, fade_out_stop: float | None = None, color_index: int | None = None) -> None + +

+ + +
+ +

Change the current project's soundtrack settings.

+ +
+ Source code in pytvpaint/george/grg_project.py +
415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
def tv_sound_project_adjust(
+    track_index: int,
+    mute: bool | None = None,
+    volume: float | None = None,
+    offset: float | None = None,
+    fade_in_start: float | None = None,
+    fade_in_stop: float | None = None,
+    fade_out_start: float | None = None,
+    fade_out_stop: float | None = None,
+    color_index: int | None = None,
+) -> None:
+    """Change the current project's soundtrack settings."""
+    cur_options = tv_sound_project_info(tv_project_current_id(), track_index)
+    args: list[int | float | None] = []
+
+    optional_args = [
+        (int(mute) if mute is not None else None, int(cur_options.mute)),
+        (volume, cur_options.volume),
+        (offset, cur_options.offset),
+        (fade_in_start, cur_options.fade_in_start),
+        (fade_in_stop, cur_options.fade_in_stop),
+        (fade_out_start, cur_options.fade_out_start),
+        (fade_out_stop, cur_options.fade_out_stop),
+    ]
+    for arg, default_value in optional_args:
+        args.append(arg if arg is not None else default_value)
+
+    args.append(color_index)
+    send_cmd("tv_SoundProjectAdjust", track_index, *args, error_values=[-2, -3])
+
+
+
+ +
+ + +
+ + + +

+ tv_project_header_info_get(project_id: str) -> str + +

+ + +
+ +

Get the project header info.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid project id

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid project id",
+)
+def tv_project_header_info_get(project_id: str) -> str:
+    """Get the project header info.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid project id
+    """
+    return send_cmd(
+        "tv_ProjectHeaderInfo",
+        project_id,
+        error_values=[GrgErrorValue.ERROR],
+    ).strip('"')
+
+
+
+ +
+ + +
+ + + +

+ tv_project_header_info_set(project_id: str, text: str) -> None + +

+ + +
+ +

Set the project header info.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid project id

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid project id",
+)
+def tv_project_header_info_set(project_id: str, text: str) -> None:
+    """Set the project header info.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid project id
+    """
+    send_cmd(
+        "tv_ProjectHeaderInfo",
+        project_id,
+        text,
+        error_values=[GrgErrorValue.ERROR],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_project_header_author_get(project_id: str) -> str + +

+ + +
+ +

Get the project author info.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid project id

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid project id",
+)
+def tv_project_header_author_get(project_id: str) -> str:
+    """Get the project author info.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid project id
+    """
+    return send_cmd(
+        "tv_ProjectHeaderAuthor",
+        project_id,
+        error_values=[GrgErrorValue.ERROR],
+    ).strip('"')
+
+
+
+ +
+ + +
+ + + +

+ tv_project_header_author_set(project_id: str, text: str) -> None + +

+ + +
+ +

Set the project author info.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid project id

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid project id",
+)
+def tv_project_header_author_set(project_id: str, text: str) -> None:
+    """Set the project author info.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid project id
+    """
+    send_cmd(
+        "tv_ProjectHeaderAuthor",
+        project_id,
+        text,
+        error_values=[GrgErrorValue.ERROR],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_project_header_notes_get(project_id: str) -> str + +

+ + +
+ +

Get the project notes.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid project id

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid project id",
+)
+def tv_project_header_notes_get(project_id: str) -> str:
+    """Get the project notes.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid project id
+    """
+    return send_cmd(
+        "tv_ProjectHeaderNotes",
+        project_id,
+        error_values=[GrgErrorValue.ERROR],
+    ).strip('"')
+
+
+
+ +
+ + +
+ + + +

+ tv_project_header_notes_set(project_id: str, text: str) -> None + +

+ + +
+ +

Set the project notes.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.NoObjectWithIdError + +
+

if given an invalid project id

+
+
+ +
+ Source code in pytvpaint/george/grg_project.py +
533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
@try_cmd(
+    raise_exc=NoObjectWithIdError,
+    exception_msg="Invalid project id",
+)
+def tv_project_header_notes_set(project_id: str, text: str) -> None:
+    """Set the project notes.
+
+    Raises:
+        NoObjectWithIdError: if given an invalid project id
+    """
+    send_cmd(
+        "tv_ProjectHeaderNotes",
+        project_id,
+        text,
+        error_values=[GrgErrorValue.ERROR],
+    )
+
+
+
+ +
+ + +
+ + + +

+ tv_start_frame_get() -> int + +

+ + +
+ +

Get the start frame of the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
551
+552
+553
def tv_start_frame_get() -> int:
+    """Get the start frame of the current project."""
+    return int(send_cmd("tv_StartFrame"))
+
+
+
+ +
+ + +
+ + + +

+ tv_start_frame_set(start_frame: int) -> int + +

+ + +
+ +

Set the start frame of the current project.

+ +
+ Source code in pytvpaint/george/grg_project.py +
556
+557
+558
def tv_start_frame_set(start_frame: int) -> int:
+    """Set the start frame of the current project."""
+    return int(send_cmd("tv_StartFrame", start_frame))
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/george/scene/index.html b/api/george/scene/index.html new file mode 100644 index 0000000..58d1935 --- /dev/null +++ b/api/george/scene/index.html @@ -0,0 +1,1850 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Scene - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Scene related George functions

+ + +
+ + + + +
+ +

Scene related George functions.

+ + + +
+ + + + + + + + + + +
+ + + +

+ tv_scene_enum_id(position: int) -> int + +

+ + +
+ +

Get the id of the scene at the given position in the current project.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.george.exceptions.GeorgeError + +
+

if no scene found at the provided position

+
+
+ +
+ Source code in pytvpaint/george/grg_scene.py +
 9
+10
+11
+12
+13
+14
+15
+16
@try_cmd(exception_msg="No scene at provided position")
+def tv_scene_enum_id(position: int) -> int:
+    """Get the id of the scene at the given position in the current project.
+
+    Raises:
+        GeorgeError: if no scene found at the provided position
+    """
+    return int(send_cmd("tv_SceneEnumId", position, error_values=[GrgErrorValue.NONE]))
+
+
+
+ +
+ + +
+ + + +

+ tv_scene_current_id() -> int + +

+ + +
+ +

Get the id of the current scene.

+ +
+ Source code in pytvpaint/george/grg_scene.py +
19
+20
+21
def tv_scene_current_id() -> int:
+    """Get the id of the current scene."""
+    return int(send_cmd("tv_SceneCurrentId"))
+
+
+
+ +
+ + +
+ + + +

+ tv_scene_move(scene_id: int, position: int) -> None + +

+ + +
+ +

Move a scene to another position.

+ +
+ Source code in pytvpaint/george/grg_scene.py +
24
+25
+26
def tv_scene_move(scene_id: int, position: int) -> None:
+    """Move a scene to another position."""
+    send_cmd("tv_SceneMove", scene_id, position)
+
+
+
+ +
+ + +
+ + + +

+ tv_scene_new() -> None + +

+ + +
+ +

Create a new scene (with a new clip) after the current scene.

+ +
+ Source code in pytvpaint/george/grg_scene.py +
29
+30
+31
def tv_scene_new() -> None:
+    """Create a new scene (with a new clip) after the current scene."""
+    send_cmd("tv_SceneNew")
+
+
+
+ +
+ + +
+ + + +

+ tv_scene_duplicate(scene_id: int) -> None + +

+ + +
+ +

Duplicate the given scene.

+ +
+ Source code in pytvpaint/george/grg_scene.py +
34
+35
+36
def tv_scene_duplicate(scene_id: int) -> None:
+    """Duplicate the given scene."""
+    send_cmd("tv_SceneDuplicate", scene_id)
+
+
+
+ +
+ + +
+ + + +

+ tv_scene_close(scene_id: int) -> None + +

+ + +
+ +

Remove the given scene.

+ +
+ Source code in pytvpaint/george/grg_scene.py +
39
+40
+41
def tv_scene_close(scene_id: int) -> None:
+    """Remove the given scene."""
+    send_cmd("tv_SceneClose", scene_id)
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/objects/camera/index.html b/api/objects/camera/index.html new file mode 100644 index 0000000..41b9408 --- /dev/null +++ b/api/objects/camera/index.html @@ -0,0 +1,2407 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Camera - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Camera class

+ + +
+ + + + +
+ +

Camera related objects and classes.

+ + + +
+ + + + + + + + +
+ + + +

+ Camera(clip: Clip, data: george.TVPCamera | None = None) + +

+ + +
+

+ Bases: pytvpaint.utils.Refreshable

+ + +

The Camera class represents the camera in a TVPaint clip.

+

There's only one camera in the clip so instantiating multiple objects of this class won't create cameras.

+ +
+ Source code in pytvpaint/camera.py +
26
+27
+28
+29
+30
def __init__(self, clip: Clip, data: george.TVPCamera | None = None) -> None:
+    super().__init__()
+    self._clip = clip
+    self._data: george.TVPCamera = data or george.tv_camera_info_get()
+    self._points: list[CameraPoint] = []
+
+
+ + + +
+ + + + + + + +
+ + + +

+ clip: Clip + + + property + + +

+ + +
+ +

The camera's clip.

+
+ +
+ +
+ + + +

+ points: Iterator[CameraPoint] + + + property + + +

+ + +
+ +

Iterator for the CameraPoint objects of the camera.

+
+ +
+ + + + +
+ + + +

+ refresh() -> None + +

+ + +
+ +

Refreshed the camera data.

+ +
+ Source code in pytvpaint/camera.py +
32
+33
+34
+35
+36
def refresh(self) -> None:
+    """Refreshed the camera data."""
+    if not self.refresh_on_call and self._data:
+        return
+    self._data = george.tv_camera_info_get()
+
+
+
+ +
+ + +
+ + + +

+ make_current() -> None + +

+ + +
+ +

Makes the parent clip the current one, thereby making sure the correct camera will be set.

+ +
+ Source code in pytvpaint/camera.py +
47
+48
+49
def make_current(self) -> None:
+    """Makes the parent clip the current one, thereby making sure the correct camera will be set."""
+    self._clip.make_current()
+
+
+
+ +
+ + +
+ + + +

+ anti_aliasing() -> int + +

+ + +
+ +

The antialiasing value of the camera.

+ +
+ Source code in pytvpaint/camera.py +
106
+107
+108
+109
+110
@refreshed_property
+@set_as_current
+def anti_aliasing(self) -> int:
+    """The antialiasing value of the camera."""
+    return self._data.anti_aliasing
+
+
+
+ +
+ + +
+ + + +

+ insert_point(index: int, x: int, y: int, angle: int, scale: float) -> CameraPoint + +

+ + +
+ +

Insert a new point in the camera path.

+ +
+ Source code in pytvpaint/camera.py +
123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
@set_as_current
+def insert_point(
+    self,
+    index: int,
+    x: int,
+    y: int,
+    angle: int,
+    scale: float,
+) -> CameraPoint:
+    """Insert a new point in the camera path."""
+    return CameraPoint.new(self, index, x, y, angle, scale)
+
+
+
+ +
+ + +
+ + + +

+ get_point_data_at(position: float) -> george.TVPCameraPoint + +

+ + +
+ +

Get the points data interpolated at that position (between 0 and 1).

+ +
+ Source code in pytvpaint/camera.py +
145
+146
+147
+148
+149
@set_as_current
+def get_point_data_at(self, position: float) -> george.TVPCameraPoint:
+    """Get the points data interpolated at that position (between 0 and 1)."""
+    position = max(0.0, min(position, 1.0))
+    return george.tv_camera_interpolation(position)
+
+
+
+ +
+ + +
+ + + +

+ remove_point(index: int) -> None + +

+ + +
+ +

Remove a point at that index.

+ +
+ Source code in pytvpaint/camera.py +
151
+152
+153
+154
+155
+156
+157
+158
@set_as_current
+def remove_point(self, index: int) -> None:
+    """Remove a point at that index."""
+    try:
+        point = next(p for i, p in enumerate(self.points) if i == index)
+        point.remove()
+    except StopIteration:
+        pass
+
+
+
+ +
+ + + +
+ +
+ + +
+ +
+ + + +

+ CameraPoint(index: int, camera: Camera, data: george.TVPCameraPoint | None = None) + +

+ + +
+

+ Bases: pytvpaint.utils.Removable

+ + +

A CameraPoint is a point on the camera path.

+

You can use them to animate the camera movement.

+ +
+ Source code in pytvpaint/camera.py +
167
+168
+169
+170
+171
+172
+173
+174
+175
+176
def __init__(
+    self,
+    index: int,
+    camera: Camera,
+    data: george.TVPCameraPoint | None = None,
+) -> None:
+    super().__init__()
+    self._index: int = index
+    self._camera: Camera = camera
+    self._data = data or george.tv_camera_enum_points(self._index)
+
+
+ + + +
+ + + + + + + +
+ + + +

+ data: george.TVPCameraPoint + + + property + + +

+ + +
+ +

Returns the raw data of the point.

+
+ +
+ +
+ + + +

+ index: int + + + property + + +

+ + +
+ +

The index of the point in the path.

+
+ +
+ +
+ + + +

+ camera: Camera + + + property + + +

+ + +
+ +

The camera instance it belongs to.

+
+ +
+ + + + +
+ + + +

+ refresh() -> None + +

+ + +
+ +

Refreshed the camera point data.

+ +
+ Source code in pytvpaint/camera.py +
178
+179
+180
+181
+182
+183
def refresh(self) -> None:
+    """Refreshed the camera point data."""
+    super().refresh()
+    if not self.refresh_on_call and self._data:
+        return
+    self._data = george.tv_camera_enum_points(self._index)
+
+
+
+ +
+ + +
+ + + +

+ new(camera: Camera, index: int, x: int, y: int, angle: int, scale: float) -> CameraPoint + + + classmethod + + +

+ + +
+ +

Create a new point and add it to the camera path at that index.

+ +
+ Source code in pytvpaint/camera.py +
276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
@classmethod
+def new(
+    cls,
+    camera: Camera,
+    index: int,
+    x: int,
+    y: int,
+    angle: int,
+    scale: float,
+) -> CameraPoint:
+    """Create a new point and add it to the camera path at that index."""
+    george.tv_camera_insert_point(index, x, y, angle, scale)
+    return cls(index, camera)
+
+
+
+ +
+ + +
+ + + +

+ remove() -> None + +

+ + +
+ +

Remove the camera point.

+ +
+ Warning +

the point instance won't be usable after that call

+
+
+ Source code in pytvpaint/camera.py +
290
+291
+292
+293
+294
+295
+296
+297
def remove(self) -> None:
+    """Remove the camera point.
+
+    Warning:
+        the point instance won't be usable after that call
+    """
+    george.tv_camera_remove_point(self.index)
+    self.mark_removed()
+
+
+
+ +
+ + + +
+ +
+ + +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/objects/clip/index.html b/api/objects/clip/index.html new file mode 100644 index 0000000..a336c4b --- /dev/null +++ b/api/objects/clip/index.html @@ -0,0 +1,6508 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Clip - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Clip class

+ + +
+ + + + +
+ +

Clip object class module.

+ + + +
+ + + + + + + + +
+ + + +

+ Clip(clip_id: int, project: Project | None = None) + +

+ + +
+

+ Bases: pytvpaint.utils.Removable, pytvpaint.utils.Renderable

+ + +

A Clip is a container for layers and is part of a Scene.

+ +

Constructs a Clip from an existing TVPaint clip (giving its id).

+ +
+ Note +

You should use Clip.new to create a new clip

+
+ + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
clip_id + int + +
+

an existing clip id

+
+
+ required +
project + pytvpaint.project.Project | None + +
+

the project or the current one if None

+
+
+ None +
+ +
+ Source code in pytvpaint/clip.py +
30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
def __init__(
+    self,
+    clip_id: int,
+    project: Project | None = None,
+) -> None:
+    """Constructs a Clip from an existing TVPaint clip (giving its id).
+
+    Note:
+        You should use `Clip.new` to create a new clip
+
+    Args:
+        clip_id: an existing clip id
+        project: the project or the current one if None
+    """
+    from pytvpaint.project import Project
+
+    super().__init__()
+    self._id = clip_id
+    self._project = project or Project.current_project()
+    self._data = george.tv_clip_info(self.id)
+
+
+ + + +
+ + + + + + + +
+ + + +

+ id: int + + + property + + +

+ + +
+ +

The clip id.

+
+ +
+ +
+ + + +

+ project: Project + + + property + + +

+ + +
+ +

The clip's project.

+
+ +
+ +
+ + + +

+ scene: Scene + + + property + writable + + +

+ + +
+ +

The clip's scene.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if clip cannot be found in the project

+
+
+
+ +
+ +
+ + + +

+ camera: Camera + + + property + + +

+ + +
+ +

The clip camera.

+
+ +
+ +
+ + + +

+ position: int + + + property + writable + + +

+ + +
+ +

The position of the clip in the scene.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if clip cannot be found in the project

+
+
+
+ +
+ +
+ + + +

+ name: str + + + property + writable + + +

+ + +
+ +

The clip name.

+
+ +
+ +
+ + + +

+ duration: int + + + property + + +

+ + +
+ +

The duration of the clip in frames. Takes into account the mark in/out of the clip.

+
+ +
+ +
+ + + +

+ is_selected: bool + + + property + writable + + +

+ + +
+ +

Returns True if the clip is selected.

+
+ +
+ +
+ + + +

+ is_visible: bool + + + property + writable + + +

+ + +
+ +

Returns True if the clip is visible.

+
+ +
+ +
+ + + +

+ color_index: int + + + property + writable + + +

+ + +
+ +

Get the clip color index.

+
+ +
+ +
+ + + +

+ action_text: str + + + property + writable + + +

+ + +
+ +

Get the action text of the clip.

+
+ +
+ +
+ + + +

+ dialog_text: str + + + property + writable + + +

+ + +
+ +

Get the dialog text of the clip.

+
+ +
+ +
+ + + +

+ note_text: str + + + property + writable + + +

+ + +
+ +

Get the note text of the clip.

+
+ +
+ +
+ + + +

+ current_frame: int + + + property + writable + + +

+ + +
+ +

Returns the current frame in the clip (timeline) relative to the project start frame.

+
+ +
+ +
+ + + +

+ layer_ids: Iterator[int] + + + property + + +

+ + +
+ +

Iterator over the layer ids.

+
+ +
+ +
+ + + +

+ layers: Iterator[Layer] + + + property + + +

+ + +
+ +

Iterator over the clip's layers.

+
+ +
+ +
+ + + +

+ layer_names: Iterator[str] + + + property + + +

+ + +
+ +

Iterator over the clip's layer names.

+
+ +
+ +
+ + + +

+ current_layer: Layer + + + property + + +

+ + +
+ +

Get the current layer in the clip.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if clip cannot be found in the project

+
+
+
+ +
+ +
+ + + +

+ selected_layers: Iterator[Layer] + + + property + + +

+ + +
+ +

Iterator over the selected layers.

+
+ +
+ +
+ + + +

+ visible_layers: Iterator[Layer] + + + property + + +

+ + +
+ +

Iterator over the visible layers.

+
+ +
+ +
+ + + +

+ mark_in: int | None + + + property + writable + + +

+ + +
+ +

Get the mark in of the clip.

+
+ +
+ +
+ + + +

+ mark_out: int | None + + + property + writable + + +

+ + +
+ +

Get the mark out of the clip.

+
+ +
+ +
+ + + +

+ layer_colors: Iterator[LayerColor] + + + property + + +

+ + +
+ +

Iterator over the layer colors.

+
+ +
+ +
+ + + +

+ bookmarks: Iterator[int] + + + property + + +

+ + +
+ +

Iterator over the clip bookmarks.

+
+ +
+ +
+ + + +

+ sounds: Iterator[ClipSound] + + + property + + +

+ + +
+ +

Iterates through the clip's soundtracks.

+
+ +
+ + + + +
+ + + +

+ new(name: str, scene: Scene | None = None, project: Project | None = None) -> Clip + + + classmethod + + +

+ + +
+ +

Creates a new clip.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
name + str + +
+

the new clip name

+
+
+ required +
scene + pytvpaint.scene.Scene | None + +
+

the scene or the current one if None. Defaults to None.

+
+
+ None +
project + pytvpaint.project.Project | None + +
+

the project or the current one if None. Defaults to None.

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Clip + pytvpaint.clip.Clip + +
+

the newly created clip

+
+
+ +
+ Source code in pytvpaint/clip.py +
61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
@classmethod
+def new(
+    cls,
+    name: str,
+    scene: Scene | None = None,
+    project: Project | None = None,
+) -> Clip:
+    """Creates a new clip.
+
+    Args:
+        name: the new clip name
+        scene: the scene or the current one if None. Defaults to None.
+        project: the project or the current one if None. Defaults to None.
+
+    Returns:
+        Clip: the newly created clip
+    """
+    from pytvpaint.project import Project
+
+    project = project or Project.current_project()
+    project.make_current()
+
+    scene = scene or project.current_scene
+    scene.make_current()
+
+    name = utils.get_unique_name(project.clip_names, name)
+    george.tv_clip_new(name)
+
+    return Clip.current_clip()
+
+
+
+ +
+ + +
+ + + +

+ refresh() -> None + +

+ + +
+ +

Refreshes the clip data.

+ +
+ Source code in pytvpaint/clip.py +
91
+92
+93
+94
+95
+96
def refresh(self) -> None:
+    """Refreshes the clip data."""
+    super().refresh()
+    if not self.refresh_on_call and self._data:
+        return
+    self._data = george.tv_clip_info(self._id)
+
+
+
+ +
+ + +
+ + + +

+ make_current() -> None + +

+ + +
+ +

Make the clip the current one.

+ +
+ Source code in pytvpaint/clip.py +
 98
+ 99
+100
+101
+102
def make_current(self) -> None:
+    """Make the clip the current one."""
+    if george.tv_clip_current_id() == self.id:
+        return
+    george.tv_clip_select(self.id)
+
+
+
+ +
+ + +
+ + + +

+ start() -> int + +

+ + +
+ +

The start frame of the clip.

+ +
+ Source code in pytvpaint/clip.py +
170
+171
+172
+173
@refreshed_property
+def start(self) -> int:
+    """The start frame of the clip."""
+    return self._data.first_frame + self.project.start_frame
+
+
+
+ +
+ + +
+ + + +

+ end() -> int + +

+ + +
+ +

The end frame of the clip.

+ +
+ Source code in pytvpaint/clip.py +
175
+176
+177
+178
@refreshed_property
+def end(self) -> int:
+    """The end frame of the clip."""
+    return self._data.last_frame + self.project.start_frame
+
+
+
+ +
+ + +
+ + + +

+ timeline_start() -> int + +

+ + +
+ +

The start frame of the clip relative to the project's timeline.

+ +
+ Source code in pytvpaint/clip.py +
185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
@refreshed_property
+def timeline_start(self) -> int:
+    """The start frame of the clip relative to the project's timeline."""
+    # get clip real start in project timeline
+    clip_real_start = 0
+    for clip in self.project.clips:
+        if clip == self:
+            break
+        clip_start = clip.mark_in or clip.start
+        clip_end = clip.mark_out or clip.end
+        clip_duration = (clip_end - clip_start) + 1
+        clip_real_start += clip_duration
+
+    return clip_real_start + self.project.start_frame
+
+
+
+ +
+ + +
+ + + +

+ timeline_end() -> int + +

+ + +
+ +

The end frame of the clip relative to the project's timeline.

+ +
+ Source code in pytvpaint/clip.py +
200
+201
+202
+203
+204
+205
+206
@refreshed_property
+def timeline_end(self) -> int:
+    """The end frame of the clip relative to the project's timeline."""
+    clip_start = self.mark_in or self.start
+    clip_end = self.mark_out or self.end
+    clip_duration = clip_end - clip_start
+    return self.timeline_start + clip_duration
+
+
+
+ +
+ + +
+ + + +

+ frame_count() -> int + +

+ + +
+ +

The clip's frame count.

+ +
+ Source code in pytvpaint/clip.py +
208
+209
+210
+211
@refreshed_property
+def frame_count(self) -> int:
+    """The clip's frame count."""
+    return self._data.frame_count
+
+
+
+ +
+ + +
+ + + +

+ is_current() -> bool + +

+ + +
+ +

Returns True if the clip is the current one.

+ +
+ Source code in pytvpaint/clip.py +
273
+274
+275
+276
@refreshed_property
+def is_current(self) -> bool:
+    """Returns True if the clip is the current one."""
+    return Clip.current_clip_id() == self.id
+
+
+
+ +
+ + +
+ + + +

+ current_clip_id() -> int + + + staticmethod + + +

+ + +
+ +

Returns the id of the current clip.

+ +
+ Source code in pytvpaint/clip.py +
278
+279
+280
+281
@staticmethod
+def current_clip_id() -> int:
+    """Returns the id of the current clip."""
+    return george.tv_clip_current_id()
+
+
+
+ +
+ + +
+ + + +

+ current_clip() -> Clip + + + staticmethod + + +

+ + +
+ +

Returns the current clip object.

+ +
+ Source code in pytvpaint/clip.py +
283
+284
+285
+286
+287
+288
@staticmethod
+def current_clip() -> Clip:
+    """Returns the current clip object."""
+    from pytvpaint.project import Project
+
+    return Clip(Clip.current_clip_id(), Project.current_project())
+
+
+
+ +
+ + +
+ + + +

+ duplicate() -> Clip + +

+ + +
+ +

Duplicates the clip.

+ +
+ Note +

a new unique name is choosen for the duplicated clip with get_unique_name.

+
+
+ Source code in pytvpaint/clip.py +
302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
@set_as_current
+def duplicate(self) -> Clip:
+    """Duplicates the clip.
+
+    Note:
+        a new unique name is choosen for the duplicated clip with `get_unique_name`.
+    """
+    george.tv_clip_duplicate(self.id)
+    new_clip = self.project.current_clip
+    new_clip.name = utils.get_unique_name(self.project.clip_names, new_clip.name)
+    return new_clip
+
+
+
+ +
+ + +
+ + + +

+ remove() -> None + +

+ + +
+ +

Removes the clip.

+ +
+ Warning +

the instance is not usable after that call because it's marked as removed.

+
+
+ Source code in pytvpaint/clip.py +
314
+315
+316
+317
+318
+319
+320
+321
def remove(self) -> None:
+    """Removes the clip.
+
+    Warning:
+        the instance is not usable after that call because it's marked as removed.
+    """
+    george.tv_clip_close(self.id)
+    self.mark_removed()
+
+
+
+ +
+ + +
+ + + +

+ get_layer(by_id: int | None = None, by_name: str | None = None) -> Layer | None + +

+ + +
+ +

Get a specific layer by id or name.

+ +
+ Source code in pytvpaint/clip.py +
356
+357
+358
+359
+360
+361
+362
def get_layer(
+    self,
+    by_id: int | None = None,
+    by_name: str | None = None,
+) -> Layer | None:
+    """Get a specific layer by id or name."""
+    return utils.get_tvp_element(self.layers, by_id, by_name)
+
+
+
+ +
+ + +
+ + + +

+ add_layer(layer_name: str) -> Layer + +

+ + +
+ +

Add a new layer in the layer stack.

+ +
+ Source code in pytvpaint/clip.py +
364
+365
+366
+367
@set_as_current
+def add_layer(self, layer_name: str) -> Layer:
+    """Add a new layer in the layer stack."""
+    return Layer.new(name=layer_name, clip=self)
+
+
+
+ +
+ + +
+ + + +

+ load_media(media_path: Path, start_count: tuple[int, int] | None = None, stretch: bool = False, time_stretch: bool = False, preload: bool = False, with_name: str = '', field_order: george.FieldOrder = george.FieldOrder.LOWER) -> Layer + +

+ + +
+ +

Loads a media (single frame, video, ...) into a new Layer in the clip.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
media_path + pathlib.Path + +
+

the path of the media. If it's a file sequence, give the path of the first image.

+
+
+ required +
start_count + tuple[int, int] | None + +
+

the start and number of image of sequence to load. Defaults to None.

+
+
+ None +
stretch + bool + +
+

Stretch each image to the size of the layer. Defaults to None.

+
+
+ False +
time_stretch + bool + +
+

Once loaded, the layer will have a new number of image corresponding to the project framerate. Defaults to None.

+
+
+ False +
preload + bool + +
+

Load all the images in memory, no more reference on the files. Defaults to None.

+
+
+ False +
with_name + str + +
+

the name of the new layer

+
+
+ '' +
field_order + pytvpaint.george.FieldOrder + +
+

the field order. Defaults to None.

+
+
+ pytvpaint.george.FieldOrder.LOWER +
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Layer + pytvpaint.layer.Layer + +
+

the new layer created

+
+
+ +
+ Source code in pytvpaint/clip.py +
379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
@set_as_current
+@george.undoable
+def load_media(
+    self,
+    media_path: Path,
+    start_count: tuple[int, int] | None = None,
+    stretch: bool = False,
+    time_stretch: bool = False,
+    preload: bool = False,
+    with_name: str = "",
+    field_order: george.FieldOrder = george.FieldOrder.LOWER,
+) -> Layer:
+    """Loads a media (single frame, video, ...) into a new Layer in the clip.
+
+    Args:
+        media_path: the path of the media. If it's a file sequence, give the path of the first image.
+        start_count: the start and number of image of sequence to load. Defaults to None.
+        stretch: Stretch each image to the size of the layer. Defaults to None.
+        time_stretch: Once loaded, the layer will have a new number of image corresponding to the project framerate. Defaults to None.
+        preload: Load all the images in memory, no more reference on the files. Defaults to None.
+        with_name: the name of the new layer
+        field_order: the field order. Defaults to None.
+
+    Returns:
+        Layer: the new layer created
+    """
+    media_path = Path(media_path)
+
+    george.tv_load_sequence(
+        media_path,
+        start_count,
+        field_order,
+        stretch,
+        time_stretch,
+        preload,
+    )
+
+    new_layer = Layer.current_layer()
+    new_layer.name = with_name or media_path.stem
+
+    return new_layer
+
+
+
+ +
+ + +
+ + + +

+ render(output_path: Path | str | FileSequence, start: int | None = None, end: int | None = None, use_camera: bool = False, layer_selection: list[Layer] | None = None, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = None, format_opts: list[str] | None = None) -> None + +

+ + +
+ +

Render the clip to a single frame or frame sequence or movie.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
output_path + pathlib.Path | str | fileseq.filesequence.FileSequence + +
+

a single file or file sequence pattern

+
+
+ required +
start + int | None + +
+

the start frame to render or the mark in or the clip's start if None. Defaults to None.

+
+
+ None +
end + int | None + +
+

the end frame to render or the mark out or the clip's end if None. Defaults to None.

+
+
+ None +
use_camera + bool + +
+

use the camera for rendering, otherwise render the whole canvas. Defaults to False.

+
+
+ False +
layer_selection + list[pytvpaint.layer.Layer] | None + +
+

list of layers to render, if None render all of them. Defaults to None.

+
+
+ None +
alpha_mode + pytvpaint.george.AlphaSaveMode + +
+

the alpha mode for rendering. Defaults to george.AlphaSaveMode.PREMULTIPLY.

+
+
+ pytvpaint.george.AlphaSaveMode.PREMULTIPLY +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the background mode for rendering. Defaults to None.

+
+
+ None +
format_opts + list[str] | None + +
+

custom format options. Defaults to None.

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if requested range (start-end) not in clip range/bounds

+
+
+ ValueError + +
+

if output is a movie, and it's duration is equal to 1 frame

+
+
+ FileNotFoundError + +
+

if the render failed and no files were found on disk or missing frames

+
+
+ +
+ Note +

This functions uses the clip's range as a basis (start-end). This is different from a project range, which +uses the project timeline. For more details on the differences in frame ranges and the timeline in TVPaint, +please check the Usage/Rendering section of the documentation.

+
+
+ Warning +

Even tough pytvpaint does a pretty good job of correcting the frame ranges for rendering, we're still +encountering some weird edge cases where TVPaint will consider the range invalid for seemingly no reason.

+
+
+ Source code in pytvpaint/clip.py +
452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
@set_as_current
+def render(
+    self,
+    output_path: Path | str | FileSequence,
+    start: int | None = None,
+    end: int | None = None,
+    use_camera: bool = False,
+    layer_selection: list[Layer] | None = None,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = None,
+    format_opts: list[str] | None = None,
+) -> None:
+    """Render the clip to a single frame or frame sequence or movie.
+
+    Args:
+        output_path: a single file or file sequence pattern
+        start: the start frame to render or the mark in or the clip's start if None. Defaults to None.
+        end: the end frame to render or the mark out or the clip's end if None. Defaults to None.
+        use_camera: use the camera for rendering, otherwise render the whole canvas. Defaults to False.
+        layer_selection: list of layers to render, if None render all of them. Defaults to None.
+        alpha_mode: the alpha mode for rendering. Defaults to george.AlphaSaveMode.PREMULTIPLY.
+        background_mode: the background mode for rendering. Defaults to None.
+        format_opts: custom format options. Defaults to None.
+
+    Raises:
+        ValueError: if requested range (start-end) not in clip range/bounds
+        ValueError: if output is a movie, and it's duration is equal to 1 frame
+        FileNotFoundError: if the render failed and no files were found on disk or missing frames
+
+    Note:
+        This functions uses the clip's range as a basis (start-end). This  is different from a project range, which
+        uses the project timeline. For more details on the differences in frame ranges and the timeline in TVPaint,
+        please check the `Usage/Rendering` section of the documentation.
+
+    Warning:
+        Even tough pytvpaint does a pretty good job of correcting the frame ranges for rendering, we're still
+        encountering some weird edge cases where TVPaint will consider the range invalid for seemingly no reason.
+    """
+    default_start = self.mark_in or self.start
+    default_end = self.mark_out or self.end
+
+    self._render(
+        output_path,
+        default_start,
+        default_end,
+        start,
+        end,
+        use_camera,
+        layer_selection=layer_selection,
+        alpha_mode=alpha_mode,
+        background_mode=background_mode,
+        format_opts=format_opts,
+    )
+
+
+
+ +
+ + +
+ + + +

+ export_tvp(export_path: Path | str) -> None + +

+ + +
+ +

Exports the clip in .tvp format which can be imported as a project in TVPaint.

+ + + +

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if output extension is not (.tvp)

+
+
+ FileNotFoundError + +
+

if the render failed and no files were found on disk

+
+
+ +
+ Source code in pytvpaint/clip.py +
506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
@set_as_current
+def export_tvp(self, export_path: Path | str) -> None:
+    """Exports the clip in .tvp format which can be imported as a project in TVPaint.
+
+    Raises:
+        ValueError: if output extension is not (.tvp)
+        FileNotFoundError: if the render failed and no files were found on disk
+    """
+    export_path = Path(export_path)
+
+    if export_path.suffix != ".tvp":
+        raise ValueError("The file extension must be .tvp")
+
+    export_path.parent.mkdir(exist_ok=True, parents=True)
+    george.tv_save_clip(export_path)
+
+    if not export_path.exists():
+        raise FileNotFoundError(
+            f"Could not find output at : {export_path.as_posix()}"
+        )
+
+
+
+ +
+ + +
+ + + +

+ export_json(export_path: Path | str, save_format: george.SaveFormat, folder_pattern: str = '[%3li] %ln', file_pattern: str = '[%3ii] %ln', layer_selection: list[Layer] | None = None, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = None, format_opts: list[str] | None = None, all_images: bool = False, ignore_duplicates: bool = False) -> None + +

+ + +
+ +

Exports the instances (or all the images) of layers in the clip and a JSON file describing the structure of that clip.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

the JSON export path

+
+
+ required +
save_format + pytvpaint.george.SaveFormat + +
+

file format to use for rendering

+
+
+ required +
folder_pattern + str + +
+

the folder name pattern (%li: layer index, %ln: layer name, %fi: file index (added in 11.0.8)). Defaults to None.

+
+
+ '[%3li] %ln' +
file_pattern + str + +
+

the file name pattern (%li: layer index, %ln: layer name, %ii: image index, %in: image name, %fi: file index (added in 11.0.8)). Defaults to None.

+
+
+ '[%3ii] %ln' +
layer_selection + list[pytvpaint.layer.Layer] | None + +
+

list of layers to render or all if None. Defaults to None.

+
+
+ None +
alpha_mode + pytvpaint.george.AlphaSaveMode + +
+

the export alpha mode. Defaults to george.AlphaSaveMode.PREMULTIPLY.

+
+
+ pytvpaint.george.AlphaSaveMode.PREMULTIPLY +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the export background mode. Defaults to None.

+
+
+ None +
format_opts + list[str] | None + +
+

custom format options. Defaults to None.

+
+
+ None +
all_images + bool + +
+

export all images (not only the instances). Defaults to False.

+
+
+ False +
ignore_duplicates + bool + +
+

Ignore duplicates images. Defaults to None.

+
+
+ False +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ FileNotFoundError + +
+

if the export failed and no files were found on disk

+
+
+ +
+ Source code in pytvpaint/clip.py +
527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
@set_as_current
+def export_json(
+    self,
+    export_path: Path | str,
+    save_format: george.SaveFormat,
+    folder_pattern: str = r"[%3li] %ln",
+    file_pattern: str = r"[%3ii] %ln",
+    layer_selection: list[Layer] | None = None,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = None,
+    format_opts: list[str] | None = None,
+    all_images: bool = False,
+    ignore_duplicates: bool = False,
+) -> None:
+    """Exports the instances (or all the images) of layers in the clip and a JSON file describing the structure of that clip.
+
+    Args:
+        export_path: the JSON export path
+        save_format: file format to use for rendering
+        folder_pattern: the folder name pattern (%li: layer index, %ln: layer name, %fi: file index (added in 11.0.8)). Defaults to None.
+        file_pattern: the file name pattern (%li: layer index, %ln: layer name, %ii: image index, %in: image name, %fi: file index (added in 11.0.8)). Defaults to None.
+        layer_selection: list of layers to render or all if None. Defaults to None.
+        alpha_mode: the export alpha mode. Defaults to george.AlphaSaveMode.PREMULTIPLY.
+        background_mode: the export background mode. Defaults to None.
+        format_opts: custom format options. Defaults to None.
+        all_images: export all images (not only the instances). Defaults to False.
+        ignore_duplicates: Ignore duplicates images. Defaults to None.
+
+    Raises:
+        FileNotFoundError: if the export failed and no files were found on disk
+    """
+    export_path = Path(export_path)
+    export_path.parent.mkdir(exist_ok=True, parents=True)
+
+    fill_background = bool(
+        background_mode not in [None, george.BackgroundMode.NONE]
+    )
+
+    with utils.render_context(
+        alpha_mode, background_mode, save_format, format_opts, layer_selection
+    ):
+        george.tv_clip_save_structure_json(
+            export_path,
+            save_format,
+            fill_background=fill_background,
+            folder_pattern=folder_pattern,
+            file_pattern=file_pattern,
+            visible_layers_only=True,
+            all_images=all_images,
+            ignore_duplicates=ignore_duplicates,
+        )
+
+    if not export_path.exists():
+        raise FileNotFoundError(
+            f"Could not find output at : {export_path.as_posix()}"
+        )
+
+
+
+ +
+ + +
+ + + +

+ export_psd(export_path: Path | str, mode: george.PSDSaveMode, start: int | None = None, end: int | None = None, layer_selection: list[Layer] | None = None, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = None, format_opts: list[str] | None = None) -> None + +

+ + +
+ +

Save the current clip as a PSD.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

the PSD save path

+
+
+ required +
mode + pytvpaint.george.PSDSaveMode + +
+

whether to save all the images, only the instances or inside the markin

+
+
+ required +
start + int | None + +
+

the start frame. Defaults to None.

+
+
+ None +
end + int | None + +
+

the end frame. Defaults to None.

+
+
+ None +
layer_selection + list[pytvpaint.layer.Layer] | None + +
+

layers to render. Defaults to None (render all the layers).

+
+
+ None +
alpha_mode + pytvpaint.george.AlphaSaveMode + +
+

the alpha save mode. Defaults to george.AlphaSaveMode.PREMULTIPLY.

+
+
+ pytvpaint.george.AlphaSaveMode.PREMULTIPLY +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the export background mode. Defaults to None.

+
+
+ None +
format_opts + list[str] | None + +
+

custom format options. Defaults to None.

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ FileNotFoundError + +
+

if the export failed and no files were found on disk

+
+
+ +
+ Source code in pytvpaint/clip.py +
584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
@set_as_current
+def export_psd(
+    self,
+    export_path: Path | str,
+    mode: george.PSDSaveMode,
+    start: int | None = None,
+    end: int | None = None,
+    layer_selection: list[Layer] | None = None,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = None,
+    format_opts: list[str] | None = None,
+) -> None:
+    """Save the current clip as a PSD.
+
+    Args:
+        export_path: the PSD save path
+        mode: whether to save all the images, only the instances or inside the markin
+        start: the start frame. Defaults to None.
+        end: the end frame. Defaults to None.
+        layer_selection: layers to render. Defaults to None (render all the layers).
+        alpha_mode: the alpha save mode. Defaults to george.AlphaSaveMode.PREMULTIPLY.
+        background_mode: the export background mode. Defaults to None.
+        format_opts: custom format options. Defaults to None.
+
+    Raises:
+        FileNotFoundError: if the export failed and no files were found on disk
+    """
+    start = start or self.mark_in or self.start
+    end = end or self.mark_out or self.end
+
+    export_path = Path(export_path)
+    image = start if mode == george.PSDSaveMode.IMAGE else None
+
+    with utils.render_context(
+        alpha_mode,
+        background_mode,
+        george.SaveFormat.PSD,
+        format_opts,
+        layer_selection,
+    ):
+        george.tv_clip_save_structure_psd(
+            export_path,
+            mode,
+            image=image,
+            mark_in=start,
+            mark_out=end,
+        )
+
+    if mode == george.PSDSaveMode.MARKIN:
+        # raises error if sequence not found
+        check_path = export_path.with_suffix(f".#{export_path.suffix}").as_posix()
+        assert FileSequence.findSequenceOnDisk(check_path)
+    else:
+        if not export_path.exists():
+            raise FileNotFoundError(
+                f"Could not find output at : {export_path.as_posix()}"
+            )
+
+
+
+ +
+ + +
+ + + +

+ export_csv(export_path: Path | str, save_format: george.SaveFormat, all_images: bool = False, exposure_label: str = '', layer_selection: list[Layer] | None = None, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = None, format_opts: list[str] | None = None) -> None + +

+ + +
+ +

Save the current clip as a CSV.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

the .csv export path

+
+
+ required +
save_format + pytvpaint.george.SaveFormat + +
+

file format to use for rendering

+
+
+ required +
all_images + bool + +
+

export all images or only instances. Defaults to None.

+
+
+ False +
exposure_label + str + +
+

give a label when the image is an exposure. Defaults to None.

+
+
+ '' +
layer_selection + list[pytvpaint.layer.Layer] | None + +
+

layers to render. Defaults to None (render all the layers).

+
+
+ None +
alpha_mode + pytvpaint.george.AlphaSaveMode + +
+

the alpha save mode. Defaults to george.AlphaSaveMode.PREMULTIPLY.

+
+
+ pytvpaint.george.AlphaSaveMode.PREMULTIPLY +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the export background mode. Defaults to None.

+
+
+ None +
format_opts + list[str] | None + +
+

custom format options. Defaults to None.

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if the extension is not .csv

+
+
+ FileNotFoundError + +
+

if the render failed and no files were found on disk

+
+
+ +
+ Source code in pytvpaint/clip.py +
642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
@set_as_current
+def export_csv(
+    self,
+    export_path: Path | str,
+    save_format: george.SaveFormat,
+    all_images: bool = False,
+    exposure_label: str = "",
+    layer_selection: list[Layer] | None = None,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = None,
+    format_opts: list[str] | None = None,
+) -> None:
+    """Save the current clip as a CSV.
+
+    Args:
+        export_path: the .csv export path
+        save_format: file format to use for rendering
+        all_images: export all images or only instances. Defaults to None.
+        exposure_label: give a label when the image is an exposure. Defaults to None.
+        layer_selection: layers to render. Defaults to None (render all the layers).
+        alpha_mode: the alpha save mode. Defaults to george.AlphaSaveMode.PREMULTIPLY.
+        background_mode: the export background mode. Defaults to None.
+        format_opts: custom format options. Defaults to None.
+
+    Raises:
+        ValueError: if the extension is not .csv
+        FileNotFoundError: if the render failed and no files were found on disk
+    """
+    export_path = Path(export_path)
+
+    if export_path.suffix != ".csv":
+        raise ValueError("Export path must have .csv extension")
+
+    with utils.render_context(
+        alpha_mode, background_mode, save_format, format_opts, layer_selection
+    ):
+        george.tv_clip_save_structure_csv(export_path, all_images, exposure_label)
+
+    if not export_path.exists():
+        raise FileNotFoundError(
+            f"Could not find output at : {export_path.as_posix()}"
+        )
+
+
+
+ +
+ + +
+ + + +

+ export_sprites(export_path: Path | str, layout: george.SpriteLayout | None = None, space: int = 0, layer_selection: list[Layer] | None = None, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = None, format_opts: list[str] | None = None) -> None + +

+ + +
+ +

Save the current clip as sprites in one image.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

description

+
+
+ required +
layout + pytvpaint.george.SpriteLayout | None + +
+

the sprite layout. Defaults to None.

+
+
+ None +
space + int + +
+

the space between each sprite in the image. Defaults to None.

+
+
+ 0 +
layer_selection + list[pytvpaint.layer.Layer] | None + +
+

layers to render. Defaults to None (render all the layers).

+
+
+ None +
alpha_mode + pytvpaint.george.AlphaSaveMode + +
+

the alpha save mode. Defaults to george.AlphaSaveMode.PREMULTIPLY.

+
+
+ pytvpaint.george.AlphaSaveMode.PREMULTIPLY +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the export background mode. Defaults to None.

+
+
+ None +
format_opts + list[str] | None + +
+

custom format options. Defaults to None.

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ FileNotFoundError + +
+

if the export failed and no files were found on disk

+
+
+ +
+ Source code in pytvpaint/clip.py +
685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
@set_as_current
+def export_sprites(
+    self,
+    export_path: Path | str,
+    layout: george.SpriteLayout | None = None,
+    space: int = 0,
+    layer_selection: list[Layer] | None = None,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = None,
+    format_opts: list[str] | None = None,
+) -> None:
+    """Save the current clip as sprites in one image.
+
+    Args:
+        export_path (Path | str): _description_
+        layout: the sprite layout. Defaults to None.
+        space: the space between each sprite in the image. Defaults to None.
+        layer_selection: layers to render. Defaults to None (render all the layers).
+        alpha_mode: the alpha save mode. Defaults to george.AlphaSaveMode.PREMULTIPLY.
+        background_mode: the export background mode. Defaults to None.
+        format_opts: custom format options. Defaults to None.
+
+    Raises:
+        FileNotFoundError: if the export failed and no files were found on disk
+    """
+    export_path = Path(export_path)
+    save_format = george.SaveFormat.from_extension(export_path.suffix)
+
+    with utils.render_context(
+        alpha_mode, background_mode, save_format, format_opts, layer_selection
+    ):
+        george.tv_clip_save_structure_sprite(export_path, layout, space)
+
+    if not export_path.exists():
+        raise FileNotFoundError(
+            f"Could not find output at : {export_path.as_posix()}"
+        )
+
+
+
+ +
+ + +
+ + + +

+ export_flix(export_path: Path | str, start: int | None = None, end: int | None = None, import_parameters: str = '', file_parameters: str = '', send: bool = False, layer_selection: list[Layer] | None = None, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = None, format_opts: list[str] | None = None) -> None + +

+ + +
+ +

Save the current clip for Flix.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

the .xml export path

+
+
+ required +
start + int | None + +
+

the start frame. Defaults to None.

+
+
+ None +
end + int | None + +
+

the end frame. Defaults to None.

+
+
+ None +
import_parameters + str + +
+

the attribute(s) of the global tag (waitForSource/...). Defaults to None.

+
+
+ '' +
file_parameters + str + +
+

the attribute(s) of each (file) tag (dialogue/...). Defaults to None.

+
+
+ '' +
send + bool + +
+

open a browser with the prefilled url. Defaults to None.

+
+
+ False +
layer_selection + list[pytvpaint.layer.Layer] | None + +
+

layers to render. Defaults to None (render all the layers).

+
+
+ None +
alpha_mode + pytvpaint.george.AlphaSaveMode + +
+

the alpha save mode. Defaults to george.AlphaSaveMode.PREMULTIPLY.

+
+
+ pytvpaint.george.AlphaSaveMode.PREMULTIPLY +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the export background mode. Defaults to None.

+
+
+ None +
format_opts + list[str] | None + +
+

custom format options. Defaults to None.

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if the extension is not .xml

+
+
+ FileNotFoundError + +
+

if the export failed and no files were found on disk

+
+
+ +
+ Source code in pytvpaint/clip.py +
723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
@set_as_current
+def export_flix(
+    self,
+    export_path: Path | str,
+    start: int | None = None,
+    end: int | None = None,
+    import_parameters: str = "",
+    file_parameters: str = "",
+    send: bool = False,
+    layer_selection: list[Layer] | None = None,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = None,
+    format_opts: list[str] | None = None,
+) -> None:
+    """Save the current clip for Flix.
+
+    Args:
+        export_path: the .xml export path
+        start: the start frame. Defaults to None.
+        end: the end frame. Defaults to None.
+        import_parameters: the attribute(s) of the global <flixImport> tag (waitForSource/...). Defaults to None.
+        file_parameters: the attribute(s) of each <image> (file) tag (dialogue/...). Defaults to None.
+        send: open a browser with the prefilled url. Defaults to None.
+        layer_selection: layers to render. Defaults to None (render all the layers).
+        alpha_mode: the alpha save mode. Defaults to george.AlphaSaveMode.PREMULTIPLY.
+        background_mode: the export background mode. Defaults to None.
+        format_opts: custom format options. Defaults to None.
+
+    Raises:
+        ValueError: if the extension is not .xml
+        FileNotFoundError: if the export failed and no files were found on disk
+    """
+    export_path = Path(export_path)
+
+    if export_path.suffix != ".xml":
+        raise ValueError("Export path must have .xml extension")
+
+    original_file = self.project.path
+    import_parameters = (
+        import_parameters
+        or 'waitForSource="1" multipleSetups="1" replaceSelection="0"'
+    )
+
+    # The project needs to be saved
+    self.project.save()
+
+    # save alpha mode and save format values
+    with utils.render_context(
+        alpha_mode, background_mode, None, format_opts, layer_selection
+    ):
+        george.tv_clip_save_structure_flix(
+            export_path,
+            start,
+            end,
+            import_parameters,
+            file_parameters,
+            send,
+            original_file,
+        )
+
+    if not export_path.exists():
+        raise FileNotFoundError(
+            f"Could not find output at : {export_path.as_posix()}"
+        )
+
+
+
+ +
+ + +
+ + + +

+ set_layer_color(layer_color: LayerColor) -> None + +

+ + +
+ +

Set the layer color at the provided index.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
layer_color + pytvpaint.layer.LayerColor + +
+

the layer color instance.

+
+
+ required +
+ +
+ Source code in pytvpaint/clip.py +
846
+847
+848
+849
+850
+851
+852
+853
+854
def set_layer_color(self, layer_color: LayerColor) -> None:
+    """Set the layer color at the provided index.
+
+    Args:
+        layer_color: the layer color instance.
+    """
+    george.tv_layer_color_set_color(
+        self.id, layer_color.index, layer_color.color, layer_color.name
+    )
+
+
+
+ +
+ + +
+ + + +

+ get_layer_color(by_index: int | None = None, by_name: str | None = None) -> LayerColor | None + +

+ + +
+ +

Get a layer color by index or name.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if none of the arguments by_index and by_name where provided

+
+
+ +
+ Source code in pytvpaint/clip.py +
856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
def get_layer_color(
+    self,
+    by_index: int | None = None,
+    by_name: str | None = None,
+) -> LayerColor | None:
+    """Get a layer color by index or name.
+
+    Raises:
+        ValueError: if none of the arguments `by_index` and `by_name` where provided
+    """
+    if not by_index and by_name:
+        raise ValueError(
+            "At least one value (by_index or by_name) must be provided"
+        )
+
+    if by_index is not None:
+        return next(c for i, c in enumerate(self.layer_colors) if i == by_index)
+
+    try:
+        return next(c for c in self.layer_colors if c.name == by_name)
+    except StopIteration:
+        return None
+
+
+
+ +
+ + +
+ + + +

+ add_bookmark(frame: int) -> None + +

+ + +
+ +

Add a bookmark at that frame.

+ +
+ Source code in pytvpaint/clip.py +
888
+889
+890
def add_bookmark(self, frame: int) -> None:
+    """Add a bookmark at that frame."""
+    george.tv_bookmark_set(frame - self.project.start_frame)
+
+
+
+ +
+ + +
+ + + +

+ remove_bookmark(frame: int) -> None + +

+ + +
+ +

Remove a bookmark at that frame.

+ +
+ Source code in pytvpaint/clip.py +
892
+893
+894
def remove_bookmark(self, frame: int) -> None:
+    """Remove a bookmark at that frame."""
+    george.tv_bookmark_clear(frame - self.project.start_frame)
+
+
+
+ +
+ + +
+ + + +

+ clear_bookmarks() -> None + +

+ + +
+ +

Remove all the bookmarks.

+ +
+ Source code in pytvpaint/clip.py +
896
+897
+898
+899
+900
def clear_bookmarks(self) -> None:
+    """Remove all the bookmarks."""
+    bookmarks = list(self.bookmarks)
+    for frame in bookmarks:
+        self.remove_bookmark(frame)
+
+
+
+ +
+ + +
+ + + +

+ go_to_previous_bookmark() -> None + +

+ + +
+ +

Go to the previous bookmarks frame.

+ +
+ Source code in pytvpaint/clip.py +
902
+903
+904
+905
@set_as_current
+def go_to_previous_bookmark(self) -> None:
+    """Go to the previous bookmarks frame."""
+    george.tv_bookmark_prev()
+
+
+
+ +
+ + +
+ + + +

+ go_to_next_bookmark() -> None + +

+ + +
+ +

Go to the next bookmarks frame.

+ +
+ Source code in pytvpaint/clip.py +
907
+908
+909
+910
@set_as_current
+def go_to_next_bookmark(self) -> None:
+    """Go to the next bookmarks frame."""
+    george.tv_bookmark_next()
+
+
+
+ +
+ + +
+ + + +

+ get_sound(by_id: int | None = None, by_path: Path | str | None = None) -> ClipSound | None + +

+ + +
+ +

Get a clip sound by id or by path.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if sound object could not be found in clip

+
+
+ +
+ Source code in pytvpaint/clip.py +
922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
def get_sound(
+    self,
+    by_id: int | None = None,
+    by_path: Path | str | None = None,
+) -> ClipSound | None:
+    """Get a clip sound by id or by path.
+
+    Raises:
+        ValueError: if sound object could not be found in clip
+    """
+    for sound in self.sounds:
+        if (by_id and sound.id == by_id) or (by_path and sound.path == by_path):
+            return sound
+
+    return None
+
+
+
+ +
+ + +
+ + + +

+ add_sound(sound_path: Path | str) -> ClipSound + +

+ + +
+ +

Adds a new clip soundtrack.

+ +
+ Source code in pytvpaint/clip.py +
938
+939
+940
def add_sound(self, sound_path: Path | str) -> ClipSound:
+    """Adds a new clip soundtrack."""
+    return ClipSound.new(sound_path, parent=self)
+
+
+
+ +
+ + + +
+ +
+ + +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/objects/clip_sound/index.html b/api/objects/clip_sound/index.html new file mode 100644 index 0000000..fea991c --- /dev/null +++ b/api/objects/clip_sound/index.html @@ -0,0 +1,1749 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + ClipSound - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

ClipSound class

+ + +
+ + + + +
+

+ Bases: pytvpaint.sound.BaseSound['Clip']

+ + +

A clip sound.

+ +
+ Source code in pytvpaint/sound.py +
204
+205
+206
+207
+208
+209
+210
+211
+212
def __init__(
+    self,
+    track_index: int,
+    clip: Clip | None = None,
+) -> None:
+    from pytvpaint.clip import Clip
+
+    clip = clip or Clip.current_clip()
+    super().__init__(track_index, clip)
+
+
+ + + +
+ + + + + + + +
+ + + +

+ clip: Clip + + + property + + +

+ + +
+ +

The sound's clip.

+
+ +
+ + + + +
+ + + +

+ make_current() -> None + +

+ + +
+ +

Makes the sound clip current.

+ +
+ Source code in pytvpaint/sound.py +
237
+238
+239
def make_current(self) -> None:
+    """Makes the sound clip current."""
+    self.clip.make_current()
+
+
+
+ +
+ + +
+ + + +

+ remove() -> None + +

+ + +
+ +

Remove the sound from the clip.

+ +
+ Source code in pytvpaint/sound.py +
241
+242
+243
+244
+245
@set_as_current
+def remove(self) -> None:
+    """Remove the sound from the clip."""
+    george.tv_sound_clip_remove(self.track_index)
+    self.mark_removed()
+
+
+
+ +
+ + +
+ + + +

+ reload() -> None + +

+ + +
+ +

Reload the sound from file.

+ +
+ Source code in pytvpaint/sound.py +
247
+248
+249
+250
@set_as_current
+def reload(self) -> None:
+    """Reload the sound from file."""
+    george.tv_sound_clip_reload(self._parent.id, self.track_index)
+
+
+
+ +
+ + + +
+ +
+ + +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/objects/layer/index.html b/api/objects/layer/index.html new file mode 100644 index 0000000..0099000 --- /dev/null +++ b/api/objects/layer/index.html @@ -0,0 +1,6892 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Layer - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Layer class

+ + +
+ + + + +
+

+ Bases: pytvpaint.utils.Removable

+ + +

A Layer is inside a clip and contains drawings.

+ +
+ Source code in pytvpaint/layer.py +
334
+335
+336
+337
+338
+339
+340
def __init__(self, layer_id: int, clip: Clip | None = None) -> None:
+    from pytvpaint.clip import Clip
+
+    super().__init__()
+    self._id = layer_id
+    self._clip = clip or Clip.current_clip()
+    self._data = george.tv_layer_info(self.id)
+
+
+ + + +
+ + + + + + + +
+ + + +

+ id: int + + + property + + +

+ + +
+ +

The layers unique identifier.

+ +
+ Warning +

layer ids are not persistent across project load/close

+
+ +
+ +
+ + + +

+ project: Project + + + property + + +

+ + +
+ +

The project containing this layer.

+
+ +
+ +
+ + + +

+ scene: Scene + + + property + + +

+ + +
+ +

The scene containing this layer.

+
+ +
+ +
+ + + +

+ clip: Clip + + + property + + +

+ + +
+ +

The clip containing this layer.

+
+ +
+ +
+ + + +

+ position: int + + + property + writable + + +

+ + +
+ +

The position in the layer stack.

+ +
+ Note +

layer positions start at 0

+
+ +
+ +
+ + + +

+ opacity: int + + + property + writable + + +

+ + +
+ +

Get the layer opacity value.

+ +
+ Note +

In George, this is called density, we renamed it to opacity as it seems more appropriate

+
+ +
+ +
+ + + +

+ color: LayerColor + + + property + writable + + +

+ + +
+ +

Get the layer color.

+
+ +
+ +
+ + + +

+ is_current: bool + + + property + + +

+ + +
+ +

Returns True if the layer is the current one in the clip.

+
+ +
+ +
+ + + +

+ is_selected: bool + + + property + writable + + +

+ + +
+ +

Returns True if the layer is selected.

+
+ +
+ +
+ + + +

+ is_visible: bool + + + property + writable + + +

+ + +
+ +

Returns True if the layer is visible.

+
+ +
+ +
+ + + +

+ is_locked: bool + + + property + writable + + +

+ + +
+ +

Returns True if the layer is locked.

+
+ +
+ +
+ + + +

+ is_collapsed: bool + + + property + writable + + +

+ + +
+ +

Returns True if the layer is collapsed.

+
+ +
+ +
+ + + +

+ blending_mode: george.BlendingMode + + + property + writable + + +

+ + +
+ +

Get the layer blending mode value.

+
+ +
+ +
+ + + +

+ stencil: george.StencilMode + + + property + writable + + +

+ + +
+ +

Get the layer stencil mode value.

+
+ +
+ +
+ + + +

+ thumbnails_visible: bool + + + property + writable + + +

+ + +
+ +

Returns True if thumbnails are shown on that layer.

+
+ +
+ +
+ + + +

+ auto_break_instance: bool + + + property + writable + + +

+ + +
+ +

Get the auto break instance value.

+
+ +
+ +
+ + + +

+ auto_create_instance: bool + + + property + writable + + +

+ + +
+ +

Get the auto create instance value.

+
+ +
+ +
+ + + +

+ pre_behavior: george.LayerBehavior + + + property + writable + + +

+ + +
+ +

Get the pre-behavior value.

+
+ +
+ +
+ + + +

+ post_behavior: george.LayerBehavior + + + property + writable + + +

+ + +
+ +

Get the post-behavior value.

+
+ +
+ +
+ + + +

+ is_position_locked: bool + + + property + writable + + +

+ + +
+ +

Returns True if the layer position is locked.

+
+ +
+ +
+ + + +

+ preserve_transparency: george.LayerTransparency + + + property + writable + + +

+ + +
+ +

Get the preserve transparency value.

+
+ +
+ +
+ + + +

+ is_anim_layer: bool + + + property + + +

+ + +
+ +

Returns True if the layer is an animation layer.

+
+ +
+ +
+ + + +

+ marks: Iterator[tuple[int, LayerColor]] + + + property + + +

+ + +
+ +

Iterator over the layer marks including the frame and the color.

+ + + +

Yields:

+ + + + + + + + + + + + + + + + + +
Name TypeDescription
frame + int + +
+

the mark frame

+
+
color + pytvpaint.layer.LayerColor + +
+

the mark color

+
+
+
+ +
+ +
+ + + +

+ selected_frames: list[int] + + + property + + +

+ + +
+ +

Get the list of selected frames.

+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ list[int] + +
+

Array of selected frame numbers

+
+
+
+ +
+ + + + +
+ + + +

+ refresh() -> None + +

+ + +
+ +

Refreshes the layer data.

+ +
+ Source code in pytvpaint/layer.py +
342
+343
+344
+345
+346
+347
+348
+349
+350
+351
def refresh(self) -> None:
+    """Refreshes the layer data."""
+    super().refresh()
+    if not self.refresh_on_call and self._data:
+        return
+    try:
+        self._data = george.tv_layer_info(self._id)
+    except GeorgeError:
+        self.mark_removed()
+        self.refresh()
+
+
+
+ +
+ + +
+ + + +

+ name(value: str) -> None + +

+ + +
+ +

Set the layer name.

+ +
+ Note +

it uses get_unique_name to find a unique layer name across all the layers in the clip

+
+
+ Source code in pytvpaint/layer.py +
420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
@name.setter
+@set_as_current
+def name(self, value: str) -> None:
+    """Set the layer name.
+
+    Note:
+        it uses `get_unique_name` to find a unique layer name across all the layers in the clip
+    """
+    if value == self.name:
+        return
+    value = utils.get_unique_name(self.clip.layer_names, value)
+    george.tv_layer_rename(self.id, value)
+
+
+
+ +
+ + +
+ + + +

+ layer_type() -> george.LayerType + +

+ + +
+ +

The layer type.

+ +
+ Source code in pytvpaint/layer.py +
433
+434
+435
+436
@refreshed_property
+def layer_type(self) -> george.LayerType:
+    """The layer type."""
+    return self._data.type
+
+
+
+ +
+ + +
+ + + +

+ start() -> int + +

+ + +
+ +

The layer start frame according to the project's start frame.

+ +
+ Source code in pytvpaint/layer.py +
454
+455
+456
+457
@refreshed_property
+def start(self) -> int:
+    """The layer start frame according to the project's start frame."""
+    return self._data.first_frame + self.project.start_frame
+
+
+
+ +
+ + +
+ + + +

+ end() -> int + +

+ + +
+ +

The layer end frame according to the project's start frame.

+ +
+ Source code in pytvpaint/layer.py +
459
+460
+461
+462
@refreshed_property
+def end(self) -> int:
+    """The layer end frame according to the project's start frame."""
+    return self._data.last_frame + self.project.start_frame
+
+
+
+ +
+ + +
+ + + +

+ make_current() -> None + +

+ + +
+ +

Make the layer current, it also makes the clip current.

+ +
+ Source code in pytvpaint/layer.py +
480
+481
+482
+483
+484
+485
+486
def make_current(self) -> None:
+    """Make the layer current, it also makes the clip current."""
+    if self.is_current:
+        return
+    if self.clip:
+        self.clip.make_current()
+    george.tv_layer_set(self.id)
+
+
+
+ +
+ + +
+ + + +

+ convert_to_anim_layer() -> None + +

+ + +
+ +

Converts the layer to an animation layer.

+ +
+ Source code in pytvpaint/layer.py +
626
+627
+628
+629
@set_as_current
+def convert_to_anim_layer(self) -> None:
+    """Converts the layer to an animation layer."""
+    george.tv_layer_anim(self.id)
+
+
+
+ +
+ + +
+ + + +

+ load_dependencies() -> None + +

+ + +
+ +

Load all dependencies of the layer in memory.

+ +
+ Source code in pytvpaint/layer.py +
636
+637
+638
def load_dependencies(self) -> None:
+    """Load all dependencies of the layer in memory."""
+    george.tv_layer_load_dependencies(self.id)
+
+
+
+ +
+ + +
+ + + +

+ current_layer_id() -> int + + + staticmethod + + +

+ + +
+ +

Returns the current layer id.

+ +
+ Source code in pytvpaint/layer.py +
640
+641
+642
+643
@staticmethod
+def current_layer_id() -> int:
+    """Returns the current layer id."""
+    return george.tv_layer_current_id()
+
+
+
+ +
+ + +
+ + + +

+ current_layer() -> Layer + + + classmethod + + +

+ + +
+ +

Returns the current layer object.

+ +
+ Source code in pytvpaint/layer.py +
645
+646
+647
+648
+649
+650
@classmethod
+def current_layer(cls) -> Layer:
+    """Returns the current layer object."""
+    from pytvpaint.clip import Clip
+
+    return cls(layer_id=cls.current_layer_id(), clip=Clip.current_clip())
+
+
+
+ +
+ + +
+ + + +

+ shift(new_start: int) -> None + +

+ + +
+ +

Move the layer to a new frame.

+ +
+ Source code in pytvpaint/layer.py +
652
+653
+654
+655
@set_as_current
+def shift(self, new_start: int) -> None:
+    """Move the layer to a new frame."""
+    george.tv_layer_shift(self.id, new_start - self.project.start_frame)
+
+
+
+ +
+ + +
+ + + +

+ merge(layer: Layer, blending_mode: george.BlendingMode, stamp: bool = False, erase: bool = False, keep_color_grp: bool = True, keep_img_mark: bool = True, keep_instance_name: bool = True) -> None + +

+ + +
+ +

Merge this layer with the given one.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
layer + pytvpaint.layer.Layer + +
+

the layer to merge with

+
+
+ required +
blending_mode + pytvpaint.george.BlendingMode + +
+

the blending mode to use

+
+
+ required +
stamp + bool + +
+

Use stamp mode

+
+
+ False +
erase + bool + +
+

Remove the source layer

+
+
+ False +
keep_color_grp + bool + +
+

Keep the color group

+
+
+ True +
keep_img_mark + bool + +
+

Keep the image mark

+
+
+ True +
keep_instance_name + bool + +
+

Keep the instance name

+
+
+ True +
+ +
+ Source code in pytvpaint/layer.py +
657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
@set_as_current
+def merge(
+    self,
+    layer: Layer,
+    blending_mode: george.BlendingMode,
+    stamp: bool = False,
+    erase: bool = False,
+    keep_color_grp: bool = True,
+    keep_img_mark: bool = True,
+    keep_instance_name: bool = True,
+) -> None:
+    """Merge this layer with the given one.
+
+    Args:
+        layer: the layer to merge with
+        blending_mode: the blending mode to use
+        stamp: Use stamp mode
+        erase: Remove the source layer
+        keep_color_grp: Keep the color group
+        keep_img_mark: Keep the image mark
+        keep_instance_name: Keep the instance name
+    """
+    george.tv_layer_merge(
+        layer.id,
+        blending_mode,
+        stamp,
+        erase,
+        keep_color_grp,
+        keep_img_mark,
+        keep_instance_name,
+    )
+
+
+
+ +
+ + +
+ + + +

+ merge_all(keep_color_grp: bool = True, keep_img_mark: bool = True, keep_instance_name: bool = True) -> None + + + staticmethod + + +

+ + +
+ +

Merge all the layers in the stack.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
keep_color_grp + bool + +
+

Keep the color group

+
+
+ True +
keep_img_mark + bool + +
+

Keep the image mark

+
+
+ True +
keep_instance_name + bool + +
+

Keep the instance name

+
+
+ True +
+ +
+ Source code in pytvpaint/layer.py +
689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
@staticmethod
+def merge_all(
+    keep_color_grp: bool = True,
+    keep_img_mark: bool = True,
+    keep_instance_name: bool = True,
+) -> None:
+    """Merge all the layers in the stack.
+
+    Args:
+        keep_color_grp: Keep the color group
+        keep_img_mark: Keep the image mark
+        keep_instance_name: Keep the instance name
+    """
+    george.tv_layer_merge_all(keep_color_grp, keep_img_mark, keep_instance_name)
+
+
+
+ +
+ + +
+ + + +

+ new(name: str, clip: Clip | None = None, color: LayerColor | None = None) -> Layer + + + staticmethod + + +

+ + +
+ +

Create a new layer.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
name + str + +
+

the name of the new layer

+
+
+ required +
clip + pytvpaint.clip.Clip | None + +
+

the parent clip

+
+
+ None +
color + pytvpaint.layer.LayerColor | None + +
+

the layer color

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Layer + pytvpaint.layer.Layer + +
+

the new layer

+
+
+ +
+ Note +

The layer name is checked against all other layers to have a unique name using get_unique_name. +This can take a while if you have a lot of layers.

+
+
+ Source code in pytvpaint/layer.py +
704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
@staticmethod
+@george.undoable
+def new(
+    name: str,
+    clip: Clip | None = None,
+    color: LayerColor | None = None,
+) -> Layer:
+    """Create a new layer.
+
+    Args:
+        name: the name of the new layer
+        clip: the parent clip
+        color: the layer color
+
+    Returns:
+        Layer: the new layer
+
+    Note:
+        The layer name is checked against all other layers to have a unique name using `get_unique_name`.
+        This can take a while if you have a lot of layers.
+    """
+    from pytvpaint.clip import Clip
+
+    clip = clip or Clip.current_clip()
+    clip.make_current()
+
+    name = utils.get_unique_name(clip.layer_names, name)
+    layer_id = george.tv_layer_create(name)
+
+    layer = Layer(layer_id=layer_id, clip=clip)
+
+    if color:
+        layer.color = color
+
+    return layer
+
+
+
+ +
+ + +
+ + + +

+ new_anim_layer(name: str, clip: Clip | None = None, color: LayerColor | None = None) -> Layer + + + classmethod + + +

+ + +
+ +

Create a new animation layer.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
name + str + +
+

the name of the new layer

+
+
+ required +
clip + pytvpaint.clip.Clip | None + +
+

the parent clip

+
+
+ None +
color + pytvpaint.layer.LayerColor | None + +
+

the layer color

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Layer + pytvpaint.layer.Layer + +
+

the new animation layer

+
+
+ +
+ Note +

It activates the thumbnail visibility

+
+
+ Source code in pytvpaint/layer.py +
740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
@classmethod
+@george.undoable
+def new_anim_layer(
+    cls,
+    name: str,
+    clip: Clip | None = None,
+    color: LayerColor | None = None,
+) -> Layer:
+    """Create a new animation layer.
+
+    Args:
+        name: the name of the new layer
+        clip: the parent clip
+        color: the layer color
+
+    Returns:
+        Layer: the new animation layer
+
+    Note:
+        It activates the thumbnail visibility
+    """
+    layer = cls.new(name, clip, color)
+    layer.convert_to_anim_layer()
+    layer.thumbnails_visible = True
+    return layer
+
+
+
+ +
+ + +
+ + + +

+ new_background_layer(name: str, clip: Clip | None = None, color: LayerColor | None = None, image: Path | str | None = None, stretch: bool = False) -> Layer + + + classmethod + + +

+ + +
+ +

Create a new background layer with hold as pre- and post-behavior.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
name + str + +
+

the name of the new layer

+
+
+ required +
clip + pytvpaint.clip.Clip | None + +
+

the parent clip

+
+
+ None +
color + pytvpaint.layer.LayerColor | None + +
+

the layer color

+
+
+ None +
image + pathlib.Path | str | None + +
+

the background image to load

+
+
+ None +
stretch + bool + +
+

whether to stretch the image to fit the view

+
+
+ False +
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Layer + pytvpaint.layer.Layer + +
+

the new animation layer

+
+
+ +
+ Source code in pytvpaint/layer.py +
766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
@classmethod
+@george.undoable
+def new_background_layer(
+    cls,
+    name: str,
+    clip: Clip | None = None,
+    color: LayerColor | None = None,
+    image: Path | str | None = None,
+    stretch: bool = False,
+) -> Layer:
+    """Create a new background layer with hold as pre- and post-behavior.
+
+    Args:
+        name: the name of the new layer
+        clip: the parent clip
+        color: the layer color
+        image: the background image to load
+        stretch: whether to stretch the image to fit the view
+
+    Returns:
+        Layer: the new animation layer
+    """
+    from pytvpaint.clip import Clip
+
+    clip = clip or Clip.current_clip()
+    layer = cls.new(name, clip, color)
+    layer.pre_behavior = george.LayerBehavior.HOLD
+    layer.post_behavior = george.LayerBehavior.HOLD
+    layer.thumbnails_visible = True
+
+    image = Path(image or "")
+    if image.is_file():
+        layer.convert_to_anim_layer()
+        layer.load_image(image, frame=clip.start, stretch=stretch)
+
+    return layer
+
+
+
+ +
+ + +
+ + + +

+ duplicate(name: str) -> Layer + +

+ + +
+ +

Duplicate this layer.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
name + str + +
+

the duplicated layer name

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Layer + pytvpaint.layer.Layer + +
+

the duplicated layer

+
+
+ +
+ Source code in pytvpaint/layer.py +
803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
@set_as_current
+@george.undoable
+def duplicate(self, name: str) -> Layer:
+    """Duplicate this layer.
+
+    Args:
+        name: the duplicated layer name
+
+    Returns:
+        Layer: the duplicated layer
+    """
+    name = utils.get_unique_name(self.clip.layer_names, name)
+    layer_id = george.tv_layer_duplicate(name)
+
+    return Layer(layer_id=layer_id, clip=self.clip)
+
+
+
+ +
+ + +
+ + + +

+ remove() -> None + +

+ + +
+ +

Remove the layer from the clip.

+ +
+ Warning +

The current instance won't be usable after this call since it will be mark removed.

+
+
+ Source code in pytvpaint/layer.py +
819
+820
+821
+822
+823
+824
+825
+826
+827
+828
def remove(self) -> None:
+    """Remove the layer from the clip.
+
+    Warning:
+        The current instance won't be usable after this call since it will be mark removed.
+    """
+    self.clip.make_current()
+    self.is_locked = False
+    george.tv_layer_kill(self.id)
+    self.mark_removed()
+
+
+
+ +
+ + +
+ + + +

+ render(output_path: Path | str | FileSequence, start: int | None = None, end: int | None = None, use_camera: bool = False, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = None, format_opts: list[str] | None = None) -> None + +

+ + +
+ +

Render the layer to a single frame or frame sequence or movie.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
output_path + pathlib.Path | str | fileseq.filesequence.FileSequence + +
+

a single file or file sequence pattern

+
+
+ required +
start + int | None + +
+

the start frame to render the layer's start if None. Defaults to None.

+
+
+ None +
end + int | None + +
+

the end frame to render or the layer's end if None. Defaults to None.

+
+
+ None +
use_camera + bool + +
+

use the camera for rendering, otherwise render the whole canvas. Defaults to False.

+
+
+ False +
alpha_mode + pytvpaint.george.AlphaSaveMode + +
+

the alpha mode for rendering. Defaults to george.AlphaSaveMode.PREMULTIPLY.

+
+
+ pytvpaint.george.AlphaSaveMode.PREMULTIPLY +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the background mode for rendering. Defaults to None.

+
+
+ None +
format_opts + list[str] | None + +
+

custom format options. Defaults to None.

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if requested range (start-end) not in clip range/bounds

+
+
+ ValueError + +
+

if output is a movie

+
+
+ FileNotFoundError + +
+

if the render failed and no files were found on disk or missing frames

+
+
+ +
+ Note +

This functions uses the layer's range as a basis (start-end). This is different from a project range, which +uses the project timeline. For more details on the differences in frame ranges and the timeline in TVPaint, +please check the Usage/Rendering section of the documentation.

+
+
+ Warning +

Even tough pytvpaint does a pretty good job of correcting the frame ranges for rendering, we're still +encountering some weird edge cases where TVPaint will consider the range invalid for seemingly no reason.

+
+
+ Source code in pytvpaint/layer.py +
830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
@set_as_current
+def render(
+    self,
+    output_path: Path | str | FileSequence,
+    start: int | None = None,
+    end: int | None = None,
+    use_camera: bool = False,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = None,
+    format_opts: list[str] | None = None,
+) -> None:
+    """Render the layer to a single frame or frame sequence or movie.
+
+    Args:
+        output_path: a single file or file sequence pattern
+        start: the start frame to render the layer's start if None. Defaults to None.
+        end: the end frame to render or the layer's end if None. Defaults to None.
+        use_camera: use the camera for rendering, otherwise render the whole canvas. Defaults to False.
+        alpha_mode: the alpha mode for rendering. Defaults to george.AlphaSaveMode.PREMULTIPLY.
+        background_mode: the background mode for rendering. Defaults to None.
+        format_opts: custom format options. Defaults to None.
+
+    Raises:
+        ValueError: if requested range (start-end) not in clip range/bounds
+        ValueError: if output is a movie
+        FileNotFoundError: if the render failed and no files were found on disk or missing frames
+
+    Note:
+        This functions uses the layer's range as a basis (start-end). This  is different from a project range, which
+        uses the project timeline. For more details on the differences in frame ranges and the timeline in TVPaint,
+        please check the `Usage/Rendering` section of the documentation.
+
+    Warning:
+        Even tough pytvpaint does a pretty good job of correcting the frame ranges for rendering, we're still
+        encountering some weird edge cases where TVPaint will consider the range invalid for seemingly no reason.
+    """
+    start = self.start if start is None else start
+    end = self.end if end is None else end
+    self.clip.render(
+        output_path=output_path,
+        start=start,
+        end=end,
+        use_camera=use_camera,
+        layer_selection=[self],
+        alpha_mode=alpha_mode,
+        background_mode=background_mode,
+        format_opts=format_opts,
+    )
+
+
+
+ +
+ + +
+ + + +

+ render_frame(export_path: Path | str, frame: int | None = None, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = george.BackgroundMode.NONE, format_opts: list[str] | None = None) -> Path + +

+ + +
+ +

Render a frame from the layer.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str + +
+

the frame export path (the extension determines the output format)

+
+
+ required +
frame + int | None + +
+

the frame to render or the current frame if None. Defaults to None.

+
+
+ None +
alpha_mode + pytvpaint.george.AlphaSaveMode + +
+

the render alpha mode

+
+
+ pytvpaint.george.AlphaSaveMode.PREMULTIPLY +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the render background mode

+
+
+ pytvpaint.george.BackgroundMode.NONE +
format_opts + list[str] | None + +
+

custom output format options to pass when rendering

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ FileNotFoundError + +
+

if the render failed or output not found on disk

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
Path + pathlib.Path + +
+

render output path

+
+
+ +
+ Source code in pytvpaint/layer.py +
879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
@set_as_current
+def render_frame(
+    self,
+    export_path: Path | str,
+    frame: int | None = None,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = george.BackgroundMode.NONE,
+    format_opts: list[str] | None = None,
+) -> Path:
+    """Render a frame from the layer.
+
+    Args:
+        export_path: the frame export path (the extension determines the output format)
+        frame: the frame to render or the current frame if None. Defaults to None.
+        alpha_mode: the render alpha mode
+        background_mode: the render background mode
+        format_opts: custom output format options to pass when rendering
+
+    Raises:
+        FileNotFoundError: if the render failed or output not found on disk
+
+    Returns:
+        Path: render output path
+    """
+    export_path = Path(export_path)
+    save_format = george.SaveFormat.from_extension(export_path.suffix)
+    export_path.parent.mkdir(parents=True, exist_ok=True)
+
+    frame = frame or self.clip.current_frame
+    self.clip.current_frame = frame
+
+    with utils.render_context(
+        alpha_mode,
+        background_mode,
+        save_format,
+        format_opts,
+        layer_selection=[self],
+    ):
+        george.tv_save_image(export_path)
+
+    if not export_path.exists():
+        raise FileNotFoundError(
+            f"Could not find rendered image ({frame}) at : {export_path.as_posix()}"
+        )
+
+    return export_path
+
+
+
+ +
+ + +
+ + + +

+ render_instances(export_path: Path | str | FileSequence, start: int | None = None, end: int | None = None, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = None, format_opts: list[str] | None = None) -> FileSequence + +

+ + +
+ +

Render all layer instances in the provided range for the current layer.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
export_path + pathlib.Path | str | fileseq.filesequence.FileSequence + +
+

the export path (the extension determines the output format)

+
+
+ required +
start + int | None + +
+

the start frame to render the layer's start if None. Defaults to None.

+
+
+ None +
end + int | None + +
+

the end frame to render or the layer's end if None. Defaults to None.

+
+
+ None +
alpha_mode + pytvpaint.george.AlphaSaveMode + +
+

the render alpha mode

+
+
+ pytvpaint.george.AlphaSaveMode.PREMULTIPLY +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the render background mode

+
+
+ None +
format_opts + list[str] | None + +
+

custom output format options to pass when rendering

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if requested range (start-end) not in layer range/bounds

+
+
+ ValueError + +
+

if output is a movie

+
+
+ FileNotFoundError + +
+

if the render failed or output not found on disk

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
FileSequence + fileseq.filesequence.FileSequence + +
+

instances output sequence

+
+
+ +
+ Source code in pytvpaint/layer.py +
926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
@set_as_current
+def render_instances(
+    self,
+    export_path: Path | str | FileSequence,
+    start: int | None = None,
+    end: int | None = None,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = None,
+    format_opts: list[str] | None = None,
+) -> FileSequence:
+    """Render all layer instances in the provided range for the current layer.
+
+    Args:
+        export_path: the export path (the extension determines the output format)
+        start: the start frame to render the layer's start if None. Defaults to None.
+        end: the end frame to render or the layer's end if None. Defaults to None.
+        alpha_mode: the render alpha mode
+        background_mode: the render background mode
+        format_opts: custom output format options to pass when rendering
+
+    Raises:
+        ValueError: if requested range (start-end) not in layer range/bounds
+        ValueError: if output is a movie
+        FileNotFoundError: if the render failed or output not found on disk
+
+    Returns:
+        FileSequence: instances output sequence
+    """
+    file_sequence, start, end, is_sequence, is_image = utils.handle_output_range(
+        export_path, self.start, self.end, start, end
+    )
+
+    if start < self.start or end > self.end:
+        raise ValueError(
+            f"Render ({start}-{end}) not in clip range ({(self.start, self.end)})"
+        )
+    if not is_image:
+        raise ValueError(
+            f"Video formats ({file_sequence.extension()}) are not supported for instance rendering !"
+        )
+
+    # render to output
+    frames = []
+    for layer_instance in self.instances:
+        cur_frame = layer_instance.start
+        instance_output = Path(file_sequence.frame(cur_frame))
+        self.render_frame(
+            instance_output, cur_frame, alpha_mode, background_mode, format_opts
+        )
+        frames.append(str(cur_frame))
+
+    file_sequence.setFrameSet(FrameSet(",".join(frames)))
+    return file_sequence
+
+
+
+ +
+ + +
+ + + +

+ load_image(image_path: str | Path, frame: int | None = None, stretch: bool = False) -> None + +

+ + +
+ +

Load an image in the current layer at a given frame.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
image_path + str | pathlib.Path + +
+

path to the image to load

+
+
+ required +
frame + int | None + +
+

the frame where the image will be loaded, if none provided, image will be loaded at current frame

+
+
+ None +
stretch + bool + +
+

whether to stretch the image to fit the view

+
+
+ False +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ FileNotFoundError + +
+

if the file doesn't exist at provided path

+
+
+ +
+ Source code in pytvpaint/layer.py +
 980
+ 981
+ 982
+ 983
+ 984
+ 985
+ 986
+ 987
+ 988
+ 989
+ 990
+ 991
+ 992
+ 993
+ 994
+ 995
+ 996
+ 997
+ 998
+ 999
+1000
+1001
+1002
+1003
+1004
@set_as_current
+def load_image(
+    self, image_path: str | Path, frame: int | None = None, stretch: bool = False
+) -> None:
+    """Load an image in the current layer at a given frame.
+
+    Args:
+        image_path: path to the image to load
+        frame: the frame where the image will be loaded, if none provided, image will be loaded at current frame
+        stretch: whether to stretch the image to fit the view
+
+    Raises:
+        FileNotFoundError: if the file doesn't exist at provided path
+    """
+    image_path = Path(image_path)
+    if not image_path.exists():
+        raise FileNotFoundError(f"Image not found at : {image_path}")
+
+    frame = frame or self.clip.current_frame
+    with utils.restore_current_frame(self.clip, frame):
+        # if no instance at the specified frame, then create a new one
+        if not self.get_instance(frame):
+            self.add_instance(frame)
+
+        george.tv_load_image(image_path.as_posix(), stretch)
+
+
+
+ +
+ + +
+ + + +

+ get_mark_color(frame: int) -> LayerColor | None + +

+ + +
+ +

Get the mark color at a specific frame.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
frame + int + +
+

frame with a mark

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.layer.LayerColor | None + +
+

LayerColor | None: the layer color if there was a mark

+
+
+ +
+ Source code in pytvpaint/layer.py +
1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
def get_mark_color(self, frame: int) -> LayerColor | None:
+    """Get the mark color at a specific frame.
+
+    Args:
+        frame: frame with a mark
+
+    Returns:
+        LayerColor | None: the layer color if there was a mark
+    """
+    frame = frame - self.project.start_frame
+    color_index = george.tv_layer_mark_get(self.id, frame)
+    if not color_index:
+        return None
+
+    return self.clip.get_layer_color(by_index=color_index)
+
+
+
+ +
+ + +
+ + + +

+ add_mark(frame: int, color: LayerColor) -> None + +

+ + +
+ +

Add a mark to a frame.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
frame + int + +
+

frame to put a mark on

+
+
+ required +
color + pytvpaint.layer.LayerColor + +
+

the color index

+
+
+ required +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ TypeError + +
+

if the layer is not an animation layer

+
+
+ +
+ Source code in pytvpaint/layer.py +
1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
def add_mark(self, frame: int, color: LayerColor) -> None:
+    """Add a mark to a frame.
+
+    Args:
+        frame: frame to put a mark on
+        color: the color index
+
+    Raises:
+        TypeError: if the layer is not an animation layer
+    """
+    if not self.is_anim_layer:
+        raise TypeError(
+            f"Can't add a mark because this is not an animation layer ({self})"
+        )
+    frame = frame - self.project.start_frame
+    george.tv_layer_mark_set(self.id, frame, color.index)
+
+
+
+ +
+ + +
+ + + +

+ remove_mark(frame: int) -> None + +

+ + +
+ +

Remove a mark.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
frame + int + +
+

a frame number with a mark

+
+
+ required +
+ +
+ Source code in pytvpaint/layer.py +
1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
def remove_mark(self, frame: int) -> None:
+    """Remove a mark.
+
+    Args:
+        frame: a frame number with a mark
+    """
+    # Setting it at 0 clears the mark
+    self.add_mark(frame, LayerColor(0, self.clip))
+
+
+
+ +
+ + +
+ + + +

+ clear_marks() -> None + +

+ + +
+ +

Clear all the marks in the layer.

+ +
+ Source code in pytvpaint/layer.py +
1062
+1063
+1064
+1065
def clear_marks(self) -> None:
+    """Clear all the marks in the layer."""
+    for frame, _ in self.marks:
+        self.remove_mark(frame)
+
+
+
+ +
+ + +
+ + + +

+ select_frames(start: int, end: int) -> None + +

+ + +
+ +

Select the frames from a start and count.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
start + int + +
+

the selection start frame

+
+
+ required +
end + int + +
+

the selected end frame

+
+
+ required +
+ +
+ Source code in pytvpaint/layer.py +
1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
@set_as_current
+def select_frames(self, start: int, end: int) -> None:
+    """Select the frames from a start and count.
+
+    Args:
+        start: the selection start frame
+        end: the selected end frame
+    """
+    if not self.is_anim_layer:
+        log.warning(
+            "Selection may display weird behaviour when applied to a non animation layer"
+        )
+    frame_count = (end - start) + 1
+    george.tv_layer_select(start - self.clip.start, frame_count)
+
+
+
+ +
+ + +
+ + + +

+ select_all_frames() -> None + +

+ + +
+ +

Select all frames in the layer.

+ +
+ Source code in pytvpaint/layer.py +
1082
+1083
+1084
+1085
+1086
@set_as_current
+def select_all_frames(self) -> None:
+    """Select all frames in the layer."""
+    frame, frame_count = george.tv_layer_select_info(full=True)
+    george.tv_layer_select(frame, frame_count)
+
+
+
+ +
+ + +
+ + + +

+ clear_selection() -> None + +

+ + +
+ +

Clear frame selection in the layer.

+ +
+ Source code in pytvpaint/layer.py +
1088
+1089
+1090
+1091
+1092
@set_as_current
+def clear_selection(self) -> None:
+    """Clear frame selection in the layer."""
+    # selecting frames after the layer's end frame will result in a empty selection, thereby clearing the selection
+    george.tv_layer_select(self.end + 1, 0)
+
+
+
+ +
+ + +
+ + + +

+ cut_selection() -> None + +

+ + +
+ +

Cut the selected instances.

+ +
+ Source code in pytvpaint/layer.py +
1104
+1105
+1106
+1107
@set_as_current
+def cut_selection(self) -> None:
+    """Cut the selected instances."""
+    george.tv_layer_cut()
+
+
+
+ +
+ + +
+ + + +

+ copy_selection() -> None + +

+ + +
+ +

Copy the selected instances.

+ +
+ Source code in pytvpaint/layer.py +
1109
+1110
+1111
+1112
@set_as_current
+def copy_selection(self) -> None:
+    """Copy the selected instances."""
+    george.tv_layer_copy()
+
+
+
+ +
+ + +
+ + + +

+ paste_selection() -> None + +

+ + +
+ +

Paste the previously copied instances.

+ +
+ Source code in pytvpaint/layer.py +
1114
+1115
+1116
+1117
@set_as_current
+def paste_selection(self) -> None:
+    """Paste the previously copied instances."""
+    george.tv_layer_paste()
+
+
+
+ +
+ + +
+ + + +

+ instances() -> Iterator[LayerInstance] + +

+ + +
+ +

Iterates over the layer instances.

+ + + +

Yields:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.layer.LayerInstance + +
+

each LayerInstance present in the layer

+
+
+ +
+ Source code in pytvpaint/layer.py +
1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
@refreshed_property
+@set_as_current
+def instances(self) -> Iterator[LayerInstance]:
+    """Iterates over the layer instances.
+
+    Yields:
+        each LayerInstance present in the layer
+    """
+    # instances start at layer start
+    current_instance = LayerInstance(self, self.start)
+
+    while True:
+        yield current_instance
+
+        nex_instance = current_instance.next
+        if nex_instance is None:
+            break
+
+        current_instance = nex_instance
+
+
+
+ +
+ + +
+ + + +

+ get_instance(frame: int, strict: bool = False) -> LayerInstance | None + +

+ + +
+ +

Get the instance at that frame.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
frame + int + +
+

the instance frame

+
+
+ required +
strict + bool + +
+

True will only return Instance if the given frame is the start of the instance. Default is False

+
+
+ False +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.layer.LayerInstance | None + +
+

the instance if found else None

+
+
+ +
+ Source code in pytvpaint/layer.py +
1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
def get_instance(self, frame: int, strict: bool = False) -> LayerInstance | None:
+    """Get the instance at that frame.
+
+    Args:
+        frame: the instance frame
+        strict: True will only return Instance if the given frame is the start of the instance. Default is False
+
+    Returns:
+        the instance if found else None
+    """
+    for layer_instance in self.instances:
+        if strict:
+            if layer_instance.start != frame:
+                continue
+            return layer_instance
+
+        if not (layer_instance.start <= frame <= layer_instance.end):
+            continue
+        return layer_instance
+
+    return None
+
+
+
+ +
+ + +
+ + + +

+ get_instances(from_frame: int, to_frame: int) -> Iterator[LayerInstance] + +

+ + +
+ +

Iterates over the layer instances and returns the one in the range (from_frame-to_frame).

+ + + +

Yields:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.layer.LayerInstance + +
+

each LayerInstance in the range (from_frame-to_frame)

+
+
+ +
+ Source code in pytvpaint/layer.py +
1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
def get_instances(self, from_frame: int, to_frame: int) -> Iterator[LayerInstance]:
+    """Iterates over the layer instances and returns the one in the range (from_frame-to_frame).
+
+    Yields:
+        each LayerInstance in the range (from_frame-to_frame)
+    """
+    for layer_instance in self.instances:
+        if layer_instance.end < from_frame:
+            continue
+        if from_frame <= layer_instance.start <= to_frame:
+            yield layer_instance
+        if layer_instance.start > to_frame:
+            break
+
+
+
+ +
+ + +
+ + + +

+ add_instance(start: int | None = None, nb_frames: int = 1, direction: george.InsertDirection | None = None, split: bool = False) -> LayerInstance + +

+ + +
+ +

Crates a new instance.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
start + int | None + +
+

start frame. Defaults to clip current frame if none provided

+
+
+ None +
nb_frames + int + +
+

number of frames in the new instance. Default is 1, this is the total number of frames created.

+
+
+ 1 +
direction + pytvpaint.george.InsertDirection | None + +
+

direction where new frames will be added/inserted

+
+
+ None +
split + bool + +
+

True to make each added frame a new image

+
+
+ False +
+ + + +

Raises:

+ + + + + + + + + + + + + + + + + + + + + +
TypeDescription
+ TypeError + +
+

if the layer is not an animation layer

+
+
+ ValueError + +
+

if the number of frames nb_frames is inferior or equal to 0

+
+
+ ValueError + +
+

if an instance already exists at the given range (start + nb_frames)

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
LayerInstance + pytvpaint.layer.LayerInstance + +
+

new layer instance

+
+
+ +
+ Source code in pytvpaint/layer.py +
1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
def add_instance(
+    self,
+    start: int | None = None,
+    nb_frames: int = 1,
+    direction: george.InsertDirection | None = None,
+    split: bool = False,
+) -> LayerInstance:
+    """Crates a new instance.
+
+    Args:
+        start: start frame. Defaults to clip current frame if none provided
+        nb_frames: number of frames in the new instance. Default is 1, this is the total number of frames created.
+        direction: direction where new frames will be added/inserted
+        split: True to make each added frame a new image
+
+    Raises:
+        TypeError: if the layer is not an animation layer
+        ValueError: if the number of frames `nb_frames` is inferior or equal to 0
+        ValueError: if an instance already exists at the given range (start + nb_frames)
+
+    Returns:
+        LayerInstance: new layer instance
+    """
+    if not self.is_anim_layer:
+        raise TypeError("The layer needs to be an animation layer")
+
+    if nb_frames <= 0:
+        raise ValueError("Instance number of frames must be at least 1")
+
+    if start and self.get_instance(start):
+        raise ValueError(
+            "An instance already exists at the designated frame range. "
+            "Edit or delete it before adding a new one."
+        )
+
+    start = start if start is not None else self.clip.current_frame
+    self.clip.make_current()
+
+    temp_layer = Layer.new_anim_layer(str(uuid4()))
+    temp_layer.make_current()
+
+    with utils.restore_current_frame(self.clip, 1):
+        if nb_frames > 1:
+            if split:
+                george.tv_layer_insert_image(count=nb_frames, direction=direction)
+            else:
+                layer_instance = next(temp_layer.instances)
+                layer_instance.length = nb_frames
+
+        temp_layer.select_all_frames()
+        temp_layer.copy_selection()
+        self.clip.current_frame = start
+        self.make_current()
+        self.paste_selection()
+        temp_layer.remove()
+
+    return LayerInstance(self, start)
+
+
+
+ +
+ + +
+ + + +

+ rename_instances(mode: george.InstanceNamingMode, prefix: str | None = None, suffix: str | None = None, process: george.InstanceNamingProcess | None = None) -> None + +

+ + +
+ +

Rename all the instances.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
mode + pytvpaint.george.InstanceNamingMode + +
+

the instance renaming mode

+
+
+ required +
prefix + str | None + +
+

the prefix to add to each name

+
+
+ None +
suffix + str | None + +
+

the suffix to add to each name

+
+
+ None +
process + pytvpaint.george.InstanceNamingProcess | None + +
+

the instance naming process

+
+
+ None +
+ +
+ Source code in pytvpaint/layer.py +
1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
def rename_instances(
+    self,
+    mode: george.InstanceNamingMode,
+    prefix: str | None = None,
+    suffix: str | None = None,
+    process: george.InstanceNamingProcess | None = None,
+) -> None:
+    """Rename all the instances.
+
+    Args:
+        mode: the instance renaming mode
+        prefix: the prefix to add to each name
+        suffix: the suffix to add to each name
+        process: the instance naming process
+    """
+    george.tv_instance_name(self.id, mode, prefix, suffix, process)
+
+
+
+ +
+ + + +
+ +
+ + +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/objects/layer_color/index.html b/api/objects/layer_color/index.html new file mode 100644 index 0000000..a1d73f3 --- /dev/null +++ b/api/objects/layer_color/index.html @@ -0,0 +1,2065 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + LayerColor - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

LayerColor class

+ + +
+ + + + +
+

+ Bases: pytvpaint.utils.Refreshable

+ + +

The color of a layer identified by an index. Layer colors are specific to a clip.

+ +

Construct a LayerColor from an index and a clip (if None it gets the current clip).

+ +
+ Source code in pytvpaint/layer.py +
226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
def __init__(
+    self,
+    color_index: int,
+    clip: Clip | None = None,
+) -> None:
+    """Construct a LayerColor from an index and a clip (if None it gets the current clip)."""
+    from pytvpaint.clip import Clip
+
+    super().__init__()
+    self._index = color_index
+    self._clip = clip or Clip.current_clip()
+    self._data = george.tv_layer_color_get_color(self.clip.id, self._index)
+
+
+ + + +
+ + + + + + + +
+ + + +

+ index: int + + + property + + +

+ + +
+ +

The layer color index.

+
+ +
+ +
+ + + +

+ clip: Clip + + + property + + +

+ + +
+ +

The layer color clip.

+
+ +
+ +
+ + + +

+ is_visible: bool + + + property + + +

+ + +
+ +

Get the visibility of the color index.

+
+ +
+ + + + +
+ + + +

+ refresh() -> None + +

+ + +
+ +

Refreshes the layer color data.

+ +
+ Source code in pytvpaint/layer.py +
239
+240
+241
+242
+243
def refresh(self) -> None:
+    """Refreshes the layer color data."""
+    if not self.refresh_on_call and self._data:
+        return
+    self._data = george.tv_layer_color_get_color(self._clip.id, self._index)
+
+
+
+ +
+ + +
+ + + +

+ name(value: str) -> None + +

+ + +
+ +

Set the name of the color.

+ +
+ Source code in pytvpaint/layer.py +
270
+271
+272
+273
+274
+275
@name.setter
+def name(self, value: str) -> None:
+    """Set the name of the color."""
+    clip_layer_color_names = (color.name for color in self.clip.layer_colors)
+    value = utils.get_unique_name(clip_layer_color_names, value)
+    george.tv_layer_color_set_color(self.clip.id, self.index, self.color, value)
+
+
+
+ +
+ + +
+ + + +

+ color(value: george.RGBColor) -> None + +

+ + +
+ +

Set the color value.

+ +
+ Source code in pytvpaint/layer.py +
286
+287
+288
+289
@color.setter
+def color(self, value: george.RGBColor) -> None:
+    """Set the color value."""
+    george.tv_layer_color_set_color(self.clip.id, self.index, value)
+
+
+
+ +
+ + +
+ + + +

+ lock_layers(lock: bool) -> None + +

+ + +
+ +

Lock or unlock all layers with this color.

+ +
+ Source code in pytvpaint/layer.py +
297
+298
+299
+300
+301
+302
+303
def lock_layers(self, lock: bool) -> None:
+    """Lock or unlock all layers with this color."""
+    self.clip.make_current()
+    if lock:
+        george.tv_layer_color_lock(self.index)
+    else:
+        george.tv_layer_color_unlock(self.index)
+
+
+
+ +
+ + +
+ + + +

+ show_layers(show: bool, mode: george.LayerColorDisplayOpt = george.LayerColorDisplayOpt.DISPLAY) -> None + +

+ + +
+ +

Show or hide layers with this color.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
show + bool + +
+

whether to show the layers using this color or not

+
+
+ required +
mode + pytvpaint.george.LayerColorDisplayOpt + +
+

the display mode. Defaults to george.LayerColorDisplayOpt.DISPLAY.

+
+
+ pytvpaint.george.LayerColorDisplayOpt.DISPLAY +
+ +
+ Source code in pytvpaint/layer.py +
305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
def show_layers(
+    self,
+    show: bool,
+    mode: george.LayerColorDisplayOpt = george.LayerColorDisplayOpt.DISPLAY,
+) -> None:
+    """Show or hide layers with this color.
+
+    Args:
+        show: whether to show the layers using this color or not
+        mode: the display mode. Defaults to george.LayerColorDisplayOpt.DISPLAY.
+    """
+    self.clip.make_current()
+    if show:
+        george.tv_layer_color_show(mode, self.index)
+    else:
+        george.tv_layer_color_hide(mode, self.index)
+
+
+
+ +
+ + +
+ + + +

+ select_layers(select: bool) -> None + +

+ + +
+ +

Select or unselect layers with this color.

+ +
+ Source code in pytvpaint/layer.py +
322
+323
+324
+325
+326
+327
+328
def select_layers(self, select: bool) -> None:
+    """Select or unselect layers with this color."""
+    self.clip.make_current()
+    if select:
+        george.tv_layer_color_select(self.index)
+    else:
+        george.tv_layer_color_unselect(self.index)
+
+
+
+ +
+ + + +
+ +
+ + +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/objects/layer_instance/index.html b/api/objects/layer_instance/index.html new file mode 100644 index 0000000..5b788ed --- /dev/null +++ b/api/objects/layer_instance/index.html @@ -0,0 +1,2319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + LayerInstance - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

LayerInstance class

+ + +
+ + + + +
+ + +

A layer instance is a frame where there is a drawing. It only has a start frame.

+ +
+ Note +

LayerInstance is special because we can't track their position, meaning that if the user move an instance the Python object values won't match.

+
+ + + +
+ + + + + + + +
+ + + +

+ name: str + + + property + writable + + +

+ + +
+ +

Get or set the instance name.

+
+ +
+ +
+ + + +

+ length: int + + + property + writable + + +

+ + +
+ +

Get or set the instance's number of frames or length.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

If the length provided is inferior to 1

+
+
+
+ +
+ +
+ + + +

+ end: int + + + property + writable + + +

+ + +
+ +

Get or set the instance's end frame.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

If the end frame provided is inferior to the instance's start frame

+
+
+
+ +
+ +
+ + + +

+ next: LayerInstance | None + + + property + + +

+ + +
+ +

Returns the next instance.

+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.layer.LayerInstance | None + +
+

the next instance or None if at the end of the layer

+
+
+
+ +
+ +
+ + + +

+ previous: LayerInstance | None + + + property + + +

+ + +
+ +

Get the previous instance.

+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.layer.LayerInstance | None + +
+

the previous instance, None if there isn't

+
+
+
+ +
+ + + + +
+ + + +

+ split(at_frame: int) -> LayerInstance + +

+ + +
+ +

Split the instance into two instances at the given frame.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
at_frame + int + +
+

the frame where the split will occur

+
+
+ required +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

If at_frame is superior to the instance's end frame

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
LayerInstance + pytvpaint.layer.LayerInstance + +
+

the new layer instance

+
+
+ +
+ Source code in pytvpaint/layer.py +
110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
def split(self, at_frame: int) -> LayerInstance:
+    """Split the instance into two instances at the given frame.
+
+    Args:
+        at_frame: the frame where the split will occur
+
+    Raises:
+        ValueError: If `at_frame` is superior to the instance's end frame
+
+    Returns:
+        LayerInstance: the new layer instance
+    """
+    if at_frame > self.end:
+        raise ValueError(
+            f"`at_frame` must be in range of the instance's start-end ({self.start}-{self.end})"
+        )
+
+    self.layer.make_current()
+    real_frame = at_frame - self.layer.project.start_frame
+    george.tv_exposure_break(real_frame)
+
+    return LayerInstance(self.layer, at_frame)
+
+
+
+ +
+ + +
+ + + +

+ duplicate(direction: george.InsertDirection = george.InsertDirection.AFTER) -> None + +

+ + +
+ +

Duplicate the instance and insert it in the given direction.

+ +
+ Source code in pytvpaint/layer.py +
133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
def duplicate(
+    self, direction: george.InsertDirection = george.InsertDirection.AFTER
+) -> None:
+    """Duplicate the instance and insert it in the given direction."""
+    self.layer.make_current()
+
+    # tvp won't insert images if the insert frame is the same as the instance start, let's move it
+    move_frame = self.layer.clip.current_frame
+    if move_frame == self.start and self.layer.start != self.start:
+        move_frame = self.layer.start
+    else:
+        move_frame = self.layer.end + 1
+
+    with utils.restore_current_frame(self.layer.clip, move_frame):
+        self.copy()
+        at_frame = (
+            self.end if direction == george.InsertDirection.AFTER else self.start
+        )
+        self.paste(at_frame=at_frame)
+
+
+
+ +
+ + +
+ + + +

+ cut() -> None + +

+ + +
+ +

Cut all the frames/images/exposures of the instance and store them in the image buffer.

+ +
+ Source code in pytvpaint/layer.py +
153
+154
+155
+156
+157
def cut(self) -> None:
+    """Cut all the frames/images/exposures of the instance and store them in the image buffer."""
+    self.layer.make_current()
+    self.select()
+    self.layer.cut_selection()
+
+
+
+ +
+ + +
+ + + +

+ copy() -> None + +

+ + +
+ +

Copy all the frames/images/exposures of the instance and store them in the image buffer.

+ +
+ Source code in pytvpaint/layer.py +
159
+160
+161
+162
+163
def copy(self) -> None:
+    """Copy all the frames/images/exposures of the instance and store them in the image buffer."""
+    self.layer.make_current()
+    self.select()
+    self.layer.copy_selection()
+
+
+
+ +
+ + +
+ + + +

+ paste(at_frame: int | None) -> None + +

+ + +
+ +

Paste all the frames/images/exposures stored in the image buffer to the current instance at the given frame.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
at_frame + int | None + +
+

the frame where the stored frames will be pasted. Default is the current frame

+
+
+ required +
+ +
+ Source code in pytvpaint/layer.py +
165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
def paste(self, at_frame: int | None) -> None:
+    """Paste all the frames/images/exposures stored in the image buffer to the current instance at the given frame.
+
+    Args:
+        at_frame: the frame where the stored frames will be pasted. Default is the current frame
+    """
+    at_frame = at_frame if at_frame is not None else self.layer.clip.current_frame
+
+    self.layer.make_current()
+    with utils.restore_current_frame(self.layer.clip, at_frame):
+        self.layer.paste_selection()
+
+
+
+ +
+ + +
+ + + +

+ select() -> None + +

+ + +
+ +

Select all frames in this instance.

+ +
+ Source code in pytvpaint/layer.py +
177
+178
+179
def select(self) -> None:
+    """Select all frames in this instance."""
+    self.layer.select_frames(self.start, (self.length - 1))
+
+
+
+ +
+ + + +
+ +
+ + +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/objects/project/index.html b/api/objects/project/index.html new file mode 100644 index 0000000..63da315 --- /dev/null +++ b/api/objects/project/index.html @@ -0,0 +1,5048 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Project - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Project class

+ + +
+ + + + +
+ +

Project class.

+ + + +
+ + + + + + + + +
+ + + +

+ Project(project_id: str) + +

+ + +
+

+ Bases: pytvpaint.utils.Refreshable, pytvpaint.utils.Renderable

+ + +

A TVPaint project is the highest object that contains everything in the data hierarchy.

+

It looks like this: Project -> Scene -> Clip -> Layer -> LayerInstance

+ +
+ Source code in pytvpaint/project.py +
32
+33
+34
+35
+36
def __init__(self, project_id: str) -> None:
+    super().__init__()
+    self._id = project_id
+    self._is_closed = False
+    self._data = george.tv_project_info(self._id)
+
+
+ + + +
+ + + + + + + +
+ + + +

+ id: str + + + property + + +

+ + +
+ +

The project id.

+ +
+ Note +

the id is persistent on project load/close.

+
+ +
+ +
+ + + +

+ position: int + + + property + + +

+ + +
+ +

The project's position in the project tabs.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if project cannot be found in open projects

+
+
+ +
+ Note +

the indices go from right to left in the UI

+
+ +
+ +
+ + + +

+ is_closed: bool + + + property + + +

+ + +
+ +

Returns True if the project is closed.

+
+ +
+ +
+ + + +

+ exists: bool + + + property + + +

+ + +
+ +

Checks if the project exists on disk.

+
+ +
+ +
+ + + +

+ is_current: bool + + + property + + +

+ + +
+ +

Returns True if the project is the current selected one in the UI.

+
+ +
+ +
+ + + +

+ name: str + + + property + + +

+ + +
+ +

The name of the project which is the filename without the extension.

+
+ +
+ +
+ + + +

+ width: int + + + property + + +

+ + +
+ +

The width of the canvas.

+
+ +
+ +
+ + + +

+ height: int + + + property + + +

+ + +
+ +

The height of the canvas.

+
+ +
+ +
+ + + +

+ fps: float + + + property + + +

+ + +
+ +

The project's framerate.

+
+ +
+ +
+ + + +

+ playback_fps: float + + + property + + +

+ + +
+ +

The project's playback framerate.

+
+ +
+ +
+ + + +

+ field_order: george.FieldOrder + + + property + + +

+ + +
+ +

The field order.

+
+ +
+ +
+ + + +

+ start_frame: int + + + property + writable + + +

+ + +
+ +

The project's start frame.

+
+ +
+ +
+ + + +

+ end_frame: int + + + property + + +

+ + +
+ +

The project's end frame, meaning the last frame of the last clip in the project's timeline.

+
+ +
+ +
+ + + +

+ current_frame: int + + + property + writable + + +

+ + +
+ +

Get the current frame relative to the timeline.

+
+ +
+ +
+ + + +

+ background_mode: george.BackgroundMode + + + property + writable + + +

+ + +
+ +

Get/Set the background mode.

+
+ +
+ +
+ + + +

+ background_colors: tuple[george.RGBColor, george.RGBColor] | george.RGBColor | None + + + property + writable + + +

+ + +
+ +

Get/Set the background color(s).

+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ tuple[pytvpaint.george.RGBColor, pytvpaint.george.RGBColor] | pytvpaint.george.RGBColor | None + +
+

a tuple of two colors if checker, a single color if solid or None if empty

+
+
+
+ +
+ +
+ + + +

+ header_info: str + + + property + writable + + +

+ + +
+ +

The project's header info.

+
+ +
+ +
+ + + +

+ author: str + + + property + writable + + +

+ + +
+ +

The project's author info.

+
+ +
+ +
+ + + +

+ notes: str + + + property + writable + + +

+ + +
+ +

The project's notes text.

+
+ +
+ +
+ + + +

+ current_scene: Scene + + + property + + +

+ + +
+ +

Get the current scene of the project.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if scene cannot be found in project

+
+
+
+ +
+ +
+ + + +

+ scenes: Iterator[Scene] + + + property + + +

+ + +
+ +

Yields the project's scenes.

+
+ +
+ +
+ + + +

+ current_clip: Clip + + + property + + +

+ + +
+ +

Returns the project's current clip.

+
+ +
+ +
+ + + +

+ clips: Iterator[Clip] + + + property + + +

+ + +
+ +

Iterates over all the clips in the project's scenes.

+
+ +
+ +
+ + + +

+ clip_names: Iterator[str] + + + property + + +

+ + +
+ +

Optimized way to get the clip names. Useful for get_unique_name.

+
+ +
+ +
+ + + +

+ sounds: Iterator[ProjectSound] + + + property + + +

+ + +
+ +

Iterator over the project sounds.

+
+ +
+ +
+ + + +

+ mark_in: int | None + + + property + writable + + +

+ + +
+ +

Get the project mark in or None if no mark in set.

+
+ +
+ +
+ + + +

+ mark_out: int | None + + + property + writable + + +

+ + +
+ +

Get the project mark out or None if no mark out set.

+
+ +
+ + + + +
+ + + +

+ refresh() -> None + +

+ + +
+ +

Refreshed the project data.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if project has been closed

+
+
+ +
+ Source code in pytvpaint/project.py +
48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
def refresh(self) -> None:
+    """Refreshed the project data.
+
+    Raises:
+        ValueError: if project has been closed
+    """
+    if self._is_closed:
+        msg = "Project already closed, load the project again to get data"
+        raise ValueError(msg)
+    if not self.refresh_on_call and self._data:
+        return
+
+    self._data = george.tv_project_info(self._id)
+
+
+
+ +
+ + +
+ + + +

+ make_current() -> None + +

+ + +
+ +

Make the project the current one.

+ +
+ Source code in pytvpaint/project.py +
107
+108
+109
+110
+111
def make_current(self) -> None:
+    """Make the project the current one."""
+    if self.is_current:
+        return
+    george.tv_project_select(self.id)
+
+
+
+ +
+ + +
+ + + +

+ path() -> Path + +

+ + +
+ +

The project path on disk.

+ +
+ Source code in pytvpaint/project.py +
113
+114
+115
+116
@refreshed_property
+def path(self) -> Path:
+    """The project path on disk."""
+    return self._data.path
+
+
+
+ +
+ + +
+ + + +

+ resize(width: int, height: int, overwrite: bool = False, resize_opt: george.ResizeOption | None = None) -> Project + +

+ + +
+ +

Resize the current project and returns a new one.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
width + int + +
+

the new width

+
+
+ required +
height + int + +
+

the new height

+
+
+ required +
overwrite + bool + +
+

overwrite the original project, default is to create a new project

+
+
+ False +
resize_opt + pytvpaint.george.ResizeOption | None + +
+

how to resize the project

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.project.Project + +
+

the newly resized project

+
+
+ +
+ Source code in pytvpaint/project.py +
135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
@set_as_current
+def resize(
+    self,
+    width: int,
+    height: int,
+    overwrite: bool = False,
+    resize_opt: george.ResizeOption | None = None,
+) -> Project:
+    """Resize the current project and returns a new one.
+
+    Args:
+        width: the new width
+        height: the new height
+        overwrite: overwrite the original project, default is to create a new project
+        resize_opt: how to resize the project
+
+    Returns:
+        the newly resized project
+    """
+    if (width, height) == (self.width, self.height):
+        return self
+
+    origin_position = self.position
+    origin_path = self.path
+
+    if resize_opt:
+        george.tv_resize_page(width, height, resize_opt)
+    else:
+        george.tv_resize_project(width, height)
+
+    # The resized project is at the same position and replaced the original one
+    resized_id = george.tv_project_enum_id(origin_position)
+    resized_project = Project(resized_id)
+
+    if overwrite:
+        resized_project.save(origin_path)
+
+    return resized_project
+
+
+
+ +
+ + +
+ + + +

+ set_fps(fps: float, time_stretch: bool = False, preview: bool = False) -> None + +

+ + +
+ +

Set the project's framerate.

+ +
+ Source code in pytvpaint/project.py +
188
+189
+190
+191
+192
+193
+194
+195
+196
@set_as_current
+def set_fps(
+    self,
+    fps: float,
+    time_stretch: bool = False,
+    preview: bool = False,
+) -> None:
+    """Set the project's framerate."""
+    george.tv_frame_rate_set(fps, time_stretch, preview)
+
+
+
+ +
+ + +
+ + + +

+ pixel_aspect_ratio() -> float + +

+ + +
+ +

The project's pixel aspect ratio.

+ +
+ Source code in pytvpaint/project.py +
203
+204
+205
+206
+207
@refreshed_property
+@set_as_current
+def pixel_aspect_ratio(self) -> float:
+    """The project's pixel aspect ratio."""
+    return self._data.pixel_aspect_ratio
+
+
+
+ +
+ + +
+ + + +

+ clear_background() -> None + +

+ + +
+ +

Clear the background color and set it to None.

+ +
+ Source code in pytvpaint/project.py +
278
+279
+280
+281
+282
+283
+284
+285
@set_as_current
+def clear_background(self) -> None:
+    """Clear the background color and set it to None."""
+    self.background_mode = george.BackgroundMode.NONE
+    self.background_colors = (
+        george.RGBColor(255, 255, 255),
+        george.RGBColor(0, 0, 0),
+    )
+
+
+
+ +
+ + +
+ + + +

+ get_project(by_id: str | None = None, by_name: str | None = None) -> Project | None + + + classmethod + + +

+ + +
+ +

Find a project by id or by name.

+ +
+ Source code in pytvpaint/project.py +
314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
@classmethod
+def get_project(
+    cls,
+    by_id: str | None = None,
+    by_name: str | None = None,
+) -> Project | None:
+    """Find a project by id or by name."""
+    for project in Project.open_projects():
+        if (by_id and project.id == by_id) or (by_name and project.name == by_name):
+            return project
+
+    return None
+
+
+
+ +
+ + +
+ + + +

+ current_scene_ids() -> Iterator[int] + + + staticmethod + + +

+ + +
+ +

Yields the current project's scene ids.

+ +
+ Source code in pytvpaint/project.py +
327
+328
+329
+330
@staticmethod
+def current_scene_ids() -> Iterator[int]:
+    """Yields the current project's scene ids."""
+    return utils.position_generator(lambda pos: george.tv_scene_enum_id(pos))
+
+
+
+ +
+ + +
+ + + +

+ get_scene(by_id: int | None = None, by_name: str | None = None) -> Scene | None + +

+ + +
+ +

Find a scene in the project by id or name.

+ +
+ Source code in pytvpaint/project.py +
353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
def get_scene(
+    self,
+    by_id: int | None = None,
+    by_name: str | None = None,
+) -> Scene | None:
+    """Find a scene in the project by id or name."""
+    for scene in self.scenes:
+        if (by_id and scene.id == by_id) or (by_name and scene.name == by_name):
+            return scene
+
+    return None
+
+
+
+ +
+ + +
+ + + +

+ add_scene() -> Scene + +

+ + +
+ +

Add a new scene in the project.

+ +
+ Source code in pytvpaint/project.py +
365
+366
+367
+368
+369
+370
@set_as_current
+def add_scene(self) -> Scene:
+    """Add a new scene in the project."""
+    from pytvpaint.scene import Scene
+
+    return Scene.new(project=self)
+
+
+
+ +
+ + +
+ + + +

+ get_clip(by_id: int | None = None, by_name: str | None = None, scene_id: int | None = None) -> Clip | None + +

+ + +
+ +

Find a clip by id, name or scene_id.

+ +
+ Source code in pytvpaint/project.py +
397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
def get_clip(
+    self,
+    by_id: int | None = None,
+    by_name: str | None = None,
+    scene_id: int | None = None,
+) -> Clip | None:
+    """Find a clip by id, name or scene_id."""
+    clips = self.clips
+    if scene_id:
+        selected_scene = self.get_scene(by_id=scene_id)
+        clips = selected_scene.clips if selected_scene else clips
+
+    for clip in clips:
+        if (by_id and clip.id == by_id) or (by_name and clip.name == by_name):
+            return clip
+
+    return None
+
+
+
+ +
+ + +
+ + + +

+ add_clip(clip_name: str, scene: Scene | None = None) -> Clip + +

+ + +
+ +

Add a new clip in the given scene or the current one if no scene provided.

+ +
+ Source code in pytvpaint/project.py +
415
+416
+417
+418
def add_clip(self, clip_name: str, scene: Scene | None = None) -> Clip:
+    """Add a new clip in the given scene or the current one if no scene provided."""
+    scene = scene or self.current_scene
+    return scene.add_clip(clip_name)
+
+
+
+ +
+ + +
+ + + +

+ add_sound(sound_path: Path | str) -> ProjectSound + +

+ + +
+ +

Add a new sound clip to the project.

+ +
+ Source code in pytvpaint/project.py +
430
+431
+432
def add_sound(self, sound_path: Path | str) -> ProjectSound:
+    """Add a new sound clip to the project."""
+    return ProjectSound.new(sound_path, parent=self)
+
+
+
+ +
+ + +
+ + + +

+ render(output_path: Path | str | FileSequence, start: int | None = None, end: int | None = None, use_camera: bool = False, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = None, format_opts: list[str] | None = None) -> None + +

+ + +
+ +

Render the project to a single frame or frame sequence or movie.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
output_path + pathlib.Path | str | fileseq.filesequence.FileSequence + +
+

a single file or file sequence pattern

+
+
+ required +
start + int | None + +
+

the start frame to render or the mark in or the project's start frame if None. Defaults to None.

+
+
+ None +
end + int | None + +
+

the end frame to render or the mark out or the project's end frame if None. Defaults to None.

+
+
+ None +
use_camera + bool + +
+

use the camera for rendering, otherwise render the whole canvas. Defaults to False.

+
+
+ False +
alpha_mode + pytvpaint.george.AlphaSaveMode + +
+

the alpha mode for rendering. Defaults to george.AlphaSaveMode.PREMULTIPLY.

+
+
+ pytvpaint.george.AlphaSaveMode.PREMULTIPLY +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the background mode for rendering. Defaults to george.BackgroundMode.NONE.

+
+
+ None +
format_opts + list[str] | None + +
+

custom format options. Defaults to None.

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if requested range (start-end) not in project range/bounds

+
+
+ ValueError + +
+

if output is a movie, and it's duration is equal to 1 frame

+
+
+ FileNotFoundError + +
+

if the render failed and no files were found on disk or missing frames

+
+
+ +
+ Note +

This functions uses the project's timeline as a basis for the range (start-end). This timeline includes all +the project's clips and is different from a clip range. For more details on the differences in frame ranges +and the timeline in TVPaint, please check the Usage/Rendering section of the documentation.

+
+
+ Warning +

Even tough pytvpaint does a pretty good job of correcting the frame ranges for rendering, we're still +encountering some weird edge cases where TVPaint will consider the range invalid for seemingly no reason.

+
+
+ Source code in pytvpaint/project.py +
456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
@set_as_current
+def render(
+    self,
+    output_path: Path | str | FileSequence,
+    start: int | None = None,
+    end: int | None = None,
+    use_camera: bool = False,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = None,
+    format_opts: list[str] | None = None,
+) -> None:
+    """Render the project to a single frame or frame sequence or movie.
+
+    Args:
+        output_path: a single file or file sequence pattern
+        start: the start frame to render or the mark in or the project's start frame if None. Defaults to None.
+        end: the end frame to render or the mark out or the project's end frame if None. Defaults to None.
+        use_camera: use the camera for rendering, otherwise render the whole canvas. Defaults to False.
+        alpha_mode: the alpha mode for rendering. Defaults to george.AlphaSaveMode.PREMULTIPLY.
+        background_mode: the background mode for rendering. Defaults to george.BackgroundMode.NONE.
+        format_opts: custom format options. Defaults to None.
+
+    Raises:
+        ValueError: if requested range (start-end) not in project range/bounds
+        ValueError: if output is a movie, and it's duration is equal to 1 frame
+        FileNotFoundError: if the render failed and no files were found on disk or missing frames
+
+    Note:
+        This functions uses the project's timeline as a basis for the range (start-end). This timeline includes all
+        the project's clips and is different from a clip range. For more details on the differences in frame ranges
+        and the timeline in TVPaint, please check the `Usage/Rendering` section of the documentation.
+
+    Warning:
+        Even tough pytvpaint does a pretty good job of correcting the frame ranges for rendering, we're still
+        encountering some weird edge cases where TVPaint will consider the range invalid for seemingly no reason.
+    """
+    default_start = self.mark_in or self.start_frame
+    default_end = self.mark_out or self.end_frame
+
+    self._render(
+        output_path,
+        default_start,
+        default_end,
+        start,
+        end,
+        use_camera,
+        layer_selection=None,
+        alpha_mode=alpha_mode,
+        background_mode=background_mode,
+        format_opts=format_opts,
+    )
+
+
+
+ +
+ + +
+ + + +

+ render_clips(clips: list[Clip], output_path: Path | str | FileSequence, use_camera: bool = False, alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY, background_mode: george.BackgroundMode | None = None, format_opts: list[str] | None = None) -> None + +

+ + +
+ +

Render sequential clips as a single output.

+ +
+ Source code in pytvpaint/project.py +
508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
@set_as_current
+def render_clips(
+    self,
+    clips: list[Clip],
+    output_path: Path | str | FileSequence,
+    use_camera: bool = False,
+    alpha_mode: george.AlphaSaveMode = george.AlphaSaveMode.PREMULTIPLY,
+    background_mode: george.BackgroundMode | None = None,
+    format_opts: list[str] | None = None,
+) -> None:
+    """Render sequential clips as a single output."""
+    clips = sorted(clips, key=lambda c: c.position)
+    start = clips[0].timeline_start
+    end = clips[-1].timeline_end
+    self.render(
+        output_path,
+        start,
+        end,
+        use_camera,
+        alpha_mode,
+        background_mode,
+        format_opts,
+    )
+
+
+
+ +
+ + +
+ + + +

+ current_project_id() -> str + + + staticmethod + + +

+ + +
+ +

Returns the current project id.

+ +
+ Source code in pytvpaint/project.py +
532
+533
+534
+535
@staticmethod
+def current_project_id() -> str:
+    """Returns the current project id."""
+    return george.tv_project_current_id()
+
+
+
+ +
+ + +
+ + + +

+ current_project() -> Project + + + staticmethod + + +

+ + +
+ +

Returns the current project.

+ +
+ Source code in pytvpaint/project.py +
537
+538
+539
+540
@staticmethod
+def current_project() -> Project:
+    """Returns the current project."""
+    return Project(project_id=Project.current_project_id())
+
+
+
+ +
+ + +
+ + + +

+ open_projects_ids() -> Iterator[str] + + + staticmethod + + +

+ + +
+ +

Yields the ids of the currently open projects.

+ +
+ Source code in pytvpaint/project.py +
542
+543
+544
+545
@staticmethod
+def open_projects_ids() -> Iterator[str]:
+    """Yields the ids of the currently open projects."""
+    return utils.position_generator(lambda pos: george.tv_project_enum_id(pos))
+
+
+
+ +
+ + +
+ + + +

+ open_projects() -> Iterator[Project] + + + classmethod + + +

+ + +
+ +

Iterator over the currently open projects.

+ +
+ Source code in pytvpaint/project.py +
547
+548
+549
+550
+551
@classmethod
+def open_projects(cls) -> Iterator[Project]:
+    """Iterator over the currently open projects."""
+    for project_id in Project.open_projects_ids():
+        yield Project(project_id)
+
+
+
+ +
+ + +
+ + + +

+ new(project_path: Path | str, width: int = 1920, height: int = 1080, pixel_aspect_ratio: float = 1.0, frame_rate: float = 24.0, field_order: george.FieldOrder = george.FieldOrder.NONE, start_frame: int = 1) -> Project + + + classmethod + + +

+ + +
+ +

Create a new project.

+ +
+ Source code in pytvpaint/project.py +
607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
@classmethod
+def new(
+    cls,
+    project_path: Path | str,
+    width: int = 1920,
+    height: int = 1080,
+    pixel_aspect_ratio: float = 1.0,
+    frame_rate: float = 24.0,
+    field_order: george.FieldOrder = george.FieldOrder.NONE,
+    start_frame: int = 1,
+) -> Project:
+    """Create a new project."""
+    george.tv_project_new(
+        Path(project_path).resolve().as_posix(),
+        width,
+        height,
+        pixel_aspect_ratio,
+        frame_rate,
+        field_order,
+        start_frame,
+    )
+    return cls.current_project()
+
+
+
+ +
+ + +
+ + + +

+ new_from_camera(export_path: Path | str | None = None) -> Project + +

+ + +
+ +

Create a new cropped project from the camera view.

+ +
+ Source code in pytvpaint/project.py +
630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
@set_as_current
+def new_from_camera(self, export_path: Path | str | None = None) -> Project:
+    """Create a new cropped project from the camera view."""
+    cam_project_id = george.tv_project_render_camera(self.id)
+    cam_project = Project(cam_project_id)
+
+    if export_path:
+        export_path = Path(export_path)
+        export_path.mkdir(exist_ok=True, parents=True)
+        cam_project.save(export_path)
+
+    return cam_project
+
+
+
+ +
+ + +
+ + + +

+ duplicate() -> Project + +

+ + +
+ +

Duplicate the project and return the new one.

+ +
+ Source code in pytvpaint/project.py +
643
+644
+645
+646
+647
+648
+649
@set_as_current
+def duplicate(self) -> Project:
+    """Duplicate the project and return the new one."""
+    george.tv_project_duplicate()
+    duplicated = Project.current_project()
+    self.make_current()
+    return duplicated
+
+
+
+ +
+ + +
+ + + +

+ close() -> None + +

+ + +
+ +

Closes the project.

+ +
+ Source code in pytvpaint/project.py +
651
+652
+653
+654
def close(self) -> None:
+    """Closes the project."""
+    self._is_closed = True
+    george.tv_project_close(self._id)
+
+
+
+ +
+ + +
+ + + +

+ close_all(close_tvp: bool = False) -> None + + + classmethod + + +

+ + +
+ +

Closes all open projects.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
close_tvp + bool + +
+

close the TVPaint instance as well

+
+
+ False +
+ +
+ Source code in pytvpaint/project.py +
656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
@classmethod
+def close_all(cls, close_tvp: bool = False) -> None:
+    """Closes all open projects.
+
+    Args:
+        close_tvp: close the TVPaint instance as well
+    """
+    for project in list(cls.open_projects()):
+        project.close()
+
+    if close_tvp:
+        george.tv_quit()
+
+
+
+ +
+ + +
+ + + +

+ load(project_path: Path | str, silent: bool = True) -> Project + + + classmethod + + +

+ + +
+ +

Load an existing .tvpp/.tvp project or .tvpx file.

+ +
+ Source code in pytvpaint/project.py +
669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
@classmethod
+def load(cls, project_path: Path | str, silent: bool = True) -> Project:
+    """Load an existing .tvpp/.tvp project or .tvpx file."""
+    project_path = Path(project_path)
+
+    # Check if project not already open, if so, return it
+    for project in cls.open_projects():
+        if project.path == project_path:
+            return project
+
+    george.tv_load_project(project_path, silent)
+    return cls.current_project()
+
+
+
+ +
+ + +
+ + + +

+ save(save_path: Path | str | None = None) -> None + +

+ + +
+ +

Saves the project on disk.

+ +
+ Source code in pytvpaint/project.py +
682
+683
+684
+685
def save(self, save_path: Path | str | None = None) -> None:
+    """Saves the project on disk."""
+    save_path = Path(save_path or self.path).resolve()
+    george.tv_save_project(save_path.as_posix())
+
+
+
+ +
+ + +
+ + + +

+ load_panel(panel_path: Path | str) -> None + +

+ + +
+ +

Load an external TVPaint panel.

+ +
+ Source code in pytvpaint/project.py +
687
+688
+689
+690
@set_as_current
+def load_panel(self, panel_path: Path | str) -> None:
+    """Load an external TVPaint panel."""
+    george.tv_load_project(panel_path, silent=True)
+
+
+
+ +
+ + +
+ + + +

+ load_palette(palette_path: Path | str) -> None + +

+ + +
+ +

Load a palette.

+ +
+ Source code in pytvpaint/project.py +
692
+693
+694
+695
@set_as_current
+def load_palette(self, palette_path: Path | str) -> None:
+    """Load a palette."""
+    george.tv_save_palette(palette_path)
+
+
+
+ +
+ + +
+ + + +

+ save_palette(save_path: Path | str | None = None) -> None + +

+ + +
+ +

Save a palette to the given path.

+ +
+ Source code in pytvpaint/project.py +
697
+698
+699
+700
+701
@set_as_current
+def save_palette(self, save_path: Path | str | None = None) -> None:
+    """Save a palette to the given path."""
+    save_path = Path(save_path or self.path)
+    george.tv_save_project(save_path)
+
+
+
+ +
+ + +
+ + + +

+ save_video_dependencies() -> None + +

+ + +
+ +

Saves the video dependencies.

+ +
+ Source code in pytvpaint/project.py +
703
+704
+705
+706
@set_as_current
+def save_video_dependencies(self) -> None:
+    """Saves the video dependencies."""
+    george.tv_project_save_video_dependencies()
+
+
+
+ +
+ + +
+ + + +

+ save_audio_dependencies() -> None + +

+ + +
+ +

Saves audio dependencies.

+ +
+ Source code in pytvpaint/project.py +
708
+709
+710
+711
@set_as_current
+def save_audio_dependencies(self) -> None:
+    """Saves audio dependencies."""
+    george.tv_project_save_audio_dependencies()
+
+
+
+ +
+ + + +
+ +
+ + +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/objects/project_sound/index.html b/api/objects/project_sound/index.html new file mode 100644 index 0000000..fe088b0 --- /dev/null +++ b/api/objects/project_sound/index.html @@ -0,0 +1,1747 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + ProjectSound - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

ProjectSound class

+ + +
+ + + + +
+

+ Bases: pytvpaint.sound.BaseSound['Project']

+ + +

A Project sound.

+ +
+ Source code in pytvpaint/sound.py +
256
+257
+258
+259
+260
+261
+262
+263
+264
def __init__(
+    self,
+    track_index: int,
+    project: Project | None = None,
+) -> None:
+    from pytvpaint.project import Project
+
+    project = project or Project.current_project()
+    super().__init__(track_index, project)
+
+
+ + + +
+ + + + + + + +
+ + + +

+ project: Project + + + property + + +

+ + +
+ +

The sound's project.

+
+ +
+ + + + +
+ + + +

+ remove() -> None + +

+ + +
+ +

Remove the sound from the project.

+ +
+ Source code in pytvpaint/sound.py +
289
+290
+291
+292
+293
@set_as_current
+def remove(self) -> None:
+    """Remove the sound from the project."""
+    george.tv_sound_project_remove(self.track_index)
+    self.mark_removed()
+
+
+
+ +
+ + +
+ + + +

+ reload() -> None + +

+ + +
+ +

Reload the sound from file.

+ +
+ Source code in pytvpaint/sound.py +
295
+296
+297
def reload(self) -> None:
+    """Reload the sound from file."""
+    george.tv_sound_project_reload(self.project.id, self.track_index)
+
+
+
+ +
+ + +
+ + + +

+ make_current() -> None + +

+ + +
+ +

Makes the sound project current.

+ +
+ Source code in pytvpaint/sound.py +
299
+300
+301
def make_current(self) -> None:
+    """Makes the sound project current."""
+    self.project.make_current()
+
+
+
+ +
+ + + +
+ +
+ + +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/objects/scene/index.html b/api/objects/scene/index.html new file mode 100644 index 0000000..8e05afb --- /dev/null +++ b/api/objects/scene/index.html @@ -0,0 +1,2332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + Scene - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

Scene class

+ + +
+ + + + +
+ +

Scene class.

+ + + +
+ + + + + + + + +
+ + + +

+ Scene(scene_id: int, project: Project) + +

+ + +
+

+ Bases: pytvpaint.utils.Removable

+ + +

A Scene is a collection of clips. A Scene is inside a project.

+ +
+ Source code in pytvpaint/scene.py +
19
+20
+21
+22
def __init__(self, scene_id: int, project: Project) -> None:
+    super().__init__()
+    self._id: int = scene_id
+    self._project = project
+
+
+ + + +
+ + + + + + + +
+ + + +

+ is_current: bool + + + property + + +

+ + +
+ +

Returns True if the scene is the current one.

+
+ +
+ +
+ + + +

+ id: int + + + property + + +

+ + +
+ +

The scene id.

+
+ +
+ +
+ + + +

+ project: Project + + + property + + +

+ + +
+ +

The scene's project.

+
+ +
+ +
+ + + +

+ position: int + + + property + writable + + +

+ + +
+ +

The scene's position in the project.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if scene cannot be found in the project

+
+
+
+ +
+ +
+ + + +

+ clip_ids: Iterator[int] + + + property + + +

+ + +
+ +

Returns an iterator over the clip ids.

+
+ +
+ +
+ + + +

+ clips: Iterator[Clip] + + + property + + +

+ + +
+ +

Yields the scene clips.

+
+ +
+ + + + +
+ + + +

+ current_scene_id() -> int + + + staticmethod + + +

+ + +
+ +

Returns the current scene id (the current clip's scene).

+ +
+ Source code in pytvpaint/scene.py +
34
+35
+36
+37
@staticmethod
+def current_scene_id() -> int:
+    """Returns the current scene id (the current clip's scene)."""
+    return george.tv_scene_current_id()
+
+
+
+ +
+ + +
+ + + +

+ current_scene() -> Scene + + + staticmethod + + +

+ + +
+ +

Returns the current scene of the current project.

+ +
+ Source code in pytvpaint/scene.py +
39
+40
+41
+42
+43
+44
+45
@staticmethod
+def current_scene() -> Scene:
+    """Returns the current scene of the current project."""
+    return Scene(
+        scene_id=george.tv_scene_current_id(),
+        project=Project.current_project(),
+    )
+
+
+
+ +
+ + +
+ + + +

+ new(project: Project | None = None) -> Scene + + + classmethod + + +

+ + +
+ +

Creates a new scene in the provided project.

+ +
+ Source code in pytvpaint/scene.py +
47
+48
+49
+50
+51
+52
+53
@classmethod
+def new(cls, project: Project | None = None) -> Scene:
+    """Creates a new scene in the provided project."""
+    project = project or Project.current_project()
+    project.make_current()
+    george.tv_scene_new()
+    return cls.current_scene()
+
+
+
+ +
+ + +
+ + + +

+ make_current() -> None + +

+ + +
+ +

Make this scene the current one.

+ +
+ Source code in pytvpaint/scene.py +
55
+56
+57
+58
+59
+60
+61
+62
def make_current(self) -> None:
+    """Make this scene the current one."""
+    if self.is_current:
+        return
+
+    # In order to select the scene, we select any child clip inside of it
+    first_clip_id = george.tv_clip_enum_id(self.id, 0)
+    george.tv_clip_select(first_clip_id)
+
+
+
+ +
+ + +
+ + + +

+ get_clip(by_id: int | None = None, by_name: str | None = None) -> Clip | None + +

+ + +
+ +

Find a clip by id or by name.

+ +
+ Source code in pytvpaint/scene.py +
110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
def get_clip(
+    self,
+    by_id: int | None = None,
+    by_name: str | None = None,
+) -> Clip | None:
+    """Find a clip by id or by name."""
+    for clip in self.clips:
+        if (by_id and clip.id == by_id) or (by_name and clip.name == by_name):
+            return clip
+
+    return None
+
+
+
+ +
+ + +
+ + + +

+ add_clip(clip_name: str) -> Clip + +

+ + +
+ +

Adds a new clip to the scene.

+ +
+ Source code in pytvpaint/scene.py +
122
+123
+124
+125
+126
@set_as_current
+def add_clip(self, clip_name: str) -> Clip:
+    """Adds a new clip to the scene."""
+    self.make_current()
+    return Clip.new(name=clip_name, project=self.project)
+
+
+
+ +
+ + +
+ + + +

+ duplicate() -> Scene + +

+ + +
+ +

Duplicate the scene and return it.

+ +
+ Source code in pytvpaint/scene.py +
128
+129
+130
+131
+132
+133
+134
def duplicate(self) -> Scene:
+    """Duplicate the scene and return it."""
+    self.project.make_current()
+    george.tv_scene_duplicate(self.id)
+    dup_pos = self.position + 1
+    dup_id = george.tv_scene_enum_id(dup_pos)
+    return Scene(dup_id, self.project)
+
+
+
+ +
+ + +
+ + + +

+ remove() -> None + +

+ + +
+ +

Remove the scene and all the clips inside.

+ +
+ Warning +

All Clip instances will be invalid after removing the scene. +There's no protection mechanism to prevent accessing clip data that doesn't exist anymore.

+
+
+ Source code in pytvpaint/scene.py +
136
+137
+138
+139
+140
+141
+142
+143
+144
def remove(self) -> None:
+    """Remove the scene and all the clips inside.
+
+    Warning:
+        All `Clip` instances will be invalid after removing the scene.
+        There's no protection mechanism to prevent accessing clip data that doesn't exist anymore.
+    """
+    george.tv_scene_close(self._id)
+    self.mark_removed()
+
+
+
+ +
+ + + +
+ +
+ + +
+ + + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/api/utils/index.html b/api/utils/index.html new file mode 100644 index 0000000..ee1de04 --- /dev/null +++ b/api/utils/index.html @@ -0,0 +1,3766 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Utils - PyTVPaint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + +

Utilities

+ + +
+ + + + +
+ +

Utility functions and classes which are not specific to anything else in the codebase.

+ + + +
+ + + + + + + + +
+ + + +

+ RefreshedProperty + + +

+ + +
+

+ Bases: property

+ + +

Custom property that calls .refresh() before getting the actual value.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ Refreshable() + +

+ + +
+

+ Bases: abc.ABC

+ + +

Abstract class that denotes an object that have data that can be refreshed (a TVPaint project for example).

+ +
+ Source code in pytvpaint/utils.py +
51
+52
def __init__(self) -> None:
+    self.refresh_on_call = True
+
+
+ + + +
+ + + + + + + + + + +
+ + + +

+ refresh() -> None + + + abstractmethod + + +

+ + +
+ +

Refreshes the object data.

+ +
+ Source code in pytvpaint/utils.py +
54
+55
+56
+57
@abstractmethod
+def refresh(self) -> None:
+    """Refreshes the object data."""
+    raise NotImplementedError("Function refresh() needs to be implemented")
+
+
+
+ +
+ + + +
+ +
+ + +
+ +
+ + + +

+ Removable() + +

+ + +
+

+ Bases: pytvpaint.utils.Refreshable

+ + +

Abstract class that denotes an object that can be removed from TVPaint (a Layer for example).

+ +
+ Source code in pytvpaint/utils.py +
63
+64
+65
def __init__(self) -> None:
+    super().__init__()
+    self._is_removed: bool = False
+
+
+ + + +
+ + + + + + + +
+ + + +

+ is_removed: bool + + + property + + +

+ + +
+ +

Checks if the object is removed by trying to refresh its data.

+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
bool + bool + +
+

whether if it was removed or not

+
+
+
+ +
+ + + + +
+ + + +

+ refresh() -> None + +

+ + +
+ +

Does a refresh of the object data.

+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if the object has been mark removed

+
+
+ +
+ Source code in pytvpaint/utils.py +
74
+75
+76
+77
+78
+79
+80
+81
def refresh(self) -> None:
+    """Does a refresh of the object data.
+
+    Raises:
+        ValueError: if the object has been mark removed
+    """
+    if self._is_removed:
+        raise ValueError(f"{self.__class__.__name__} has been removed!")
+
+
+
+ +
+ + +
+ + + +

+ remove() -> None + + + abstractmethod + + +

+ + +
+ +

Removes the object in TVPaint.

+ +
+ Source code in pytvpaint/utils.py +
96
+97
+98
+99
@abstractmethod
+def remove(self) -> None:
+    """Removes the object in TVPaint."""
+    raise NotImplementedError("Function refresh() needs to be implemented")
+
+
+
+ +
+ + +
+ + + +

+ mark_removed() -> None + +

+ + +
+ +

Marks the object as removed and is therefor not usable.

+ +
+ Source code in pytvpaint/utils.py +
101
+102
+103
def mark_removed(self) -> None:
+    """Marks the object as removed and is therefor not usable."""
+    self._is_removed = True
+
+
+
+ +
+ + + +
+ +
+ + +
+ +
+ + + +

+ Renderable() + +

+ + +
+

+ Bases: abc.ABC

+ + +

Abstract class that denotes an object that can be removed from TVPaint (a Layer for example).

+ +
+ Source code in pytvpaint/utils.py +
109
+110
def __init__(self) -> None:
+    super().__init__()
+
+
+ + + +
+ + + + + + + +
+ + + +

+ current_frame: int + + + abstractmethod + property + writable + + +

+ + +
+ +

Gives the current frame.

+
+ +
+ + + + + +
+ +
+ + +
+ +
+ + + +

+ CanMakeCurrent + + +

+ + +
+

+ Bases: typing_extensions.Protocol

+ + +

Describes an object that can do make_current and has an id.

+ + + + +
+ + + + + + + + + + + +
+ +
+ + +
+ +
+ + + +

+ HasCurrentFrame + + +

+ + +
+

+ Bases: typing_extensions.Protocol

+ + +

Class that has a current frame property.

+ + + + +
+ + + + + + + +
+ + + +

+ current_frame: int + + + property + writable + + +

+ + +
+ +

The current frame, clip or project.

+
+ +
+ + + + + +
+ +
+ + +
+ + + +
+ + + +

+ get_unique_name(names: Iterable[str], stub: str) -> str + +

+ + +
+ +

Get a unique name from a list of names and a stub prefix. It does auto increment it.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
names + collections.abc.Iterable[str] + +
+

existing names

+
+
+ required +
stub + str + +
+

the base name

+
+
+ required +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if the stub is empty

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
Name TypeDescription
str + str + +
+

a unique name with the stub prefix

+
+
+ +
+ Source code in pytvpaint/utils.py +
208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
def get_unique_name(names: Iterable[str], stub: str) -> str:
+    """Get a unique name from a list of names and a stub prefix. It does auto increment it.
+
+    Args:
+        names (Iterable[str]): existing names
+        stub (str): the base name
+
+    Raises:
+        ValueError: if the stub is empty
+
+    Returns:
+        str: a unique name with the stub prefix
+    """
+    if not stub:
+        raise ValueError("Stub is empty")
+
+    number_re = re.compile(r"(?P<number>\d+)$", re.I)
+
+    stub_without_number = number_re.sub("", stub)
+    max_number = 0
+    padding_length = 1
+
+    for name in names:
+        without_number = number_re.sub("", name)
+
+        if without_number != stub_without_number:
+            continue
+
+        res = number_re.search(name)
+        number = res.group("number") if res else "1"
+
+        padding_length = max(padding_length, len(number))
+        max_number = max(max_number, int(number))
+
+    if max_number == 0:
+        return stub
+
+    next_number = max_number + 1
+    return f"{stub_without_number}{next_number:0{padding_length}}"
+
+
+
+ +
+ + +
+ + + +

+ position_generator(fn: Callable[[int], T], stop_when: type[GeorgeError] = GeorgeError) -> Iterator[T] + +

+ + +
+ +

Utility generator that yields the result of a function according to a position.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
fn + typing.Callable[[int], pytvpaint.utils.T] + +
+

the function to run at each iteration

+
+
+ required +
stop_when + Type[pytvpaint.george.exceptions.GeorgeError] + +
+

exception at which we stop. Defaults to GeorgeError.

+
+
+ pytvpaint.george.exceptions.GeorgeError +
+ + + +

Yields:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.utils.T + +
+

Iterator[T]: an generator of the resulting values

+
+
+ +
+ Source code in pytvpaint/utils.py +
252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
def position_generator(
+    fn: Callable[[int], T],
+    stop_when: type[GeorgeError] = GeorgeError,
+) -> Iterator[T]:
+    """Utility generator that yields the result of a function according to a position.
+
+    Args:
+        fn (Callable[[int], T]): the function to run at each iteration
+        stop_when (Type[GeorgeError], optional): exception at which we stop. Defaults to GeorgeError.
+
+    Yields:
+        Iterator[T]: an generator of the resulting values
+    """
+    pos = 0
+
+    while True:
+        try:
+            yield fn(pos)
+        except stop_when:
+            break
+        pos += 1
+
+
+
+ +
+ + +
+ + + +

+ set_as_current(func: Callable[Params, ReturnType]) -> Callable[Params, ReturnType] + +

+ + +
+ +

Decorator to apply on object methods.

+

Sets the current TVPaint object as 'current'. +Useful when George functions only apply on the current project, clip, layer or scene.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
func + typing.Callable[pytvpaint.utils.Params, pytvpaint.utils.ReturnType] + +
+

the method apply on

+
+
+ required +
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ typing.Callable[pytvpaint.utils.Params, pytvpaint.utils.ReturnType] + +
+

Callable[Params, ReturnType]: the wrapped method

+
+
+ +
+ Source code in pytvpaint/utils.py +
291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
def set_as_current(func: Callable[Params, ReturnType]) -> Callable[Params, ReturnType]:
+    """Decorator to apply on object methods.
+
+    Sets the current TVPaint object as 'current'.
+    Useful when George functions only apply on the current project, clip, layer or scene.
+
+    Args:
+        func (Callable[Params, ReturnType]): the method apply on
+
+    Returns:
+        Callable[Params, ReturnType]: the wrapped method
+    """
+
+    def wrapper(*args: Params.args, **kwargs: Params.kwargs) -> ReturnType:
+        self = cast(CanMakeCurrent, args[0])
+        self.make_current()
+        return func(*args, **kwargs)
+
+    return wrapper
+
+
+
+ +
+ + +
+ + + +

+ render_context(alpha_mode: george.AlphaSaveMode | None = None, background_mode: george.BackgroundMode | None = None, save_format: george.SaveFormat | None = None, format_opts: list[str] | None = None, layer_selection: list[Layer] | None = None) -> Generator[None, None, None] + +

+ + +
+ +

Context used to do renders in TVPaint.

+

It does the following things:

+
    +
  • Set the alpha mode and save format (with custom options)
  • +
  • Hide / Show the given layers (some render functions only render by visibility)
  • +
  • Restore the previous values after rendering
  • +
+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
alpha_mode + pytvpaint.george.AlphaSaveMode | None + +
+

the render alpha save mode

+
+
+ None +
save_format + pytvpaint.george.SaveFormat | None + +
+

the render format to use. Defaults to None.

+
+
+ None +
background_mode + pytvpaint.george.BackgroundMode | None + +
+

the render background mode

+
+
+ None +
format_opts + list[str] | None + +
+

the custom format options as strings. Defaults to None.

+
+
+ None +
layer_selection + list[pytvpaint.layer.Layer] | None + +
+

the layers to render. Defaults to None.

+
+
+ None +
+ +
+ Source code in pytvpaint/utils.py +
312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
@contextlib.contextmanager
+def render_context(
+    alpha_mode: george.AlphaSaveMode | None = None,
+    background_mode: george.BackgroundMode | None = None,
+    save_format: george.SaveFormat | None = None,
+    format_opts: list[str] | None = None,
+    layer_selection: list[Layer] | None = None,
+) -> Generator[None, None, None]:
+    """Context used to do renders in TVPaint.
+
+    It does the following things:
+
+    - Set the alpha mode and save format (with custom options)
+    - Hide / Show the given layers (some render functions only render by visibility)
+    - Restore the previous values after rendering
+
+    Args:
+        alpha_mode: the render alpha save mode
+        save_format: the render format to use. Defaults to None.
+        background_mode: the render background mode
+        format_opts: the custom format options as strings. Defaults to None.
+        layer_selection: the layers to render. Defaults to None.
+    """
+    from pytvpaint.clip import Clip
+
+    # Save the current state
+    pre_alpha_save_mode = george.tv_alpha_save_mode_get()
+    pre_save_format, pre_save_args = george.tv_save_mode_get()
+    pre_background_mode, pre_background_colors = george.tv_background_get()
+
+    # Set the save mode values
+    if alpha_mode:
+        george.tv_alpha_save_mode_set(alpha_mode)
+    if background_mode:
+        george.tv_background_set(background_mode)
+    if save_format:
+        george.tv_save_mode_set(save_format, *(format_opts or []))
+
+    layers_visibility = []
+    if layer_selection:
+        clip = Clip.current_clip()
+        layers_visibility = [(layer, layer.is_visible) for layer in clip.layers]
+        # Show and hide the clip layers to render
+        for layer, _ in layers_visibility:
+            should_be_visible = not layer_selection or layer in layer_selection
+            layer.is_visible = should_be_visible
+
+    # Do the render
+    yield
+
+    # Restore the previous values
+    if alpha_mode:
+        george.tv_alpha_save_mode_set(pre_alpha_save_mode)
+    if save_format:
+        george.tv_save_mode_set(pre_save_format, *pre_save_args)
+    if background_mode:
+        george.tv_background_set(pre_background_mode, pre_background_colors)
+
+    # Restore the layer visibility
+    if layers_visibility:
+        for layer, was_visible in layers_visibility:
+            layer.is_visible = was_visible
+
+
+
+ +
+ + +
+ + + +

+ restore_current_frame(tvp_element: HasCurrentFrame, frame: int) -> Generator[None, None, None] + +

+ + +
+ +

Context that temporarily changes the current frame to the one provided and restores it when done.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
tvp_element + pytvpaint.utils.HasCurrentFrame + +
+

clip to change

+
+
+ required +
frame + int + +
+

frame to set. Defaults to None.

+
+
+ required +
+ +
+ Source code in pytvpaint/utils.py +
388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
@contextlib.contextmanager
+def restore_current_frame(
+    tvp_element: HasCurrentFrame, frame: int
+) -> Generator[None, None, None]:
+    """Context that temporarily changes the current frame to the one provided and restores it when done.
+
+    Args:
+        tvp_element: clip to change
+        frame: frame to set. Defaults to None.
+    """
+    previous_frame = tvp_element.current_frame
+
+    if frame != previous_frame:
+        tvp_element.current_frame = frame
+
+    yield
+
+    if tvp_element.current_frame != previous_frame:
+        tvp_element.current_frame = previous_frame
+
+
+
+ +
+ + +
+ + + +

+ get_tvp_element(tvp_elements: Iterator[TVPElementType], by_id: int | str | None = None, by_name: str | None = None, by_path: str | Path | None = None) -> TVPElementType | None + +

+ + +
+ +

Search for a TVPaint element by attributes.

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
tvp_elements + collections.abc.Iterator[pytvpaint.utils.TVPElementType] + +
+

a collection of TVPaint objects

+
+
+ required +
by_id + int | str | None + +
+

search by id. Defaults to None.

+
+
+ None +
by_name + str | None + +
+

search by name, search is case-insensitive. Defaults to None.

+
+
+ None +
by_path + str | pathlib.Path | None + +
+

search by path. Defaults to None.

+
+
+ None +
+ + + +

Raises:

+ + + + + + + + + + + + + +
TypeDescription
+ ValueError + +
+

if bad arguments were given

+
+
+ + + +

Returns:

+ + + + + + + + + + + + + +
TypeDescription
+ pytvpaint.utils.TVPElementType | None + +
+

TVPElementType | None: the found element

+
+
+ +
+ Source code in pytvpaint/utils.py +
420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
def get_tvp_element(
+    tvp_elements: Iterator[TVPElementType],
+    by_id: int | str | None = None,
+    by_name: str | None = None,
+    by_path: str | Path | None = None,
+) -> TVPElementType | None:
+    """Search for a TVPaint element by attributes.
+
+    Args:
+        tvp_elements: a collection of TVPaint objects
+        by_id: search by id. Defaults to None.
+        by_name: search by name, search is case-insensitive. Defaults to None.
+        by_path: search by path. Defaults to None.
+
+    Raises:
+        ValueError: if bad arguments were given
+
+    Returns:
+        TVPElementType | None: the found element
+    """
+    if by_id is None and by_name is None:
+        raise ValueError(
+            "At least one of the values (id or name) must be provided, none found !"
+        )
+
+    for element in tvp_elements:
+        if by_id is not None and element.id != by_id:
+            continue
+        if by_name is not None and element.name.lower() != by_name.lower():
+            continue
+        if by_path is not None and getattr(element, "path") != Path(by_path):
+            continue
+        return element
+
+    return None
+
+
+
+ +
+ + +
+ + + +

+ handle_output_range(output_path: Path | str | FileSequence, default_start: int, default_end: int, start: int | None = None, end: int | None = None) -> tuple[FileSequence, int, int, bool, bool] + +

+ + +
+ +

Handle the different options for output paths and range.

+

Whether the user provides a range (start-end) or a filesequence with a range or not, this functions ensures we +always end up with a valid range to render

+ + + +

Parameters:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionDefault
output_path + pathlib.Path | str | fileseq.filesequence.FileSequence + +
+

user provided output path

+
+
+ required +
default_start + int + +
+

the default start to use if none provided or found in the file sequence object

+
+
+ required +
default_end + int + +
+

the default end to use if none provided or found in the file sequence object

+
+
+ required +
start + int | None + +
+

user provided start frame or None

+
+
+ None +
end + int | None + +
+

user provided end frame or None

+
+
+ None +
+ + + +

Returns:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Name TypeDescription
file_sequence + fileseq.filesequence.FileSequence + +
+

output path as a FileSequence object

+
+
start + int + +
+

computed start frame

+
+
end + int + +
+

computed end frame

+
+
is_sequence + bool + +
+

whether the output is a sequence or not

+
+
is_image + bool + +
+

whether the output is an image or not (a movie)

+
+
+ +
+ Source code in pytvpaint/utils.py +
457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
def handle_output_range(
+    output_path: Path | str | FileSequence,
+    default_start: int,
+    default_end: int,
+    start: int | None = None,
+    end: int | None = None,
+) -> tuple[FileSequence, int, int, bool, bool]:
+    """Handle the different options for output paths and range.
+
+    Whether the user provides a range (start-end) or a filesequence with a range or not, this functions ensures we
+    always end up with a valid range to render
+
+    Args:
+        output_path: user provided output path
+        default_start: the default start to use if none provided or found in the file sequence object
+        default_end: the default end to use if none provided or found in the file sequence object
+        start: user provided start frame or None
+        end: user provided end frame or None
+
+    Returns:
+        file_sequence: output path as a FileSequence object
+        start: computed start frame
+        end: computed end frame
+        is_sequence: whether the output is a sequence or not
+        is_image: whether the output is an image or not (a movie)
+    """
+    # we handle all outputs as a FileSequence, makes it a bit easier to handle ranges and padding
+    if not isinstance(output_path, FileSequence):
+        file_sequence = FileSequence(Path(output_path).as_posix())
+    else:
+        file_sequence = output_path
+
+    frame_set = file_sequence.frameSet()
+    is_image = george.SaveFormat.is_image(file_sequence.extension())
+
+    # if the provided sequence has a range, and we don't, use the sequence range
+    if frame_set and len(frame_set) >= 1 and is_image:
+        start = start or file_sequence.start()
+        end = end or file_sequence.end()
+
+    # check characteristics of file sequence
+    fseq_has_range = frame_set and len(frame_set) > 1
+    fseq_is_single_image = frame_set and len(frame_set) == 1
+    fseq_no_range_padding = not frame_set and file_sequence.padding()
+    range_is_seq = start is not None and end is not None and start != end
+    range_is_single_image = start is not None and end is not None and start == end
+
+    is_single_image = bool(
+        is_image and (fseq_is_single_image or not frame_set) and range_is_single_image
+    )
+    is_sequence = bool(
+        is_image and (fseq_has_range or fseq_no_range_padding or range_is_seq)
+    )
+
+    # if no range provided, use clip mark in/out, if none, use clip start/end
+    if start is None:
+        start = default_start
+    if is_single_image and not end:
+        end = start
+    else:
+        if end is None:
+            end = default_end
+
+    frame_set = FrameSet(f"{start}-{end}")
+
+    if not file_sequence.padding() and is_image and len(frame_set) > 1:
+        file_sequence.setPadding("#")
+
+    # we should have a range by now, set it in the sequence
+    if (is_image and not is_single_image) or file_sequence.padding():
+        file_sequence.setFrameSet(frame_set)
+
+    return file_sequence, start, end, is_sequence, is_image
+
+
+
+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + +
+
+ + + + + +
+ +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/assets/_mkdocstrings.css b/assets/_mkdocstrings.css new file mode 100644 index 0000000..4b7d98b --- /dev/null +++ b/assets/_mkdocstrings.css @@ -0,0 +1,109 @@ + +/* Avoid breaking parameter names, etc. in table cells. */ +.doc-contents td code { + word-break: normal !important; +} + +/* No line break before first paragraph of descriptions. */ +.doc-md-description, +.doc-md-description>p:first-child { + display: inline; +} + +/* Max width for docstring sections tables. */ +.doc .md-typeset__table, +.doc .md-typeset__table table { + display: table !important; + width: 100%; +} + +.doc .md-typeset__table tr { + display: table-row; +} + +/* Defaults in Spacy table style. */ +.doc-param-default { + float: right; +} + +/* Symbols in Navigation and ToC. */ +:root, +[data-md-color-scheme="default"] { + --doc-symbol-attribute-fg-color: #953800; + --doc-symbol-function-fg-color: #8250df; + --doc-symbol-method-fg-color: #8250df; + --doc-symbol-class-fg-color: #0550ae; + --doc-symbol-module-fg-color: #5cad0f; + + --doc-symbol-attribute-bg-color: #9538001a; + --doc-symbol-function-bg-color: #8250df1a; + --doc-symbol-method-bg-color: #8250df1a; + --doc-symbol-class-bg-color: #0550ae1a; + --doc-symbol-module-bg-color: #5cad0f1a; +} + +[data-md-color-scheme="slate"] { + --doc-symbol-attribute-fg-color: #ffa657; + --doc-symbol-function-fg-color: #d2a8ff; + --doc-symbol-method-fg-color: #d2a8ff; + --doc-symbol-class-fg-color: #79c0ff; + --doc-symbol-module-fg-color: #baff79; + + --doc-symbol-attribute-bg-color: #ffa6571a; + --doc-symbol-function-bg-color: #d2a8ff1a; + --doc-symbol-method-bg-color: #d2a8ff1a; + --doc-symbol-class-bg-color: #79c0ff1a; + --doc-symbol-module-bg-color: #baff791a; +} + +code.doc-symbol { + border-radius: .1rem; + font-size: .85em; + padding: 0 .3em; + font-weight: bold; +} + +code.doc-symbol-attribute { + color: var(--doc-symbol-attribute-fg-color); + background-color: var(--doc-symbol-attribute-bg-color); +} + +code.doc-symbol-attribute::after { + content: "attr"; +} + +code.doc-symbol-function { + color: var(--doc-symbol-function-fg-color); + background-color: var(--doc-symbol-function-bg-color); +} + +code.doc-symbol-function::after { + content: "func"; +} + +code.doc-symbol-method { + color: var(--doc-symbol-method-fg-color); + background-color: var(--doc-symbol-method-bg-color); +} + +code.doc-symbol-method::after { + content: "meth"; +} + +code.doc-symbol-class { + color: var(--doc-symbol-class-fg-color); + background-color: var(--doc-symbol-class-bg-color); +} + +code.doc-symbol-class::after { + content: "class"; +} + +code.doc-symbol-module { + color: var(--doc-symbol-module-fg-color); + background-color: var(--doc-symbol-module-bg-color); +} + +code.doc-symbol-module::after { + content: "mod"; +} \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000..1cf13b9 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.bd41221c.min.js b/assets/javascripts/bundle.bd41221c.min.js new file mode 100644 index 0000000..70bcbf1 --- /dev/null +++ b/assets/javascripts/bundle.bd41221c.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var _i=Object.create;var br=Object.defineProperty;var Ai=Object.getOwnPropertyDescriptor;var Ci=Object.getOwnPropertyNames,Ft=Object.getOwnPropertySymbols,ki=Object.getPrototypeOf,vr=Object.prototype.hasOwnProperty,eo=Object.prototype.propertyIsEnumerable;var Zr=(e,t,r)=>t in e?br(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,F=(e,t)=>{for(var r in t||(t={}))vr.call(t,r)&&Zr(e,r,t[r]);if(Ft)for(var r of Ft(t))eo.call(t,r)&&Zr(e,r,t[r]);return e};var to=(e,t)=>{var r={};for(var o in e)vr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Ft)for(var o of Ft(e))t.indexOf(o)<0&&eo.call(e,o)&&(r[o]=e[o]);return r};var gr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Hi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ci(t))!vr.call(e,n)&&n!==r&&br(e,n,{get:()=>t[n],enumerable:!(o=Ai(t,n))||o.enumerable});return e};var jt=(e,t,r)=>(r=e!=null?_i(ki(e)):{},Hi(t||!e||!e.__esModule?br(r,"default",{value:e,enumerable:!0}):r,e));var ro=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var no=gr((xr,oo)=>{(function(e,t){typeof xr=="object"&&typeof oo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(xr,function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(C){return!!(C&&C!==document&&C.nodeName!=="HTML"&&C.nodeName!=="BODY"&&"classList"in C&&"contains"in C.classList)}function c(C){var ct=C.type,Ne=C.tagName;return!!(Ne==="INPUT"&&s[ct]&&!C.readOnly||Ne==="TEXTAREA"&&!C.readOnly||C.isContentEditable)}function p(C){C.classList.contains("focus-visible")||(C.classList.add("focus-visible"),C.setAttribute("data-focus-visible-added",""))}function l(C){C.hasAttribute("data-focus-visible-added")&&(C.classList.remove("focus-visible"),C.removeAttribute("data-focus-visible-added"))}function f(C){C.metaKey||C.altKey||C.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(C){o=!1}function h(C){a(C.target)&&(o||c(C.target))&&p(C.target)}function w(C){a(C.target)&&(C.target.classList.contains("focus-visible")||C.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(C.target))}function A(C){document.visibilityState==="hidden"&&(n&&(o=!0),Z())}function Z(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function te(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(C){C.target.nodeName&&C.target.nodeName.toLowerCase()==="html"||(o=!1,te())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",A,!0),Z(),r.addEventListener("focus",h,!0),r.addEventListener("blur",w,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var zr=gr((kt,Vr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof kt=="object"&&typeof Vr=="object"?Vr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof kt=="object"?kt.ClipboardJS=r():t.ClipboardJS=r()})(kt,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Li}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(D){try{return document.execCommand(D)}catch(M){return!1}}var h=function(M){var O=f()(M);return u("cut"),O},w=h;function A(D){var M=document.documentElement.getAttribute("dir")==="rtl",O=document.createElement("textarea");O.style.fontSize="12pt",O.style.border="0",O.style.padding="0",O.style.margin="0",O.style.position="absolute",O.style[M?"right":"left"]="-9999px";var I=window.pageYOffset||document.documentElement.scrollTop;return O.style.top="".concat(I,"px"),O.setAttribute("readonly",""),O.value=D,O}var Z=function(M,O){var I=A(M);O.container.appendChild(I);var W=f()(I);return u("copy"),I.remove(),W},te=function(M){var O=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},I="";return typeof M=="string"?I=Z(M,O):M instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(M==null?void 0:M.type)?I=Z(M.value,O):(I=f()(M),u("copy")),I},J=te;function C(D){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?C=function(O){return typeof O}:C=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},C(D)}var ct=function(){var M=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},O=M.action,I=O===void 0?"copy":O,W=M.container,K=M.target,Ce=M.text;if(I!=="copy"&&I!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(K!==void 0)if(K&&C(K)==="object"&&K.nodeType===1){if(I==="copy"&&K.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(I==="cut"&&(K.hasAttribute("readonly")||K.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Ce)return J(Ce,{container:W});if(K)return I==="cut"?w(K):J(K,{container:W})},Ne=ct;function Pe(D){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Pe=function(O){return typeof O}:Pe=function(O){return O&&typeof Symbol=="function"&&O.constructor===Symbol&&O!==Symbol.prototype?"symbol":typeof O},Pe(D)}function xi(D,M){if(!(D instanceof M))throw new TypeError("Cannot call a class as a function")}function Xr(D,M){for(var O=0;O0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof W.action=="function"?W.action:this.defaultAction,this.target=typeof W.target=="function"?W.target:this.defaultTarget,this.text=typeof W.text=="function"?W.text:this.defaultText,this.container=Pe(W.container)==="object"?W.container:document.body}},{key:"listenClick",value:function(W){var K=this;this.listener=p()(W,"click",function(Ce){return K.onClick(Ce)})}},{key:"onClick",value:function(W){var K=W.delegateTarget||W.currentTarget,Ce=this.action(K)||"copy",It=Ne({action:Ce,container:this.container,target:this.target(K),text:this.text(K)});this.emit(It?"success":"error",{action:Ce,text:It,trigger:K,clearSelection:function(){K&&K.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(W){return hr("action",W)}},{key:"defaultTarget",value:function(W){var K=hr("target",W);if(K)return document.querySelector(K)}},{key:"defaultText",value:function(W){return hr("text",W)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(W){var K=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(W,K)}},{key:"cut",value:function(W){return w(W)}},{key:"isSupported",value:function(){var W=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],K=typeof W=="string"?[W]:W,Ce=!!document.queryCommandSupported;return K.forEach(function(It){Ce=Ce&&!!document.queryCommandSupported(It)}),Ce}}]),O}(a()),Li=Mi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s},438:function(o,n,i){var s=i(828);function a(l,f,u,h,w){var A=p.apply(this,arguments);return l.addEventListener(u,A,w),{destroy:function(){l.removeEventListener(u,A,w)}}}function c(l,f,u,h,w){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(A){return a(A,f,u,h,w)}))}function p(l,f,u,h){return function(w){w.delegateTarget=s(w.target,f),w.delegateTarget&&h.call(l,w)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(o,n,i){var s=i(879),a=i(438);function c(u,h,w){if(!u&&!h&&!w)throw new Error("Missing required arguments");if(!s.string(h))throw new TypeError("Second argument must be a String");if(!s.fn(w))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,h,w);if(s.nodeList(u))return l(u,h,w);if(s.string(u))return f(u,h,w);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,h,w){return u.addEventListener(h,w),{destroy:function(){u.removeEventListener(h,w)}}}function l(u,h,w){return Array.prototype.forEach.call(u,function(A){A.addEventListener(h,w)}),{destroy:function(){Array.prototype.forEach.call(u,function(A){A.removeEventListener(h,w)})}}}function f(u,h,w){return a(document.body,u,h,w)}o.exports=c},817:function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var Va=/["'&<>]/;qn.exports=za;function za(e){var t=""+e,r=Va.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function V(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function z(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||a(u,h)})})}function a(u,h){try{c(o[u](h))}catch(w){f(i[0][3],w)}}function c(u){u.value instanceof ot?Promise.resolve(u.value.v).then(p,l):f(i[0][2],u)}function p(u){a("next",u)}function l(u){a("throw",u)}function f(u,h){u(h),i.shift(),i.length&&a(i[0][0],i[0][1])}}function so(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof ue=="function"?ue(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function k(e){return typeof e=="function"}function pt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Wt=pt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ve(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=ue(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(A){t={error:A}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(k(l))try{l()}catch(A){i=A instanceof Wt?A.errors:[A]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=ue(f),h=u.next();!h.done;h=u.next()){var w=h.value;try{co(w)}catch(A){i=i!=null?i:[],A instanceof Wt?i=z(z([],V(i)),V(A.errors)):i.push(A)}}}catch(A){o={error:A}}finally{try{h&&!h.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Wt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)co(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ve(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ve(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Er=Ie.EMPTY;function Dt(e){return e instanceof Ie||e&&"closed"in e&&k(e.remove)&&k(e.add)&&k(e.unsubscribe)}function co(e){k(e)?e():e.unsubscribe()}var ke={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var lt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?Er:(this.currentObservers=null,a.push(r),new Ie(function(){o.currentObservers=null,Ve(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new j;return r.source=this,r},t.create=function(r,o){return new vo(r,o)},t}(j);var vo=function(e){se(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Er},t}(v);var St={now:function(){return(St.delegate||Date).now()},delegate:void 0};var Ot=function(e){se(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=St);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(ut.cancelAnimationFrame(o),r._scheduled=void 0)},t}(zt);var yo=function(e){se(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(qt);var de=new yo(xo);var L=new j(function(e){return e.complete()});function Kt(e){return e&&k(e.schedule)}function _r(e){return e[e.length-1]}function Je(e){return k(_r(e))?e.pop():void 0}function Ae(e){return Kt(_r(e))?e.pop():void 0}function Qt(e,t){return typeof _r(e)=="number"?e.pop():t}var dt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Yt(e){return k(e==null?void 0:e.then)}function Bt(e){return k(e[ft])}function Gt(e){return Symbol.asyncIterator&&k(e==null?void 0:e[Symbol.asyncIterator])}function Jt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Di(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Xt=Di();function Zt(e){return k(e==null?void 0:e[Xt])}function er(e){return ao(this,arguments,function(){var r,o,n,i;return Ut(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,ot(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,ot(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,ot(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function tr(e){return k(e==null?void 0:e.getReader)}function N(e){if(e instanceof j)return e;if(e!=null){if(Bt(e))return Ni(e);if(dt(e))return Vi(e);if(Yt(e))return zi(e);if(Gt(e))return Eo(e);if(Zt(e))return qi(e);if(tr(e))return Ki(e)}throw Jt(e)}function Ni(e){return new j(function(t){var r=e[ft]();if(k(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Vi(e){return new j(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):ce,ye(1),r?Qe(t):jo(function(){return new or}))}}function $r(e){return e<=0?function(){return L}:x(function(t,r){var o=[];t.subscribe(S(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new v}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,h=0,w=!1,A=!1,Z=function(){f==null||f.unsubscribe(),f=void 0},te=function(){Z(),l=u=void 0,w=A=!1},J=function(){var C=l;te(),C==null||C.unsubscribe()};return x(function(C,ct){h++,!A&&!w&&Z();var Ne=u=u!=null?u:r();ct.add(function(){h--,h===0&&!A&&!w&&(f=Pr(J,c))}),Ne.subscribe(ct),!l&&h>0&&(l=new it({next:function(Pe){return Ne.next(Pe)},error:function(Pe){A=!0,Z(),f=Pr(te,n,Pe),Ne.error(Pe)},complete:function(){w=!0,Z(),f=Pr(te,s),Ne.complete()}}),N(C).subscribe(l))})(p)}}function Pr(e,t){for(var r=[],o=2;oe.next(document)),e}function R(e,t=document){return Array.from(t.querySelectorAll(e))}function P(e,t=document){let r=me(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function me(e,t=document){return t.querySelector(e)||void 0}function Re(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var la=T(d(document.body,"focusin"),d(document.body,"focusout")).pipe(be(1),q(void 0),m(()=>Re()||document.body),B(1));function vt(e){return la.pipe(m(t=>e.contains(t)),Y())}function Vo(e,t){return T(d(e,"mouseenter").pipe(m(()=>!0)),d(e,"mouseleave").pipe(m(()=>!1))).pipe(t?be(t):ce,q(!1))}function Ue(e){return{x:e.offsetLeft,y:e.offsetTop}}function zo(e){return T(d(window,"load"),d(window,"resize")).pipe(Me(0,de),m(()=>Ue(e)),q(Ue(e)))}function ir(e){return{x:e.scrollLeft,y:e.scrollTop}}function et(e){return T(d(e,"scroll"),d(window,"resize")).pipe(Me(0,de),m(()=>ir(e)),q(ir(e)))}function qo(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)qo(e,r)}function E(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)qo(o,n);return o}function ar(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function gt(e){let t=E("script",{src:e});return H(()=>(document.head.appendChild(t),T(d(t,"load"),d(t,"error").pipe(b(()=>Ar(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),_(()=>document.head.removeChild(t)),ye(1))))}var Ko=new v,ma=H(()=>typeof ResizeObserver=="undefined"?gt("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>{for(let t of e)Ko.next(t)})),b(e=>T(qe,$(e)).pipe(_(()=>e.disconnect()))),B(1));function pe(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Ee(e){return ma.pipe(y(t=>t.observe(e)),b(t=>Ko.pipe(g(({target:r})=>r===e),_(()=>t.unobserve(e)),m(()=>pe(e)))),q(pe(e)))}function xt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function sr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var Qo=new v,fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)Qo.next(t)},{threshold:0}))).pipe(b(e=>T(qe,$(e)).pipe(_(()=>e.disconnect()))),B(1));function yt(e){return fa.pipe(y(t=>t.observe(e)),b(t=>Qo.pipe(g(({target:r})=>r===e),_(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function Yo(e,t=16){return et(e).pipe(m(({y:r})=>{let o=pe(e),n=xt(e);return r>=n.height-o.height-t}),Y())}var cr={drawer:P("[data-md-toggle=drawer]"),search:P("[data-md-toggle=search]")};function Bo(e){return cr[e].checked}function Be(e,t){cr[e].checked!==t&&cr[e].click()}function We(e){let t=cr[e];return d(t,"change").pipe(m(()=>t.checked),q(t.checked))}function ua(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function da(){return T(d(window,"compositionstart").pipe(m(()=>!0)),d(window,"compositionend").pipe(m(()=>!1))).pipe(q(!1))}function Go(){let e=d(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:Bo("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Re();if(typeof o!="undefined")return!ua(o,r)}return!0}),le());return da().pipe(b(t=>t?L:e))}function ve(){return new URL(location.href)}function st(e,t=!1){if(G("navigation.instant")&&!t){let r=E("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function Jo(){return new v}function Xo(){return location.hash.slice(1)}function Zo(e){let t=E("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function ha(e){return T(d(window,"hashchange"),e).pipe(m(Xo),q(Xo()),g(t=>t.length>0),B(1))}function en(e){return ha(e).pipe(m(t=>me(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function At(e){let t=matchMedia(e);return nr(r=>t.addListener(()=>r(t.matches))).pipe(q(t.matches))}function tn(){let e=matchMedia("print");return T(d(window,"beforeprint").pipe(m(()=>!0)),d(window,"afterprint").pipe(m(()=>!1))).pipe(q(e.matches))}function Ur(e,t){return e.pipe(b(r=>r?t():L))}function Wr(e,t){return new j(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function De(e,t){return Wr(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),B(1))}function rn(e,t){let r=new DOMParser;return Wr(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),B(1))}function on(e,t){let r=new DOMParser;return Wr(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),B(1))}function nn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function an(){return T(d(window,"scroll",{passive:!0}),d(window,"resize",{passive:!0})).pipe(m(nn),q(nn()))}function sn(){return{width:innerWidth,height:innerHeight}}function cn(){return d(window,"resize",{passive:!0}).pipe(m(sn),q(sn()))}function pn(){return Q([an(),cn()]).pipe(m(([e,t])=>({offset:e,size:t})),B(1))}function pr(e,{viewport$:t,header$:r}){let o=t.pipe(X("size")),n=Q([o,r]).pipe(m(()=>Ue(e)));return Q([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function ba(e){return d(e,"message",t=>t.data)}function va(e){let t=new v;return t.subscribe(r=>e.postMessage(r)),t}function ln(e,t=new Worker(e)){let r=ba(t),o=va(t),n=new v;n.subscribe(o);let i=o.pipe(ee(),oe(!0));return n.pipe(ee(),$e(r.pipe(U(i))),le())}var ga=P("#__config"),Et=JSON.parse(ga.textContent);Et.base=`${new URL(Et.base,ve())}`;function we(){return Et}function G(e){return Et.features.includes(e)}function ge(e,t){return typeof t!="undefined"?Et.translations[e].replace("#",t.toString()):Et.translations[e]}function Te(e,t=document){return P(`[data-md-component=${e}]`,t)}function ne(e,t=document){return R(`[data-md-component=${e}]`,t)}function xa(e){let t=P(".md-typeset > :first-child",e);return d(t,"click",{once:!0}).pipe(m(()=>P(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function mn(e){if(!G("announce.dismiss")||!e.childElementCount)return L;if(!e.hidden){let t=P(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new v;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),xa(e).pipe(y(r=>t.next(r)),_(()=>t.complete()),m(r=>F({ref:e},r)))})}function ya(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function fn(e,t){let r=new v;return r.subscribe(({hidden:o})=>{e.hidden=o}),ya(e,t).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))}function Ct(e,t){return t==="inline"?E("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},E("div",{class:"md-tooltip__inner md-typeset"})):E("div",{class:"md-tooltip",id:e,role:"tooltip"},E("div",{class:"md-tooltip__inner md-typeset"}))}function un(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return E("aside",{class:"md-annotation",tabIndex:0},Ct(t),E("a",{href:r,class:"md-annotation__index",tabIndex:-1},E("span",{"data-md-annotation-id":e})))}else return E("aside",{class:"md-annotation",tabIndex:0},Ct(t),E("span",{class:"md-annotation__index",tabIndex:-1},E("span",{"data-md-annotation-id":e})))}function dn(e){return E("button",{class:"md-clipboard md-icon",title:ge("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function Dr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,E("del",null,p)," "],[]).slice(0,-1),i=we(),s=new URL(e.location,i.base);G("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=we();return E("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},E("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&E("div",{class:"md-search-result__icon md-icon"}),r>0&&E("h1",null,e.title),r<=0&&E("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return E("span",{class:`md-tag ${p}`},c)}),o>0&&n.length>0&&E("p",{class:"md-search-result__terms"},ge("search.result.term.missing"),": ",...n)))}function hn(e){let t=e[0].score,r=[...e],o=we(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreDr(l,1)),...c.length?[E("details",{class:"md-search-result__more"},E("summary",{tabIndex:-1},E("div",null,c.length>0&&c.length===1?ge("search.result.more.one"):ge("search.result.more.other",c.length))),...c.map(l=>Dr(l,1)))]:[]];return E("li",{class:"md-search-result__item"},p)}function bn(e){return E("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>E("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?ar(r):r)))}function Nr(e){let t=`tabbed-control tabbed-control--${e}`;return E("div",{class:t,hidden:!0},E("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function vn(e){return E("div",{class:"md-typeset__scrollwrap"},E("div",{class:"md-typeset__table"},e))}function Ea(e){let t=we(),r=new URL(`../${e.version}/`,t.base);return E("li",{class:"md-version__item"},E("a",{href:`${r}`,class:"md-version__link"},e.title))}function gn(e,t){return e=e.filter(r=>{var o;return!((o=r.properties)!=null&&o.hidden)}),E("div",{class:"md-version"},E("button",{class:"md-version__current","aria-label":ge("select.version")},t.title),E("ul",{class:"md-version__list"},e.map(Ea)))}var wa=0;function Ta(e,t){document.body.append(e);let{width:r}=pe(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=sr(t),n=typeof o!="undefined"?et(o):$({x:0,y:0}),i=T(vt(t),Vo(t)).pipe(Y());return Q([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Ue(t),l=pe(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function Ge(e){let t=e.title;if(!t.length)return L;let r=`__tooltip_${wa++}`,o=Ct(r,"inline"),n=P(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new v;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),T(i.pipe(g(({active:s})=>s)),i.pipe(be(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Me(16,de)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(_t(125,de),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Ta(o,e).pipe(y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))}).pipe(ze(ie))}function Sa(e,t){let r=H(()=>Q([zo(e),et(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=pe(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return vt(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),ye(+!o||1/0))))}function xn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new v,s=i.pipe(ee(),oe(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),yt(e).pipe(U(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),T(i.pipe(g(({active:a})=>a)),i.pipe(be(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Me(16,de)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(_t(125,de),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),d(n,"click").pipe(U(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),d(n,"mousedown").pipe(U(s),ae(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Re())==null||p.blur()}}),r.pipe(U(s),g(a=>a===o),Ye(125)).subscribe(()=>e.focus()),Sa(e,t).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))})}function Oa(e){return e.tagName==="CODE"?R(".c, .c1, .cm",e):[e]}function Ma(e){let t=[];for(let r of Oa(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function yn(e,t){t.append(...Array.from(e.childNodes))}function lr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Ma(t)){let[,c]=a.textContent.match(/\((\d+)\)/);me(`:scope > li:nth-child(${c})`,e)&&(s.set(c,un(c,i)),a.replaceWith(s.get(c)))}return s.size===0?L:H(()=>{let a=new v,c=a.pipe(ee(),oe(!0)),p=[];for(let[l,f]of s)p.push([P(".md-typeset",f),P(`:scope > li:nth-child(${l})`,e)]);return o.pipe(U(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?yn(f,u):yn(u,f)}),T(...[...s].map(([,l])=>xn(l,t,{target$:r}))).pipe(_(()=>a.complete()),le())})}function En(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return En(t)}}function wn(e,t){return H(()=>{let r=En(e);return typeof r!="undefined"?lr(r,e,t):L})}var Tn=jt(zr());var La=0;function Sn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Sn(t)}}function _a(e){return Ee(e).pipe(m(({width:t})=>({scrollable:xt(e).width>t})),X("scrollable"))}function On(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new v,i=n.pipe($r(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[];if(Tn.default.isSupported()&&(e.closest(".copy")||G("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${La++}`;let p=dn(c.id);c.insertBefore(p,e),G("content.tooltips")&&s.push(Ge(p))}let a=e.closest(".highlight");if(a instanceof HTMLElement){let c=Sn(a);if(typeof c!="undefined"&&(a.classList.contains("annotate")||G("content.code.annotate"))){let p=lr(c,e,t);s.push(Ee(a).pipe(U(i),m(({width:l,height:f})=>l&&f),Y(),b(l=>l?p:L)))}}return _a(e).pipe(y(c=>n.next(c)),_(()=>n.complete()),m(c=>F({ref:e},c)),$e(...s))});return G("content.lazy")?yt(e).pipe(g(n=>n),ye(1),b(()=>o)):o}function Aa(e,{target$:t,print$:r}){let o=!0;return T(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),y(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Mn(e,t){return H(()=>{let r=new v;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),Aa(e,t).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}var Ln=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var qr,ka=0;function Ha(){return typeof mermaid=="undefined"||mermaid instanceof Element?gt("https://unpkg.com/mermaid@10.7.0/dist/mermaid.min.js"):$(void 0)}function _n(e){return e.classList.remove("mermaid"),qr||(qr=Ha().pipe(y(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Ln,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),B(1))),qr.subscribe(()=>ro(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${ka++}`,r=E("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),qr.pipe(m(()=>({ref:e})))}var An=E("table");function Cn(e){return e.replaceWith(An),An.replaceWith(vn(e)),$({ref:e})}function $a(e){let t=e.find(r=>r.checked)||e[0];return T(...e.map(r=>d(r,"change").pipe(m(()=>P(`label[for="${r.id}"]`))))).pipe(q(P(`label[for="${t.id}"]`)),m(r=>({active:r})))}function kn(e,{viewport$:t,target$:r}){let o=P(".tabbed-labels",e),n=R(":scope > input",e),i=Nr("prev");e.append(i);let s=Nr("next");return e.append(s),H(()=>{let a=new v,c=a.pipe(ee(),oe(!0));Q([a,Ee(e)]).pipe(U(c),Me(1,de)).subscribe({next([{active:p},l]){let f=Ue(p),{width:u}=pe(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let h=ir(o);(f.xh.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),Q([et(o),Ee(o)]).pipe(U(c)).subscribe(([p,l])=>{let f=xt(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),T(d(i,"click").pipe(m(()=>-1)),d(s,"click").pipe(m(()=>1))).pipe(U(c)).subscribe(p=>{let{width:l}=pe(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(U(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=P(`label[for="${p.id}"]`);l.replaceChildren(E("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),d(l.firstElementChild,"click").pipe(U(c),g(f=>!(f.metaKey||f.ctrlKey)),y(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return G("content.tabs.link")&&a.pipe(Le(1),ae(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let w of R("[data-tabs]"))for(let A of R(":scope > input",w)){let Z=P(`label[for="${A.id}"]`);if(Z!==p&&Z.innerText.trim()===f){Z.setAttribute("data-md-switching",""),A.click();break}}window.scrollTo({top:e.offsetTop-u});let h=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...h])])}}),a.pipe(U(c)).subscribe(()=>{for(let p of R("audio, video",e))p.pause()}),$a(n).pipe(y(p=>a.next(p)),_(()=>a.complete()),m(p=>F({ref:e},p)))}).pipe(ze(ie))}function Hn(e,{viewport$:t,target$:r,print$:o}){return T(...R(".annotate:not(.highlight)",e).map(n=>wn(n,{target$:r,print$:o})),...R("pre:not(.mermaid) > code",e).map(n=>On(n,{target$:r,print$:o})),...R("pre.mermaid",e).map(n=>_n(n)),...R("table:not([class])",e).map(n=>Cn(n)),...R("details",e).map(n=>Mn(n,{target$:r,print$:o})),...R("[data-tabs]",e).map(n=>kn(n,{viewport$:t,target$:r})),...R("[title]",e).filter(()=>G("content.tooltips")).map(n=>Ge(n)))}function Ra(e,{alert$:t}){return t.pipe(b(r=>T($(!0),$(!1).pipe(Ye(2e3))).pipe(m(o=>({message:r,active:o})))))}function $n(e,t){let r=P(".md-typeset",e);return H(()=>{let o=new v;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ra(e,t).pipe(y(n=>o.next(n)),_(()=>o.complete()),m(n=>F({ref:e},n)))})}function Pa({viewport$:e}){if(!G("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Ke(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=We("search");return Q([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),q(!1))}function Rn(e,t){return H(()=>Q([Ee(e),Pa(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),B(1))}function Pn(e,{header$:t,main$:r}){return H(()=>{let o=new v,n=o.pipe(ee(),oe(!0));o.pipe(X("active"),je(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(R("[title]",e)).pipe(g(()=>G("content.tooltips")),re(s=>Ge(s)));return r.subscribe(o),t.pipe(U(n),m(s=>F({ref:e},s)),$e(i.pipe(U(n))))})}function Ia(e,{viewport$:t,header$:r}){return pr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=pe(e);return{active:o>=n}}),X("active"))}function In(e,t){return H(()=>{let r=new v;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=me(".md-content h1");return typeof o=="undefined"?L:Ia(o,t).pipe(y(n=>r.next(n)),_(()=>r.complete()),m(n=>F({ref:e},n)))})}function Fn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Ee(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),X("bottom"))));return Q([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function Fa(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(re(o=>d(o,"change").pipe(m(()=>o))),q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),B(1))}function jn(e){let t=R("input",e),r=E("meta",{name:"theme-color"});document.head.appendChild(r);let o=E("meta",{name:"color-scheme"});document.head.appendChild(o);let n=At("(prefers-color-scheme: light)");return H(()=>{let i=new v;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;a{let s=Te("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(Oe(ie)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),Fa(t).pipe(U(n.pipe(Le(1))),at(),y(s=>i.next(s)),_(()=>i.complete()),m(s=>F({ref:e},s)))})}function Un(e,{progress$:t}){return H(()=>{let r=new v;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(y(o=>r.next({value:o})),_(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Kr=jt(zr());function ja(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Wn({alert$:e}){Kr.default.isSupported()&&new j(t=>{new Kr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||ja(P(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(y(t=>{t.trigger.focus()}),m(()=>ge("clipboard.copied"))).subscribe(e)}function Dn(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function Ua(e,t){let r=new Map;for(let o of R("url",e)){let n=P("loc",o),i=[Dn(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of R("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(Dn(new URL(a),t))}}return r}function mr(e){return on(new URL("sitemap.xml",e)).pipe(m(t=>Ua(t,new URL(e))),he(()=>$(new Map)))}function Wa(e,t){if(!(e.target instanceof Element))return L;let r=e.target.closest("a");if(r===null)return L;if(r.target||e.metaKey||e.ctrlKey)return L;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(new URL(r.href))):L}function Nn(e){let t=new Map;for(let r of R(":scope > *",e.head))t.set(r.outerHTML,r);return t}function Vn(e){for(let t of R("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function Da(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...G("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=me(o),i=me(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=Nn(document);for(let[o,n]of Nn(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Te("container");return Fe(R("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new j(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),L}),ee(),oe(document))}function zn({location$:e,viewport$:t,progress$:r}){let o=we();if(location.protocol==="file:")return L;let n=mr(o.base);$(document).subscribe(Vn);let i=d(document.body,"click").pipe(je(n),b(([c,p])=>Wa(c,p)),le()),s=d(window,"popstate").pipe(m(ve),le());i.pipe(ae(t)).subscribe(([c,{offset:p}])=>{history.replaceState(p,""),history.pushState(null,"",c)}),T(i,s).subscribe(e);let a=e.pipe(X("pathname"),b(c=>rn(c,{progress$:r}).pipe(he(()=>(st(c,!0),L)))),b(Vn),b(Da),le());return T(a.pipe(ae(e,(c,p)=>p)),e.pipe(X("pathname"),b(()=>e),X("hash")),e.pipe(Y((c,p)=>c.pathname===p.pathname&&c.hash===p.hash),b(()=>i),y(()=>history.back()))).subscribe(c=>{var p,l;history.state!==null||!c.hash?window.scrollTo(0,(l=(p=history.state)==null?void 0:p.y)!=null?l:0):(history.scrollRestoration="auto",Zo(c.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),d(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(X("offset"),be(100)).subscribe(({offset:c})=>{history.replaceState(c,"")}),a}var Qn=jt(Kn());function Yn(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,Qn.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function Ht(e){return e.type===1}function fr(e){return e.type===3}function Bn(e,t){let r=ln(e);return T($(location.protocol!=="file:"),We("search")).pipe(He(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:G("search.suggest")}}})),r}function Gn({document$:e}){let t=we(),r=De(new URL("../versions.json",t.base)).pipe(he(()=>L)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>d(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),ae(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?L:(i.preventDefault(),$(c))}}return L}),b(i=>{let{version:s}=n.get(i);return mr(new URL(i)).pipe(m(a=>{let p=ve().href.replace(t.base,"");return a.has(p.split("#")[0])?new URL(`../${s}/${p}`,t.base):new URL(i)}))})))).subscribe(n=>st(n,!0)),Q([r,o]).subscribe(([n,i])=>{P(".md-header__topic").appendChild(gn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var s;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let a=((s=t.version)==null?void 0:s.default)||"latest";Array.isArray(a)||(a=[a]);e:for(let c of a)for(let p of n.aliases.concat(n.version))if(new RegExp(c,"i").test(p)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let a of ne("outdated"))a.hidden=!1})}function Ka(e,{worker$:t}){let{searchParams:r}=ve();r.has("q")&&(Be("search",!0),e.value=r.get("q"),e.focus(),We("search").pipe(He(i=>!i)).subscribe(()=>{let i=ve();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=vt(e),n=T(t.pipe(He(Ht)),d(e,"keyup"),o).pipe(m(()=>e.value),Y());return Q([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),B(1))}function Jn(e,{worker$:t}){let r=new v,o=r.pipe(ee(),oe(!0));Q([t.pipe(He(Ht)),r],(i,s)=>s).pipe(X("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(X("focus")).subscribe(({focus:i})=>{i&&Be("search",i)}),d(e.form,"reset").pipe(U(o)).subscribe(()=>e.focus());let n=P("header [for=__search]");return d(n,"click").subscribe(()=>e.focus()),Ka(e,{worker$:t}).pipe(y(i=>r.next(i)),_(()=>r.complete()),m(i=>F({ref:e},i)),B(1))}function Xn(e,{worker$:t,query$:r}){let o=new v,n=Yo(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=P(":scope > :first-child",e),a=P(":scope > :last-child",e);We("search").subscribe(l=>a.setAttribute("role",l?"list":"presentation")),o.pipe(ae(r),Ir(t.pipe(He(Ht)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?ge("search.result.none"):ge("search.result.placeholder");break;case 1:s.textContent=ge("search.result.one");break;default:let u=ar(l.length);s.textContent=ge("search.result.other",u)}});let c=o.pipe(y(()=>a.innerHTML=""),b(({items:l})=>T($(...l.slice(0,10)),$(...l.slice(10)).pipe(Ke(4),jr(n),b(([f])=>f)))),m(hn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(re(l=>{let f=me("details",l);return typeof f=="undefined"?L:d(f,"toggle").pipe(U(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(fr),m(({data:l})=>l)).pipe(y(l=>o.next(l)),_(()=>o.complete()),m(l=>F({ref:e},l)))}function Qa(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=ve();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Zn(e,t){let r=new v,o=r.pipe(ee(),oe(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),d(e,"click").pipe(U(o)).subscribe(n=>n.preventDefault()),Qa(e,t).pipe(y(n=>r.next(n)),_(()=>r.complete()),m(n=>F({ref:e},n)))}function ei(e,{worker$:t,keyboard$:r}){let o=new v,n=Te("search-query"),i=T(d(n,"keydown"),d(n,"focus")).pipe(Oe(ie),m(()=>n.value),Y());return o.pipe(je(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{switch(a.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(g(fr),m(({data:a})=>a)).pipe(y(a=>o.next(a)),_(()=>o.complete()),m(()=>({ref:e})))}function ti(e,{index$:t,keyboard$:r}){let o=we();try{let n=Bn(o.search,t),i=Te("search-query",e),s=Te("search-result",e);d(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>Be("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Re();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of R(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,h])=>h-u);f.click()}c.claim()}break;case"Escape":case"Tab":Be("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...R(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Re()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Jn(i,{worker$:n});return T(a,Xn(s,{worker$:n,query$:a})).pipe($e(...ne("search-share",e).map(c=>Zn(c,{query$:a})),...ne("search-suggest",e).map(c=>ei(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,qe}}function ri(e,{index$:t,location$:r}){return Q([t,r.pipe(q(ve()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>Yn(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=E("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ya(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return Q([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function Qr(e,o){var n=o,{header$:t}=n,r=to(n,["header$"]);let i=P(".md-sidebar__scrollwrap",e),{y:s}=Ue(i);return H(()=>{let a=new v,c=a.pipe(ee(),oe(!0)),p=a.pipe(Me(0,de));return p.pipe(ae(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(He()).subscribe(()=>{for(let l of R(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=pe(f);f.scrollTo({top:u-h/2})}}}),fe(R("label[tabindex]",e)).pipe(re(l=>d(l,"click").pipe(Oe(ie),m(()=>l),U(c)))).subscribe(l=>{let f=P(`[id="${l.htmlFor}"]`);P(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),Ya(e,r).pipe(y(l=>a.next(l)),_(()=>a.complete()),m(l=>F({ref:e},l)))})}function oi(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return Lt(De(`${r}/releases/latest`).pipe(he(()=>L),m(o=>({version:o.tag_name})),Qe({})),De(r).pipe(he(()=>L),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>F(F({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return De(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ni(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return De(r).pipe(he(()=>L),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))}function ii(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return oi(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ni(r,o)}return L}var Ba;function Ga(e){return Ba||(Ba=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(ne("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return L}return ii(e.href).pipe(y(o=>__md_set("__source",o,sessionStorage)))}).pipe(he(()=>L),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),B(1)))}function ai(e){let t=P(":scope > :last-child",e);return H(()=>{let r=new v;return r.subscribe(({facts:o})=>{t.appendChild(bn(o)),t.classList.add("md-source__repository--active")}),Ga(e).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}function Ja(e,{viewport$:t,header$:r}){return Ee(document.body).pipe(b(()=>pr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),X("hidden"))}function si(e,t){return H(()=>{let r=new v;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(G("navigation.tabs.sticky")?$({hidden:!1}):Ja(e,t)).pipe(y(o=>r.next(o)),_(()=>r.complete()),m(o=>F({ref:e},o)))})}function Xa(e,{viewport$:t,header$:r}){let o=new Map,n=R(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=me(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(X("height"),m(({height:a})=>{let c=Te("main"),p=P(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Ee(document.body).pipe(X("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let h=f.offsetParent;for(;h;h=h.offsetParent)u+=h.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),je(i),b(([c,p])=>t.pipe(Rr(([l,f],{offset:{y:u},size:h})=>{let w=u+h.height>=Math.floor(a.height);for(;f.length;){let[,A]=f[0];if(A-p=u&&!w)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),q({prev:[],next:[]}),Ke(2,1),m(([a,c])=>a.prev.length{let i=new v,s=i.pipe(ee(),oe(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),G("toc.follow")){let a=T(t.pipe(be(1),m(()=>{})),t.pipe(be(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),je(o.pipe(Oe(ie))),ae(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=sr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:h}=pe(f);f.scrollTo({top:u-h/2,behavior:p})}}})}return G("navigation.tracking")&&t.pipe(U(s),X("offset"),be(250),Le(1),U(n.pipe(Le(1))),at({delay:250}),ae(i)).subscribe(([,{prev:a}])=>{let c=ve(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),Xa(e,{viewport$:t,header$:r}).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))})}function Za(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),Ke(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return Q([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),U(o.pipe(Le(1))),oe(!0),at({delay:250}),m(s=>({hidden:s})))}function pi(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new v,s=i.pipe(ee(),oe(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(U(s),X("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),d(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),Za(e,{viewport$:t,main$:o,target$:n}).pipe(y(a=>i.next(a)),_(()=>i.complete()),m(a=>F({ref:e},a)))}function li({document$:e}){e.pipe(b(()=>R(".md-ellipsis")),re(t=>yt(t).pipe(U(e.pipe(Le(1))),g(r=>r),m(()=>t),ye(1))),g(t=>t.offsetWidth{let r=t.innerText,o=t.closest("a")||t;return o.title=r,Ge(o).pipe(U(e.pipe(Le(1))),_(()=>o.removeAttribute("title")))})).subscribe(),e.pipe(b(()=>R(".md-status")),re(t=>Ge(t))).subscribe()}function mi({document$:e,tablet$:t}){e.pipe(b(()=>R(".md-toggle--indeterminate")),y(r=>{r.indeterminate=!0,r.checked=!1}),re(r=>d(r,"change").pipe(Fr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),ae(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function es(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function fi({document$:e}){e.pipe(b(()=>R("[data-md-scrollfix]")),y(t=>t.removeAttribute("data-md-scrollfix")),g(es),re(t=>d(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function ui({viewport$:e,tablet$:t}){Q([We("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(Ye(r?400:100))),ae(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ts(){return location.protocol==="file:"?gt(`${new URL("search/search_index.js",Yr.base)}`).pipe(m(()=>__index),B(1)):De(new URL("search/search_index.json",Yr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var rt=No(),Rt=Jo(),wt=en(Rt),Br=Go(),_e=pn(),ur=At("(min-width: 960px)"),hi=At("(min-width: 1220px)"),bi=tn(),Yr=we(),vi=document.forms.namedItem("search")?ts():qe,Gr=new v;Wn({alert$:Gr});var Jr=new v;G("navigation.instant")&&zn({location$:Rt,viewport$:_e,progress$:Jr}).subscribe(rt);var di;((di=Yr.version)==null?void 0:di.provider)==="mike"&&Gn({document$:rt});T(Rt,wt).pipe(Ye(125)).subscribe(()=>{Be("drawer",!1),Be("search",!1)});Br.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=me("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=me("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Re();o instanceof HTMLLabelElement&&o.click()}});li({document$:rt});mi({document$:rt,tablet$:ur});fi({document$:rt});ui({viewport$:_e,tablet$:ur});var tt=Rn(Te("header"),{viewport$:_e}),$t=rt.pipe(m(()=>Te("main")),b(e=>Fn(e,{viewport$:_e,header$:tt})),B(1)),rs=T(...ne("consent").map(e=>fn(e,{target$:wt})),...ne("dialog").map(e=>$n(e,{alert$:Gr})),...ne("header").map(e=>Pn(e,{viewport$:_e,header$:tt,main$:$t})),...ne("palette").map(e=>jn(e)),...ne("progress").map(e=>Un(e,{progress$:Jr})),...ne("search").map(e=>ti(e,{index$:vi,keyboard$:Br})),...ne("source").map(e=>ai(e))),os=H(()=>T(...ne("announce").map(e=>mn(e)),...ne("content").map(e=>Hn(e,{viewport$:_e,target$:wt,print$:bi})),...ne("content").map(e=>G("search.highlight")?ri(e,{index$:vi,location$:Rt}):L),...ne("header-title").map(e=>In(e,{viewport$:_e,header$:tt})),...ne("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Ur(hi,()=>Qr(e,{viewport$:_e,header$:tt,main$:$t})):Ur(ur,()=>Qr(e,{viewport$:_e,header$:tt,main$:$t}))),...ne("tabs").map(e=>si(e,{viewport$:_e,header$:tt})),...ne("toc").map(e=>ci(e,{viewport$:_e,header$:tt,main$:$t,target$:wt})),...ne("top").map(e=>pi(e,{viewport$:_e,header$:tt,main$:$t,target$:wt})))),gi=rt.pipe(b(()=>os),$e(rs),B(1));gi.subscribe();window.document$=rt;window.location$=Rt;window.target$=wt;window.keyboard$=Br;window.viewport$=_e;window.tablet$=ur;window.screen$=hi;window.print$=bi;window.alert$=Gr;window.progress$=Jr;window.component$=gi;})(); +//# sourceMappingURL=bundle.bd41221c.min.js.map + diff --git a/assets/javascripts/bundle.bd41221c.min.js.map b/assets/javascripts/bundle.bd41221c.min.js.map new file mode 100644 index 0000000..1663dab --- /dev/null +++ b/assets/javascripts/bundle.bd41221c.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/tslib.es6.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an