Attachment 'lines-antialias.py'

Download

   1 #!/usr/bin
   2 # coding: UTF8
   3 '''
   4 Нарисовать отрезок от (x1,y1) до (x2,y2) толщиной в P точек
   5 с учётом антиалиасинга (частичного перекрашивания точек,
   6 заметаемых отрезком не полностью).
   7 '''
   8 from math import *
   9 import pygame, sys
  10 
  11 pygame.init()
  12 scale=1         # размер точки
  13 screen=pygame.display.set_mode((1024,768))
  14 
  15 def dot(coord, color):
  16     "Нарисовать точку"
  17     r=pygame.Rect(coord[0]*scale,coord[1]*scale,scale,scale)
  18     r=r.clip(screen.get_rect())
  19     screen.fill(color, (coord[0]*scale,coord[1]*scale,scale,scale))
  20 
  21 def mixdot(coord, color, opacity):
  22     "Нарисовать полупрозрачную точку"
  23     if not pygame.Rect(coord[0]*scale,coord[1]*scale,1,1).clip(screen.get_rect()):
  24         return
  25     back = screen.get_at((int(coord[0]*scale),int(coord[1]*scale)))
  26     fore = [a*(1-opacity)+b*opacity for a,b in zip(back,color)]
  27     dot(coord,fore)
  28 
  29 def dist(A,B):
  30     "Расстояние от точки A до точки B"
  31     return sqrt((B[0]-A[0])**2+(B[1]-A[1])**2)
  32 
  33 def ldist(M,A,B):
  34     "Расстояние от точки M до прямой AB"
  35     return ((A[1]-B[1])*M[0]+(B[0]-A[0])*M[1]+(A[0]*B[1]-B[0]*A[1]))/dist(A,B)
  36 
  37 def l8(A,B,W,color):
  38     "Построение отрезка в первом квадранте, без проверок"
  39     X=A[0]-W/2.
  40     for y in xrange(A[1],B[1]+1):
  41         x=X
  42         while True:
  43             d=ldist((x,y),A,B)
  44             if d>W/2.+0.5:
  45                 X+=1
  46             elif d<-W/2.-0.5:
  47                 break
  48             elif abs(d)>W/2.-0.5:
  49                 mixdot((x,y),color,W/2.+0.5-abs(d))
  50             else:
  51                 dot((x,y),color)
  52             x+=1
  53             pygame.display.flip()
  54 
  55 def line(A,B,W,color):
  56     "Построение отрезка AB шириной W"
  57     if A == B: return
  58     if A[1]>B[1]: A,B=B,A   # переводим шаг по Y в положительный"
  59     L=dist(A,B)
  60     # Прямоугольник
  61     A0=(A[0]-(B[1]-A[1])*W/(2*L),A[1]+(B[0]-A[0])*W/(2*L))
  62     A1=(A[0]+(B[1]-A[1])*W/(2*L),A[1]-(B[0]-A[0])*W/(2*L))
  63     B0=(B[0]-(B[1]-A[1])*W/(2*L),B[1]+(B[0]-A[0])*W/(2*L))
  64     B1=(B[0]+(B[1]-A[1])*W/(2*L),B[1]-(B[0]-A[0])*W/(2*L))
  65     dx=A[0]>B[0] and -1 or 1    # Шаг по X
  66     X=dx>0 and min(A0[0],A1[0]) or max(A0[0],A1[0]) # верхний угол
  67     for y in dx>0 and xrange(int(A1[1]),int(B0[1]+1)) or xrange(int(A0[1]),int(B1[1]+1)):
  68         x=X
  69         while True:
  70             d=ldist((x,y),A,B)
  71             if d*dx>W/2.+0.5:   # слишком рано начали сканировать точки
  72                 X+=dx           # в следующий раз начнём попозже
  73                 x=X
  74                 continue
  75             elif d*dx<-W/2.-0.5:    # вышли за границу прямоугольника
  76                 break
  77             elif ldist((x,y),A0,A1)>=0:     # точка после начала прямоугольника
  78                 if ldist((x,y),B1,B0)>0:    # точка перед окончанием прямоугольника
  79                     if abs(d)>W/2.-0.5:     # точка на границе
  80                         mixdot((x,y),color,W/2.+0.5-abs(d))
  81                     else:
  82                         dot((x,y),color)
  83                 else:                       # точка после окончания прямоугольника
  84                     break                   # сканирование не требуется
  85             x+=dx
  86     if scale > 1:               # Отладочная тонкая линия
  87         pygame.draw.circle(screen,pygame.Color("red"),(int(scale*A0[0]),int(scale*A0[1])),2)
  88         pygame.draw.lines(screen,pygame.Color("red"),False,[(scale*x,scale*y) for x,y in (A0,B0,B1,A1)])
  89 
  90 scale=16
  91 col=pygame.Color("yellowgreen")
  92 
  93 while True:
  94     event = pygame.event.wait()
  95     if   event.type == pygame.QUIT: sys.exit()
  96     elif event.type == pygame.MOUSEMOTION:
  97         if event.buttons[0]:
  98             screen.blit(S,(0,0))
  99             line(M,(event.pos[0]/scale,event.pos[1]/scale),3,col)
 100     elif event.type == pygame.MOUSEBUTTONDOWN:
 101         if event.button == 1:
 102             S=screen.copy()     # картинка без линии
 103             M=(event.pos[0]/scale,event.pos[1]/scale)
 104     pygame.display.flip()

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.

You are not allowed to attach a file to this page.