📄 mkicon.py
字号:
list.append(int(outerx * float(y0) / outery + 0.3))
if y0 <= innery:
list.append(int(innerx * float(y0) / innery + 0.3))
list.sort()
for x in range(int(list[0]), int(list[-1]+1)):
pixel(x, y, cY, canvas)
# And draw a border.
border(canvas, size, [(int(width-1),0,TR), (0,int(height-1),BL)])
return canvas
def document(size):
canvas = {}
# The document used in the PSCP/PSFTP icon.
width = round(13*size)
height = round(16*size)
lineht = round(1*size)
if lineht < 1: lineht = 1
linespc = round(0.7*size)
if linespc < 1: linespc = 1
nlines = int((height-linespc)/(lineht+linespc))
height = nlines*(lineht+linespc)+linespc # round this so it fits better
# Start by drawing a big white rectangle.
for y in range(int(height)):
for x in range(int(width)):
pixel(x, y, cW, canvas)
# Now draw lines of text.
for line in range(nlines):
# Decide where this line of text begins.
if line == 0:
start = round(4*size)
elif line < 5*nlines/7:
start = round((line - (nlines/7)) * size)
else:
start = round(1*size)
if start < round(1*size):
start = round(1*size)
# Decide where it ends.
endpoints = [10, 8, 11, 6, 5, 7, 5]
ey = line * 6.0 / (nlines-1)
eyf = math.floor(ey)
eyc = math.ceil(ey)
exf = endpoints[int(eyf)]
exc = endpoints[int(eyc)]
if eyf == eyc:
end = exf
else:
end = exf * (eyc-ey) + exc * (ey-eyf)
end = round(end * size)
liney = height - (lineht+linespc) * (line+1)
for x in range(int(start), int(end)):
for y in range(int(lineht)):
pixel(x, y+liney, cK, canvas)
# And draw a border.
border(canvas, size, \
[(0,0,TL),(int(width-1),0,TR),(0,int(height-1),BL), \
(int(width-1),int(height-1),BR)])
return canvas
def hat(size):
canvas = {}
# The secret-agent hat in the Pageant icon.
topa = [6]*9+[5,3,1,0,0,1,2,2,1,1,1,9,9,10,10,11,11,12,12]
topa = [round(x*size) for x in topa]
botl = round(topa[0]+2.4*math.sqrt(size))
botr = round(topa[-1]+2.4*math.sqrt(size))
width = round(len(topa)*size)
# Line equations for the top and bottom of the hat brim, in the
# form y=mx+c. c, of course, needs scaling by size, but m is
# independent of size.
brimm = 1.0 / 3.75
brimtopc = round(4*size/3)
brimbotc = round(10*size/3)
for x in range(int(width)):
xs = float(x) * (len(topa)-1) / (width-1)
xf = math.floor(xs)
xc = math.ceil(xs)
topf = topa[int(xf)]
topc = topa[int(xc)]
if xf == xc:
top = topf
else:
top = topf * (xc-xs) + topc * (xs-xf)
top = math.floor(top)
bot = round(botl + (botr-botl) * x/(width-1))
for y in range(int(top), int(bot)):
pixel(x, y, cK, canvas)
# Now draw the brim.
for x in range(int(width)):
brimtop = brimtopc + brimm * x
brimbot = brimbotc + brimm * x
for y in range(int(math.floor(brimtop)), int(math.ceil(brimbot))):
tophere = max(min(brimtop - y, 1), 0)
bothere = max(min(brimbot - y, 1), 0)
grey = bothere - tophere
# Only draw brim pixels over pixels which are (a) part
# of the main hat, and (b) not right on its edge.
if canvas.has_key((x,y)) and \
canvas.has_key((x,y-1)) and \
canvas.has_key((x,y+1)) and \
canvas.has_key((x-1,y)) and \
canvas.has_key((x+1,y)):
pixel(x, y, greypix(grey), canvas)
return canvas
def key(size):
canvas = {}
# The key in the PuTTYgen icon.
keyheadw = round(9.5*size)
keyheadh = round(12*size)
keyholed = round(4*size)
keyholeoff = round(2*size)
# Ensure keyheadh and keyshafth have the same parity.
keyshafth = round((2*size - (int(keyheadh)&1)) / 2) * 2 + (int(keyheadh)&1)
keyshaftw = round(18.5*size)
keyhead = [round(x*size) for x in [12,11,8,10,9,8,11,12]]
squarepix = []
# Ellipse for the key head, minus an off-centre circular hole.
for y in range(int(keyheadh)):
dy = (y-(keyheadh-1)/2.0) / (keyheadh/2.0)
dyh = (y-(keyheadh-1)/2.0) / (keyholed/2.0)
for x in range(int(keyheadw)):
dx = (x-(keyheadw-1)/2.0) / (keyheadw/2.0)
dxh = (x-(keyheadw-1)/2.0-keyholeoff) / (keyholed/2.0)
if dy*dy+dx*dx <= 1 and dyh*dyh+dxh*dxh > 1:
pixel(x + keyshaftw, y, cy, canvas)
# Rectangle for the key shaft, extended at the bottom for the
# key head detail.
for x in range(int(keyshaftw)):
top = round((keyheadh - keyshafth) / 2)
bot = round((keyheadh + keyshafth) / 2)
xs = float(x) * (len(keyhead)-1) / round((len(keyhead)-1)*size)
xf = math.floor(xs)
xc = math.ceil(xs)
in_head = 0
if xc < len(keyhead):
in_head = 1
yf = keyhead[int(xf)]
yc = keyhead[int(xc)]
if xf == xc:
bot = yf
else:
bot = yf * (xc-xs) + yc * (xs-xf)
for y in range(int(top),int(bot)):
pixel(x, y, cy, canvas)
if in_head:
last = (x, y)
if x == 0:
squarepix.append((x, int(top), TL))
if x == 0:
squarepix.append(last + (BL,))
if last != None and not in_head:
squarepix.append(last + (BR,))
last = None
# And draw a border.
border(canvas, size, squarepix)
return canvas
def linedist(x1,y1, x2,y2, x,y):
# Compute the distance from the point x,y to the line segment
# joining x1,y1 to x2,y2. Returns the distance vector, measured
# with x,y at the origin.
vectors = []
# Special case: if x1,y1 and x2,y2 are the same point, we
# don't attempt to extrapolate it into a line at all.
if x1 != x2 or y1 != y2:
# First, find the nearest point to x,y on the infinite
# projection of the line segment. So we construct a vector
# n perpendicular to that segment...
nx = y2-y1
ny = x1-x2
# ... compute the dot product of (x1,y1)-(x,y) with that
# vector...
nd = (x1-x)*nx + (y1-y)*ny
# ... multiply by the vector we first thought of...
ndx = nd * nx
ndy = nd * ny
# ... and divide twice by the length of n.
ndx = ndx / (nx*nx+ny*ny)
ndy = ndy / (nx*nx+ny*ny)
# That gives us a displacement vector from x,y to the
# nearest point. See if it's within the range of the line
# segment.
cx = x + ndx
cy = y + ndy
if cx >= min(x1,x2) and cx <= max(x1,x2) and \
cy >= min(y1,y2) and cy <= max(y1,y2):
vectors.append((ndx,ndy))
# Now we have up to three candidate result vectors: (ndx,ndy)
# as computed just above, and the two vectors to the ends of
# the line segment, (x1-x,y1-y) and (x2-x,y2-y). Pick the
# shortest.
vectors = vectors + [(x1-x,y1-y), (x2-x,y2-y)]
bestlen, best = None, None
for v in vectors:
vlen = v[0]*v[0]+v[1]*v[1]
if bestlen == None or bestlen > vlen:
bestlen = vlen
best = v
return best
def spanner(size):
canvas = {}
# The spanner in the config box icon.
headcentre = 0.5 + round(4*size)
headradius = headcentre + 0.1
headhighlight = round(1.5*size)
holecentre = 0.5 + round(3*size)
holeradius = round(2*size)
holehighlight = round(1.5*size)
shaftend = 0.5 + round(25*size)
shaftwidth = round(2*size)
shafthighlight = round(1.5*size)
cmax = shaftend + shaftwidth
# Define three line segments, such that the shortest distance
# vectors from any point to each of these segments determines
# everything we need to know about where it is on the spanner
# shape.
segments = [
((0,0), (holecentre, holecentre)),
((headcentre, headcentre), (headcentre, headcentre)),
((headcentre+headradius/math.sqrt(2), headcentre+headradius/math.sqrt(2)),
(cmax, cmax))
]
for y in range(int(cmax)):
for x in range(int(cmax)):
vectors = [linedist(a,b,c,d,x,y) for ((a,b),(c,d)) in segments]
dists = [memoisedsqrt(vx*vx+vy*vy) for (vx,vy) in vectors]
# If the distance to the hole line is less than
# holeradius, we're not part of the spanner.
if dists[0] < holeradius:
continue
# If the distance to the head `line' is less than
# headradius, we are part of the spanner; likewise if
# the distance to the shaft line is less than
# shaftwidth _and_ the resulting shaft point isn't
# beyond the shaft end.
if dists[1] > headradius and \
(dists[2] > shaftwidth or x+vectors[2][0] >= shaftend):
continue
# We're part of the spanner. Now compute the highlight
# on this pixel. We do this by computing a `slope
# vector', which points from this pixel in the
# direction of its nearest edge. We store an array of
# slope vectors, in polar coordinates.
angles = [math.atan2(vy,vx) for (vx,vy) in vectors]
slopes = []
if dists[0] < holeradius + holehighlight:
slopes.append(((dists[0]-holeradius)/holehighlight,angles[0]))
if dists[1]/headradius < dists[2]/shaftwidth:
if dists[1] > headradius - headhighlight and dists[1] < headradius:
slopes.append(((headradius-dists[1])/headhighlight,math.pi+angles[1]))
else:
if dists[2] > shaftwidth - shafthighlight and dists[2] < shaftwidth:
slopes.append(((shaftwidth-dists[2])/shafthighlight,math.pi+angles[2]))
# Now we find the smallest distance in that array, if
# any, and that gives us a notional position on a
# sphere which we can use to compute the final
# highlight level.
bestdist = None
bestangle = 0
for dist, angle in slopes:
if bestdist == None or bestdist > dist:
bestdist = dist
bestangle = angle
if bestdist == None:
bestdist = 1.0
sx = (1.0-bestdist) * math.cos(bestangle)
sy = (1.0-bestdist) * math.sin(bestangle)
sz = math.sqrt(1.0 - sx*sx - sy*sy)
shade = sx-sy+sz / math.sqrt(3) # can range from -1 to +1
shade = 1.0 - (1-shade)/3
pixel(x, y, yellowpix(shade), canvas)
# And draw a border.
border(canvas, size, [])
return canvas
def box(size, back):
canvas = {}
# The back side of the cardboard box in the installer icon.
boxwidth = round(15 * size)
boxheight = round(12 * size)
boxdepth = round(4 * size)
boxfrontflapheight = round(5 * size)
boxrightflapheight = round(3 * size)
# Three shades of basically acceptable brown, all achieved by
# halftoning between two of the Windows-16 colours. I'm quite
# pleased that was feasible at all!
dark = halftone(cr, cK)
med = halftone(cr, cy)
light = halftone(cr, cY)
# We define our halftoning parity in such a way that the black
# pixels along the RHS of the visible part of the box back
# match up with the one-pixel black outline around the
# right-hand side of the box. In other words, we want the pixel
# at (-1, boxwidth-1) to be black, and hence the one at (0,
# boxwidth) too.
parityadjust = int(boxwidth) % 2
# The entire back of the box.
if back:
for x in range(int(boxwidth + boxdepth)):
ytop = max(-x-1, -boxdepth-1)
ybot = min(boxheight, boxheight+boxwidth-1-x)
for y in range(int(ytop), int(ybot)):
pixel(x, y, dark[(x+y+parityadjust) % 2], canvas)
# Even when drawing the back of the box, we still draw the
# whole shape, because that means we get the right overall size
# (the flaps make the box front larger than the box back) and
# it'll all be overwritten anyway.
# The front face of the box.
for x in range(int(boxwidth)):
for y in range(int(boxheight)):
pixel(x, y, med[(x+y+parityadjust) % 2], canvas)
# The right face of the box.
for x in range(int(boxwidth), int(boxwidth+boxdepth)):
ybot = boxheight + boxwidth-x
ytop = ybot - boxheight
for y in range(int(ytop), int(ybot)):
pixel(x, y, dark[(x+y+parityadjust) % 2], canvas)
# The front flap of the box.
for y in range(int(boxfrontflapheight)):
xadj = int(round(-0.5*y))
for x in range(int(xadj), int(xadj+boxwidth)):
pixel(x, y, light[(x+y+parityadjust) % 2], canvas)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -