Pascal Lines and Brianchon Lines

../../../_images/render2.png

In this example we are going to create and display Pascal lines, their intersections, Brianchon lines and diagonals on a quadric surface. You can see the full code at pascal.py.

Setup

We start off by importing the necessary libraries.

import bpy
import numpy as np

import ddg
import ddg.datastructures.nets.utils
import ddg.geometry.subspaces as subspaces
import ddg.visualization.blender.curve as cutils
from ddg.geometry.intersection import join, meet
from ddg.visualization.blender import curve, mesh

We are also going to clear the whole scene.

# Clear existing mesh and curve objects in Blender
mesh.clear()
curve.clear()

Functions

Now we are going to define two helper functions which we will use later. First, however, we are going to create a square which we will use as a profile curve.

# Create a small square to be used as a profile curve for lines
profile_curve = cutils.create_curve(
    0.01 * np.array([[0.5, 0, 0], [0.5, 1, 0], [-0.5, 1, 0], [-0.5, 0, 0]])
)
cutils.add_curve(
    profile_curve, name="ProfileCurve", collection=bpy.data.collections.get("aux")
)

Now we will define the first function which will draw projective lines.

# Helper function to draw projective lines
def draw_line(
    line,
    material=None,
    sampling=1,
    collection=None,
    attributes={},
):
    line_net = ddg.to_smooth_net(line, convex=True, affine=True)
    line_net = ddg.sample_smooth_net(line_net, sampling)
    line_net = ddg.datastructures.nets.utils.embed(line_net)
    return ddg.to_blender_object(
        line_net,
        material=material,
        collection=bpy.data.collections.get(collection),
        attributes=attributes,
    )


Next we define the helper function to draw points.

# Helper function to draw points in projective space
def draw_point(
    point,
    material=None,
    collection=None,
    sphere_radius=0.03,
):
    point_net = ddg.to_smooth_net(point, convex=True, affine=True)
    point_net = ddg.datastructures.nets.utils.embed(point_net)
    return ddg.to_blender_object(
        point_net,
        material=material,
        collection=bpy.data.collections.get(collection),
        sphere_radius=sphere_radius,
    )


Math

With the set up complete, we can now calculate the intersections. First we have to create a quadric.

# Create a quadric surface with specific coefficients
quadric = ddg.geometry.quadrics.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 = [subspaces.subspace_from_affine_points(co) for co in pt_coords]

With this we can create the actual lines and intersections.

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

# Find intersections of Pascal lines
pascal_intersections = [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 = join(pascal_intersections[0], pascal_intersections[1])

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

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

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

# Dualize Pascal line to create a Brianchon triple intersection point
brianchon_triple_intersection = pascal_line.dualize()

Visualization

Finally we can visualize all of this in Blender. We start by creating the quadric Blender object.

# Sample and visualize the quadric surface
quadric_dnet = ddg.sample_smooth_net(quadric_net, [100, "t"])
quadric_dnet = ddg.datastructures.nets.utils.embed(quadric_dnet)
ddg.to_blender_object(
    quadric_dnet,
    curve_properties={"bevel_depth": 0.005},
    material="black",
    collection=bpy.data.collections.get("Pascal"),
)

Next we dualize the quadric.

# Dualize the quadric surface and visualize it
dual_quadric_net = ddg.to_smooth_net(quadric.dualize(), affine=True)
dual_quadric_dnet = ddg.sample_smooth_net(dual_quadric_net, [100, "t"])
dual_quadric_dnet = ddg.datastructures.nets.utils.embed(dual_quadric_dnet)
ddg.to_blender_object(
    dual_quadric_dnet,
    curve_properties={"bevel_depth": 0.005},
    material="black",
    collection=bpy.data.collections.get("Brianchon"),
)

And finally we visualize the lines, intersections and points.

# Visualize Pascal lines, intersections, and points
for line in pascal_lines:
    draw_line(line, collection="Pascal", material="blue")
draw_line(
    pascal_line, material="yellow", collection="Pascal", attributes={"name": "THE line"}
)
for point in pascal_intersections:
    draw_point(point, collection="Pascal", material="red")
for point in points:
    draw_point(point, collection="Pascal", material="green")

# Visualize Brianchon lines, diagonals, intersections, and the triple intersection point
for line in brianchon_lines:
    draw_line(line, collection="Brianchon", material="green")
for line in brianchon_diagonals:
    draw_line(
        line, collection="Brianchon", material="red", attributes={"name": "diagonal"}
    )
for point in brianchon_intersections:
    draw_point(point, collection="Brianchon", material="blue")
draw_point(brianchon_triple_intersection, collection="Brianchon", material="yellow")