Skip to content

Instantly share code, notes, and snippets.

@bbbradsmith
Created December 30, 2022 04:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bbbradsmith/484e48841a55a7ff1c2b00fb190e5f29 to your computer and use it in GitHub Desktop.
Save bbbradsmith/484e48841a55a7ff1c2b00fb190e5f29 to your computer and use it in GitHub Desktop.
Texture mapping diagrams for Wikipedia
# generates SVG diagram:
# https://commons.wikimedia.org/wiki/File:Affine_texture_mapping_tri_vs_quad.svg
import math
GRID = 8
FTW = 100 # top board width
FBW = 200 # bottom board width
FH = 156
COLBG = "#B4B3D2"
GY = 84 # top of board
TY = 300 # text position
HFTW = FTW/2
HFBW = FBW/2
PAD = 50
PW = PAD+3*(PAD+FBW) # paper dimensions
PH = 336
GX0 = PAD+HFBW
GX1 = GX0+PAD+FBW
GX2 = GX1+PAD+FBW
def path(coords,name=None,fill=None,opacity=None,stroke=None,indent=1):
s = '\t' * indent
s += '<path '
if name: s += 'id="%s" ' % name
s += 'd="M'
for (x,y) in coords:
s += " %5.1f,%5.1f" % (x,y)
s += ' Z"'
if opacity: s += ' opacity="%s"' % opacity
if stroke: s += ' stroke="%s"' % stroke
if fill: s += ' fill="%s"' % fill
s += '/>'
print(s)
def affine_tri(u,v,ox,oy):
u /= GRID
v /= GRID
y = v * FH
xw = (1-v)*FTW + v*FBW
x0 = ox - (xw / 2)
x1 = ox + (xw / 2)
if (u <= (1-v)):
x = x0 + u * FTW
else:
x = x1 - ((1-u) * FBW)
return (x,y+oy)
def affine_quad(u,v,ox,oy):
u /= GRID
v /= GRID
y = v * FH
xw = (1-v)*FTW + v*FBW
x = (u - 0.5) * xw
return (x+ox,y+oy)
def perspective(u,v,ox,oy):
u /= GRID
v /= GRID
z0 = FBW / FTW
z1 = FBW / FBW
# reversal of perspective interpolation
vr = (v * z1) / ((v * z1) + (1-v) * z0)
y = vr * FH
xw = (1-vr)*FTW + vr*FBW
x = (u - 0.5) * xw
return (x+ox,y+oy)
def checkerboard(ox,oy,func):
for y in range(GRID):
for x in range(GRID):
if (((x^y)&1) == 0):
continue
c = []
c.append(func(x+0,y+0,ox,oy))
c.append(func(x+1,y+0,ox,oy))
c.append(func(x+1,y+1,ox,oy))
c.append(func(x+0,y+1,ox,oy))
path(c,indent=2)
print('<?xml version="1.0" encoding="UTF-8"?>')
print('<svg version="1.1" width="%d" height="%d" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg">' % (PW,PH,PW,PH))
print('\t<rect id="background" width="%d" height="%d" fill="%s"/>' % (PW,PH,COLBG))
path([(GX0-HFTW,GY),
(GX0+HFTW,GY),
(GX0+HFBW,GY+FH),
(GX0-HFBW,GY+FH)],
name="board-bg-tri",fill="white")
print('\t<g id="checkerboard-tri">')
checkerboard(GX0,GY,affine_tri)
print('\t</g>')
path([(GX1-HFTW,GY),
(GX1+HFTW,GY),
(GX1+HFBW,GY+FH),
(GX1-HFBW,GY+FH)],
name="board-bg-quad",fill="white")
print('\t<g id="checkerboard-quad">')
checkerboard(GX1,GY,affine_quad)
print('\t</g>')
path([(GX2-HFTW,GY),
(GX2+HFTW,GY),
(GX2+HFBW,GY+FH),
(GX2-HFBW,GY+FH)],
name="board-bg-persp",fill="white")
print('\t<g id="checkerboard-persp">')
checkerboard(GX2,GY,perspective)
print('\t</g>')
print('\t<g id="labels" fill="black" font-family="sans-serif" font-size="40px" text-anchor="middle">')
print('\t\t<text x="%d" y="%d" text-anchor="middle">Triangle</text>' % (GX0,TY))
print('\t\t<text x="%d" y="%d" text-anchor="middle">Quad</text>' % (GX1,TY))
print('\t\t<text x="%d" y="%d" text-anchor="middle">Perspective</text>' % (GX2,TY))
print('\t</g>')
print('</svg>')
# generates SVG diagram:
# https://commons.wikimedia.org/wiki/File:Perspective_correct_texture_mapping.svg
import math
STROKE = 3 # outline stroke width
GRID = 8
FTW = 100 # top board width
FBW = 200 # bottom board width
FH = 156
COLBG = "#B4B3D2"
GY = 84 # top of board
TY = 300 # text position
HFTW = FTW/2
HFBW = FBW/2
SY = GY-FBW+FH
PAD = 50
PW = PAD+3*(PAD+FBW) # paper dimensions
PH = 336
GX0 = PAD+HFBW
GX1 = GX0+PAD+FBW
GX2 = GX1+PAD+FBW
def path(coords,name=None,fill=None,opacity=None,stroke=None,indent=1):
s = '\t' * indent
s += '<path '
if name: s += 'id="%s" ' % name
s += 'd="M'
for (x,y) in coords:
s += " %5.1f,%5.1f" % (x,y)
s += ' Z"'
if opacity: s += ' opacity="%s"' % opacity
if stroke: s += ' stroke="%s" stroke-linejoin="round"' % stroke
if fill: s += ' fill="%s"' % fill
s += '/>'
print(s)
def square(u,v,ox,oy):
u /= GRID
v /= GRID
oy += SY - GY
x = (u - 0.5) * FBW
y = v * FBW
return (x+ox,y+oy)
def affine_tri(u,v,ox,oy):
u /= GRID
v /= GRID
y = v * FH
xw = (1-v)*FTW + v*FBW
x0 = ox - (xw / 2)
x1 = ox + (xw / 2)
if (u <= (1-v)):
x = x0 + u * FTW
else:
x = x1 - ((1-u) * FBW)
return (x,y+oy)
def perspective(u,v,ox,oy):
u /= GRID
v /= GRID
z0 = FBW / FTW
z1 = FBW / FBW
# reversal of perspective interpolation
vr = (v * z1) / ((v * z1) + (1-v) * z0)
y = vr * FH
xw = (1-vr)*FTW + vr*FBW
x = (u - 0.5) * xw
return (x+ox,y+oy)
def checkerboard(ox,oy,func):
for y in range(GRID):
for x in range(GRID):
if (((x^y)&1) == 0):
continue
c = []
c.append(func(x+0,y+0,ox,oy))
c.append(func(x+1,y+0,ox,oy))
c.append(func(x+1,y+1,ox,oy))
c.append(func(x+0,y+1,ox,oy))
path(c,indent=2)
def outline(coords,name):
def nd(a,b): # returns normalized direction vector b-a
(ax,ay) = a
(bx,by) = b
(nx,ny) = ((bx-ax),(by-ay))
m = math.sqrt(nx*nx + ny*ny)
return (nx/m, ny/m)
def nudge(a,n,d): # shift a in direction n by d
(ax,ay) = a
(nx,ny) = n
return (ax + d*nx, ay + d*ny)
def nudge2(a,n0,d0,n1,d1): # double nudge
return nudge(nudge(a,n0,d0),n1,d1)
h = nd(coords[0],coords[1]) # top/bottom horizontal
v0 = nd(coords[0],coords[3]) # left "vertical"
v1 = nd(coords[1],coords[2]) # right "vertical"
SH = STROKE/2 # distance away from a corner
SW = STROKE*1.25 # distance away from a shared edge
SB = SW
if coords[0][0] != coords[3][0]: # little extra nudge for bottom left angle
SB *= math.sqrt((coords[2][0]-coords[3][0])/(coords[1][0]-coords[0][0]))
p0 = [nudge2(coords[3],h, SH,v0,-SW),
nudge2(coords[0],h, SH,v0, SH),
nudge2(coords[1],h,-SW,v1, SH)]
p1 = [nudge2(coords[1],h,-SH,v1, SW),
nudge2(coords[2],h,-SH,v1,-SH),
nudge2(coords[3],h, SB,v0,-SH)]
print('\t<g id="%s" fill="none" stroke-width="%d">' % (name,STROKE))
path(p0,opacity=".5",stroke="#F02020",indent=2)
path(p1,opacity=".5",stroke="#46F000",indent=2)
print('\t</g>')
print('<?xml version="1.0" encoding="UTF-8"?>')
print('<svg version="1.1" width="%d" height="%d" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg">' % (PW,PH,PW,PH))
print('\t<rect id="background" width="%d" height="%d" fill="%s"/>' % (PW,PH,COLBG))
coords = [(GX0-HFBW,SY),
(GX0+HFBW,SY),
(GX0+HFBW,SY+FBW),
(GX0-HFBW,SY+FBW)]
path(coords,name="board-bg-square",fill="white")
print('\t<g id="checkerboard-square">')
checkerboard(GX0,GY,square)
print('\t</g>')
outline(coords,"outline-square")
coords = [(GX1-HFTW,GY),
(GX1+HFTW,GY),
(GX1+HFBW,GY+FH),
(GX1-HFBW,GY+FH)]
path(coords,name="board-bg-tri",fill="white")
print('\t<g id="checkerboard-tri">')
checkerboard(GX1,GY,affine_tri)
print('\t</g>')
outline(coords,"outline-tri")
coords = [(GX2-HFTW,GY),
(GX2+HFTW,GY),
(GX2+HFBW,GY+FH),
(GX2-HFBW,GY+FH)]
path(coords,name="board-bg-persp",fill="white")
print('\t<g id="checkerboard-persp">')
checkerboard(GX2,GY,perspective)
print('\t</g>')
outline(coords,"outline-perspective")
print('\t<g id="labels" fill="black" font-family="sans-serif" font-size="40px" text-anchor="middle">')
print('\t\t<text x="%d" y="%d" text-anchor="middle">Flat</text>' % (GX0,TY))
print('\t\t<text x="%d" y="%d" text-anchor="middle">Affine</text>' % (GX1,TY))
print('\t\t<text x="%d" y="%d" text-anchor="middle">Correct</text>' % (GX2,TY))
print('\t</g>')
print('</svg>')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment