#!/usr/bin/python
# traces2vecs.py
# This program is for converting traces in a graph to an approximation
# of those traces as vectors whose values are the values on the axes of 
# the figure.  It relies on the user having access to idraw (comes with
# NEURON).
# traces2vecs.py accepts as input a postscript file and writes either
# NEURON or matlab/octave style vectors of the traces in the graph.
# This postscript file is assumed to be the result of importing
# a research paper's figure (as a tif) into a new idraw figure and then
# editing the figure (using idraw) by selecting the polygon writing tool (e)
# and then left clicking to start and add points to a polygon, the right
# click will remove one or more accidental points from the line you are
# creating and the middle click supplies the end point for the line.
# When done tracing the curves in idraw, select file -> save and
# type or click a file name.  When the file is saved the pixel values
# of all the points will be written as text at the end of the file.
# The begining of the file will include the image that was imported.
# Important:
# The first three points you click will need to be the axis
# traces2vecs.py assumes that the first three points (at the end of the postscript
# file) delimit the axis by tracing
# the (x,y) points on the axis:
# (0, y_axis_extreme), (0,0), (x_axis_extreme,0)
# The order of the mouse clicks on the max y, (0,0), max x axis is important
# because the values of these points are user pre-supplied in a data file
# called axis_limits.dat whose first line is the x_axis_extreme and the second
# line is y_axis_extreme.  traces2vecs.py uses the linear transformation created
# by using axis_limits.dat and the pixel values recorded on the first three
# clicks.
# The output files are given the names traceX.dat where
# trace0.dat should reflect the values in axis_limits.dat and
# traceX with X from 1 to the number of traces are the subsequent traces.


""" usage:
 traces2vecs.py input.ps
 or
 traces2vecs.py input.id
 (it doesn't matter what the input file is called as long it's idraw output)
 Running assumes axis_limits.dat exists in the current dir and has four lines:
 the max y value (the yvalue where your first click will be)
 the min y value (the y value where your second click will be
 the min x value (the x value where your second click will be)
 the max x value (the x value where your third click will be)
 which are the paper's scale values.
"""

# note that the extreme points on the axis are called max below
# and that each point has an x,y value, e.g. the first point
#
# Variables with suffix pix are pixel values, otherwise they are
# the "graphed" values, i.e. correspond to the numbers on the graph tic marks or scales
#

import os
import sys
input_ps_name = ""
if (len(sys.argv)!=2):
  print "I notice no idraw output file was supplied as an argument - I will look later for one..."
else:
  input_ps_name = sys.argv[1]
  print input_ps_name
  #
  # read in the values from the axis limits file
  print("Opening "+input_ps_name +" for finding your manually entered")
  print("axis clicks (top y, origin, left x) and subsequent traces.\n")

fid=0 # file id for idraw file given global scope for later analysis

try:
  axis_limits_file = open( "axis_limits.dat","r")
except:
    print("Couldn't open axis_limits.dat file")
    print("This file needs to be in the current directory and")
    print("contains:\nthe max y value\nthe min y value\nthe min x value")
    print("the max x value\n(on seperate lines)\n")
    print("These user supplied numbers are the paper figures values corresponding to your")
    print("first three mouse clicks on the figure (high y axis, origin, far right x axis)")
    print("Then traces2vecs.py will use the pixel values as returned by idraw matched")
    print("to the above axis_limits user supplied data to transform the subsequent curves.")
    exit(1)

  # another possibility would be to use while (fgets(buf,1000, axis_limits_file)!=NULL) {}
axis_limits_array=axis_limits_file.readlines()

global y_axis_max_y, y_axis_min_y, x_axis_min_x, x_axis_max_x 
y_axis_max_y = eval(axis_limits_array[0])
y_axis_min_y = eval(axis_limits_array[1])
x_axis_min_x = eval(axis_limits_array[2])
x_axis_max_x = eval(axis_limits_array[3])

print("From axis_limits.dat:\n")
print("y_axis_max_y = "+str(y_axis_max_y)+"\ny_axis_min_y = "+str(y_axis_min_y)+\
  "\nx_axis_min_x = "+str(x_axis_min_x)+"\nx_axis_max_x = "+str(x_axis_max_x))

found_idraw_file=0 # 0 is false: set true if find an idraw file
if (len(sys.argv)>1):
  try:
    fid=open(sys.argv[1],"r")
    found_idraw_file=-1 # set true
  except:
    print("Couldn't open "+sys.argv[1]+" file")
    print("This file needs to be in the current directory/specified path")
    exit(1)
else:
  print "No idraw file supplied as input."
  print "I am going to look for an idraw file"
  files=os.listdir(".")
  for f in files:
    if (f[-3:]=='.id'):
      try:
        print "*** Found one!: Using "+f+" as idraw file (to read)."
        fid=open(f,"r")
        found_idraw_file=-1
      except:
        print("For some reason could not open an idraw file, "+f+", I found")
        exit(1)
      break
if (~found_idraw_file):
  print ("Wasn't supplied or couldn't find an idraw (.id) file in current directory.")
  exit(1)
# print 
start_position=0
file_data=fid.readlines()
for i in range(len(file_data)):
  # print "Checking: "+file_data[i]
  if file_data[i]=="Begin %I MLine\n":
    # print "found an MLine at line "+str(i)
    start_position=i
    break
yaxis_vecs=file_data[start_position+11] # the x,y pix vector of top of y axis
yaxis_vecs_array=yaxis_vecs.split(" ")
# print "start_position="+str(start_position)
# print yaxis_vecs_array
yaxis_xvec=eval(yaxis_vecs_array[0])
yaxis_yvec=eval(yaxis_vecs_array[1])
origin_vecs=file_data[start_position+12] # the x,y pix vector of top of y axis
origin_vecs_array=origin_vecs.split(" ")
origin_xvec=eval(origin_vecs_array[0])
origin_yvec=eval(origin_vecs_array[1])
xaxis_vecs=file_data[start_position+13] # the x,y pix vector of top of y axis
xaxis_vecs_array=xaxis_vecs.split(" ")
xaxis_xvec=eval(xaxis_vecs_array[0])
xaxis_yvec=eval(xaxis_vecs_array[1])
#
# now have the pixel values for the top y, origin, left x axes so
# need to combine them with the read-in figure scale values
#
print "Using your idraw 'first three clicks' line for the axis values:\n"
print "yaxis max: ",yaxis_xvec, ", ", yaxis_yvec
print "origin:    ",origin_xvec, ", ", origin_yvec
print "xaxis max: ",xaxis_xvec, ", ", xaxis_yvec

#x_fig = ((x_axis_max_x-x_axis_min_x)/(x_axis_pix_max - x_axis_pix_origin))*(x_pix - x_axis_pix_origin)
#y_fig = ((y_axis_max_y-y_axis_min_y)/(y_axis_pix_max - y_axis_pix_origin))*(y_pix - y_axis_pix_origin)

x_axis_pix_max = xaxis_xvec
x_axis_pix_origin = origin_xvec

y_axis_pix_max = yaxis_yvec
y_axis_pix_origin = origin_yvec

def x_pix2x_fig(x_pix):
  # converts a pixel x-coordinate to a figure x coordintate
  # note that without float below it is possible that integer arithmatic will corrupt the calculation
  return (float(x_axis_max_x-x_axis_min_x)/(x_axis_pix_max - x_axis_pix_origin))*(x_pix - x_axis_pix_origin)+x_axis_min_x

def y_pix2y_fig(y_pix):
  # note that without float below it is possible that integer arithmatic will corrupt the calculation
  return (float(y_axis_max_y-y_axis_min_y)/(y_axis_pix_max - y_axis_pix_origin))*(y_pix - y_axis_pix_origin)+y_axis_min_y

print "confirmation run of conversions x_pix2xfig, y_pix2y_fig:"
print "upper y axis: "+str(x_pix2x_fig(yaxis_xvec))+", "+str(y_pix2y_fig(yaxis_yvec))
print "graph origin: "+str(x_pix2x_fig(origin_xvec))+", "+str(y_pix2y_fig(origin_yvec))
print "right x axis: "+str(x_pix2x_fig(xaxis_xvec))+", "+str(y_pix2y_fig(xaxis_yvec))

# now use the transformation functions to create a list of traces, where each trace
# is a list of (x,y) points:
# [[[trace0_x_0, trace0_y_0], [trace0_x_1, trace0_y_1], ..., [trace0_x_n0, trace0_y_n0]],
#  [[trace1_x_0, trace1_y_0], [trace1_x_1, trace1_y_1], ..., [trace1_x_n1, trace1_y_n1]],
# ...
#  [[tracej_x_0, tracej_y_0], [tracej_x_1, tracej_y_1], ..., [tracej_x_nj, tracej_y_nj]]]
# where trace0 corresponds to the first three clicks being the axis (it is OK to add subsequent clicks
# to make a greater then three point line for this first trace, the zeroth trace.
#
#  There are n_j (nj) traces in all with the ith trace containing n_i (ni) points.

# count the number of traces
num_of_traces=0
trace_start_index=[] # a list of the indicies i of file_data[i] of the line
# before a trace starts.  Includes the line that contains the number of points in the format
# "
index=0
for d in file_data:
  if (d=="Begin %I MLine\n"):
    num_of_traces = num_of_traces + 1
    trace_start_index.append(index+10)
  index=index+1
#
# now ready to read in the traces
#
num_in_trace=[]
traces = []
current_trace=[]
# print "trace_start_index:"
# print trace_start_index
for index in trace_start_index:
  current_trace=[] # reinitialize the current trace
  # the number in the current trace
  # print "file_data[index][3:]="+file_data[index][3:]
  points_in_polyline=eval(file_data[index][3:])
  # for i in range(points_in_polyline):
  #   print "i = "+str(i)
  #   print file_data[index+1+i][:]
  num_in_trace.append(points_in_polyline) # gets the 6 from "%I 6\n"
  # get the traces
  for i in range(num_in_trace[-1]):
    current_vector=file_data[index+1+i]
    current_vector_array = current_vector.split(" ")
    current_vector_x = eval(current_vector_array[0])
    current_vector_y = eval(current_vector_array[1])
    vector_list_element = [current_vector_x, current_vector_y]
    current_trace.append(vector_list_element)
  # now add the trace to the list of traces
  traces.append(current_trace)
# the traces pixel list vectors have been created
# write them as NEURON vectors traceX.dat
for X in range(len(traces)): # iterate over the number of traces
  filename="trace"+repr(X)+".dat"
  file=open(filename,"w")
  file.write("label: trace"+repr(X)+"\n")
  file.write(repr(len(traces[X]))+"\n")
  for i in range(len(traces[X])):
    file.write(repr(x_pix2x_fig(traces[X][i][0]))+" "+repr(y_pix2y_fig(traces[X][i][1]))+"\n")
  file.close()