# Decimation
# ==========

# Decimate a mesh.
import time

import numpy as np
import pyvista as pv
from pyvista import examples

mesh = examples.download_face()

# Define a camera position that shows this mesh properly
cpos = pv.CameraPosition(
    position=(0.4, -0.07, -0.31), focal_point=(0.05, -0.13, -0.06), viewup=(-0.1, 1, 0.08)
)
plot_kwargs = dict(show_edges=True, color=True)

# Preview the mesh
mesh.plot(cpos=cpos, **plot_kwargs)

# Now let's define a target reduction and compare the
# `pyvista.PolyDataFilters.decimate()` and
# `pyvista.PolyDataFilters.decimate_pro()` filters.
target_reduction = 0.7
print(f'Reducing {target_reduction * 100.0} percent out of the original mesh')

decimated = mesh.decimate(target_reduction)

decimated.plot(cpos=cpos, **plot_kwargs)

pro_decimated = mesh.decimate_pro(target_reduction, preserve_topology=True)

pro_decimated.plot(cpos=cpos, **plot_kwargs)

# Side by side comparison:
datasets = {
    'Input': mesh,
    'Decimated': decimated,
    'Pro Decimated': pro_decimated,
}

pv.plot_compare(
    datasets,
    label_size=24,
    cpos=cpos,
    **plot_kwargs,
)

# Binned Decimation
# -----------------

# `pyvista.PolyDataFilters.decimate_binned()` splits the bounds of a mesh into
# a grid of bins and merges the points in each bin into one. It does not preserve
# topology, but it is orders of magnitude faster than the filters above, which makes
# it a good fit for previews of very large surfaces.
# Decimate a larger mesh to roughly 5,000 points.
armadillo = examples.download_armadillo()
armadillo_cpos = pv.CameraPosition(
    position=(161.5, 82.1, -330.2), focal_point=(-4.3, 24.5, -1.6), viewup=(-0.1, 1, 0.12)
)
binned = armadillo.decimate_binned(target_points=5_000)
binned.plot(cpos=armadillo_cpos, show_edges=True)

# Compare each `point_generation` option with uniform and per-axis `divisions`.
sphere = pv.Sphere(theta_resolution=200, phi_resolution=200)
point_generations = {}
for divisions in (8, (4, 8, 16)):
    for point_generation in ('bin_points', 'bin_centers', 'bin_averages'):
        label = f'{point_generation}\ndivisions={divisions}'
        point_generations[label] = sphere.decimate_binned(
            divisions=divisions, point_generation=point_generation
        )


pv.plot_compare(
    point_generations, shape=(2, 3), show_edges=True, cpos='iso', window_size=[1200, 800]
)

# Time each of the three surface decimation filters on the same reduction.
reduction = 1 - 5_000 / armadillo.n_points
filters = {
    'decimate': lambda: armadillo.decimate(reduction),
    'decimate_pro': lambda: armadillo.decimate_pro(reduction),
    'decimate_binned': lambda: armadillo.decimate_binned(reduction),
}

results = {}
for name, run in filters.items():
    start = time.perf_counter()
    results[name] = run()
    print(f'{name}: {time.perf_counter() - start:.3f} s')

# Plot the results side by side.
pv.plot_compare(results, cpos=armadillo_cpos, window_size=[1200, 500], show_edges=True)

# Decimate PolyLine Mesh
# ----------------------

# Generate a fairly slow spiral polyline mesh.
n_points = 100
n_rotations = 5
phi = np.linspace(0, 2 * np.pi * n_rotations, n_points)
ratio = 1.1
r = ratio ** (phi)

points = np.zeros((n_points, 3))
points[:, 0] = r * np.cos(phi)
points[:, 1] = r * np.sin(phi)

spiral = pv.PolyData(points, lines=np.append([n_points], np.arange(n_points)))

# Decimate using `pyvista.PolyDataFilters.decimate_polyline()` filter by
# targets of 50% and 80%. To avoid errors of quickly changing features, also
# use the `maximum_error` parameter. It is in units of fraction of the largest
# length of the bounding box. Note that it limits the level of reduction
# achieved.
polylines = {
    '50%': spiral.decimate_polyline(0.5),
    '80%': spiral.decimate_polyline(0.8),
    '80%, maximum_error=0.5': spiral.decimate_polyline(0.8, maximum_error=0.5),
}
print(f'Original # of points: {spiral.n_points}')
for name, decimated in polylines.items():
    print(f'{name}: {decimated.n_points}')

# Compare each result, in red, with the original spiral, in black. The
# decimation looks OK at 50%. At 80% the structure of the inner part of the
# spiral is completely lost, and `maximum_error` captures it adequately.
pv.plot_compare(
    polylines,
    reference_mesh=spiral,
    reference_kwargs=dict(color='k', line_width=2),
    cpos='xy',
    color='r',
    line_width=5,
    window_size=[1200, 400],
)

# ----------------------------------------------------------------------
# Generated by sphinx-examples-as-code https://github.com/pyvista/sphinx-examples-as-code
