import math from operator import itemgetter class Shader(object): MODE_EVEN_ODD = 0 MODE_NONZERO = 1 def __init__(self, unshadedThreshold=1., lightestSpacing=3., darkestSpacing=0.5, angle=45, crossHatch=False): self.unshadedThreshold = unshadedThreshold self.lightestSpacing = lightestSpacing self.darkestSpacing = darkestSpacing self.angle = angle self.secondaryAngle = angle + 90 self.crossHatch = False def isActive(self): return self.unshadedThreshold > 0.000001 def setDrawingDirectionAngle(self, drawingDirectionAngle): self.drawingDirectionAngle = drawingDirectionAngle if drawingDirectionAngle is None: return if 90 < (self.angle - drawingDirectionAngle) % 360 < 270: self.angle = (self.angle + 180) % 360 if 90 < (self.secondaryAngle - drawingDirectionAngle) % 360 < 270: self.secondaryAngle = (self.secondaryAngle + 180) % 360 def shade(self, polygon, grayscale, avoidOutline=True, mode=None): if mode is None: mode = Shader.MODE_EVEN_ODD if grayscale >= self.unshadedThreshold: return [] intensity = (self.unshadedThreshold-grayscale) / float(self.unshadedThreshold) spacing = self.lightestSpacing * (1-intensity) + self.darkestSpacing * intensity lines = Shader.shadePolygon(polygon, self.angle, spacing, avoidOutline=avoidOutline, mode=mode, alternate=(self.drawingDirectionAngle is None)) if self.crossHatch: lines += Shader.shadePolygon(polygon, self.angle+90, spacing, avoidOutline=avoidOutline, mode=mode, alternate=(self.drawingDirectionAngle is None)) return lines @staticmethod def shadePolygon(polygon, angleDegrees, spacing, avoidOutline=True, mode=None, alternate=True): if mode is None: mode = Shader.MODE_EVEN_ODD rotate = complex(math.cos(angleDegrees * math.pi / 180.), math.sin(angleDegrees * math.pi / 180.)) polygon = [(line[0] / rotate,line[1] / rotate) for line in polygon] spacing = float(spacing) toAvoid = list(set(line[0].imag for line in polygon)|set(line[1].imag for line in polygon)) if len(toAvoid) <= 1: deltaY = (toAvoid[0]-spacing/2.) % spacing else: # find largest interval toAvoid.sort() largestIndex = 0 largestLen = 0 for i in range(len(toAvoid)): l = ( toAvoid[i] - toAvoid[i-1] ) % spacing if l > largestLen: largestIndex = i largestLen = l deltaY = (toAvoid[largestIndex-1] + largestLen / 2.) % spacing minY = min(min(line[0].imag,line[1].imag) for line in polygon) maxY = max(max(line[0].imag,line[1].imag) for line in polygon) y = minY + ( - minY ) % spacing + deltaY if y > minY + spacing: y -= spacing y += 0.01 odd = False all = [] while y < maxY: intersections = [] for line in polygon: z = line[0] z1 = line[1] if z1.imag == y or z.imag == y: # roundoff generated corner case -- ignore -- TODO break if z1.imag < y < z.imag or z.imag < y < z1.imag: if z1.real == z.real: intersections.append(( complex(z.real, y), z.imag