Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Tk_png_steady_tracer_to_svg.py
- # Had attempted to write this around 2010 as a beginner then figured maybe this was an impossible feat for Python
- # I still can't find a way to solve the few odd issues in it.
- from tkinter import *
- from math import *
- import webbrowser
- from PIL import ImageTk, Image
- from tkinter import filedialog
- from time import *
- def mouse_down(e):
- cv.px = int(e.x/unit) * unit
- cv.py = int(e.y/unit) * unit
- def mouse_up(e):
- cv.x = 0
- cv.y = 0
- vectors.append(tmp_vectors[:])
- tmp_vectors.clear()
- def mouse_move(e):
- cv.x = e.x
- cv.y = e.y
- draw_lines()
- def scroll_dist():
- dist = sqrt((cv.x - cv.width/2)**2 + (cv.y - cv.height/2)**2)
- if 50 < dist:
- cv.scrx = cv.scrx + (cv.x - cv.width/2)*0.00001
- cv.scry = cv.scry + (cv.y - cv.height/2)*0.00001
- canvas.xview_scroll(int(cv.scrx), 'units')
- canvas.yview_scroll(int(cv.scry), 'units')
- cv.scrx = cv.scrx - int(cv.scrx)
- cv.scry = cv.scry - int(cv.scry)
- # to resize (w,h) to fit the target area size
- def resize(w, h, target_area):
- ratio = (target_area / (w * h))**0.5
- return int(w * ratio), int(h * ratio)
- def nearest():
- cv.x = int(cv.x/unit) * unit
- cv.y = int(cv.y/unit) * unit
- def key_release(e):
- key = e.keysym
- print('---')
- if key in ('Control_L'):
- canvas.unbind('<B1-Motion>')
- cv.scrx = 0
- cv.scry = 0
- def key_press(e):
- key = e.keysym
- print('+++')
- if key in ('Control_L'):
- canvas.bind('<B1-Motion>', mouse_move)
- elif key.lower() == 'x':
- delete_selected_vector_points()
- elif key.lower() == 't':
- toggle_bezier_trace()
- # draw lines with mouse drag that snaps to the grid as vector points
- def draw_lines():
- scroll_dist()
- if cv.x:
- nearest()
- if (cv.x, cv.y) not in vectors_occupy:
- vectorize()
- canvas.create_line(cv.px, cv.py, cv.x, cv.y, fill='black', tags=('mv','pts'))
- cv.px = cv.x
- cv.py = cv.y
- # root.after(1, draw_lines) ### ??? glitches
- def vectorize():
- tmp_vectors.append((cv.x, cv.y))
- vectors_occupy.append((cv.x, cv.y))
- for xy in ((cv.x+1, cv.y),(cv.x, cv.y+1),(cv.x-1, cv.y),(cv.x, cv.y-1)):
- vectors_occupy.append(xy)
- def delete_all():
- canvas.delete('pts')
- vectors.clear()
- vectors_occupy.clear()
- def delete_selected_vector_points():
- # delete when 'x' is press over a vector point
- nearest()
- for k,v in enumerate(vectors):
- try:
- v.remove((cv.x, cv.y))
- vectors[k].remove((cv.x, cv.y))
- break
- except: 0
- for xy in ((cv.x+1, cv.y),(cv.x, cv.y+1),(cv.x-1, cv.y),(cv.x, cv.y-1)):
- try: vectors_occupy.remove(xy)
- except: 0
- # redraw every vector line
- canvas.delete('pts')
- for line in vectors:
- canvas.create_line(line, fill='black', tags=('mv','pts'))
- def bezier_curve(points):
- n = len(points)
- b = []
- for t in range(0, 200):
- t = t/200
- x = y = 0
- for i, p in enumerate(points):
- x += p[0] * binomial(n, i) * (1 - t)**(n-i) * t**i
- y += p[1] * binomial(n, i) * (1 - t)**(n-i) * t**i
- b.append((x, y))
- return b[:-1]+points[-2:-1]
- def binomial(n, k):
- if 0 <= k <= n:
- n_tok = 1
- k_tok = 1
- for t in range(1, min(k, n - k) + 1):
- n_tok *= n
- k_tok *= t
- n -= 1
- return n_tok // k_tok
- else:
- return 0
- def toggle_bezier_trace():
- cv.trace = abs(cv.trace-1)
- if cv.trace:
- canvas.itemconfig('pts', fill='red')
- svg()
- else:
- canvas.delete('svg')
- canvas.itemconfig('pts', fill='black')
- def draw_grid():
- for x in range(0, sq_units*4, unit):
- canvas.create_line([(x, 0), (x, sq_units*4)], fill='lightblue', tags=('mv','g'))
- for y in range(0, sq_units*4, unit):
- canvas.create_line([(0, y), (sq_units*4, y)], fill='lightblue', tags=('mv','g'))
- def make_image_transparent():
- # create transparent image
- img = Image.new('RGBA', (cv.image.width, cv.image.height), (0, 0, 0, 0))
- # create a pixel map
- pixels = img.load()
- for x in range(cv.image.width):
- for y in range(cv.image.height):
- r, g, b = cv.image.getpixel((x, y))
- # change pixel transparency
- pixels[x, y] = (r, g, b, 60)
- ww, hh = resize(cv.image.width, cv.image.height, sq_units*sq_units)
- cv.image_resized = img.resize((ww, hh), Image.ANTIALIAS)
- delete_all()
- # place image
- cv.image_on_canvas = ImageTk.PhotoImage(cv.image_resized)
- canvas.create_image(120, 120, image=cv.image_on_canvas, anchor="nw", tag='mv')
- canvas.delete('g')
- draw_grid()
- cv.width = ww
- cv.height = hh
- canvas.config(scrollregion=(0,0,ww+240,hh+240))
- def svg():
- for line in vectors:
- cv.svg_image = canvas.create_line(bezier_curve(line), fill='green', width=3, tags=('mv','svg'))
- root = Tk()
- root.geometry('+-10+0')
- root.title("PNG to SVG Tracer")
- class Cv(): 0
- cv = Cv()
- cv.x = 0
- cv.y = 0
- cv.px = 0
- cv.py = 0
- cv.scrx = 0
- cv.scry = 0
- cv.trace = 0
- cv.filename = 0
- cv.image = 0
- cv.png = 0
- cv.image_on_canvas = 0
- cv.image_size = 0
- cv.svg_image = 0
- cv.prev = 0
- cv.timer = time()
- unit = 20
- cv.width = 1280
- cv.height = 640
- sq_units = 4000
- vectors = []
- vectors_occupy = [] # <<<<< Python ignores this?
- tmp_vectors = []
- frame = Frame(root)
- frame.pack()
- # create canvas with scrollbars
- canvas = Canvas(frame, width=cv.width, height=cv.height, bg='white')
- canvas.pack(side=LEFT, expand=True, fill=BOTH)
- canvas.pack_propagate(False)
- # needed to detect all the keyboard events <<<<<
- canvas.focus_set()
- draw_grid()
- # create scrollbars
- vbar = Scrollbar(canvas)
- vbar.pack(side=RIGHT, fill=Y)
- hbar = Scrollbar(canvas, orient=HORIZONTAL)
- hbar.pack(side=BOTTOM, fill=X)
- # attach canvas to scrollbars
- canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
- # bind scrollbars to the canvas
- hbar.config(command=canvas.xview)
- vbar.config(command=canvas.yview)
- # set the scrollregion to all
- canvas.config(scrollregion=(0,0,sq_units,sq_units))
- '''
- '''
- def load_image():
- cv.filename = filedialog.askopenfilename(initialdir='/', title='Select file', filetypes=(('png files', '*.png'), ('all files', '*.*')))
- cv.image = Image.open(cv.filename)
- make_image_transparent()
- def save_as_svg():
- svg()
- pad = 20
- min_x = max_x = min_y = max_y = min(cv.width/2,cv.height/2)
- for vector in sum(vectors, []):
- if vector[0] < min_x:
- min_x = vector[0]
- if vector[1] < min_y:
- min_y = vector[1]
- if vector[0] > max_x:
- max_x = vector[0]
- if vector[1] > max_y:
- max_y = vector[1]
- cv.svg_width = int(max_x - min_x + pad)
- cv.svg_height = int(max_y - min_y + pad)
- cv.svg_x = -min_x + pad
- cv.svg_y = -min_y + pad
- # open new canvas then fit copy of padded cv.svg_image into it
- cv.svg = Image.new('RGBA', (cv.svg_width, cv.svg_height), (0, 0, 0, 0))
- # create a pixel map
- pixels = cv.svg.load()
- # copy cv.svg_image into new image
- for item in canvas.find_withtag('svg')[1:]:
- # r, g, b, a = cv.svg_image.getpixel((x, y))
- x, y = canvas.coords(item)
- x = int(x - cv.svg_x)
- y = int(y - cv.svg_y)
- # color = canvas.itemcget(item, 'fill')
- pixels[x, y] = (0, 0, 0, 255)
- # save new image
- cv.svg.save(cv.filename.split('.')[0] + '.svg')
- # open new image in browser
- webbrowser.open(cv.filename.split('.')[0] + '.svg')
- downsized_image = cv.image.resize((cv.image.width, cv.image.height), Image.ANTIALIAS)
- # place image
- cv.image_on_canvas = ImageTk.PhotoImage(downsized_image)
- canvas.create_image(120, 120, image=cv.image_on_canvas, anchor="nw", tag='mv')
- def add_vectors(e):
- if not cv.prev:
- cv.prev = canvas.find_withtag('selected')
- L = len(cv.prev)
- if L:
- index = cv.prev.index(cv.prev)[-1]
- canvas.create_oval(canvas.coords(cv.prev[index]), fill='darkblue', width=6, tags=('mv', 'tmp'))
- if index:
- canvas.create_oval(canvas.coords(cv.prev[index-1]), fill='blue', tags=('mv', 'tmp'))
- if index != L:
- canvas.create_oval(canvas.coords(cv.prev[index]), fill='blue', tags=('mv', 'tmp'))
- # else if a blue dot gets selected, delete both and the line in between, then connect that point to the cursor motion, then the next line to the cv.prev[index]
- elif canvas.itemcget(cv.prev[-1], 'fill') == 'blue':
- nearest()
- for vector in vectors:
- try:
- cv.index = vector.index((cv.x, cv.y))
- canvas.delete('tmp')
- cv.prev = 0
- break
- except: 0
- # create buttons
- button_open = Button(canvas, text='Load Image', command=load_image)
- button_open.pack(side=LEFT, anchor=NW)
- button_save = Button(canvas, text='Save SVG', command=save_as_svg)
- button_save.pack(side=LEFT, anchor=NW)
- button_clear = Button(canvas, text='Clear', command=delete_all)
- button_clear.pack(side=LEFT, anchor=NW)
- button_clear = Button(canvas, text='Trace', command=toggle_bezier_trace)
- button_clear.pack(side=LEFT, anchor=NW)
- canvas.bind("<KeyRelease>", key_release)
- canvas.bind("<KeyPress>", key_press)
- canvas.bind("<Button-1>", mouse_down)
- canvas.bind("<B1-Motion>", mouse_move)
- canvas.bind("<ButtonRelease-1>", mouse_up)
- root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement