#!/usr/bin/python
# -*- encoding: utf-8 -*-

from tkinter import *
import random
import math


# Dimenzije canvasa
WIDTH=500
HEIGHT=500

def razdalja(p,q):
    """Razdalja med točkama p in q v ravnini."""
    (px,py) = p
    (qx,qy) = q
    return math.sqrt((px-qx)*(px-qx) + (py-qy)*(py-qy))


######################################################################
# Optimizacija delcev, ki delujejo drug na drugega z dano silo.
# Optimizacijski problem sam poskrbi, da se animira v canvasu in da se
# izpisujejo podatki o problemu v polje info. Optimizacijski problem
# mora vsebovati metodo stop(self), s katero ga ustavimo.

class Delci():
    ime = "Delci"

    def __init__(self, n, info, canvas):
        self.info = info
        self.canvas = canvas
        self.korak = 0
        self.vozlisce = [(random.random(), random.random()) for i in range(n)]
        self.krogec = []
        for p in self.vozlisce:
            x = WIDTH * p[0]
            y = HEIGHT * p[1]
            self.krogec.append(canvas.create_oval(x-3, y-3, x+3, y+3))
        self.animacija() # Aktiviraj animacijo


    def potencial(self, x):
        magicniFaktor = 0.2
        return 1/(50*x) + x + magicniFaktor * math.cos(8 * math.pi * x)

    def energija(self):
        e = 0.0
        for p in self.vozlisce:
            for q in self.vozlisce:
                if p != q: e = e + self.potencial(razdalja(p,q))
        return e

    def spremembaEnergije(self, k, p):
        """Sprememba v energiji, če k-to točko prestavimo v p."""
        de = 0.0
        q = self.vozlisce[k] # Trenutna pozicija k-te točke
        for (j,r) in enumerate(self.vozlisce):
            if j != k:
                de = de + self.potencial(razdalja(r,p)) # Novi prispevek prištejemo
                de = de - self.potencial(razdalja(r,q)) # Stari prispevek odštejemo
        return de

    def perturbacija(self, p):
        d = 0.01
        return (p[0] + random.uniform(-d, d), p[1] + random.uniform(-d, d))

    def optimiraj(self):
        for (k,p) in enumerate(self.vozlisce):
            q = self.perturbacija(p)
            if self.spremembaEnergije(k,q) < 0.0:
                self.vozlisce[k] = q

    def stop(self):
        self.canvas.after_cancel(self.after_id)

    def animacija(self):
        self.korak = self.korak + 1
        self.optimiraj()
        for (k,id) in enumerate(self.krogec):
            x = WIDTH * self.vozlisce[k][0]
            y = HEIGHT * self.vozlisce[k][1]
            self.canvas.coords(id, x-3, y-3, x+3, y+3)
        self.info.set("i = %d, energija = %g" % (self.korak, self.energija()))
        self.after_id = self.canvas.after(10, self.animacija)

# Glavna aplikacija

class Optimizator():
    def __init__(self, master, problemi):
        # Seznam vseh optimizacijskih problemov
        self.problemi = problemi
    
        # Trenutno izbrani problem
        self.problem = self.problemi[0]
        self.aktivna_instanca = None # Trenutno aktivna instanca problema

        # Ime trenutno izbranega problema
        self.selected = StringVar(master, value=self.problem.ime)

        # Podatki o stanju problema
        self.info = StringVar(master, value="")
        Label(master, textvariable=self.info).grid(row=0, column=1, columnspan=5)

        # Področje za risanje
        self.canvas = Canvas(master, width=WIDTH, height=HEIGHT)
        self.canvas.grid(row=1, column=0, columnspan=5)

        # Menu za izbiro problema
        self.selection = Menubutton(master,
                                    textvariable=self.selected,
                                    relief = RAISED)
        menu = Menu(self.selection, tearoff=0)
        for p in self.problemi:
            def select(p=p):
                self.problem = p
                self.selected.set(p.ime)
            menu.add_command(label=p.ime, command=select)
        self.selection["menu"] = menu
        self.selection.grid(row=2, column=0)

        # Velikost problema in gumb za ponovni začetek
        self.velikost = IntVar(master, value=5)
        Label(master, text = "n = ").grid(row=2, column=2)
        Entry(master, textvariable = self.velikost).grid(row=2, column=3)
        Button(master, text = "Posodobi", command = self.restart).grid(row=2, column=4)

        # Aktiviraj optimizacijo
        self.restart()

    def restart(self):
        if self.aktivna_instanca is not None: self.aktivna_instanca.stop()
        self.canvas.delete(ALL)           
        self.aktivna_instanca = self.problem(self.velikost.get(), self.info, self.canvas)


# GLAVNI PROGRAM

root = Tk()
root.title("Optimizator")
problemi = (
    Delci,
)
application = Optimizator(root, problemi)
root.mainloop()

