Skip to content

liblaf.melon €

Modules:

  • barycentric –
  • cli –
  • compat –
  • ext –
  • io –
  • mesh –
  • proximity –
  • tet –
  • transfer –
  • tri –
  • typing –
  • utils –

Classes:

  • NearestAlgorithm –
  • NearestAlgorithmPrepared –
  • NearestPoint –
  • NearestPointOnSurface –
  • NearestPointOnSurfacePrepared –
  • NearestPointOnSurfaceResult –
  • NearestPointPrepared –
  • NearestPointResult –
  • NearestResult –
  • PVDWriter –

    .

  • SeriesReader –
  • SeriesWriter –

Functions:

  • annotate_landmarks –
  • as_mesh –
  • barycentric_to_points –
  • bounds_contains –
  • cell_neighbors –
  • compute_edges_length –
  • fast_wrapping –
  • geodesic_distance –
  • geodesic_path –
  • get_landmarks_path –
  • get_polygons_path –
  • load_landmarks –
  • load_polygons –
  • mesh_fix –
  • nearest –
  • nearest_point_on_surface –
  • sample_barycentric_coords –
  • save_landmarks –
  • save_polygons –
  • tetwild –
  • transfer_tet_cell –
  • transfer_tet_cell_to_point –
  • transfer_tri_cell_to_point_category –
  • transfer_tri_point –
  • transfer_tri_point_to_tet –

Attributes:

  • __version__ (str) –
  • __version_tuple__ (tuple[int | str, ...]) –
  • as_multi_block (ConverterDispatcher[MultiBlock]) –
  • as_pointset (ConverterDispatcher[PointSet]) –
  • as_polydata (ConverterDispatcher[PolyData]) –
  • as_structured_grid (ConverterDispatcher[StructuredGrid]) –
  • as_trimesh (ConverterDispatcher[Trimesh]) –
  • as_unstructured_grid (ConverterDispatcher[UnstructuredGrid]) –
  • load_multi_block (ReaderDispatcher[MultiBlock]) –
  • load_polydata (ReaderDispatcher[PolyData]) –
  • load_structured_grid (ReaderDispatcher[StructuredGrid]) –
  • load_trimesh (ReaderDispatcher[Trimesh]) –
  • load_unstructured_grid (ReaderDispatcher[UnstructuredGrid]) –
  • save –

__version__ module-attribute €

__version__: str = '1.1.3'

__version_tuple__ module-attribute €

__version_tuple__: tuple[int | str, ...] = (1, 1, 3)

as_multi_block module-attribute €

as_multi_block: ConverterDispatcher[MultiBlock] = (
    ConverterDispatcher(MultiBlock)
)

as_pointset module-attribute €

as_pointset: ConverterDispatcher[PointSet] = (
    ConverterDispatcher(PointSet)
)

as_polydata module-attribute €

as_polydata: ConverterDispatcher[PolyData] = (
    ConverterDispatcher(PolyData)
)

as_structured_grid module-attribute €

as_structured_grid: ConverterDispatcher[StructuredGrid] = (
    ConverterDispatcher(StructuredGrid)
)

as_trimesh module-attribute €

as_trimesh: ConverterDispatcher[Trimesh] = (
    ConverterDispatcher(Trimesh)
)

as_unstructured_grid module-attribute €

as_unstructured_grid: ConverterDispatcher[
    UnstructuredGrid
] = ConverterDispatcher(UnstructuredGrid)

load_multi_block module-attribute €

load_multi_block: ReaderDispatcher[MultiBlock] = (
    ReaderDispatcher(MultiBlock)
)

load_polydata module-attribute €

load_polydata: ReaderDispatcher[PolyData] = (
    ReaderDispatcher(PolyData)
)

load_structured_grid module-attribute €

load_structured_grid: ReaderDispatcher[StructuredGrid] = (
    ReaderDispatcher(StructuredGrid)
)

load_trimesh module-attribute €

load_trimesh: ReaderDispatcher[Trimesh] = ReaderDispatcher(
    Trimesh
)

load_unstructured_grid module-attribute €

load_unstructured_grid: ReaderDispatcher[
    UnstructuredGrid
] = ReaderDispatcher(UnstructuredGrid)

save module-attribute €

save = WriterDispatcher()

NearestAlgorithm €

Bases: ABC


              flowchart TD
              liblaf.melon.NearestAlgorithm[NearestAlgorithm]

              

              click liblaf.melon.NearestAlgorithm href "" "liblaf.melon.NearestAlgorithm"
            

Methods:

  • prepare –

prepare abstractmethod €

prepare(source: Any) -> NearestAlgorithmPrepared
Source code in src/liblaf/melon/proximity/_abc.py
22
23
@abc.abstractmethod
def prepare(self, source: Any) -> NearestAlgorithmPrepared: ...

NearestAlgorithmPrepared €

Bases: ABC


              flowchart TD
              liblaf.melon.NearestAlgorithmPrepared[NearestAlgorithmPrepared]

              

              click liblaf.melon.NearestAlgorithmPrepared href "" "liblaf.melon.NearestAlgorithmPrepared"
            

Methods:

  • query –

query abstractmethod €

query(query: Any) -> NearestResult
Source code in src/liblaf/melon/proximity/_abc.py
17
18
@abc.abstractmethod
def query(self, query: Any) -> NearestResult: ...

NearestPoint €

Bases: NearestAlgorithm


              flowchart TD
              liblaf.melon.NearestPoint[NearestPoint]
              liblaf.melon.proximity._abc.NearestAlgorithm[NearestAlgorithm]

                              liblaf.melon.proximity._abc.NearestAlgorithm --> liblaf.melon.NearestPoint
                


              click liblaf.melon.NearestPoint href "" "liblaf.melon.NearestPoint"
              click liblaf.melon.proximity._abc.NearestAlgorithm href "" "liblaf.melon.proximity._abc.NearestAlgorithm"
            

Parameters:

  • distance_threshold €

    (float, default: 0.1 ) –
  • ignore_orientation €

    (bool, default: True ) –
  • max_k €

    (int, default: 32 ) –
  • normal_threshold €

    (float, default: -inf ) –
  • workers €

    (int, default: -1 ) –

Methods:

  • prepare –

Attributes:

  • distance_threshold (float) –
  • ignore_orientation (bool) –
  • max_k (int) –
  • normal_threshold (float) –
  • workers (int) –

distance_threshold class-attribute instance-attribute €

distance_threshold: float = 0.1

ignore_orientation class-attribute instance-attribute €

ignore_orientation: bool = True

max_k class-attribute instance-attribute €

max_k: int = 32

normal_threshold class-attribute instance-attribute €

normal_threshold: float = field(
    default=-inf, validator=le(1.0)
)

workers class-attribute instance-attribute €

workers: int = -1

prepare €

prepare(source: Any) -> NearestPointPrepared
Source code in src/liblaf/melon/proximity/_nearest_point.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
@override
def prepare(self, source: Any) -> NearestPointPrepared:
    need_normals: bool = self.normal_threshold > -1.0
    source: pv.PointSet = io.as_pointset(source, point_normals=need_normals)
    tree: scipy.spatial.KDTree = scipy.spatial.KDTree(source.points)
    return NearestPointPrepared(
        source=source,
        tree=tree,
        distance_threshold=self.distance_threshold,
        max_k=self.max_k,
        normal_threshold=self.normal_threshold,
        ignore_orientation=self.ignore_orientation,
        workers=self.workers,
    )

NearestPointOnSurface €

Bases: NearestAlgorithm


              flowchart TD
              liblaf.melon.NearestPointOnSurface[NearestPointOnSurface]
              liblaf.melon.proximity._abc.NearestAlgorithm[NearestAlgorithm]

                              liblaf.melon.proximity._abc.NearestAlgorithm --> liblaf.melon.NearestPointOnSurface
                


              click liblaf.melon.NearestPointOnSurface href "" "liblaf.melon.NearestPointOnSurface"
              click liblaf.melon.proximity._abc.NearestAlgorithm href "" "liblaf.melon.proximity._abc.NearestAlgorithm"
            

Parameters:

  • distance_threshold €

    (float, default: 0.1 ) –
  • ignore_orientation €

    (bool, default: False ) –
  • normal_threshold €

    (float | None, default: 0.8 ) –

Methods:

  • prepare –

Attributes:

  • distance_threshold (float) –
  • ignore_orientation (bool) –
  • normal_threshold (float | None) –

distance_threshold class-attribute instance-attribute €

distance_threshold: float = 0.1

ignore_orientation class-attribute instance-attribute €

ignore_orientation: bool = False

normal_threshold class-attribute instance-attribute €

normal_threshold: float | None = field(
    default=0.8, validator=optional([ge(-1.0), le(1.0)])
)

prepare €

prepare(source: Any) -> NearestPointOnSurfacePrepared
Source code in src/liblaf/melon/proximity/_nearest_point_on_surface.py
113
114
115
116
117
118
119
120
121
@override
def prepare(self, source: Any) -> NearestPointOnSurfacePrepared:
    return NearestPointOnSurfacePrepared(
        source=io.as_warp_mesh(source),
        source_pv=io.as_polydata(source),
        distance_threshold=self.distance_threshold,
        ignore_orientation=self.ignore_orientation,
        normal_threshold=self.normal_threshold,
    )

NearestPointOnSurfacePrepared €

Bases: NearestAlgorithmPrepared


              flowchart TD
              liblaf.melon.NearestPointOnSurfacePrepared[NearestPointOnSurfacePrepared]
              liblaf.melon.proximity._abc.NearestAlgorithmPrepared[NearestAlgorithmPrepared]

                              liblaf.melon.proximity._abc.NearestAlgorithmPrepared --> liblaf.melon.NearestPointOnSurfacePrepared
                


              click liblaf.melon.NearestPointOnSurfacePrepared href "" "liblaf.melon.NearestPointOnSurfacePrepared"
              click liblaf.melon.proximity._abc.NearestAlgorithmPrepared href "" "liblaf.melon.proximity._abc.NearestAlgorithmPrepared"
            

Parameters:

Methods:

  • query –

Attributes:

  • distance_threshold (float) –
  • face_normals (Float[ndarray, 'M 3']) –
  • ignore_orientation (bool) –
  • length (float) –
  • normal_threshold (float | None) –
  • source (Mesh) –
  • source_pv (PolyData) –

distance_threshold instance-attribute €

distance_threshold: float

face_normals property €

face_normals: Float[ndarray, 'M 3']

ignore_orientation instance-attribute €

ignore_orientation: bool

length property €

length: float

normal_threshold instance-attribute €

normal_threshold: float | None

source instance-attribute €

source: Mesh

source_pv instance-attribute €

source_pv: PolyData

query €

query(query: Any) -> NearestPointOnSurfaceResult
Source code in src/liblaf/melon/proximity/_nearest_point_on_surface.py
37
38
39
40
41
@override
def query(self, query: Any) -> NearestPointOnSurfaceResult:
    if self.normal_threshold is None:
        return self._query_without_normal_threshold(query)
    return self._query_with_normal_threshold(query)

NearestPointOnSurfaceResult €

Bases: NearestResult


              flowchart TD
              liblaf.melon.NearestPointOnSurfaceResult[NearestPointOnSurfaceResult]
              liblaf.melon.proximity._abc.NearestResult[NearestResult]

                              liblaf.melon.proximity._abc.NearestResult --> liblaf.melon.NearestPointOnSurfaceResult
                


              click liblaf.melon.NearestPointOnSurfaceResult href "" "liblaf.melon.NearestPointOnSurfaceResult"
              click liblaf.melon.proximity._abc.NearestResult href "" "liblaf.melon.proximity._abc.NearestResult"
            

Parameters:

  • distance €

    (Float[ndarray, Q]) –
  • missing €

    (Bool[ndarray, Q]) –
  • nearest €

    (Float[ndarray, 'Q 3']) –
  • barycentric €

    (Float[ndarray, 'N 3']) –
  • triangle_id €

    (Integer[ndarray, N]) –

Attributes:

  • barycentric (Float[ndarray, 'N 3']) –
  • distance (Float[ndarray, ' Q']) –
  • missing (Bool[ndarray, ' Q']) –
  • nearest (Float[ndarray, 'Q 3']) –
  • triangle_id (Integer[ndarray, ' N']) –

barycentric instance-attribute €

barycentric: Float[ndarray, 'N 3']

distance instance-attribute €

distance: Float[ndarray, ' Q']

missing instance-attribute €

missing: Bool[ndarray, ' Q']

nearest instance-attribute €

nearest: Float[ndarray, 'Q 3']

triangle_id instance-attribute €

triangle_id: Integer[ndarray, ' N']

NearestPointPrepared €

Bases: NearestAlgorithmPrepared


              flowchart TD
              liblaf.melon.NearestPointPrepared[NearestPointPrepared]
              liblaf.melon.proximity._abc.NearestAlgorithmPrepared[NearestAlgorithmPrepared]

                              liblaf.melon.proximity._abc.NearestAlgorithmPrepared --> liblaf.melon.NearestPointPrepared
                


              click liblaf.melon.NearestPointPrepared href "" "liblaf.melon.NearestPointPrepared"
              click liblaf.melon.proximity._abc.NearestAlgorithmPrepared href "" "liblaf.melon.proximity._abc.NearestAlgorithmPrepared"
            

Parameters:

Methods:

  • query –

Attributes:

  • distance_threshold (float) –
  • ignore_orientation (bool) –
  • max_k (int) –
  • normal_threshold (float) –
  • source (PointSet) –
  • tree (KDTree) –
  • workers (int) –

distance_threshold instance-attribute €

distance_threshold: float

ignore_orientation instance-attribute €

ignore_orientation: bool

max_k instance-attribute €

max_k: int

normal_threshold instance-attribute €

normal_threshold: float

source instance-attribute €

source: PointSet

tree instance-attribute €

tree: KDTree

workers instance-attribute €

workers: int

query €

query(query: Any) -> NearestPointResult
Source code in src/liblaf/melon/proximity/_nearest_point.py
30
31
32
33
34
@override
def query(self, query: Any) -> NearestPointResult:
    if self.normal_threshold <= -1.0:
        return self._nearest_vertex(query)
    return self._nearest_vertex_with_normal_threshold(query)

NearestPointResult €

Bases: NearestResult


              flowchart TD
              liblaf.melon.NearestPointResult[NearestPointResult]
              liblaf.melon.proximity._abc.NearestResult[NearestResult]

                              liblaf.melon.proximity._abc.NearestResult --> liblaf.melon.NearestPointResult
                


              click liblaf.melon.NearestPointResult href "" "liblaf.melon.NearestPointResult"
              click liblaf.melon.proximity._abc.NearestResult href "" "liblaf.melon.proximity._abc.NearestResult"
            

Parameters:

  • distance €

    (Float[ndarray, Q]) –
  • missing €

    (Bool[ndarray, Q]) –
  • nearest €

    (Float[ndarray, 'Q 3']) –
  • vertex_id €

    (Integer[ndarray, N]) –

Attributes:

  • distance (Float[ndarray, ' Q']) –
  • missing (Bool[ndarray, ' Q']) –
  • nearest (Float[ndarray, 'Q 3']) –
  • vertex_id (Integer[ndarray, ' N']) –

distance instance-attribute €

distance: Float[ndarray, ' Q']

missing instance-attribute €

missing: Bool[ndarray, ' Q']

nearest instance-attribute €

nearest: Float[ndarray, 'Q 3']

vertex_id instance-attribute €

vertex_id: Integer[ndarray, ' N']

NearestResult €

Parameters:

  • distance €

    (Float[ndarray, Q]) –
  • missing €

    (Bool[ndarray, Q]) –
  • nearest €

    (Float[ndarray, 'Q 3']) –

Attributes:

  • distance (Float[ndarray, ' Q']) –
  • missing (Bool[ndarray, ' Q']) –
  • nearest (Float[ndarray, 'Q 3']) –

distance instance-attribute €

distance: Float[ndarray, ' Q']

missing instance-attribute €

missing: Bool[ndarray, ' Q']

nearest instance-attribute €

nearest: Float[ndarray, 'Q 3']

PVDWriter €

.

References

[1]: ParaView/Data formats - KitwarePublic

Parameters:

  • clear €

    (bool, default: False ) –
  • file €

    (Path, default: PosixPath('animation.pvd') ) –
  • fps €

    (float, default: 30.0 ) –

Attributes:

  • datasets (list[PVDDataSet]) –

    Built-in mutable sequence.

    If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

Methods:

  • __attrs_post_init__ –
  • append –
  • end –

clear class-attribute instance-attribute €

clear: bool = field(default=False, kw_only=True)

datasets class-attribute instance-attribute €

datasets: list[PVDDataSet] = field(init=False, factory=list)

file class-attribute instance-attribute €

file: Path = field(
    default=Path("animation.pvd"), converter=Path
)

folder property €

folder: Path

fps class-attribute instance-attribute €

fps: float = field(default=30.0, kw_only=True)

name property €

name: str

__attrs_post_init__ €

__attrs_post_init__() -> None
Source code in src/liblaf/melon/io/paraview/_pvd_writer.py
39
40
41
def __attrs_post_init__(self) -> None:
    if self.clear:
        shutil.rmtree(self.folder, ignore_errors=True)

append €

append(
    dataset: Any,
    timestep: float | None = None,
    *,
    ext: str,
    part: int = 0,
) -> None
Source code in src/liblaf/melon/io/paraview/_pvd_writer.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def append(
    self, dataset: Any, timestep: float | None = None, *, ext: str, part: int = 0
) -> None:
    if timestep is None:
        timestep = (
            self.datasets[-1].timestep + (1 / self.fps) if self.datasets else 0
        )
    frame_id: int = len(self.datasets)
    filename: str = f"{self.name}_{frame_id:06d}"
    filename += ext
    filepath: Path = self.folder / filename
    save(filepath, dataset)
    self.datasets.append(PVDDataSet(timestep=timestep, part=part, file=filepath))
    self.end()

end €

end() -> None
Source code in src/liblaf/melon/io/paraview/_pvd_writer.py
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def end(self) -> None:
    root = ElementTree.Element(
        "VTKFile", type="Collection", version="0.1", byte_order="LittleEndian"
    )
    collection: ElementTree.Element = ElementTree.SubElement(root, "Collection")
    root_dir: Path = self.file.absolute().parent
    for dataset in self.datasets:
        elem: ElementTree.Element = ElementTree.SubElement(collection, "DataSet")
        elem.set("timestep", str(dataset.timestep))
        elem.set("part", str(dataset.part))
        elem.set("file", dataset.file.absolute().relative_to(root_dir).as_posix())
    tree = ElementTree.ElementTree(root)
    ElementTree.indent(tree, space="  ")
    self.file.parent.mkdir(parents=True, exist_ok=True)
    tree.write(self.file, xml_declaration=True)

SeriesReader €

SeriesReader(file: StrPath, loader: Callable[[Path], T])

Bases: Sequence[T]


              flowchart TD
              liblaf.melon.SeriesReader[SeriesReader]

              

              click liblaf.melon.SeriesReader href "" "liblaf.melon.SeriesReader"
            

Parameters:

Methods:

  • __getitem__ –
  • __len__ –

Attributes:

Source code in src/liblaf/melon/io/paraview/series/_reader.py
24
25
26
def __init__(self, file: StrPath, loader: Callable[[Path], T]) -> None:
    file = Path(file)
    self.__attrs_init__(file=file, loader=loader)  # pyright: ignore[reportAttributeAccessIssue]

file instance-attribute €

file: Path

folder property €

folder: Path

loader instance-attribute €

loader: Callable[[Path], T]

series cached property €

series: Series

time_values property €

time_values: list[float]

__getitem__ €

__getitem__(index: int) -> T
__getitem__(index: slice) -> Sequence[T]
Source code in src/liblaf/melon/io/paraview/series/_reader.py
32
33
34
35
36
37
38
@override
def __getitem__(self, index: int | slice) -> T | Sequence[T]:
    __tracebackhide__ = True
    files: File | list[File] = self.series.files[index]
    if isinstance(files, File):
        return self.loader(self.folder / files.name)
    return [self.loader(self.folder / f.name) for f in files]

__len__ €

__len__() -> int
Source code in src/liblaf/melon/io/paraview/series/_reader.py
40
41
42
@override
def __len__(self) -> int:
    return len(self.series.files)

SeriesWriter €

SeriesWriter(
    file: StrPath,
    /,
    *,
    clear: bool = False,
    fps: float = 30.0,
    step: float | None = None,
)

Bases: Sequence[File], AbstractContextManager


              flowchart TD
              liblaf.melon.SeriesWriter[SeriesWriter]

              

              click liblaf.melon.SeriesWriter href "" "liblaf.melon.SeriesWriter"
            

Parameters:

Methods:

  • __enter__ –
  • __exit__ –
  • __getitem__ –
  • __len__ –
  • append –
  • end –
  • save –
  • start –

Attributes:

  • ext (str) –
  • file (Path) –
  • folder (Path) –
  • fps (float) –
  • name (str) –
  • series (Series) –
  • step (float) –
  • time (float) –
Source code in src/liblaf/melon/io/paraview/series/_writer.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def __init__(
    self,
    file: StrPath,
    /,
    *,
    clear: bool = False,
    fps: float = 30.0,
    step: float | None = None,
) -> None:
    if step is None:
        step = 1.0 / fps
    self.__attrs_init__(file=Path(file), series=Series(), step=step)  # pyright: ignore[reportAttributeAccessIssue]
    if clear:
        shutil.rmtree(self.folder, ignore_errors=True)

ext property €

ext: str

file instance-attribute €

file: Path

folder property €

folder: Path

fps property €

fps: float

name property €

name: str

series instance-attribute €

series: Series

step instance-attribute €

step: float

time property €

time: float

__enter__ €

__enter__() -> Self
Source code in src/liblaf/melon/io/paraview/series/_writer.py
66
67
68
def __enter__(self) -> Self:
    self.start()
    return self

__exit__ €

__exit__(
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: TracebackType | None,
) -> None
Source code in src/liblaf/melon/io/paraview/series/_writer.py
70
71
72
73
74
75
76
def __exit__(
    self,
    exc_type: type[BaseException] | None,
    exc_value: BaseException | None,
    traceback: types.TracebackType | None,
) -> None:
    self.end()

__getitem__ €

__getitem__(index: int) -> File
__getitem__(index: slice) -> list[File]
Source code in src/liblaf/melon/io/paraview/series/_writer.py
60
61
def __getitem__(self, index: int | slice) -> File | list[File]:
    return self.series.files[index]

__len__ €

__len__() -> int
Source code in src/liblaf/melon/io/paraview/series/_writer.py
63
64
def __len__(self) -> int:
    return len(self.series.files)

append €

append(
    data: Any,
    *,
    time: float | None = None,
    timestep: float | None = None,
) -> None
Source code in src/liblaf/melon/io/paraview/series/_writer.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def append(
    self, data: Any, *, time: float | None = None, timestep: float | None = None
) -> None:
    __tracebackhide__ = True
    filename: str = f"{self.name}_{len(self):06d}{self.ext}"
    filepath: Path = self.folder / filename
    save(filepath, data)
    if time is None:
        if timestep is None:
            timestep = self.step
        time = self.time + timestep
    self.series.files.append(
        File(name=filepath.relative_to(self.file.parent).as_posix(), time=time)
    )
    self.save()

end €

end() -> None
Source code in src/liblaf/melon/io/paraview/series/_writer.py
116
117
def end(self) -> None:
    self.save()

save €

save() -> None
Source code in src/liblaf/melon/io/paraview/series/_writer.py
119
120
121
122
def save(self) -> None:
    grapes.save(
        self.file, self.series, force_ext=".json", pydantic={"by_alias": True}
    )

start €

start() -> None
Source code in src/liblaf/melon/io/paraview/series/_writer.py
124
125
def start(self) -> None:
    pass

annotate_landmarks €

annotate_landmarks(
    left: Any,
    right: Any,
    *,
    left_landmarks: Float[ArrayLike, "L 3"] | None = None,
    right_landmarks: Float[ArrayLike, "L 3"] | None = None,
) -> tuple[Float[ndarray, "L 3"], Float[ndarray, "L 3"]]
Source code in src/liblaf/melon/ext/wrap/_annotate_landmarks.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def annotate_landmarks(
    left: Any,
    right: Any,
    *,
    left_landmarks: Float[ArrayLike, "L 3"] | None = None,
    right_landmarks: Float[ArrayLike, "L 3"] | None = None,
) -> tuple[Float[np.ndarray, "L 3"], Float[np.ndarray, "L 3"]]:
    if left_landmarks is None:
        left_landmarks = np.zeros((0, 3))
    if right_landmarks is None:
        right_landmarks = np.zeros((0, 3))
    with tempfile.TemporaryDirectory() as tmpdir_str:
        tmpdir: Path = Path(tmpdir_str).absolute()
        project_file: Path = tmpdir / "annotate-landmarks.wrap"
        left_file: Path = tmpdir / "left.obj"
        right_file: Path = tmpdir / "right.obj"
        left_landmarks_file: Path = tmpdir / "left.landmarks.json"
        right_landmarks_file: Path = tmpdir / "right.landmarks.json"
        io.save(left_file, left)
        io.save(right_file, right)
        io.save_landmarks(left_landmarks_file, left_landmarks)
        io.save_landmarks(right_landmarks_file, right_landmarks)
        template: jinja2.Template = environment.get_template("annotate-landmarks.wrap")
        project: str = template.render(
            {
                "left": left_file,
                "right": right_file,
                "left_landmarks": left_landmarks_file,
                "right_landmarks": right_landmarks_file,
            }
        )
        project_file.write_text(project)
        sp.run(["Wrap.sh", project_file], check=True)
        left_landmarks = io.load_landmarks(left_landmarks_file)
        right_landmarks = io.load_landmarks(right_landmarks_file)
    return left_landmarks, right_landmarks

as_mesh €

as_mesh(mesh: Any) -> PolyData | UnstructuredGrid
Source code in src/liblaf/melon/io/pyvista/_convert.py
11
12
13
14
15
16
def as_mesh(mesh: Any) -> pv.PolyData | pv.UnstructuredGrid:
    try:
        return as_polydata(mesh)
    except UnsupportedConverterError:
        pass
    return as_unstructured_grid(mesh)

barycentric_to_points €

barycentric_to_points(
    cells: Float[ArrayLike, "*N B D"],
    barycentric: Float[ArrayLike, "*N B"],
) -> Float[Array, "*N D"]
Source code in src/liblaf/melon/barycentric/_points.py
 6
 7
 8
 9
10
11
12
13
14
def barycentric_to_points(
    cells: Float[ArrayLike, "*N B D"], barycentric: Float[ArrayLike, "*N B"]
) -> Float[Array, "*N D"]:
    cells: Float[Array, "*N B D"] = jnp.asarray(cells)
    barycentric: Float[Array, "*N B"] = jnp.asarray(barycentric)
    points: Float[Array, "*N D"] = einops.einsum(
        barycentric, cells, "... B, ... B D -> ... D"
    )
    return points

bounds_contains €

bounds_contains(
    bounds: Float[ArrayLike, "2 3"]
    | Float[ArrayLike, " 6"]
    | BoundsTuple,
    points: Float[ArrayLike, "N 3"],
) -> Bool[Array, " N"]
Source code in src/liblaf/melon/_src/bounds.py
 8
 9
10
11
12
13
14
15
16
def bounds_contains(
    bounds: Float[ArrayLike, "2 3"] | Float[ArrayLike, " 6"] | pv.BoundsTuple,
    points: Float[ArrayLike, "N 3"],
) -> Bool[Array, " N"]:
    bounds = jnp.asarray(bounds)
    if bounds.shape == (6,):
        bounds = bounds.reshape(3, 2).T
    points = jnp.asarray(points)
    return _bounds_contains_jit(bounds, points)

cell_neighbors €

cell_neighbors(mesh_or_cells: Any) -> Integer[Array, 'N 2']
Source code in src/liblaf/melon/_src/graph.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def cell_neighbors(mesh_or_cells: Any) -> Integer[Array, "N 2"]:
    cells: Integer[Array, "C V"] = _as_cells(mesh_or_cells)
    n_cells: int = cells.shape[0]
    combinations: Integer[Array, "4 3"] = jnp.asarray(
        list(itertools.combinations(range(4), 3))
    )
    faces: Integer[Array, "C 4 3"] = cells[:, combinations]
    faces = jnp.sort(faces, axis=-1)
    cell_idx: Integer[Array, " C*4"] = einops.repeat(
        jnp.arange(n_cells), "C -> (C 4)", C=n_cells
    )
    faces: Integer[Array, "C*4 3"] = einops.rearrange(faces, "C F V -> (C F) V")
    order: Integer[Array, " C*4"] = jnp.lexsort(faces.T)
    faces_sorted: Integer[Array, " C*4 3"] = faces[order]
    cell_idx_sorted: Integer[Array, " C*4"] = cell_idx[order]
    mask: Array = jnp.all(faces_sorted[:-1] == faces_sorted[1:], axis=-1)
    neighbors: Integer[Array, "N 2"] = jnp.stack(
        [cell_idx_sorted[:-1][mask], cell_idx_sorted[1:][mask]], axis=-1
    )
    neighbors = jnp.sort(neighbors, axis=-1)
    neighbors = jnp.unique(neighbors, axis=0)
    return neighbors

compute_edges_length €

compute_edges_length(
    obj: DataSet,
) -> Float[ndarray, " edges"]
Source code in src/liblaf/melon/_src/edges.py
6
7
8
9
def compute_edges_length(obj: pv.DataSet) -> Float[np.ndarray, " edges"]:
    edges: pv.PolyData = obj.extract_all_edges()  # pyright: ignore[reportAssignmentType]
    edges = edges.compute_cell_sizes(length=True, area=False, volume=False)  # pyright: ignore[reportAssignmentType]
    return edges.cell_data["Length"]

fast_wrapping €

fast_wrapping(
    source: Any,
    target: Any,
    *,
    reflection: bool = True,
    translation: bool = True,
    scale: bool = True,
    source_landmarks: Float[ArrayLike, "L 3"] | None = None,
    target_landmarks: Float[ArrayLike, "L 3"] | None = None,
    free_polygons_floating: Bool[ArrayLike, " full"]
    | Integer[ArrayLike, " free"]
    | None = None,
    verbose: bool = False,
) -> PolyData
Source code in src/liblaf/melon/tri/_wrapping.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def fast_wrapping(
    source: Any,
    target: Any,
    *,
    # procrustes options
    reflection: bool = True,
    translation: bool = True,
    scale: bool = True,
    # FastWrapping options
    source_landmarks: Float[ArrayLike, "L 3"] | None = None,
    target_landmarks: Float[ArrayLike, "L 3"] | None = None,
    free_polygons_floating: Bool[ArrayLike, " full"]
    | Integer[ArrayLike, " free"]
    | None = None,
    verbose: bool = False,
) -> pv.PolyData:
    from liblaf.melon.ext import wrap

    if source_landmarks is not None and target_landmarks is not None:
        matrix: Float[np.ndarray, "4 4"]
        transformed: Float[np.ndarray, "L 3"]
        cost: float
        matrix, transformed, cost = tm.registration.procrustes(
            source_landmarks,
            target_landmarks,
            reflection=reflection,
            translation=translation,
            scale=scale,
        )
        logger.debug("procrustes cost: %g", cost)
        source: pv.PolyData = io.as_polydata(source)
        source = source.transform(matrix, inplace=False)  # pyright: ignore[reportAssignmentType]
        source_landmarks = transformed
    result: pv.PolyData = wrap.fast_wrapping(
        source,
        target,
        source_landmarks=source_landmarks,
        target_landmarks=target_landmarks,
        free_polygons_floating=free_polygons_floating,
        verbose=verbose,
    )
    return result

geodesic_distance €

geodesic_distance(
    mesh: Any, v_ind: Integer[ArrayLike, "*v"]
) -> Float[ndarray, " p *v"]
Source code in src/liblaf/melon/_src/geodesic.py
11
12
13
14
15
16
17
18
19
20
21
def geodesic_distance(
    mesh: Any, v_ind: Integer[ArrayLike, "*v"]
) -> Float[np.ndarray, " p *v"]:
    mesh: pv.PolyData = io.as_polydata(mesh).triangulate(inplace=False)  # pyright: ignore[reportAssignmentType]
    v_ind: Integer[np.ndarray, "*v"] = np.asarray(v_ind)
    v_ind_flat: Integer[np.ndarray, " V"] = np.ravel(v_ind)
    solver = pp3d.MeshHeatMethodDistanceSolver(mesh.points, mesh.regular_faces)
    dist_flat: Float[np.ndarray, "p v"] = np.stack(
        [solver.compute_distance(vi) for vi in v_ind_flat], axis=-1
    )
    return dist_flat.reshape((mesh.n_points, *v_ind.shape))

geodesic_path €

geodesic_path(
    mesh: Any, v_start: int, v_end: int
) -> PolyData
Source code in src/liblaf/melon/_src/geodesic.py
24
25
26
27
28
def geodesic_path(mesh: Any, v_start: int, v_end: int) -> pv.PolyData:
    mesh: pv.PolyData = io.as_polydata(mesh).triangulate(inplace=False)  # pyright: ignore[reportAssignmentType]
    solver = pp3d.EdgeFlipGeodesicSolver(mesh.points, mesh.regular_faces)
    points: Float[np.ndarray, "p 3"] = solver.find_geodesic_path(v_start, v_end)
    return pv.lines_from_points(points)

get_landmarks_path €

get_landmarks_path(path: str | PathLike[str]) -> Path
Source code in src/liblaf/melon/io/wrap/landmarks/_utils.py
5
6
7
8
9
def get_landmarks_path(path: str | os.PathLike[str]) -> Path:
    path: Path = Path(path)
    if path.suffix != ".json":
        return path.with_suffix(".landmarks.json")
    return path

get_polygons_path €

get_polygons_path(path: str | PathLike[str]) -> Path
Source code in src/liblaf/melon/io/wrap/polygons/_utils.py
5
6
7
8
9
def get_polygons_path(path: str | os.PathLike[str]) -> Path:
    path: Path = Path(path)
    if path.suffix != ".json":
        return path.with_suffix(".polygons.json")
    return path

load_landmarks €

load_landmarks(
    path: str | PathLike[str],
) -> Float[ndarray, "N 3"]
Source code in src/liblaf/melon/io/wrap/landmarks/_reader.py
12
13
14
15
16
17
def load_landmarks(path: str | os.PathLike[str]) -> Float[np.ndarray, "N 3"]:
    path: Path = get_landmarks_path(path)
    if not path.exists():
        return np.empty((0, 3), dtype=float)
    data: list[dict[str, float]] = grapes.load(path)
    return np.asarray([[p["x"], p["y"], p["z"]] for p in data])

load_polygons €

load_polygons(path: StrPath) -> Integer[ndarray, ' N']
Source code in src/liblaf/melon/io/wrap/polygons/_reader.py
17
18
19
20
def load_polygons(path: StrPath) -> Integer[np.ndarray, " N"]:
    path: Path = get_polygons_path(path)
    data: list[int] = grapes.load(path)
    return np.asarray(data)

mesh_fix €

mesh_fix(
    mesh: Any,
    *,
    check: bool = True,
    joincomp: bool = False,
    remove_smallest_components: bool = True,
) -> PolyData
Source code in src/liblaf/melon/ext/_mesh_fix.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def mesh_fix(
    mesh: Any,
    *,
    check: bool = True,
    joincomp: bool = False,
    remove_smallest_components: bool = True,
) -> pv.PolyData:
    result: pv.PolyData
    if importlib.util.find_spec("pymeshfix") is not None:
        result = _pymeshfix(
            mesh,
            joincomp=joincomp,
            remove_smallest_components=remove_smallest_components,
        )
    elif shutil.which("MeshFix"):
        result = _mesh_fix_exe(mesh)
    else:
        raise NotImplementedError
    result = tri.fix_inversion(result)
    if check:
        assert tri.is_volume(result)
    mesh: pv.PolyData = io.as_polydata(mesh)
    result.field_data.update(mesh.field_data)
    return result

nearest €

nearest(
    source: Any,
    query: Any,
    algo: NearestPoint | None = None,
) -> NearestPointResult
nearest(
    source: Any, query: Any, algo: NearestPointOnSurface
) -> NearestPointOnSurfaceResult
nearest(
    source: Any, query: Any, algo: NearestAlgorithm
) -> NearestResult
Source code in src/liblaf/melon/proximity/_nearest.py
21
22
23
24
25
26
27
def nearest(
    source: Any, query: Any, algo: NearestAlgorithm | None = None
) -> NearestResult:
    if algo is None:
        algo = NearestPoint()
    prepared: NearestAlgorithmPrepared = algo.prepare(source)
    return prepared.query(query)

nearest_point_on_surface €

nearest_point_on_surface(
    source: Any,
    target: Any,
    *,
    distance_threshold: float = 0.1,
    ignore_orientation: bool = True,
    normal_threshold: float | None = 0.8,
) -> NearestPointOnSurfaceResult
Source code in src/liblaf/melon/proximity/_nearest_point_on_surface.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def nearest_point_on_surface(
    source: Any,
    target: Any,
    *,
    distance_threshold: float = 0.1,
    ignore_orientation: bool = True,
    normal_threshold: float | None = 0.8,
) -> NearestPointOnSurfaceResult:
    algorithm = NearestPointOnSurface(
        distance_threshold=distance_threshold,
        ignore_orientation=ignore_orientation,
        normal_threshold=normal_threshold,
    )
    prepared: NearestPointOnSurfacePrepared = algorithm.prepare(source)
    return prepared.query(target)

sample_barycentric_coords €

sample_barycentric_coords(
    shape: Sequence[int], *, seed: int | ArrayLike = 0
) -> Float[Array, "*N D"]
Source code in src/liblaf/melon/barycentric/_sample.py
 8
 9
10
11
12
13
14
15
16
17
18
def sample_barycentric_coords(
    shape: Sequence[int], *, seed: int | ArrayLike = 0
) -> Float[Array, "*N D"]:
    n_samples: list[int]
    dim: int
    *n_samples, dim = shape
    key: Key = jax.random.key(seed)
    coords: Float[Array, "*N D-1"] = jax.random.uniform(key, (*n_samples, dim - 1))
    coords: Float[Array, "*N D-1"] = jnp.sort(coords, axis=-1)
    coords: Float[Array, "*N D"] = jnp.diff(coords, axis=-1, prepend=0, append=1)
    return coords

save_landmarks €

save_landmarks(
    path: str | PathLike[str],
    points: Float[ArrayLike, "N 3"],
) -> None
Source code in src/liblaf/melon/io/wrap/landmarks/_writer.py
12
13
14
15
16
17
18
19
20
def save_landmarks(
    path: str | os.PathLike[str], points: Float[ArrayLike, "N 3"]
) -> None:
    path: Path = get_landmarks_path(path)
    points: Float[np.ndarray, "N 3"] = np.asarray(points)
    data: list[dict[str, float]] = [
        {"x": p[0], "y": p[1], "z": p[2]} for p in points.tolist()
    ]
    grapes.save(path, data)

save_polygons €

save_polygons(
    path: str | PathLike[str],
    polygons: Bool[ArrayLike, " N"]
    | Integer[ArrayLike, " N"],
) -> None
Source code in src/liblaf/melon/io/wrap/polygons/_writer.py
12
13
14
15
16
17
18
19
20
def save_polygons(
    path: str | os.PathLike[str],
    polygons: Bool[ArrayLike, " N"] | Integer[ArrayLike, " N"],
) -> None:
    path: Path = get_polygons_path(path)
    polygons = np.asarray(polygons)
    if np.isdtype(polygons.dtype, "bool"):
        polygons = np.flatnonzero(polygons)
    grapes.save(path, polygons.tolist())

tetwild €

tetwild(
    surface: Any,
    *,
    fix_winding: bool = True,
    lr: float | None = None,
    epsr: float | None = None,
    level: int | None = None,
    color: bool = False,
    coarsen: bool = False,
    csg: bool = False,
    **kwargs,
) -> UnstructuredGrid
Source code in src/liblaf/melon/ext/_tetwild.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def tetwild(
    surface: Any,
    *,
    fix_winding: bool = True,
    lr: float | None = None,
    epsr: float | None = None,
    level: int | None = None,
    color: bool = False,
    coarsen: bool = False,
    csg: bool = False,
    **kwargs,
) -> pv.UnstructuredGrid:
    surface: pv.PolyData = copy_structure(surface)  # pyright: ignore[reportAssignmentType]
    if shutil.which("fTetWild"):
        mesh: pv.UnstructuredGrid = _tetwild_exe(
            surface,
            lr=lr,
            epsr=epsr,
            level=level,
            no_color=not color,
            csg=csg,
            coarsen=coarsen,
            **kwargs,
        )
    else:
        raise NotImplementedError
    if fix_winding:
        mesh = tet.fix_winding(mesh)
    return mesh

transfer_tet_cell €

transfer_tet_cell(
    source: UnstructuredGrid,
    target: UnstructuredGrid,
    data: str | Iterable[str] | None = None,
    **kwargs,
) -> UnstructuredGrid
Source code in src/liblaf/melon/transfer/_tet_cell.py
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def transfer_tet_cell(
    source: pv.UnstructuredGrid,
    target: pv.UnstructuredGrid,
    data: str | Iterable[str] | None = None,
    **kwargs,
) -> pv.UnstructuredGrid:
    from liblaf.melon import tet

    data: Collection[str] = utils.as_names(data, source.cell_data)
    output: pv.UnstructuredGrid = transfer_tet_cell_to_point(
        source, target, data, **kwargs
    )
    output = tet.point_data_to_cell_data(output, data)
    for name in data:
        target.cell_data[name] = output.cell_data[name]
    return output

transfer_tet_cell_to_point €

transfer_tet_cell_to_point(
    source: UnstructuredGrid,
    target: UnstructuredGrid,
    data: str | Iterable[str] | None = None,
    *,
    categorical: bool = False,
    snap_to_closest_point: bool = True,
    tolerance: float | None = 1e-06,
    **kwargs,
) -> UnstructuredGrid
Source code in src/liblaf/melon/transfer/_tet_cell_to_point.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def transfer_tet_cell_to_point(
    source: pv.UnstructuredGrid,
    target: pv.UnstructuredGrid,
    data: str | Iterable[str] | None = None,
    *,
    categorical: bool = False,
    snap_to_closest_point: bool = True,
    tolerance: float | None = 1e-6,
    **kwargs,
) -> pv.UnstructuredGrid:
    data: Collection[str] = utils.as_names(data, source.cell_data)
    source_filtered: pv.UnstructuredGrid = pv.UnstructuredGrid()
    source_filtered.copy_structure(source)
    source_filtered.cell_data.update(
        {name: source.cell_data[name] for name in data}, copy=False
    )
    output: pv.UnstructuredGrid = target.sample(
        source_filtered,
        categorical=categorical,
        snap_to_closest_point=snap_to_closest_point,
        tolerance=tolerance,
        **kwargs,
    )  # pyright: ignore[reportAssignmentType]
    for name in data:
        target.point_data[name] = output.point_data[name]
    return target

transfer_tri_cell_to_point_category €

transfer_tri_cell_to_point_category(
    source: Any,
    target: Any,
    *,
    data: str | Iterable[str] | None = None,
    fill: Any | Mapping[str, Any] | None = None,
    nearest: NearestPointOnSurface | None = None,
) -> Any
Source code in src/liblaf/melon/transfer/_tri_cell_to_point_category.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def transfer_tri_cell_to_point_category(
    source: Any,
    target: Any,
    *,
    data: str | Iterable[str] | None = None,
    fill: Any | Mapping[str, Any] | None = None,
    nearest: NearestPointOnSurface | None = None,
) -> Any:
    source: pv.PolyData = io.as_polydata(source)
    source.triangulate(inplace=True)
    target: pv.PolyData = io.as_polydata(target).copy()
    data: Iterable[str] = _make_data_names(data, source)
    fill = _make_fill_mapping(fill)
    if nearest is None:
        nearest = NearestPointOnSurface()
    prepared: NearestPointOnSurfacePrepared = nearest.prepare(source)
    result: NearestPointOnSurfaceResult = prepared.query(target)
    any_missing: np.bool = np.any(result.missing)
    valid: Bool[np.ndarray, " T"] = ~result.missing
    indices: Integer[np.ndarray, "V 3"] = result.triangle_id[valid]
    for name in data:
        source_data: Integer[np.ndarray, "S ..."] = source.cell_data[name]
        target_data: Integer[np.ndarray, "T ..."]
        if any_missing:
            target_data = np.full(
                (target.n_points, *source_data.shape[1:]), fill[name], source_data.dtype
            )
            target_data[valid] = source_data[indices]
        else:
            target_data = source_data[indices]
        target.point_data[name] = target_data
    return target

transfer_tri_point €

transfer_tri_point(
    source: Any,
    target: Any,
    *,
    data: str | Iterable[str] | None = None,
    fill: Any | Mapping[str, Any] | None = None,
    nearest: NearestPointOnSurface | None = None,
) -> Any
Source code in src/liblaf/melon/transfer/_tri_point.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def transfer_tri_point(
    source: Any,
    target: Any,
    *,
    data: str | Iterable[str] | None = None,
    fill: Any | Mapping[str, Any] | None = None,
    nearest: NearestPointOnSurface | None = None,
) -> Any:
    source: pv.PolyData = io.as_polydata(source)
    target: pv.PolyData = io.as_polydata(target)
    data: Iterable[str] = _make_data_names(data, source)
    fill = _make_fill_mapping(fill)
    if nearest is None:
        nearest = NearestPointOnSurface()
    prepared: NearestPointOnSurfacePrepared = nearest.prepare(source)
    result: NearestPointOnSurfaceResult = prepared.query(target)
    any_missing: np.bool = np.any(result.missing)
    valid: Bool[np.ndarray, " T"] = ~result.missing
    indices: Integer[np.ndarray, "V 3"] = source.regular_faces[
        result.triangle_id[valid]
    ]
    barycentric: Float[np.ndarray, "V 3"] = tm.triangles.points_to_barycentric(
        source.points[indices], result.nearest[valid]
    )
    for name in data:
        source_data: Float[np.ndarray, "S ..."] = source.point_data[name]
        target_data_valid: Float[np.ndarray, "V ..."] = einops.einsum(
            barycentric, source_data[indices], "V B, V B ... -> V ..."
        )
        target_data: Float[np.ndarray, "T ..."]
        if any_missing:
            target_data = np.full(
                (target.n_points, *source_data.shape[1:]), fill[name], source_data.dtype
            )
            target_data[valid] = target_data_valid
        else:
            target_data = target_data_valid
        target.point_data[name] = target_data
    return target

transfer_tri_point_to_tet €

transfer_tri_point_to_tet(
    source: Any,
    target: Any,
    *,
    data: str | Iterable[str] | None = None,
    fill: Any | Mapping[str, Any] | None = None,
    nearest: NearestPointOnSurface | None = None,
    point_id: str | None = None,
) -> UnstructuredGrid
Source code in src/liblaf/melon/transfer/_tri_point_to_tet.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def transfer_tri_point_to_tet(
    source: Any,
    target: Any,
    *,
    data: str | Iterable[str] | None = None,
    fill: Any | Mapping[str, Any] | None = None,
    nearest: NearestPointOnSurface | None = None,  # noqa: ARG001
    point_id: str | None = None,
) -> pv.UnstructuredGrid:
    source: pv.PolyData = io.as_polydata(source)
    target: pv.UnstructuredGrid = io.as_unstructured_grid(target)
    data: Iterable[str] = _make_data_names(data, source)
    fill: Mapping[str, Any] = _make_fill_mapping(fill)
    result: pv.UnstructuredGrid
    if point_id is None:
        raise NotImplementedError
    result = _transfer_with_point_id(
        source, target, data=data, point_id=point_id, fill=fill
    )
    return result