<jupyter_start><jupyter_text>The source of information is [here](https://matplotlib.org/matplotblog/posts/custom-3d-engine/)<jupyter_code>%matplotlib inline

import matplotlib
import matplotlib.pyplot
import numpy as np
import requests
import tqdm<jupyter_output><empty_output><jupyter_text>Load *simplified* version of Stanford's bunny<jupyter_code>class GeometricVertex(object):
    @staticmethod
    def from_bytes(bs):
        return np.array([float(x) for x in bs.split()[1:4]])


class Face(object):
    @staticmethod
    def from_bytes(bs):
        return np.array([int(x) for x in bs.split()[1:4]])
r = requests.get('https://matplotlib.org/matplotblog/posts/custom-3d-engine/bunny.obj')

assert r.status_code == 200

V = []
F = []
for row in tqdm.tqdm(r.content.split(b'\r\n')):
    if row.startswith(b'#'):
        print(row.decode('utf-8'))
    elif row.startswith(b'v'):
        V.append(GeometricVertex.from_bytes(row))
    elif row.startswith(b'f'):
        F.append(Face.from_bytes(row))

V = np.array(V)
F = np.array(F) - 1
V = (V-(V.max(0)+V.min(0))/2)/max(V.max(0)-V.min(0))
fig = matplotlib.pyplot.figure(figsize=(6,6))
ax = fig.add_axes([0,0,1,1], xlim=[-1,+1], ylim=[-1,+1],
                  aspect=1, frameon=False)
collection = matplotlib.collections.PolyCollection(V[F][:,:,:2], closed=True, linewidth=0.1,
                            facecolor="None", edgecolor="black")
ax.add_collection(collection)
matplotlib.pyplot.show()
def frustum(left, right, bottom, top, znear, zfar):
    M = np.zeros((4, 4), dtype=np.float32)
    M[0, 0] = +2.0 * znear / (right - left)
    M[1, 1] = +2.0 * znear / (top - bottom)
    M[2, 2] = -(zfar + znear) / (zfar - znear)
    M[0, 2] = (right + left) / (right - left)
    M[2, 1] = (top + bottom) / (top - bottom)
    M[2, 3] = -2.0 * znear * zfar / (zfar - znear)
    M[3, 2] = -1.0
    return M

def perspective(fovy, aspect, znear, zfar):
    h = np.tan(0.5*np.radians(fovy)) * znear
    w = h * aspect
    return frustum(-w, w, -h, h, znear, zfar)
V = V - (0,0,3.5)
V = np.c_[V, np.ones(len(V))] @ perspective(25,1,1,100).T
V /= V[:,3].reshape(-1,1)
fig = matplotlib.pyplot.figure(figsize=(6,6))
ax = fig.add_axes([0,0,1,1], xlim=[-1,+1], ylim=[-1,+1],
                  aspect=1, frameon=False)
collection = matplotlib.collections.PolyCollection(V[F][:,:,:2], closed=True, linewidth=0.1,
                            facecolor="None", edgecolor="black")
ax.add_collection(collection)
matplotlib.pyplot.show()
def translate(x, y, z):
    return np.array([[1, 0, 0, x],
                     [0, 1, 0, y],
                     [0, 0, 1, z],
                     [0, 0, 0, 1]], dtype=float)

def xrotate(theta):
    t = np.pi * theta / 180
    c, s = np.cos(t), np.sin(t)
    return np.array([[1, 0,  0, 0],
                     [0, c, -s, 0],
                     [0, s,  c, 0],
                     [0, 0,  0, 1]], dtype=float)

def yrotate(theta):
    t = np.pi * theta / 180
    c, s = np.cos(t), np.sin(t)
    return  np.array([[ c, 0, s, 0],
                      [ 0, 1, 0, 0],
                      [-s, 0, c, 0],
                      [ 0, 0, 0, 1]], dtype=float)
model = xrotate(20) @ yrotate(45) 
view  = translate(0,0,-3.5)
proj  = perspective(25, 1, 1, 100) 
MVP   = proj  @ view  @ model 
V = np.c_[V, np.ones(len(V))] @ MVP.T
V /= V[:,3].reshape(-1,1)
fig = matplotlib.pyplot.figure(figsize=(6,6))
ax = fig.add_axes([0,0,1,1], xlim=[-1,+1], ylim=[-1,+1],
                  aspect=1, frameon=False)
collection = matplotlib.collections.PolyCollection(V[F][:,:,:2], closed=True, linewidth=0.1,
                            facecolor="None", edgecolor="black")
ax.add_collection(collection)
matplotlib.pyplot.show()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.19520732364028, 'fraction_numerical': 0.04334948842218632, 'mean_word_length': 2.6818632309217048, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '23379', 'license': 'MIT', 'n_tokens_mistral': 1588, 'n_tokens_neox': 1492, 'n_words': 299, 'path': 'Custom_3d_engine_in_Matplotlib.ipynb', 'repo_name': 'd61h6k4/notebooks', 'size': '3714'}