#!/usr/bin/env python3 from __future__ import annotations from typing import TypeAlias from random import uniform import torch Point: TypeAlias = torch.FloatTensor Vector: TypeAlias = Point Color: TypeAlias = torch.FloatTensor def indexize_tensor(tensor, eps=1e-5): """Take tensor of quads to flat list of points plus index map.""" tensor_shape = tensor.shape tensor = tensor.view(-1, tensor_shape[-1]) # Round tensor elements to handle floating point precision errors tensor = torch.round(tensor / eps) * eps # Compute unique rows and their indices tensor, inverse_indices = tensor.unique(dim=0, return_inverse=True) # Compute index tensor index_tensor = inverse_indices.view(tensor_shape[:-1]) return tensor, index_tensor def ferguson_to_bezier(corner_points, tangent_vectors): """Take Ferguson patch to Bezier representation""" # Assume corner_points is a list of 4 points [P0, P1, P2, P3] and # tangent_vectors is a list of 4 vectors [V0, V1, V2, V3] # Bezier control points bezier_points = [None]*12 # Use tangent vectors to determine interior points and place them in the order for i in range(4): bezier_points[3*i] = corner_points[i] bezier_points[3*i + 1] = corner_points[i] + tangent_vectors[i]/3 bezier_points[3*i + 2] = corner_points[i] + 2*tangent_vectors[i]/3 return bezier_points class Mesh: def __init__(self, points, tangents, colors): self.fpoints, self.ipoints = indexize_tensor(points) self.ftangents, self.itangents = indexize_tensor(tangents) self.colors = colors @classmethod def grid(cls, width, height): dx = 1.0 / width dy = 1.0 / height points = [] tangents = [] colors = [] for i in range(width): for j in range(height): x1, y1 = i * dx, j * dy x2, y2 = (i+1) * dx, (j+1) * dy # Clockwise order: bottom left, bottom right, top right, top left square = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]] points.append(square) tangents.append([[0.0, 0.0] for _ in range(4)]) colors.append([uniform(0, 1) for _ in range(3)] + [1.0]) return cls(torch.tensor(points), torch.tensor(tangents), torch.tensor(colors))