# L-6 MCS 275 Mon 23 Jan 2017 : makedata.py

"""
Generates k clusters of some number of points with integer coordinates.
"""

from random import random, randint
import turtle
from turtle import tracer
from math import cos, sin, pi, sqrt

def cluster(nbr, ctr, rad):
    """
    Returns a list of nbr points with
    integer coordinates, in a disk with
    center ctr and radius rad.
    We use the polar representation of the points.
    """
    points = []
    points.append(ctr)
    for _ in range(1, nbr):
        rpt = rad*random()  # radius of the point
        apt = 2*pi*random() # angle of the point
        xpt = ctr[0] + round(rpt*cos(apt))
        ypt = ctr[1] + round(rpt*sin(apt))
        points.append((xpt, ypt))
    return points

def make_data():
    """
    Returns a list of lists of points with integer coordinates.
    The user is prompted for the number of lists, and for each list:
    nbr : the number of points in the cluster,
    ctr : the center of the cluster,
    rad : the radius of the cluster.
    Then nbr points with integer coordinates in a disk centered
    at ctr and with radius rad will be generated.
    """
    data = []
    ndc = int(input("Give the number of clusters : "))
    for i in range(ndc):
        nbr = int(input("-> give the number of points in cluster %d : " % i))
        ctr = float(input("give the center of the cluster %d as tuple : " % i))
        rad = float(input("         give the radius of the cluster %d : " % i))
        data.append(cluster(nbr, ctr, rad))
    return data

def typical_data():
    """
    Generates random numbers for (nbr, ctr, rad).
    """
    data = []
    ndc = int(input("Give the number of clusters : "))
    for _ in range(ndc):
        nbr = randint(10, 100)
        ctr = (randint(-200, 200), randint(-200, 200))
        rad = randint(10, 50)
        data.append(cluster(nbr, ctr, rad))
    return data

def centroid(points):
    """
    Given a list points of tuples (x, y),
    returns the centroid of the points.
    """
    nbr = len(points)
    if nbr == 0:
        return (0, 0)
    else:
        xsm = ysm = 0
        for pnt in points:
            xsm = xsm + pnt[0]
            ysm = ysm + pnt[1]
        ctr = (xsm/nbr, ysm/nbr)
        return ctr

def centroids(data):
    """
    Given in data a list of lists of points,
    returns the list of centroids.
    """
    return [centroid(points) for points in data]

def distance(p, q):
    """
    Returns the Euclidean distance between p and q.
    """
    x = p[0] - q[0]
    y = p[1] - q[1]
    return sqrt(x*x + y*y)

def radius(points, ctr):
    """
    Returns the largest distance of any point
    in points to the centroid ctr.
    """
    if len(points) == 0:
        return 0
    else:
        dst = [distance(pnt, ctr) for pnt in points]
        return max(dst)

def radii(L, C):
    """
    Given on input in L a list of lists
    and in C the corresponding centroids,
    returns the list of radii for each centroid.
    """
    R = []
    for k in range(0, len(L)):
        R.append(radius(L[k], C[k]))
    return R

def show_data(data):
    """
    For each point in data, draws
    a circle of radius 2 pixels,
    centered at the data point.
    """
    print(data)
    tracer(False)
    for points in data:
        for pnt in points:
            turtle.up()
            turtle.goto(pnt[0], pnt[1]-2)
            turtle.down()
            turtle.circle(2)
    turtle.up()
    turtle.goto(0, 0)

def show_centroids(ctr, rad):
    """
    Given on input in ctr a list of
    centroids and in rad the corresponding
    radii, draws the disks.
    """
    for k in range(len(ctr)):
        cpt = ctr[k]
        rd2 = rad[k]+2
        turtle.up()
        turtle.goto(cpt[0], cpt[1]-rd2)
        turtle.down()
        turtle.circle(rd2)
    turtle.up()

def save_to_file(data):
    """
    Prompts the user for a file name
    and writes the data to file.
    """
    name = input("give a file name : ")
    datafile = open(name, 'w')
    datafile.write(str(data))
    datafile.close()

def main():
    """
    Generates a list of clustered points.
    """
    ans = input("typical input data ? (y/n) ")
    if ans == 'y':
        data = typical_data()
    else:
        data = make_data()
    ctrs = centroids(data)
    rads = radii(data, ctrs)
    show_data(data)
    show_centroids(ctrs, rads)
    ans = input("Save to file ? (y/n) ")
    if ans == 'y':
        save_to_file(data)

if __name__ == "__main__":
    main()
