Pythonでパスツール

前回も理想のドローソフトについての記事でしたが、今だその熱を持っていて、今回はパスとかハッチングとかやってみました。

EMFやSVGなどに出力することを考えると、ライブラリが持つ機能に依存するよりは、なるだけ自前で実装したほうが良いはず。どちらのファイル形式もベジェ曲線はサポートしているので、3次ベジェ曲線によるパスのプログラミングを試みました。

 

ドローソフトと言えばこれだよね。

[ソース1](ソースは最後にあります)

制御点を無視して、直線とすることもできるようにした。

 

機能として必須なのが、パターンハッチング。
ベジェ曲線と直線の交点の計算なんてどうやるんだーっと思ったけど、情報を載せてくれる人はいるもんですなぁ。ありがたい。

斜線パターン

[ソース2]

 

5(その2):直線と曲線の交点(解の公式)

5(その2):直線と曲線の交点(解の公式)

 

パターンを繰り返すハッチング

[ソース3]

これで直線ならどんな模様でもできる。いずれ楕円も加えたい。

 

虚数とか出てきてなかなか大変でしたが、出張により久々に一人の夜を過ごし、たっぷり作業できたので、なんとか形にはなりました(後々不具合が出てくるのは必至だと思いますが)。久しぶりにがっつりプログラミング、楽しい。

次にしたいと思っているのが、ブーリアン演算による差分とか合成とか。果たして僕の能力が及ぶのか。。。

[ソース1:ベジェ曲線パス]

# -*- coding: utf-8 -*-

import Tkinter

class Node:
    p = [0.0,0.0]  #self point
    p0 = [0.0,0.0] #control point
    p1 = [0.0,0.0] #control point
    p0_avoid = False
    p1_avoid = False
    
    def __init__(self,p,p0,p1):
        self.p = p
        self.p0 = p0
        self.p1 = p1

class Path:
    nodes = []
    closed = False
    steps = 20
    
    def __init__(self,nodes = None):
        if nodes == None:
            return
        self.nodes = nodes
    
    def points(self):
        if len(self.nodes) == 0:
            return []
        points = []
        points.append(self.nodes[0].p)
        for i in range(1,len(self.nodes)):
            points += self._segment_points(i-1,i)
        if self.closed:
            points += self._segment_points(i,0)
            points = points[:-1]
        return points
        
    def _segment_points(self,i,j):
        points = []
        n0 = self.nodes[i]
        n1 = self.nodes[j]
        if n0.p1_avoid == True and n1.p0_avoid == True:
            points.append(n1.p)
        else:
            pts = self.bezier_curve_points(n0.p,n0.p1,n1.p0,n1.p)
            points += pts[1:]
        return points

    def bezier_curve_points(self,p0,p1,p2,p3):
        points = []
        for i in range(self.steps+1):
            t = float(i)/self.steps
            x = (1-t)**3*p0[0]+3*(1-t)**2*t*p1[0]+3*(1-t)*t**2*p2[0]+t**3*p3[0]
            y = (1-t)**3*p0[1]+3*(1-t)**2*t*p1[1]+3*(1-t)*t**2*p2[1]+t**3*p3[1]
            points.append((x,y))
        return points

def main():
    window = Tkinter.Tk()
    canvas = Tkinter.Canvas(window,width=300,height=200)
    canvas.pack()
    
    n0 = Node([50,50],[50,10],[80,50])
    n1 = Node([100,150],[100,110],[100,190])  
    n2 = Node([250,100],[250,150],[180,30])
    
    path = Path([n0,n1,n2])
    path.closed = True
    #n1.p1_avoid = True
    #n2.p0_avoid = True
    
    if path.closed:
        canvas.create_polygon(path.points(),fill='gray',outline='red')
    else:
        canvas.create_line(path.points(),fill='red')
    r = 3
    for n in path.nodes:
        canvas.create_oval(n.p[0]-r,n.p[1]-r,n.p[0]+r,n.p[1]+r)
        canvas.create_oval(n.p0[0]-r,n.p0[1]-r,n.p0[0]+r,n.p0[1]+r)
        canvas.create_oval(n.p1[0]-r,n.p1[1]-r,n.p1[0]+r,n.p1[1]+r)
        canvas.create_line(n.p[0],n.p[1],n.p0[0],n.p0[1])
        canvas.create_line(n.p[0],n.p[1],n.p1[0],n.p1[1])
        
    window.mainloop()

if __name__ == '__main__':
    main()

 

[ソース2:斜線ハッチング]

# -*- coding: utf-8 -*-

import math
import Tkinter

class Node:
    p = [0.0,0.0]  #self point
    p0 = [0.0,0.0] #control point
    p1 = [0.0,0.0] #control point
    p0_avoid = False
    p1_avoid = False
    
    def __init__(self,p,p0,p1):
        self.p = p
        self.p0 = p0
        self.p1 = p1

class Path:
    nodes = []
    closed = False
    steps = 20
    
    def __init__(self,nodes = None):
        if nodes == None:
            return
        self.nodes = nodes
    
    def points(self):
        if len(self.nodes) == 0:
            return []
        points = []
        points.append(self.nodes[0].p)
        for i in range(1,len(self.nodes)):
            points += self._segment_points(i-1,i)
        if self.closed:
            points += self._segment_points(i,0)
            points = points[:-1]
        return points
        
    def _segment_points(self,i,j):
        points = []
        n0 = self.nodes[i]
        n1 = self.nodes[j]
        if n0.p1_avoid == True and n1.p0_avoid == True:
            points.append(n1.p)
        else:
            pts = self.bezier_curve_points(n0.p,n0.p1,n1.p0,n1.p)
            points += pts[1:]
        return points

    def bezier_curve_points(self,p0,p1,p2,p3):
        points = []
        for i in range(self.steps+1):
            t = float(i)/self.steps
            x = (1-t)**3*p0[0]+3*(1-t)**2*t*p1[0]+3*(1-t)*t**2*p2[0]+t**3*p3[0]
            y = (1-t)**3*p0[1]+3*(1-t)**2*t*p1[1]+3*(1-t)*t**2*p2[1]+t**3*p3[1]
            points.append((x,y))
        return points

class PattanStripe:
    degree = -55 # -90 <= degree <= 90
    lines = []
    space = 10
    def __init__(self,path):
        self.path = path
        self.update()

    def update(self):
        #range
        area = [float('inf'),float('inf'),0.0,0.0] #xmin,ymin,xmax,ymax
        for p in self.path.points():
            if   area[0] > p[0]: area[0] = p[0]
            elif area[2] < p[0]: area[2] = p[0]
            if   area[1] > p[1]: area[1] = p[1]
            elif area[3] < p[1]: area[3] = p[1]
        
        #lines
        lines = []
        sp = self.space
        if self.degree == 90 or self.degree == -90:
            a = 1.0
            b = 0.0
            count = int((area[2]-area[0])/sp+1)
            for n in range(count):
                x = area[0] +sp*n -sp/2
                c = -x
                lines.append((a,b,c))
        else:
            a = math.tan(math.radians(self.degree))
            b = -1.0
            sp = abs(sp/math.cos(math.radians(self.degree)))
            dy = abs(a*(area[2]-area[0]))
            count = int(abs(area[3]-area[1]+dy)/sp+1)
            if a < 0:
                start_y = area[1]-sp/2
            else:
                start_y = area[1]-dy-sp/2
            x = area[0]
            for n in range(count):
                y = start_y +sp*n
                c = -a*x -b*y
                lines.append((a,b,c))
        #cut lines
        self.lines = []
        self.cross_points = []
        nodes = self.path.nodes + [self.path.nodes[0]]
        for line in lines:
            cross = []
            for i in range(len(self.path.nodes)):
                p0 = nodes[i].p
                p1 = nodes[i].p1
                p2 = nodes[i+1].p0
                p3 = nodes[i+1].p
                if (nodes[i].p1_avoid == True and 
                    nodes[i+1].p0_avoid == True):
                    pts = intersection_of_line_segment(p0,p3,line)
                else:
                    pts = intersection_of_bezier_and_line((p0,p1,p2,p3),line)
                cross += pts
            self.cross_points += cross #for debug
            if len(cross) % 2 != 0:
                print('error Pattan update()')
                continue
            cross.sort()
            for j in range(0,len(cross),2):
                new_line = cross[j] + cross[j+1]
                self.lines.append(new_line)

def intersection_of_line_segment((x0,y0),(x1,y1),(a,b,c)):
    line = line_generalization((x0,y0),(x1,y1))
    points = intersection_2line(line,(a,b,c))
    m = 0.000001
    xs = [x0,x1]
    ys = [y0,y1]
    new_points = []
    for p in points:
        if (min(xs)-m <= p[0] and p[0] <= max(xs)+m and
            min(ys)-m <= p[1] and p[1] <= max(ys)+m):
            new_points.append(p)
    return new_points

def intersection_2line((a0,b0,c0),(a1,b1,c1)):
    t = float(a0*b1-a1*b0)
    if t == 0:
        return []
    x = (b0*c1-b1*c0)/t
    y = (a1*c0-a0*c1)/t
    return [(x,y)]

def intersection_of_bezier_and_line((p0,p1,p2,p3),(a,b,c)):
    f0 =      a * p0[0] + b * p0[1] + c
    f1 = 3 * (a * p1[0] + b * p1[1] + c)
    f2 = 3 * (a * p2[0] + b * p2[1] + c)
    f3 =      a * p3[0] + b * p3[1] + c

    _a = -f0 + f1 - f2 + f3
    _b = 3 * f0 - 2 * f1 + f2
    _c = -3 * f0 + f1
    _d = f0
    if _a == 0:
        if _b == 0:
            if _c == 0: tlist = []
            else: tlist = -_d/_c
        else:
            tlist = quadratic_equation(_b,_c,_d)
    else:
        tlist = cubic_equation(_a,_b,_c,_d)
    points = []
    for t in tlist:
        if t < 0 or 1 < t:
            continue
        x = (1-t)**3*p0[0]+3*(1-t)**2*t*p1[0]+3*(1-t)*t**2*p2[0]+t**3*p3[0]
        y = (1-t)**3*p0[1]+3*(1-t)**2*t*p1[1]+3*(1-t)*t**2*p2[1]+t**3*p3[1]
        points.append((x,y))
    return points

def cubic_equation(a,b,c,d):
    p = -b**2/(9.0*a**2) + c/(3.0*a)
    q = b**3/(27.0*a**3) - b*c/(6.0*a**2) + d/(2.0*a)
    t = complex(q**2+p**3)
    w =(-1.0 +1j*3.0**0.5)/2.0
    
    u = [0,0,0]
    u[0] = (-q +t**0.5)**(1.0/3.0)
    u[1] = u[0] * w
    u[2] = u[0] * w**2
    v = [0,0,0]
    v[0] = (-q -t**0.5)**(1.0/3.0)
    v[1] = v[0] * w
    v[2] = v[0] * w**2
    
    x_list = []
    for i in range(3):
      for j in range(3):
        if abs(u[i]*v[j] + p) < 0.0001:
            x = u[i] + v[j]
            if abs(x.imag) < 0.0000001:
                x = x.real - b/(3.0*a)
                x_list.append(x)
    return x_list

def quadratic_equation(a,b,c):
    d = b*b-4*a*c
    if d < 0:
        return []
    if d == 0:
        x = -b/(2.0*a)
        return [x]
    else:
        x0 = (-b+d**0.5)/(2.0*a)
        x1 = (-b-d**0.5)/(2.0*a)
    return [x0,x1]

def line_generalization(p0,p1):
    if p0[0] == p1[0]:
        a = 1.0
        b = 0.0
        c = float(-p0[0])
    elif p0[1] == p1[1]:
        a = 0.0
        b = 1.0
        c = float(-p0[1])
    else:
        slope = float(p1[1]-p0[1])/(p1[0]-p0[0])
        intercept = p0[1] - slope*p0[0]
        a = slope
        b = -1.0
        c = intercept
    return a,b,c

def main():
    window = Tkinter.Tk()
    canvas = Tkinter.Canvas(window,width=300,height=200)
    canvas.pack()
    
    n0 = Node([20,50],[30,10],[10,100])
    n1 = Node([50,180],[20,170],[80,190])  
    n2 = Node([140,100],[50,90],[120,190])
    n3 = Node([280,70],[250,150],[220,10])
    
    path = Path([n0,n1,n2,n3])
    path.closed = True
    #n1.p1_avoid = True
    #n2.p0_avoid = True
    path.pattan = PattanStripe(path)
    
    if path.closed:
        canvas.create_polygon(path.points(),fill='gray',outline='red')
    else:
        canvas.create_line(path.points(),fill='red')
    r = 3
    for n in path.nodes:
        canvas.create_oval(n.p[0]-r,n.p[1]-r,n.p[0]+r,n.p[1]+r)
        canvas.create_oval(n.p0[0]-r,n.p0[1]-r,n.p0[0]+r,n.p0[1]+r)
        canvas.create_oval(n.p1[0]-r,n.p1[1]-r,n.p1[0]+r,n.p1[1]+r)
        canvas.create_line(n.p[0],n.p[1],n.p0[0],n.p0[1])
        canvas.create_line(n.p[0],n.p[1],n.p1[0],n.p1[1])
    for xy in path.pattan.lines:
        canvas.create_line(xy)
    r = 2
    for p in path.pattan.cross_points:
        #canvas.create_oval(p[0]-r,p[1]-r,p[0]+r,p[1]+r)
        pass
        
    window.mainloop()

if __name__ == '__main__':
    main()

 

[ソース3:パターンハッチング]

# -*- coding: utf-8 -*-

#import math
import Tkinter

class Node:
    p = [0.0,0.0]  #self point
    p0 = [0.0,0.0] #control point
    p1 = [0.0,0.0] #control point
    p0_avoid = False
    p1_avoid = False
    
    def __init__(self,p,p0,p1):
        self.p = p
        self.p0 = p0
        self.p1 = p1

class Path:
    nodes = []
    closed = False
    steps = 50
    
    def __init__(self,nodes = None):
        if nodes == None:
            return
        self.nodes = nodes
    
    def points(self):
        if len(self.nodes) == 0:
            return []
        points = []
        points.append(self.nodes[0].p)
        for i in range(1,len(self.nodes)):
            points += self._segment_points(i-1,i)
        if self.closed:
            points += self._segment_points(i,0)
            points = points[:-1]
        return points
        
    def _segment_points(self,i,j):
        points = []
        n0 = self.nodes[i]
        n1 = self.nodes[j]
        if n0.p1_avoid and n1.p0_avoid:
            points.append(n1.p)
        else:
            pts = self.bezier_curve_points(n0.p,n0.p1,n1.p0,n1.p)
            points += pts[1:]
        return points

    def bezier_curve_points(self,p0,p1,p2,p3):
        points = []
        for i in range(self.steps+1):
            t = float(i)/self.steps
            x = (1-t)**3*p0[0]+3*(1-t)**2*t*p1[0]+3*(1-t)*t**2*p2[0]+t**3*p3[0]
            y = (1-t)**3*p0[1]+3*(1-t)**2*t*p1[1]+3*(1-t)*t**2*p2[1]+t**3*p3[1]
            points.append((x,y))
        return points

class PattanTile:
    width = 20
    height = 20
    polylines = []
    
    lines = []
    cross_points = []
    xn = 0
    yn = 0
    
    def __init__(self,path):
        self.path = path
        self.polylines = [[0,0,0,20,20,20,20,0,0,0],[5,5,5,15,15,15]]
        self.update()

    def setPattan(self,width,height,polylines):
        self.width = width
        self.height = height
        self.polylines = polylines

    def update(self):
        area = self._area( self.path.points())
        self.xn = int((area[2]-area[0])/self.width)+1
        self.yn = int((area[3]-area[1])/self.height)+2
        tiles = [[[0,0,0,0] for j in range(self.yn)] for i in range(self.xn)]
        tiles = self._tile_cross_points(tiles,area)
        tiles = self._tile_states(tiles)
        self.lines = self._lines(tiles)

    def _area(self,points):
        area = [float('inf'),float('inf'),0.0,0.0] #xmin,ymin,xmax,ymax
        for p in points:
            if   area[0] > p[0]: area[0] = p[0]
            elif area[2] < p[0]: area[2] = p[0]
            if   area[1] > p[1]: area[1] = p[1]
            elif area[3] < p[1]: area[3] = p[1]
        return area

    def _tile_cross_points(self,tiles,area):
        nodes = self.path.nodes + [self.path.nodes[0]]
        sx = area[0]-self.width/2
        sy = area[1]-self.height/2
        for i in range(self.xn):
            for j in range(self.yn):
                x0 = sx + self.width*i
                y0 = sy + self.height*j
                x1 = x0 + self.width
                y1 = y0 + self.height
                blines = [(x0,y0,x0,y1),(x0,y0,x1,y0),
                          (x1,y0,x1,y1),(x0,y1,x1,y1)]
                cross_points = []
                for border_line in blines:
                    cross_points += self.crossPointsOfPath(border_line)
                tile = [x0,y0,0,len(cross_points)]
                tiles[i][j] = tile
                self.cross_points += cross_points
        return tiles

    def _tile_states(self,tiles):
        tile_states = [[0 for j in range(self.yn)] for i in range(self.xn)]
        for i in range(self.xn):
            for j in range(self.yn):
                cross_point_n = tiles[i][j][3]
                if cross_point_n > 1:
                    tile_states[i][j] = 4 #on border
        for i in range(self.xn):
            for j in range(self.yn):
                state = tile_states[i][j]
                if state > 0:
                    continue
                self._bflag = False
                tile_states = self._tile_fill(i,j,tile_states)
                if self._bflag:
                    state = 2 #outside path
                else:
                    state = 3 #inside path
                for k in range(self.xn):
                    for l in range(self.yn):
                        if tile_states[k][l] == 1:
                            tile_states[k][l] = state
        for i in range(self.xn):
            for j in range(self.yn):
                tiles[i][j][2] = tile_states[i][j]
        return tiles
    
    def _lines(self,tiles):
        lines = []
        for i in range(self.xn):
            for j in range(self.yn):
                state = tiles[i][j][2]
                if state == 3: #inside path
                    lines += self._pattan_lines(tiles[i][j])
                elif state == 4: #on border
                    _lines = self._pattan_lines(tiles[i][j])
                    lines += self._trim_lines(_lines)
        return lines
                    
    def _pattan_lines(self,tile):
        x,y,s,cpts = tile
        pattan_lines = []
        for polyline in self.polylines:
            for k in range(0,len(polyline)-2,2):
                x0 = x + polyline[k]
                y0 = y + polyline[k+1]
                x1 = x + polyline[k+2]
                y1 = y + polyline[k+3]
                line = [x0,y0,x1,y1]
                pattan_lines.append(line)
        return pattan_lines
    
    def _trim_lines(self,pattan_lines):
        trim_lines = []
        nodes = self.path.nodes + [self.path.nodes[0]]
        for line in pattan_lines:
            x0,y0,x1,y1 = line
            cross_points = self.crossPointsOfPath(line)
            p0_in_path = self.isPointInPath(x0,y0)
            p1_in_path = self.isPointInPath(x1,y1)
            cnt = len(cross_points)
            if cnt == 0:
                if p0_in_path and p1_in_path:
                    trim_lines.append(line)
                    pass
            elif cnt == 1:
                p = cross_points[0]
                if p0_in_path:
                    line = (x0,y0,p[0],p[1])
                else:
                    line = (p[0],p[1],x1,y1)
                    pass
                trim_lines.append(line)
            else:
                p = [(x0,y0)]+cross_points+[(x1,y1)]
                p.sort()
                if p0_in_path:
                    in_path = True
                else:
                    in_path = False
                for i in range(len(cross_points)+1):
                    if in_path:
                        line = (p[i][0],p[i][1],p[i+1][0],p[i+1][1])
                        trim_lines.append(line)
                    if in_path:
                        in_path = False
                    else:
                        in_path = True
            self.cross_points += cross_points
        return trim_lines

    def _tile_fill(self,x,y,array):
        array[x][y] = 1
    
        if y-1 > 0:
            if array[x][y-1]==0:
                array = self._tile_fill(x,y-1,array)
        
        if x+1 < self.xn:
            if array[x+1][y]==0:
                array = self._tile_fill(x+1,y,array)
    
        if y+1 < self.yn:
            if array[x][y+1]==0:
                array = self._tile_fill(x,y+1,array)
                
        if x-1 > 0:
            if array[x-1][y]==0:
                array = self._tile_fill(x-1,y,array)
                
        if x==0 or y==0 or x==self.xn-1 or y==self.yn-1:
            self._bflag = True
            
        return array
    
    def isPointInPath(self,x,y):
        nodes = self.path.nodes + [self.path.nodes[0]]
        cross_points = []
        line = (0,1,-y)
        pts = self.crossPointsOfPath(line)
        for p in pts:
            if x < p[0]:
                cross_points.append(p)
        if len(cross_points) % 2 == 1:
            return True
        else:
            return False
        
    def crossPointsOfPath(self,line):
        # line = [x0,y0,x1,y1] or [p0,p1] or [a,b,c]
        cnt = len(line)
        if cnt == 2:
            x0,y0 = line[0]
            x1,y1 = line[1]
            line_eq = line_generalization(line[0],line[1])
        elif cnt == 3:
            line_eq = line
        elif cnt == 4:
            x0,y0,x1,y1 = line
            line_eq = line_generalization((x0,y0),(x1,y1))
        cross_points = []
        nodes = self.path.nodes + [self.path.nodes[0]]
        m = 0.000001
        for k in range(len(self.path.nodes)):
            p0 = nodes[k].p
            p1 = nodes[k].p1
            p2 = nodes[k+1].p0
            p3 = nodes[k+1].p
            if nodes[k].p1_avoid and nodes[k+1].p0_avoid:
                ps = intersection_of_line_segment(p0,p3,line_eq)
            else:
                ps = intersection_of_bezier_and_line((p0,p1,p2,p3),line_eq)
            if cnt == 3: #no limit line lengh
                cross_points += ps
            else:
                for p in ps:
                    xmax,xmin = max((x0,x1)),min(x0,x1)
                    ymax,ymin = max((y0,y1)),min(y0,y1)
                    if (xmin-m <= p[0] and p[0] <= xmax+m and
                        ymin-m <= p[1] and p[1] <= ymax+m):
                        cross_points.append(p)
        return cross_points

def intersection_of_line_segment((x0,y0),(x1,y1),(a,b,c)):
    line = line_generalization((x0,y0),(x1,y1))
    points = intersection_2line(line,(a,b,c))
    m = 0.000001
    xs = [x0,x1]
    ys = [y0,y1]
    new_points = []
    for p in points:
        if (min(xs)-m <= p[0] and p[0] <= max(xs)+m and
            min(ys)-m <= p[1] and p[1] <= max(ys)+m):
            new_points.append(p)
    return new_points

def intersection_2line((a0,b0,c0),(a1,b1,c1)):
    t = float(a0*b1-a1*b0)
    if t == 0:
        return []
    x = (b0*c1-b1*c0)/t
    y = (a1*c0-a0*c1)/t
    return [(x,y)]

def intersection_of_bezier_and_line((p0,p1,p2,p3),(a,b,c)):
    f0 =      a * p0[0] + b * p0[1] + c
    f1 = 3 * (a * p1[0] + b * p1[1] + c)
    f2 = 3 * (a * p2[0] + b * p2[1] + c)
    f3 =      a * p3[0] + b * p3[1] + c

    _a = -f0 + f1 - f2 + f3
    _b = 3 * f0 - 2 * f1 + f2
    _c = -3 * f0 + f1
    _d = f0
    if _a == 0:
        if _b == 0:
            if _c == 0: tlist = []
            else: tlist = -_d/_c
        else:
            tlist = quadratic_equation(_b,_c,_d)
    else:
        tlist = cubic_equation(_a,_b,_c,_d)
    points = []
    for t in tlist:
        if t < 0 or 1 < t:
            continue
        x = (1-t)**3*p0[0]+3*(1-t)**2*t*p1[0]+3*(1-t)*t**2*p2[0]+t**3*p3[0]
        y = (1-t)**3*p0[1]+3*(1-t)**2*t*p1[1]+3*(1-t)*t**2*p2[1]+t**3*p3[1]
        points.append((x,y))
    return points

def cubic_equation(a,b,c,d):
    p = -b**2/(9.0*a**2) + c/(3.0*a)
    q = b**3/(27.0*a**3) - b*c/(6.0*a**2) + d/(2.0*a)
    t = complex(q**2+p**3)
    w =(-1.0 +1j*3.0**0.5)/2.0
    
    u = [0,0,0]
    u[0] = (-q +t**0.5)**(1.0/3.0)
    u[1] = u[0] * w
    u[2] = u[0] * w**2
    v = [0,0,0]
    v[0] = (-q -t**0.5)**(1.0/3.0)
    v[1] = v[0] * w
    v[2] = v[0] * w**2
    
    x_list = []
    for i in range(3):
      for j in range(3):
        if abs(u[i]*v[j] + p) < 0.0001:
            x = u[i] + v[j]
            if abs(x.imag) < 0.0000001:
                x = x.real - b/(3.0*a)
                x_list.append(x)
    return x_list

def quadratic_equation(a,b,c):
    d = b*b-4*a*c
    if d < 0:
        return []
    if d == 0:
        x = -b/(2.0*a)
        return [x]
    else:
        x0 = (-b+d**0.5)/(2.0*a)
        x1 = (-b-d**0.5)/(2.0*a)
    return [x0,x1]

def line_generalization(p0,p1):
    if p0[0] == p1[0]:
        a = 1.0
        b = 0.0
        c = float(-p0[0])
    elif p0[1] == p1[1]:
        a = 0.0
        b = 1.0
        c = float(-p0[1])
    else:
        slope = float(p1[1]-p0[1])/(p1[0]-p0[0])
        intercept = p0[1] - slope*p0[0]
        a = slope
        b = -1.0
        c = intercept
    return a,b,c

def main():
    window = Tkinter.Tk()
    canvas = Tkinter.Canvas(window,width=300,height=200)
    canvas.pack()
    
    n0 = Node([20,50],[30,10],[10,100])
    n1 = Node([50,180],[20,170],[80,190])  
    n2 = Node([140,100],[50,90],[120,190])
    n3 = Node([280,70],[250,150],[220,10])
    
    path = Path([n0,n1,n2,n3])
    path.closed = True
    n1.p1_avoid = True
    n2.p0_avoid = True
    path.pattan = PattanTile(path)
    
    if path.closed:
        canvas.create_polygon(path.points(),fill='gray',outline='red')
    else:
        canvas.create_line(path.points(),fill='red')
    r = 3
    for n in path.nodes:
        canvas.create_oval(n.p[0]-r,n.p[1]-r,n.p[0]+r,n.p[1]+r)
        canvas.create_oval(n.p0[0]-r,n.p0[1]-r,n.p0[0]+r,n.p0[1]+r)
        canvas.create_oval(n.p1[0]-r,n.p1[1]-r,n.p1[0]+r,n.p1[1]+r)
        canvas.create_line(n.p[0],n.p[1],n.p0[0],n.p0[1])
        canvas.create_line(n.p[0],n.p[1],n.p1[0],n.p1[1])
    for xy in path.pattan.lines:
        canvas.create_line(xy,fill='blue')
    r = 2
    for p in path.pattan.cross_points:
        #canvas.create_oval(p[0]-r,p[1]-r,p[0]+r,p[1]+r)
        pass
        
    window.mainloop()

if __name__ == '__main__':
    main()

Updated: 2019年1月15日 — 23:21

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です