"""Example: Pascal lines and Brianchon lines.

Create and display Pascal lines,
their intersections, and Brianchon lines
and diagonals on a quadric surface.
"""

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

# [setup-1]
import bpy
import numpy as np

import ddg

# [setup-1]

# [setup-2]
# Clear existing mesh and curve objects in Blender
ddg.blender.mesh.clear()
ddg.blender.curve.clear()
# [setup-2]


#############################################
# Functions
#############################################

# [functions-1]
# Helper function to draw projective lines
def draw_line(
    line,
    name,
    material=None,
    collection=None,
):
    bobj = ddg.blender.convert(
        line,
        name,
        material=material,
        collection=bpy.data.collections.get(collection),
    )
    bobj.data.bevel_depth = 0.015
    return bobj


# [functions-1]


#############################################
# Math
#############################################

# [math-1]
# Create a quadric surface with specific coefficients
quadric = ddg.geometry.Quadric(np.diag([1, 3, -1]))
quadric_net = ddg.to_smooth_net(quadric, affine=True)

# Generate parameters for points on the quadric surface
# (order matters)
pt_parameters = [(k + 0.2) * 2 * np.pi for k in [0, 0.125, 0.25, 0.375, 0.5, 0.75]]
pt_coords = [quadric_net(t) for t in pt_parameters]
points = [ddg.geometry.subspace_from_affine_points(co) for co in pt_coords]
# [math-1]

# [math-2]
# Create Pascal lines by joining pairs of points
pascal_lines = [ddg.geometry.join(points[i], points[(i + 1) % 6]) for i in range(6)]

# Find intersections of Pascal lines
pascal_intersections = [
    ddg.geometry.meet(pascal_lines[i], pascal_lines[i + 3]) for i in range(3)
]

# Create a Pascal line that goes through two of the intersections
pascal_line = ddg.geometry.join(pascal_intersections[0], pascal_intersections[1])

# Create Brianchon lines by dualizing the points
brianchon_lines = [p.dual() for p in points]

# Dualize Pascal lines to create Brianchon intersections
brianchon_intersections = [l.dual() for l in pascal_lines]

# Dualize Pascal intersection points to create Brianchon diagonals
brianchon_diagonals = [p.dual() for p in pascal_intersections]

# Dualize Pascal line to create a Brianchon triple intersection point
brianchon_triple_intersection = pascal_line.dual()
# [math-2]

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

pascal_collection = ddg.blender.collection.collection("Pascal")
brianchon_collection = ddg.blender.collection.collection("Brianchon")
black = ddg.blender.material.material(name="black", color=(0, 0, 0))
red = ddg.blender.material.material(name="red", color=(1, 0, 0))
green = ddg.blender.material.material(name="green", color=(0, 1, 0))
blue = ddg.blender.material.material(name="blue", color=(0, 0, 1))
yellow = ddg.blender.material.material(name="yellow", color=(1, 1, 0))

# [visualization-1]
# Sample and visualize the quadric surface
bobj = ddg.blender.convert(
    quadric,
    "quadric",
    material=black,
    collection=pascal_collection,
)
bobj.data.bevel_depth = 0.005
# [visualization-1]

# [visualization-2]
# Dualize the quadric surface and visualize it
bobj = ddg.blender.convert(
    quadric.dual(),
    "dual_quadric",
    material=black,
    collection=brianchon_collection,
)
bobj.data.bevel_depth = 0.005
# [visualization-2]

# [visualization-3]
# Visualize Pascal lines, intersections, and points
for (i, line) in enumerate(pascal_lines):
    draw_line(line, f"helper_line_{i}", collection="Pascal", material=blue)
draw_line(pascal_line, "THE line", material=yellow, collection="Pascal")
for (i, point) in enumerate(pascal_intersections):
    ddg.blender.vertices(
        point,
        f"pascal_intersections_{i}",
        radius=0.03,
        collection=bpy.data.collections.get("Pascal"),
        material=red,
    )
for (i, point) in enumerate(points):
    ddg.blender.vertices(
        point,
        f"points_{i}",
        radius=0.03,
        collection=bpy.data.collections.get("Pascal"),
        material=green,
    )

# Visualize Brianchon lines, diagonals, intersections, and the triple intersection point
for (i, line) in enumerate(brianchon_lines):
    draw_line(line, f"brianchon_lines_{i}", collection="Brianchon", material=green)
for (i, line) in enumerate(brianchon_diagonals):
    draw_line(line, f"brianchon_diagonals_{i}", collection="Brianchon", material=red)
for (i, point) in enumerate(brianchon_intersections):
    ddg.blender.vertices(
        point,
        f"brianchon_intersections_{i}",
        radius=0.03,
        collection=bpy.data.collections.get("Brianchon"),
        material=blue,
    )
ddg.blender.vertices(
    brianchon_triple_intersection,
    "brianchon_triple_intersection",
    radius=0.03,
    collection=bpy.data.collections.get("Brianchon"),
    material=yellow,
)
# [visualization-3]

# [camera-and-lighting]
cam = ddg.blender.camera.camera("Camera", "ORTHO", (0, 0, 1))
bpy.context.scene.camera = cam
cam.data.ortho_scale = 10
bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[0].default_value = (
    1,
    1,
    1,
    1,
)
bpy.data.worlds["World"].node_tree.nodes["Background"].inputs[1].default_value = 1
# [camera-and-lighting]
