Wednesday, March 4, 2026

Soccer ball

import turtle, math


screen = turtle.Screen()

screen.bgcolor("#1a1a2e")

screen.title("Soccer Ball")

screen.setup(700, 700)

screen.tracer(0)


t = turtle.Turtle()

t.hideturtle(); t.speed(0)


CX, CY, R = 0, 0, 220   # ball centre & radius in pixels


# ── Build 60 correct vertices of a truncated icosahedron ──────────

phi = (1 + math.sqrt(5)) / 2


def norm3(v):

  x, y, z = v

  d = math.sqrt(x*x+y*y+z*z)

  return (x/d,y/d,z/d)


def dot3(a,b):

  return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]


def cross3(a,b):

  return(a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0])


def sub3(a,b):

  return(a[0]-b[0],a[1]-b[1],a[2]-b[2])


raw = set()

for s1 in (-1,1):

  for s2 in (-1,1):

    raw.add((0,   s1,      s2*3*phi))

    raw.add((s2*3*phi, 0,    s1))

    raw.add((s1,  s2*3*phi,  0))

    for s3 in (-1,1):

      raw.add((s1,    s2*(2+phi),   s3*2*phi))

      raw.add((s3*2*phi,  s1,       s2*(2+phi)))

      raw.add((s2*(2+phi),s3*2*phi,   s1))

      raw.add((s1*2,    s2*(1+2*phi), s3*phi))

      raw.add((s3*phi,  s1*2,     s2*(1+2*phi)))

      raw.add((s2*(1+2*phi), s3*phi,  s1*2))


verts = [norm3(v) for v in raw]   # 60 unit-sphere vertices

N = len(verts)


# ── Adjacency (shortest edge) 

def d3(a,b): return math.sqrt(sum((a[k]-b[k])**2 for k in range(3)))


min_d = min(d3(verts[i],verts[j]) for i in range(N) for j in range(i+1,N))

ETOL  = 0.02

adj   = [[] for _ in range(N)]

for i in range(N):

  for j in range(i+1,N):

    if abs(d3(verts[i],verts[j])-min_d) < ETOL:

      adj[i].append(j); adj[j].append(i)


# ── Face enumeration via half-edge walk 

def next_vert(i, j):

  """Return k: the next vertex CCW around the face containing directed edge i→j."""

  others = [k for k in adj[j] if k != i]

  if len(others) == 1:

    return others[0]

  nj = verts[j]

  raw_in  = sub3(verts[i], nj)

  proj_in = dot3(raw_in, nj)

  tang_in = (raw_in[0]-proj_in*nj[0], raw_in[1]-proj_in*nj[1], raw_in[2]-proj_in*nj[2])

  mag = math.sqrt(dot3(tang_in,tang_in))

  if mag > 1e-10: tang_in = tuple(c/mag for c in tang_in)


  best, best_k = None, None

  for k in others:

    raw_out  = sub3(verts[k], nj)

    proj_out = dot3(raw_out, nj)

    tang_out = (raw_out[0]-proj_out*nj[0], raw_out[1]-proj_out*nj[1], raw_out[2]-proj_out*nj[2])

    cr   = cross3(tang_in, tang_out)

    sine = dot3(cr, nj)

    cosine = dot3(tang_in, tang_out)

    angle = math.atan2(sine, cosine)

    if best is None or angle < best:

      best, best_k = angle, k

  return best_k


face_set, face_list = set(), []

for i in range(N):

  for j in adj[i]:

    face = [i, j]

    ci, cj = i, j

    for _ in range(7):

      k = next_vert(ci, cj)

      if k == i: break

      face.append(k); ci, cj = cj, k

    key = frozenset(face)

    if key not in face_set and len(face) in (5, 6):

      face_set.add(key); face_list.append(face)


# ── Rotation & projection 

AY, AX = 0.52, -0.28


def rotate(v, ay, ax):

  x,y,z = v

  x2 =  x*math.cos(ay)+z*math.sin(ay); z2 = -x*math.sin(ay)+z*math.cos(ay); y2=y

  y3 =  y2*math.cos(ax)-z2*math.sin(ax); z3 = y2*math.sin(ax)+z2*math.cos(ax)

  return (x2, y3, z3)


def project(v):

  x,y,z = rotate(v, AY, AX)

  fov   = 3.8

  s   = R * fov / (fov + z + 1)

  return (CX + x*s, CY + y*s, z)


# ── Lighting / shading 

LIGHT = norm3((0.5, 0.7, 1.0))


def shade(face_idxs, is_pent):

  fv  = [verts[i] for i in face_idxs]

  cn  = norm3(tuple(sum(v[k] for v in fv)/len(fv) for k in range(3)))

  rcn = rotate(cn, AY, AX)

  diff = max(0.0, dot3(rcn, LIGHT))

  spec = max(0.0, diff)**12 * 0.5

  if is_pent:

    bright = 0.15 + 0.65*diff + spec

    base   = (18, 18, 18)

  else:

    bright = 0.38 + 0.55*diff + spec

    base   = (248, 248, 248)

  r = int(min(255, base[0]*bright + 255*spec))

  g = int(min(255, base[1]*bright + 255*spec))

  b = int(min(255, base[2]*bright + 255*spec))

  return f"#{r:02x}{g:02x}{b:02x}"


# ── Drawing helpers 

def draw_disk(x, y, r, fill, pen, lw=2):

  t.penup()

  t.goto(x, y-r)

  t.pendown()

  t.pencolor(pen)

  t.pensize(lw)

  t.fillcolor(fill)

  t.begin_fill()

  t.end_fill()


def draw_poly(pts, fill, pen, lw):

  t.penup(); t.goto(pts[0]); t.pendown()

  t.pencolor(pen); t.pensize(lw); t.fillcolor(fill)

  t.begin_fill()

  for p in pts[1:]: t.goto(p)

  t.goto(pts[0]); t.end_fill()


def in_ball(px, py):

  return (px-CX)**2+(py-CY)**2 <= R**2


# ── RENDER 

# Ground shadow

t.penup(); t.pencolor("#0c0c1a"); t.fillcolor("#0c0c1a")

sw, sh = 170, 22

t.goto(CX-sw, CY-R-14); t.pendown(); t.begin_fill()

for s in range(61):

  a = math.pi + s*math.pi/60

  t.goto(CX+sw*math.cos(a), CY-R-14+sh*math.sin(a))

t.end_fill()


# White ball base

draw_disk(CX, CY, R, "white", "#1a1a1a", lw=3)


# Sort faces back→front and draw

sorted_faces = sorted(face_list, key=lambda f: sum(project(verts[i])[2] for i in f)/len(f))


for face in sorted_faces:

  projs  = [project(verts[i]) for i in face]

  avg_z  = sum(p[2] for p in projs)/len(projs)

  if avg_z < -0.10:

    continue              # back-face cull

  pts2d  = [(p[0],p[1]) for p in projs]

  if not any(in_ball(px,py) for px,py in pts2d):

    continue


  is_pent = len(face) == 5

  col   = shade(face, is_pent)

  pen   = "#000000" if is_pent else "#555555"

  lw    = 2.5 if is_pent else 1.2

  draw_poly(pts2d, col, pen, lw)


# Clean ball outline

t.penup(); t.goto(CX, CY-R); t.pendown()

t.pencolor("#111111"); t.pensize(4); t.fillcolor("")


# Specular highlights

for hx,hy,hr,col in [(CX-65,CY+80,40,"#ffffff"),(CX-55,CY+68,20,"#ffffff")]:

  t.penup(); t.goto(hx,hy-hr); t.pendown()

  t.pencolor(col); t.pensize(1); t.fillcolor(col)

  t.begin_fill()

  t.end_fill()


screen.update()

turtle.done()

Read aloud a PDF file

import PyPDF2, pyttsx3


engine = pyttsx3.init()


with open("File.pdf", "rb") as file:

  reader = PyPDF2.PdfReader(file)

  for page in reader.pages:

    text = page.extract_text()

    if text:

      engine.say(text)


engine.runAndWait()