"""Example: Shadow of a quadric

Visualization of a shadow of a quadric cast by a light source.
Note that materials and world settings are saved in the
corresponding .blend file.
"""

#############################################
# Setup
#############################################

# [setup-1]
# Import necessary libraries and modules
import numpy as np

import ddg
import ddg.geometry as geo
from ddg.geometry.intersection import intersect
from ddg.math.projective import dehomogenize, homogenize
from ddg.visualization.blender.scene import clear

# [setup-1]

# [setup-2]
# Clear the Blender scene
clear()
# [setup-2]


#############################################
# Constants
#############################################

# [constants-1]
# example, visualize_generators = 'ellipsoid', False
example, visualize_generators = "one_sheeted_hyperboloid", True

# Define parameters for the quadric, the location of the light source
# and the normal and level of the projection plane
if example == "ellipsoid":
    a, b, c, d = 4, 0.5, 1, -2
    light_x, light_y, light_z = 1, 4, 4
    normal_x, normal_y, normal_z, level = 3, 1, 4, -10
if example == "one_sheeted_hyperboloid":
    a, b, c, d = 1, 0.5, -1, -1
    light_x, light_y, light_z = 1, 1.2, 4
    normal_x, normal_y, normal_z, level = 3, 1, 4, -15

# Define the position of the light source and normalize the planes
# normal
light = np.array([light_x, light_y, light_z])
normal_tmp = np.array([normal_x, normal_y, normal_z])
normal = normal_tmp / np.linalg.norm(normal_tmp)
# [constants-1]

# [constants-2]
# Sampling for Blender objects
quadric_sampling = [0.1, 100, "c"]
projection_plane_sampling = [1, 100, "c"]
polar_plane_sampling = [1, 100, "c"]
polar_plane_intersection_sampling = [1, 100, "c"]
t_cone_sampling = [0.1, 100, "c"]
projection_sampling = [0.03, 100, "c"]

if example == "ellipsoid":
    bounding_box_for_projection_plane = np.array([15, 25, 25])
    camera_loc, camera_look_at = [31, 0, -5], [0, -5, -3]

if example == "one_sheeted_hyperboloid":
    bounding_box_for_projection_plane = 5 * np.array([15, 25, 25])
    camera_loc, camera_look_at = [18, -65, 10], [0, 0, -5]

if visualize_generators:
    density_of_generators = 1 / 12 * np.pi
    generators_sampling = 0.1
    generators_bounding_box = [100, 100, 5]
# [constants-2]


#############################################
# Main Construction
#############################################

# [main-construction-1]
# Create a quadric
quadric = geo.quadrics.Quadric(np.diag([a, b, c, d]))
# [main-construction-1]

# [main-construction-2]
# Construct the touching cone of the light source w.r.t. the quadric
t_cone = geo.quadrics.touching_cone(homogenize(light), quadric)
t_cone = t_cone.normalize(affine=True)
# [main-construction-2]

# [main-construction-3]
# Create a polar plane and find its intersection with the quadric
light_p = geo.subspaces.Point(homogenize(light))
polar_plane = geo.quadrics.polarize(light_p, quadric)
intersection = intersect(polar_plane, quadric)
# [main-construction-3]

# [main-construction-4]
# Create a projection plane
projection_plane = geo.subspaces.hyperplane_from_normal(normal, level=level)
# Intersect the touching cone and the projection plane
projection = intersect(t_cone, projection_plane)
# [main-construction-4]


#############################################
# Visualization
#############################################

# [visualization-1]
# Create a Blender object for the quadric
quadric_bobj = ddg.to_blender_object_helper(
    quadric,
    shade_smooth=True,
    sampling=quadric_sampling,
    bounding_box=[None, None, 5],
    material="quadric",
    name="quadric",
)
# Visualize the generators if necessary
if visualize_generators:
    discrete_intersection = ddg.sample_smooth_net(
        ddg.to_smooth_net(intersection), density_of_generators
    )
    generators = [
        intersect(
            quadric,
            geo.quadrics.polarize(
                geo.subspaces.subspace_from_affine_points(discrete_intersection.fct(i)),
                quadric,
            ),
        )
        for i, in discrete_intersection.domain.traverser
    ]
    generator_bobjs = [
        ddg.to_blender_object_helper(
            g,
            sampling=generators_sampling,
            bounding_box=generators_bounding_box,
            material="quadric_emission",
            name="generator",
        )
        for g in generators
    ]
# [visualization-1]

# [visualization-2]
# Create a camera
camera_bobj = ddg.visualization.blender.camera.camera(location=camera_loc)
ddg.visualization.blender.camera.look_at_point(camera_bobj, camera_look_at)
# Create a light
light_bobj = ddg.visualization.blender.light.light(type_="SPOT", location=light)
if example == "ellipsoid":
    # Rotate the light source to match the touching cone orientation
    axis = ddg.geometry.quadrics.axis(t_cone)
    two_points = ddg.geometry.conversion.quadric_to_subspaces(
        ddg.geometry.intersection.intersect(quadric, axis)
    )
    ddg.visualization.blender.light.look_at_point(
        light_bobj, two_points[0].affine_point
    )
# Set light properties
light_bobj.data.spot_size = np.pi
light_bobj.scale *= 10
light_bobj.data.energy = 4000
light_bobj.data.spot_blend = 0
light_bobj.data.shadow_soft_size = 0.01
# [visualization-2]


# [visualization-3]
# Create a tangent cone
t_cone_snet = ddg.to_smooth_net(t_cone)
t_cone_snet.domain.intervals[1] = [0, 200]
t_cone_dnet = ddg.sample_smooth_net(t_cone_snet, sampling=t_cone_sampling)
t_cone_bobj = ddg.to_blender_object_helper(
    t_cone_snet, sampling=t_cone_sampling, material="tcone", name="touching_cone"
)
# [visualization-3]

# [visualization-4]
# Projection plane and intersection
center = intersect(
    geo.subspaces.subspace_from_affine_points(np.array([0, 0, 0]), light),
    projection_plane,
)
projection_plane_centered = projection_plane.center(center)
bb_trafo = lambda bmesh: ddg.visualization.blender.bmesh.cut_bounding_box(
    bmesh, bounding_box_for_projection_plane, dehomogenize(center.point)
)
projection_plane_bobj = ddg.to_blender_object_helper(
    projection_plane_centered,
    sampling=projection_plane_sampling,
    material="projection_plane",
    name="projection_plane",
    bmesh_transformations=[bb_trafo],
)
projection_bobj = ddg.to_blender_object_helper(
    projection, sampling=projection_sampling, material="projection", name="projection"
)
# [visualization-4]

# [visualization-5]
# Polar plane and intersection
polar_plane_orth = ddg.geometry.subspaces.orthonormalize_subspace(polar_plane)
polar_plane_bobj = ddg.to_blender_object_helper(
    polar_plane_orth,
    sampling=polar_plane_sampling,
    material="polar_plane",
    name="polar_plane",
)

polar_plane_intersection_bobj = ddg.to_blender_object_helper(
    intersection,
    sampling=polar_plane_intersection_sampling,
    material="polar",
    name="shadow_line",
    bounding=500,
)
# [visualization-5]
