#Py3D.py
#Contains all code to render and run 3D games

#Imports required modules
import time,sys,math
from operator import itemgetter

#Import pygame
import pygame
from pygame.locals import *

#Variables

black=(0,0,0)
white=(255,255,255)
blue=(0,0,255)
red=(255,0,0)
green=(0,255,0)
yellow=(255,255,0)
cyan=(0,255,255)
magenta=(255,0,255)
light_blue=(100,170,255)
pink=(255,150,255)
purple=(180,65,255)
grey=(150,150,150)
dark_grey=(80,80,80)
light_grey=(180,180,180)
dark_blue=(0,10,100)
dark_green=(10,110,10)
lime_green=(200,250,40)
orange=(250,130,0)

#Classes

#Main game class
class Game(object):
    def __init__(self,fov=75,ticks_per_second=40,max_fps=120,use_wait=True,
                 update_callback=None,frame_callback=None,time_source=None,max_frame_skip=5,
                 min_brightness=100,min_colour=white, debug=False, caption="Py3D Game", gui=None,
                 tick_handler=None, render_distance=500, light_distance=0):

        #Starts pygame
        pygame.init()

        #Screen Related
        self.screen=pygame.display.set_mode((0,0),pygame.FULLSCREEN)
        self.width,self.height=self.screen.get_size()

        #Settings
        self.ticks_per_second = ticks_per_second
        self.max_fps = max_fps
        self.max_frame_skip = max_frame_skip
        self.use_wait = use_wait
        self.dilation = 1.0
        self.fov=fov
        pygame.display.set_caption(caption)
        self.debug = debug
        self.auto_move = True

        #Rendering Settings
        self.displayNodes = 0
        self.displayEdges = 0
        self.displayFaces = 1
        self.displayLights = 0
        self.displayNorms = 0
        self.displayRays = 0
        self.background = black
        self.node_colour = green
        self.edge_colour = blue
        self.nodeRadius = 5
        self.min_brightness=min_brightness
        self.min_colour=min_colour
        self.render_distance=render_distance
        self.light_distance=light_distance

        #Clock Related
        self._wait = time.sleep
        if time_source is not None:
            self._get_ticks = time_source
        elif sys.platform in ('win32','cygwin'):
            self._get_ticks = time.clock
        else:
            self._get_ticks = time.time

        self.time = self._get_ticks()
        self._elapsed = 0.0
        self._frames_skipped = 0
        self._last_update = self.time
        self._last_frame = self.time
        self._update_ready = True
        self._frame_ready = True
        self._tps = 0
        self.tps = 0
        self.fps = 0
        self.ups = 0
        self.frame_count = 0
        self.update_count = 0

        #Other
        self._done = False
        self._skip_console = False
        self.objects = {}
        self.tick_handler = tick_handler
        self.gui = gui
        self.instance = Instance(0,0,0,0,0,0)
        self.lights = []
        self.events= []

    #Properties
    @property
    def fov(self):
        return self._fov
    @fov.setter
    def fov(self, val):
        if val > 0:
            self._fov = val
        else:
            self._fov = 75
        self._scr_dist=math.tan(math.radians(180-val/2+90))*self.width/2

    @property
    def ticks_per_second(self):
        return self._ticks_per_second
    @ticks_per_second.setter
    def ticks_per_second(self, val):
        if val > 0:
            self._ticks_per_second = val
        else:
            self._ticks_per_second = 25
        self._tick_step = 1.0 / self._ticks_per_second

    @property
    def max_fps(self):
        return self._max_fps
    @max_fps.setter
    def max_fps(self, val):
        if val > 0:
            self._max_fps = val
            self._frame_step = 1.0 / val
        else:
            self._max_fps = 0
            self._frame_step = 0

    @property
    def use_wait(self):
        return self._use_wait
    @use_wait.setter
    def use_wait(self, enabled):
        self._use_wait = enabled
        self._tps = float(self.max_fps)

    @property
    def max_frame_skip(self):
        return self._max_frame_skip
    @max_frame_skip.setter
    def max_frame_skip(self, n):
        if n > 0:
            self._max_frame_skip = n
        else:
            self._max_frame_skip = 0

    #Main loop
    #Updates objects, controls clock, renders game and registers iconify and close of window
    def loop(self):
        while not self._done:
            TIME = self._get_ticks()
            self._ticks = (TIME - self.time) / self.dilation
            self._elapsed += self._ticks
            self.time = TIME

            if self._elapsed >= 1.0:
                self._elapsed %= 1.0
                self.tps = self._tps
                self._tps = 0
                self.fps = self.frame_count
                self.ups = self.update_count
                self.frame_count = self.update_count = 0

            self._tps += 1
            self._update_ready = self._frame_ready = False

            if TIME >= self._last_update+self._tick_step*self.dilation:
                self._update_ready = True

            if self.max_fps == 0:
                self._frame_ready = True
            elif self._use_wait and self._max_fps > 0:
                wait_sec = self._last_frame + self._frame_step - self._get_ticks()
                if wait_sec > 0:
                    self._wait(wait_sec)
                self._frame_ready = True
            elif not self._use_wait and self._max_fps > 0:
                if self._last_frame + self._frame_step < self._get_ticks():
                    self._frame_ready = True

            if self._update_ready:
                self.update_count += 1
                self._frames_skipped += 1
                self._last_update += self._tick_step
                drift = self._tick_step / 5.0
                if not (TIME-drift < self._last_update < TIME+drift):
                    self._last_update = TIME
                self.tick_handler.pre_update(self)
                for i in self.objects:
                    j=self.objects.get(i)
                    j.update()
                    if self.auto_move:
                        j.end_update()
                for i in self.lights:
                    i.update()
                self.gui.update(self)
                self.tick_handler.post_update(self)
                self.events=pygame.event.get()
                for event in self.events:
                    if self.debug:
                        print(event)
                    if event.type == pygame.QUIT:
                        self._done = True
                        break;
                    if event.type == pygame.KEYDOWN:
                        if event.key == K_ESCAPE and event.mod&KMOD_SHIFT:
                            self._done = True
                        elif event.key == K_ESCAPE:
                            pygame.display.iconify()
                        elif event.key == K_LALT:
                            pygame.display.iconify()
                            self.console()

            if self._frame_ready:
                self.frame_count += 1
                self._frames_skipped = 0
                if self._frame_step:
                    self._last_frame += self._frame_step
                    drift = self._frame_step * self._max_frame_skip
                    if not (TIME-drift < self._last_frame < TIME+drift):
                        self._last_frame = TIME
                #-----Start of render function-----#
                screen=self.screen
                screen.fill(self.background)
                d=self._scr_dist
                j=self.instance

                dist_list=[]
                for key in self.objects:
                    wireframe=self.objects.get(key)
                    dist=getDistance(wireframe.centre(),(j.x,j.y,j.z))
                    dist_list.append([key,dist])
                for tempobj in sorted(dist_list,key=itemgetter(1),reverse=True):
                    wireframe=self.objects.get(key)
                    for i in wireframe.sub_objects:
                        if self.displayEdges:
                            for edge in i.edges:
                                try:
                                    a_xx=edge.start.x-j.x
                                    a_yy=edge.start.y-j.y
                                    a_zz=edge.start.z-j.z
                                    if a_zz<0:
                                            continue;
                                    a_x=a_xx/a_zz*d+self.width/2
                                    a_y=a_yy/a_zz*d+self.height/2

                                    b_xx=edge.stop.x-j.x
                                    b_yy=edge.stop.y-j.y
                                    b_zz=edge.stop.z-j.z
                                    if b_zz<0:
                                            continue;
                                    b_x=b_xx/b_zz*d+self.width/2
                                    b_y=b_yy/b_zz*d+self.height/2
                                except ZeroDivisionError:
                                    continue;

                                pygame.draw.aaline(screen, self.edge_colour, (int(a_x),int(a_y)), (int(b_x),int(b_y)), 1)

                        if self.displayNodes:
                            for node in i.nodes:
                                try:
                                    xx=node.x-j.x
                                    yy=node.y-j.y
                                    zz=node.z-j.z
                                    if zz<0:
                                            continue;
                                    x=xx/zz*d+self.width/2
                                    y=yy/zz*d+self.height/2
                                except ZeroDivisionError:
                                    continue;

                                pygame.draw.circle(screen, self.node_colour, (int(x),int(y)), self.nodeRadius, 0)

                        if self.displayFaces:
                            avg_z=[]
                            k=0
                            for face in i.faces:
                                dist=0
                                for node in face.corners:
                                    dist+=math.hypot(math.hypot(max(node.x,j.x)-min(node.x,j.x),max(node.z,j.z)-min(node.z,j.z)),max(node.y,j.y)-min(node.y,j.y))/face.num_corners
                                avg_z.append([k,dist])
                                k+=1
                            for temp in sorted(avg_z,key=itemgetter(1),reverse=True):
                                if temp[1]>self.render_distance:
                                    continue;
                                face=i.faces[temp[0]]
                                for tri in face.triangles:
                                    meanX = sum([node.x for node in tri.corners]) / 3
                                    meanY = sum([node.y for node in tri.corners]) / 3
                                    meanZ = sum([node.z for node in tri.corners]) / 3
                                    centre=[meanX,meanY,meanZ]
                                    cornerlist=[]
                                    for node in tri.corners:
                                        try:
                                            xx=node.x-j.x
                                            yy=node.y-j.y
                                            zz=node.z-j.z
                                            if zz<0:
                                                continue;
                                            x=xx/zz*d+self.width/2
                                            y=yy/zz*d+self.height/2
                                            cornerlist.append((x,y))
                                        except ZeroDivisionError:
                                            zz=0
                                            continue;
                                    if not len(cornerlist)==len(tri.corners):
                                        continue;
                                    light=[0,0,0]
                                    for lightobj in self.lights:
                                        norm=[tri.norm[0]+centre[0],tri.norm[1]+centre[1],tri.norm[2]+centre[2]]
                                        ldist=getDistance(centre,(lightobj.x,lightobj.y,lightobj.z))
                                        nldist=getDistance(norm,(lightobj.x,lightobj.y,lightobj.z))
                                        angle=math.degrees(math.acos(clamp((tri.ndist**2+ldist**2-nldist**2)/(2*tri.ndist*ldist),-1,1)))
                                        if angle>=90:
                                            continue;
                                        #Specular shading
                                        lvector=[lightobj.x-centre[0],lightobj.y-centre[1],lightobj.z-centre[2]]
                                        dist=lvector[0]*tri.norm[0]+lvector[1]*tri.norm[1]+lvector[2]*tri.norm[2]
                                        mlight=[lightobj.x-2*dist*tri.norm[0],lightobj.y-2*dist*tri.norm[1],lightobj.z-2*dist*tri.norm[2]]
                                        reflected_light=[centre[0]+(centre[0]-mlight[0]),centre[1]+(centre[1]-mlight[1]),centre[2]+(centre[2]-mlight[2])]
                                        cam_dist=getDistance((j.x,j.y,j.z),centre)
                                        caml_dist=getDistance((j.x,j.y,j.z),reflected_light)
                                        refldist=getDistance(centre,reflected_light)
                                        rayangle=180-math.degrees(math.acos(clamp((cam_dist**2+refldist**2-caml_dist**2)/(2*cam_dist*refldist),-1,1)))
                                        dispersion_angle=180*(1-face.reflect)
                                        try:
                                            fraction_light=rayangle/180*(180-dispersion_angle)/dispersion_angle+1
                                        except:
                                            fraction_light=rayangle/180*(180-dispersion_angle)+1
                                        if self.displayRays:
                                            print
                                            try:
                                                a_xx=centre[0]-j.x
                                                a_yy=centre[1]-j.y
                                                a_zz=centre[2]-j.z
                                                a_x=a_xx/a_zz*d+self.width/2
                                                a_y=a_yy/a_zz*d+self.height/2

                                                b_xx=lightobj.x-j.x
                                                b_yy=lightobj.y-j.y
                                                b_zz=lightobj.z-j.z
                                                b_x=b_xx/b_zz*d+self.width/2
                                                b_y=b_yy/b_zz*d+self.height/2

                                                pygame.draw.aaline(screen, self.edge_colour, (int(a_x),int(a_y)), (int(b_x),int(b_y)), 1)

                                            except ZeroDivisionError:
                                                pass

                                            try:
                                                a_xx=centre[0]-j.x
                                                a_yy=centre[1]-j.y
                                                a_zz=centre[2]-j.z
                                                a_x=a_xx/a_zz*d+self.width/2
                                                a_y=a_yy/a_zz*d+self.height/2

                                                b_xx=reflected_light[0]-j.x
                                                b_yy=reflected_light[1]-j.y
                                                b_zz=reflected_light[2]-j.z
                                                b_x=b_xx/b_zz*d+self.width/2
                                                b_y=b_yy/b_zz*d+self.height/2

                                                pygame.draw.aaline(screen, self.edge_colour, (int(a_x),int(a_y)), (int(b_x),int(b_y)), 1)

                                            except ZeroDivisionError:
                                                pass

                                            try:
                                                a_xx=centre[0]-j.x
                                                a_yy=centre[1]-j.y
                                                a_zz=centre[2]-j.z
                                                a_x=a_xx/a_zz*d+self.width/2
                                                a_y=a_yy/a_zz*d+self.height/2

                                                b_xx=mlight[0]-j.x
                                                b_yy=mlight[1]-j.y
                                                b_zz=mlight[2]-j.z
                                                b_x=b_xx/b_zz*d+self.width/2
                                                b_y=b_yy/b_zz*d+self.height/2

                                                pygame.draw.aaline(screen, self.edge_colour, (int(a_x),int(a_y)), (int(b_x),int(b_y)), 1)

                                            except ZeroDivisionError:
                                                pass

                                        #Light density shading
                                        fraction_light=(fraction_light*face.reflect)+(math.sin(math.radians(90-angle))*(1-face.reflect))
                                        #Inverse squared
                                        if self.light_distance:
                                            fraction_light/=(ldist/self.light_distance)**2
                                        #Getting colour
                                        temp_col=[lightobj.colour[0]*lightobj.brightness*fraction_light/100,lightobj.colour[1]*lightobj.brightness*fraction_light/100,lightobj.colour[2]*lightobj.brightness*fraction_light/100]
                                        light[0]=min(255,temp_col[0]+light[0])
                                        light[1]=min(255,temp_col[1]+light[1])
                                        light[2]=min(255,temp_col[2]+light[2])
                                    colour0=min(face.col[0]*(light[0]/255*(255-(self.min_brightness*self.min_colour[0]/100))+self.min_brightness*self.min_colour[0]/100)/255,255)
                                    colour1=min(face.col[1]*(light[1]/255*(255-(self.min_brightness*self.min_colour[1]/100))+self.min_brightness*self.min_colour[1]/100)/255,255)
                                    colour2=min(face.col[2]*(light[2]/255*(255-(self.min_brightness*self.min_colour[2]/100))+self.min_brightness*self.min_colour[2]/100)/255,255)
                                    pygame.draw.polygon(screen, (colour0,colour1,colour2), cornerlist, 0)

                                    if self.displayNorms:
                                        try:
                                            a_xx=centre[0]-j.x
                                            a_yy=centre[1]-j.y
                                            a_zz=centre[2]-j.z
                                            if a_zz<0:
                                                    continue;
                                            a_x=a_xx/a_zz*d+self.width/2
                                            a_y=a_yy/a_zz*d+self.height/2

                                            b_xx=tri.norm[0]+centre[0]-j.x
                                            b_yy=tri.norm[1]+centre[1]-j.y
                                            b_zz=tri.norm[2]+centre[2]-j.z
                                            if b_zz<0:
                                                    continue;
                                            b_x=b_xx/b_zz*d+self.width/2
                                            b_y=b_yy/b_zz*d+self.height/2
                                        except ZeroDivisionError:
                                            continue;

                                        pygame.draw.aaline(screen, self.edge_colour, (int(a_x),int(a_y)), (int(b_x),int(b_y)), 1)

                if self.displayLights:
                    for light in self.lights:
                        try:
                            xx=light.x-j.x
                            yy=light.y-j.y
                            zz=light.z-j.z
                            if zz<0:
                                    continue;
                            x=xx/zz*d+self.width/2
                            y=yy/zz*d+self.height/2
                        except ZeroDivisionError:
                            continue;

                        pygame.draw.circle(screen, self.node_colour, (int(x),int(y)), self.nodeRadius, 0)

                self.gui.draw(self)
                pygame.display.flip()

    #Adds a light object
    def add_light(self,x=0,y=0,z=0,brightness=100,colour=white):
        self.lights.append(Light(x=x,y=y,z=z,brightness=brightness,colour=colour))

    #Outputs Objects
    def __str__(self):
        for key in self.objects:
            print("\n"+key)
            print(self.objects.get(key))
        return ""

    #Run whenever the colsole is called up
    def console(self):
        pygame.event.pump()
        while True:
            command = input(">>>")
            #Carrys out command
            if command == "output":
                print(self)
            elif command == "wireframe":
                if self.displayFaces:
                    self.displayNodes = 1
                    self.displayEdges = 1
                    self.displayFaces = 0
                else:
                    self.displayNodes = 0
                    self.displayEdges = 0
                    self.displayFaces = 1
            elif command == "quit" or command == "exit":
                self._done = True
            elif command == "debug":
                if self.debug:
                    self.debug = False
                else:
                    print(self)
                    self.debug = True
            elif command == "performance":
                print("FPS:", str(self.fps))
                print("TPS:", str(self.tps))
                print("UPS:", str(self.ups))
            elif command[0:3] == "set":
                variable = command [4:]
                value = input(">>>")
                if variable == "maxfps":
                    self.max_fps = int(value)
                elif variable == "tickspersecond":
                    self.ticks_per_second = int(value)
                elif variable == "maxframeskip":
                    self.max_frame_skip = int(value)
                elif variable == "use_wait":
                    self.use_wait = bool(value)
                elif variable == "fov":
                    self.fov = int(value)
                elif variable == "caption":
                    pygame.display.set_caption(value)
                elif variable == "debug":
                    self.debug = bool(value)
                elif variable == "automove":
                    self.auto_move = bool(value)
                elif variable == "nodes":
                    self.displayNodes =  int(value)
                elif variable == "edges":
                    self.displayEdges =  int(value)
                elif variable == "faces":
                    self.displayFaces =  int(value)
                elif variable == "lights":
                    self.displayLights =  int(value)
                elif variable == "norms":
                    self.displayNorms =  int(value)
                elif variable == "rays":
                    self.displayRays =  int(value)
                else:
                    print(variable.title(),"isn't a valid variable")
            else:
                print(command.title(),"is not a valid command!")
                continue
            break

        #Restarts game
        restart = False
        while not restart and not self._done:
            for event in pygame.event.get(pygame.VIDEOEXPOSE):
                if event.type == pygame.VIDEOEXPOSE:
                    restart = True

    #Adds objects to the game
    def add_objects(self,objects,keys):
        for num in range(len(objects)):
            self.objects[keys[num]]=objects[num]

    def save_screen(self,name):
        pygame.image.save(self.screen, name)

#Basic Instance class
class Instance(object):
    def __init__(self,x,y,z,rotate_z,rotate_y,rotate_x):
        self.x=x
        self.y=y
        self.z=z
        self.rotate_z=rotate_z
        self.rotate_y=rotate_y
        self.rotate_x=rotate_x

#Basic object class
class ObjectBase(object):
    def __init__(self,x=0,y=0,z=0,rotate_z=0,rotate_y=0,rotate_x=0,sub_objects=[]):
        self.x=x
        self.y=y
        self.z=z
        self.rotate_z=rotate_z
        self.rotate_y=rotate_y
        self.rotate_x=rotate_x
        self.sub_objects=sub_objects

    def __str__(self):
        for i,obj in enumerate(self.sub_objects):
            print(str(i)+":")
            print(obj)
        return ""

    def update(self):
        pass

    def end_update(self):
        for i in self.sub_objects:
            i.x+=self.x
            i.y+=self.y
            i.z+=self.z
            i.obj_rotate_z=self.rotate_z
            i.obj_rotate_y=self.rotate_y
            i.obj_rotate_x=self.rotate_x
            i.obj_rotate=self.centre()
            i.update()
        for i in self.sub_objects:
            i.update_rotate()
        self.rotate_z=self.rotate_y=self.rotate_x=self.x=self.y=self.z=0

    def centre(self):
        num_sub=len(self.sub_objects)
        meanX=0
        meanY=0
        meanZ=0
        for i in self.sub_objects:
            x,y,z=i.centre()
            meanX+=x/num_sub
            meanY+=y/num_sub
            meanZ+=z/num_sub
        return (meanX,meanY,meanZ)

    def rotate(self,direction,c,degrees):
        if direction=='z':
            for i in self.sub_objects:
                i.rotate('z',c,degrees)
        elif direction=='y':
            for i in self.sub_objects:
                i.rotate('y',c,degrees)
        elif direction=='x':
            for i in self.sub_objects:
                i.rotate('x',c,degrees)

    def add_subobject(self,subobject):
        subobject.parent=self
        self.sub_objects.append(subobject)

    def scale(self, centre, scale):
        for i in self.sub_objects:
            i.scale(centre,scale)

#Rendering Components(Node, Edge and Face)

#Node Class
class Node(object):
    def __init__(self, coordinates):
        self.x = coordinates[0]
        self.y = coordinates[1]
        self.z = coordinates[2]

#Edge Class
class Edge(object):
    def __init__(self, start, stop):
        self.start = start
        self.stop  = stop

#Face Class
class Face(object):
    def __init__(self,corners,colour,reflect=0):
        self.corners=corners
        self.col=colour
        self.num_corners=len(corners)
        self.triangles=[]
        self.reflect=reflect
        for i in range(len(corners)-2):
            self.triangles.append(Triangle((corners[0],corners[i+1],corners[i+2])))

class Triangle(object):
    def __init__(self,corners):
        self.corners=corners
        meanX = sum([node.x for node in self.corners]) / 3
        meanY = sum([node.y for node in self.corners]) / 3
        meanZ = sum([node.z for node in self.corners]) / 3
        center=(meanX,meanY,meanZ)
        vector0=[corners[0].x-center[0],corners[0].y-center[1],corners[0].z-center[2]]
        vector1=[corners[1].x-center[0],corners[1].y-center[1],corners[1].z-center[2]]
        self.norm=[0,0,0]
        self.norm[0]=((vector0[1]*vector1[2])-(vector0[2]*vector1[1]))*3
        self.norm[1]=((vector0[2]*vector1[0])-(vector0[0]*vector1[2]))*3
        self.norm[2]=((vector0[0]*vector1[1])-(vector0[1]*vector1[0]))*3
        mag=magnitude(self.norm)
        self.norm[0]/=mag
        self.norm[1]/=mag
        self.norm[2]/=mag
        self.ndist=getDistance(self.norm,[0,0,0])

#Basic sub-object class
class SubObjectBase(object):
    def __init__(self,parent=None,x=0,y=0,z=0,rotate_z=0,rotate_y=0,rotate_x=0):
        self.x=x
        self.y=y
        self.z=z
        self.rotate_z=rotate_z
        self.rotate_y=rotate_y
        self.rotate_x=rotate_x
        self.nodes=[]
        self.edges=[]
        self.faces=[]
        self.obj_rotate_z=0
        self.obj_rotate_y=0
        self.obj_rotate_x=0
        self.obj_rotate=[0,0,0]

    def update(self):
        if self.x:
            self.translate("x",self.x)
        if self.y:
            self.translate("y",self.y)
        if self.z:
            self.translate("z",self.z)
        self.x=0
        self.y=0
        self.z=0

    def update_rotate(self):
        if self.rotate_z:
            self.rotate('z',self.centre(),rotate_z)
        if self.rotate_y:
            self.rotate('y',self.centre(),rotate_y)
        if self.rotate_x:
            self.rotate('x',self.centre(),rotate_x)
        if self.obj_rotate_z:
            self.rotate('z',self.obj_rotate,self.obj_rotate_z)
        if self.obj_rotate_y:
            self.rotate('y',self.obj_rotate,self.obj_rotate_y)
        if self.obj_rotate_x:
            self.rotate('x',self.obj_rotate,self.obj_rotate_x)
        self.obj_rotate_z=0
        self.obj_rotate_y=0
        self.obj_rotate_x=0
        self.rotate_z=0
        self.rotate_y=0
        self.rotate_x=0

    def add_nodes(self, nodeList):
        try:
            for node in nodeList:
                self.nodes.append(Node(node))
        except:
            self.nodes.append(Node(nodeList))

    def add_edges(self, edgeList):
        try:
            for (start, stop) in edgeList:
                self.edges.append(Edge(self.nodes[start], self.nodes[stop]))
        except:
            self.edges.append(Edge(self.nodes[edgeList[0]], self.nodes[edgeList[1]]))

    def add_faces(self, faceList):
        try:
            for (corners,colour) in faceList:
                corner_list=[]
                for i in corners:
                    corner_list.append(self.nodes[i])
                for i in range(len(corner_list)-1):
                    self.edges.append(Edge(corner_list[i], corner_list[i+1]))
                self.edges.append(Edge(corner_list[len(corner_list)-1], corner_list[0]))
                for i in range(len(corner_list)-3):
                    self.edges.append(Edge(corner_list[0],corner_list[i+2]))
                self.faces.append(Face(corner_list,colour))
        except:
            corners=faceList[0]
            colour=faceList[1]
            corner_list=[]
            for i in corners:
                corner_list.append(self.nodes[i])
            for i in range(len(corner_list)-1):
                self.edges.append(Edge(corner_list[i], corner_list[i+1]))
            self.edges.append(Edge(corner_list[len(corner_list)-1], corner_list[0]))
            for i in range(len(corner_list)-3):
                self.edges.append(Edge(corner_list[0],corner_list[i+2]))
            self.faces.append(Face(corner_list,colour))

    def add_mesh(self,filename):
        verts = []
        norms = []
        vertsOut = []
        normsOut = []
        dupes={}
        for line in open(filename, "r"):
            vals = line.split()
            if len(vals)==0:
                continue;
            if vals[0][0]=="#":
                continue;
            if vals[0] == "v":
                v = vals[1:4]
                count=0
                for i in v:
                    v[count]=float(i)
                    count+=1
                if v in verts:
                    count=0
                    for i in verts:
                        if i==v:
                            break;
                        count+=1
                    dupes[str(v)]=count
                verts.append(v)
            if vals[0] == "vn":
                n = vals[1:4]
                count=0
                for i in n:
                    n[count]=float(i)
                    count+=1
                norms.append(n)
            if vals[0] == "f":
                v=[]
                n=[]
                for f in vals[1:]:
                    w = f.split("/")
                    if int(w[0]) in dupes:
                        v.append(dupes[int(w[0])])
                    else:
                        v.append(verts[int(w[0])-1])
                    try:
                        n.append(norms[int(w[2])-1])
                    except:
                        pass
                vertsOut.append(v)
                normsOut.append(n)
        count=0
        faces=[]
        for i in vertsOut:
            self.add_nodes(i)
            faces.append((count,count+len(i)))
            count+=len(i)
        faceList=[]
        for i in faces:
            l=[]
            for j in range(i[1]-i[0]):
                l.append(j+i[0])
            faceList.append((l,white))
        self.add_faces(faceList)

    def add_model(self,model):
        self.add_nodes(model[0])
        self.add_faces(model[1])

    def __str__(self):
        self.print_nodes()
        self.print_edges()
        self.print_faces()
        return ""

    def print_nodes(self):
        print(" --- Nodes --- ")
        for i, node in enumerate(self.nodes):
            print(" %d: (%.2f, %.2f, %.2f)" % (i, node.x, node.y, node.z))

    def print_edges(self):
        print("\n --- Edges --- ")
        for i, edge in enumerate(self.edges):
            print(" %d: (%.2f, %.2f, %.2f)" % (i, edge.start.x, edge.start.y, edge.start.z))

    def print_faces(self):
        print("\n --- Faces --- ")
        for i, face in enumerate(self.faces):
            print(" %d: (%.2f, %.2f, %.2f, %.2f)" % (i, face.num_corners, face.corners[0].x, face.corners[0].y, face.corners[0].z))

    def translate(self, axis, d):
        if axis in ['x', 'y', 'z']:
            for node in self.nodes:
                setattr(node, axis, getattr(node, axis) + d)

    def scale(self, centre, scale):
        centre_x=centre[0]
        centre_y=centre[1]
        centre_z=centre[2]
        for node in self.nodes:
            node.x = centre_x + scale * (node.x - centre_x)
            node.y = centre_y + scale * (node.y - centre_y)
            node.z = centre_z + scale * (node.z - centre_z)
        for face in self.faces:
            for tri in face.triangles:
                tri.norm[0] = centre_x + scale * tri.norm[0]
                tri.norm[1] = centre_y + scale * tri.norm[1]
                tri.norm[2] = centre_z + scale * tri.norm[2]

    def centre(self):
        num_nodes = len(self.nodes)
        meanX = sum([node.x for node in self.nodes]) / num_nodes
        meanY = sum([node.y for node in self.nodes]) / num_nodes
        meanZ = sum([node.z for node in self.nodes]) / num_nodes
        return (meanX,meanY,meanZ)

    def rotate(self, direction, c, degrees):
        if direction=='z':
            for node in self.nodes:
                x      = node.x - c[0]
                y      = node.y - c[1]
                d      = math.hypot(y, x)
                theta  = math.atan2(y, x) + math.radians(degrees)
                node.x = c[0] + d * math.cos(theta)
                node.y = c[1] + d * math.sin(theta)
            for face in self.faces:
                for tri in face.triangles:
                    x      = tri.norm[0]
                    y      = tri.norm[1]
                    d      = math.hypot(y, x)
                    theta  = math.atan2(y, x) + math.radians(degrees)
                    tri.norm[0] = d * math.cos(theta)
                    tri.norm[1] = d * math.sin(theta)
        if direction=='y':
            for node in self.nodes:
                y      = node.y - c[1]
                z      = node.z - c[2]
                d      = math.hypot(y, z)
                theta  = math.atan2(y, z) + math.radians(degrees)
                node.z = c[2] + d * math.cos(theta)
                node.y = c[1] + d * math.sin(theta)
            for face in self.faces:
                for tri in face.triangles:
                    y      = tri.norm[1]
                    z      = tri.norm[2]
                    d      = math.hypot(y, z)
                    theta  = math.atan2(y, z) + math.radians(degrees)
                    tri.norm[2] = d * math.cos(theta)
                    tri.norm[1] = d * math.sin(theta)
        if direction=='x':
            for node in self.nodes:
                x      = node.x - c[0]
                z      = node.z - c[2]
                d      = math.hypot(x, z)
                theta  = math.atan2(x, z) + math.radians(degrees)
                node.z = c[2] + d * math.cos(theta)
                node.x = c[0] + d * math.sin(theta)
            for face in self.faces:
                for tri in face.triangles:
                    x      = tri.norm[0]
                    z      = tri.norm[2]
                    d      = math.hypot(x, z)
                    theta  = math.atan2(x, z) + math.radians(degrees)
                    tri.norm[2] = d * math.cos(theta)
                    tri.norm[0] = d * math.sin(theta)

#Light class
class Light(object):
    def __init__(self,x=0,y=0,z=0,brightness=100,colour=white):
        self.x=x
        self.y=y
        self.z=z
        self.brightness=brightness
        self.colour=colour

    def update(self):
        pass

def getDistance(a,b):
    return math.sqrt((max(a[0],b[0])-min(a[0],b[0]))**2+(max(a[1],b[1])-min(a[1],b[1]))**2+(max(a[2],b[2])-min(a[2],b[2]))**2)

def clamp(x,a,b):
    if a<=b:
        return min(max(x,a),b)
    else:
        return min(max(x,b),a)

def convert_mesh(filename):
    text=""
    for line in open(filename, "r"):
        text+=line
    print("Done")
    count=0
    ln_start=0
    write=open(filename,"w")
    line_count=0
    for char in text:
        if char=="v" or "f":
            write.write(text[ln_start:count])
            ln_start=count
            line_count+=1
            if line_count==10000:
                line_count=0
                write.close()
                print("Converting...")
                write=open(filename,"a")
        count+=1
    write.close()

def magnitude(vector):
    return math.sqrt(max(vector[0],-vector[0])+max(vector[1],-vector[1])+max(vector[2],-vector[2]))

#Code for demo program
def main():
    print("Py3D Module")
    print("See Template.py for usage instructions and game1.py for an example program")
    while True:
        choice=input("Do you want to open the demo program Y/N? ")
        if choice!="N" and choice!="n" and choice!="y" and choice !="Y":
            print("That is not a valid choice")
            continue;
        elif choice=="Y" or choice=="y":
            input("The controls are q,w,e,a,s,d,u,j,m,i,k,l\nPress enter to continue")
            game=Game(fov=75,ticks_per_second=40,max_fps=120,use_wait=True,
                         update_callback=None,frame_callback=None,time_source=None,
                         min_brightness=5,min_colour=white, debug=False,
                          caption="Py3D Game")
            game.min_brightness=5
            game.min_colour=white
            class cube(ObjectBase):
                def update(self):
                    if pygame.key.get_pressed()[K_q]:
                        self.rotate_z=-4
                    if pygame.key.get_pressed()[K_w]:
                        self.rotate_y=4
                    if pygame.key.get_pressed()[K_e]:
                        self.rotate_z=4
                    if pygame.key.get_pressed()[K_a]:
                        self.rotate_x=4
                    if pygame.key.get_pressed()[K_s]:
                        self.rotate_y=-4
                    if pygame.key.get_pressed()[K_d]:
                        self.rotate_x=-4
                    if pygame.key.get_pressed()[K_i]:
                        self.y=-1
                    if pygame.key.get_pressed()[K_k]:
                        self.y=1
                    if pygame.key.get_pressed()[K_j]:
                        self.x=-1
                    if pygame.key.get_pressed()[K_l]:
                        self.x=1
                    if pygame.key.get_pressed()[K_u]:
                        self.z=-1
                    if pygame.key.get_pressed()[K_m]:
                        self.z=1
            cube=cube()
            cube_frame=cube.add_subobject(SubObjectBase())
            cube_frame.add_nodes(((15,15,15),(15,50,15),(50,15,15),(50,50,15),(50,50,50),(50,15,50),(15,50,50),(15,15,50)))
            cube_frame.add_faces((((0,1,3,2),white),((4,5,7,6),cyan),((0,2,5,7),red),((1,3,4,6),green),((0,1,6,7),blue),((2,3,4,5),yellow)))
            objects={}
            objects["cube"]=cube
            game.objects=objects
            game.add_light(x=0,y=0,z=0,brightness=100,colour=white)
            class TickHandler(object):
                def pre_update(self,game):
                    pass
                def post_update(self,game):
                    pass
            class GUI(object):
                def update(self,game):
                    pass
                def draw(self,game):
                    pass
            game.tick_handler=TickHandler()
            game.gui=GUI()
            game.loop()
            pygame.quit()
            break;
        elif choice=="N" or choice=="n":
            break;

if __name__=="__main__":
    main()


import random

from locust import TaskSet,  task
from pyquery import PyQuery

HOST = "https://www.archlinux.org"

class AbstractUser(TaskSet):
    min_wait = 1000
    max_wait = 5000

    def on_start(self):
        r = self.get_index()
        pq = PyQuery(r.content, parser='html')
        self.sitemap_links = []
        for a in pq.find("a"):
            # print "Element:", str(a.attrib)
            if "href" in a.attrib:
                if HOST in a.attrib['href']:
                    # print "Endpoint: ", str(a.attrib['href'])
                    self.sitemap_links.append(a.attrib['href'])
                self.sitemap_links.append(a.attrib['href'])
            # print "\nlinks\n", str(self.sitemap_links)+"\n##################################\n"

    def get_index(self):
        return self.client.get("/")

    # def stop(self):
    #     self.interrupt()

class WebUser(AbstractUser):

    @task(30)
    def load_page(self):
        if len(self.sitemap_links) > 0:
            url = random.choice(self.sitemap_links)
            self.client.get(url)
        else:
            self.get_index()

    # @task
    # def test(self):
    #     self.get_index()
    # @task(30)
    # def load_page(self):
    #     url = random.choice(self.sitemap_links)
    #     self.client.get(url)
    # @task(30)
    # class SubTaskSet(AbstractUser):

        # def on_start(self):
        #     self.client.get("/")
        #
        #
        # @task
        # def my_task(self):
        #     pass


import cv2

cap = cv2.VideoCapture(0)

def getImg(display=False,size=[960, 480]):
    _, img = cap.read()
    cap.set(cv2.CAP_PROP_FPS,60)#test
    fps = int(cap.get(60)) #j
    img = cv2.resize(img, (size[0], size[1]))
    if display:
        cv2.imshow('IMG', img)
    return img

if __name__ == '__main__':
    i = 1
    while True:
        img = getImg(True)
        if i<200:
            i+=1
            cv2.imwrite('/home/pi/auto_car/LaneDetection/vid/vid'+str(i)+'.png', img)
            print(i)
        if cv2.waitKey(1) and 0xFF == ord('q'):
            break


#! /usr/bin/python3

import tensorflow as tf
import os
import cv2
import numpy as np

from PIL import Image
from object_detection.utils import label_map_util
from object_detection.utils import visualization_utils as vis_util

def load_image_into_numpy_array(image):
    (im_width, im_height) = image.size
    return np.array(image.getdata()).reshape(im_height, im_width, 3).astype(np.uint8)

def get_recipe(PATH_TO_IMG: str):

    item_map = {
    1:"사과", 2:"토마토", 3:"바나나", 4:"피망", 5:"달걀",
    6:"오이", 7:"가지", 8:"양배추", 9:"배추", 10:"딸기",
    11:"포도", 12:"오렌지", 13:"레몬", 14:"수박", 15:"파인애플",
    16:"당근", 17:"파", 18:"마늘", 19:"양파", 20:"생강",
    21:"옥수수", 22:"감자", 23:"고구마", 24:"무", 25:"상추",
    26:"브로콜리", 27:"양송이버섯", 28:"표고버섯", 29:"팽이버섯", 30:"아스파라거스",
    31:"고추", 32:"시금치", 33:"양상추"
    }

    PATH_TO_CKPT="dataset/ratatouille.pb"
    PATH_TO_LABELS = os.path.join("dataset/data", "labelmap.pbtxt")
    NUM_CLASSES = 33

    detection_graph = tf.Graph()

    with detection_graph.as_default():
        od_graph_def = tf.compat.v1.GraphDef()
        with tf.io.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:
            serialized_graph = fid.read()
            od_graph_def.ParseFromString(serialized_graph)
            tf.import_graph_def(od_graph_def, name='')

    label_map = label_map_util.load_labelmap(PATH_TO_LABELS)
    categories = label_map_util.convert_label_map_to_categories(
        label_map, max_num_classes=NUM_CLASSES, use_display_name=True
    )
    category_index = label_map_util.create_category_index(categories)

    if not os.path.isfile(PATH_TO_IMG):
        return "File not found error", 1

    #recipe_url = "https://www.haemukja.com/recipes?utf8=%E2%9C%93&sort=rlv&name="
    recipe_url = "https://m.haemukja.com/#/search?keyword="

    with detection_graph.as_default():
        with tf.compat.v1.Session(graph=detection_graph) as sess:

            # read image
            img = np.array(Image.open(PATH_TO_IMG))
            #img = cv2.imread(PATH_TO_IMG, cv2.IMREAD_UNCHANGED)

            # expand dimensions since the model expects image to have shape: [1, None, None, 3]
            img_expand = np.expand_dims(img, axis=0)

            # extract image tensor
            image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
            #extract detection boxes
            boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
            # extract detection scores
            scores = detection_graph.get_tensor_by_name('detection_scores:0')
            # extract detection classes
            classes = detection_graph.get_tensor_by_name('detection_classes:0')
            # extract number of detections
            num_detections = detection_graph.get_tensor_by_name('num_detections:0')

            # actual detection
            (boxes, scores, classes, num_detections) = sess.run(
                [boxes, scores, classes, num_detections],
                feed_dict={image_tensor: img_expand}
            )

            # visualization of the results of a detection
            vis_util.visualize_boxes_and_labels_on_image_array(
                img,
                np.squeeze(boxes),
                np.squeeze(classes).astype(np.int32),
                np.squeeze(scores),
                category_index,
                use_normalized_coordinates=True,
                line_thickness=8
            )

            scores.flatten()
            classes.flatten()

            items = list()

            for row, r in zip(scores, range(len(scores))):
                for score, i in zip(row, range(len(row))):
                    if score >= 0.5:
                        items.append(category_index[int(classes[r][i])]['id'])

            if len(items) == 0:
                return "Ingredient not found error", 2

            item_set = set(items)
            item_set_label = [item_map[item] for item in item_set]

            for ingredient in item_set_label:
                recipe_url += ingredient+"+"

            return recipe_url[:-1], 0

#print(get_recipe("test.jpeg"))


from tornado.options import define, options
import tornado.concurrent
from trending_handler import IndexHandler, RepositoryHandler, RepositoryLanguageHandler \
    , DeveloperHandler, DeveloperLanguageHandler

define('port', default=8123, type=int)

app = tornado.web.Application([
    (r'/', IndexHandler),
    (r'/repo/', RepositoryHandler),
    (r'/repo', RepositoryHandler),
    (r'/repo/(.+)', RepositoryLanguageHandler),
    (r'/developer/', DeveloperHandler),
    (r'/developer', DeveloperHandler),
    (r'/developer/(.+)', DeveloperLanguageHandler),
])

if __name__ == '__main__':
    tornado.options.parse_command_line()
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.current().start()


import json
import os
import subprocess
import sys

from utils import listRecursive

def remote_1(args):
    scriptDir = "/computation/enigma_scripts"
    scriptName = "metaanalysis_AllComps2020.R"
    RScriptDir = "/usr/bin/Rscript"

    baseDir = args["state"]["baseDirectory"]
    transferDir = args["state"]["transferDirectory"]
    outputDir = args["state"]["outputDirectory"]

    site_list = os.path.join(outputDir, "site_list.txt")

    with open(site_list, 'w') as fh:
        for site in args["input"]:
            fh.write('%s\n' % site)

    regr_args = [
        RScriptDir,
        os.path.join(scriptDir, scriptName), baseDir, outputDir, site_list
    ]

    subprocess.call(regr_args,
                    stdout=subprocess.DEVNULL,
                    stderr=subprocess.DEVNULL)

    # Copying local results to transfer directory
    os.system("cp -rf " + baseDir + "/* " + transferDir)

    # Copying meta analysis results to transfer directory
    os.system("cp -rf " + outputDir + "/* " + transferDir)

    computation_output = {
        "output": {},
        # should be a list of files created -ross
        "success": True
    }
    return json.dumps(computation_output)

if __name__ == '__main__':

    parsed_args = json.loads(sys.stdin.read())
    phase_key = list(listRecursive(parsed_args, 'computation_phase'))

    if 'local_1' in phase_key:
        computation_output = remote_1(parsed_args)
        sys.stdout.write(computation_output)
    else:
        raise ValueError("Error occurred at Remote")


import numpy as np
import matplotlib.pyplot as plt
import spectools as s

import scipy.interpolate as si
import scipy.constants as sc

import CvD12tools as cvd
from matplotlib.collections import PatchCollection
from matplotlib.patches import Rectangle
from matplotlib.colors import colorConverter

import seaborn as sns

import Miles_FeH_interpolation as MilesFeH

####FONTS
tfsize = 28
lfsize = 24
gensize = 20
majtsize = 14
mintsize = 6
twidth = 2
msize = 10
####

sns.set_context('poster')
#For saving the data:

filename="data_IMF_slope_sigma.txt"
with open(filename, 'w') as f:
    pass

def makeErrorBoxes(xdata,ydata,xerror,yerror,fc='r',ec='None',alpha=0.25):

    # Create list for all the error patches
    errorboxes = []

    # Loop over data points; create box from errors at each point
    for x_left,y_left,width,height in zip(xdata,ydata,xerror,yerror):

        rect = Rectangle((x_left,y_left),width,height)
        errorboxes.append(rect)

    # Create patch collection with specified colour/alpha
    pc = PatchCollection(errorboxes,facecolor=fc,alpha=alpha,edgecolor=ec, zorder=1)

    # Add collection to axes
    return pc

def make_arrow(ax, x, y0, y1, c='k'):

    dx=0.0
    dy=y1-y0

    ax.arrow(x, y0, dx, dy, head_width=4.0, head_length=0.05, fc=c, ec='k', length_includes_head=True)

    return 'done'
#------------#
#Plot of IMF v velocity dispersion - comparing our galaxies to literature results
#spiniello = np.genfromtxt('/Users/zieleniewski/Documents/Oxford/D.Phil/SZThesis/Chapter_coma_plots/Spiniello2014_data.txt', delimiter=', ')
#ziel = np.genfromtxt('/Users/zieleniewski/Documents/Oxford/D.Phil/SZThesis/Chapter_coma_plots/Zieleniewski2015-16_data.txt', delimiter=', ')

#Load up my FeH data for each galaxy
m31 = np.genfromtxt('/Volumes/SPV_SWIFT/Science/Resolved_Gals_indices/IMF_Plots/FeH_index_values_mids_m31_1_30-01-15.txt', delimiter=',')
m32 = np.genfromtxt('/Volumes/SPV_SWIFT/Science/Resolved_Gals_indices/IMF_Plots/FeH_index_values_mids_m32_30-01-15.txt', delimiter=',')

fehpos = 13
fehvpos = 14
gmp2921 = np.genfromtxt('/Volumes/SPV_SWIFT/Science/Resolved_Gals_indices/IMF_Plots/Indices_v_radius_GMP2921_400kms_data_2016-4-13.txt', delimiter=',')
# gmp2921 = np.genfromtxt('GMP2921/GMP2921_global_spec_index_values_400kms_data_2016-4-13.txt', delimiter=',')
#gmp3329 = np.genfromtxt('GMP3329/Indices_v_radius_GMP3329_270kms_data_2016-4-13.txt', delimiter=',')
gmp3329 = np.genfromtxt('/Volumes/SPV_SWIFT/Science/Resolved_Gals_indices/IMF_Plots/GMP3329_global_spec_index_values_270kms_data_2016-4-13.txt', delimiter=',')
# gmp4928 = np.genfromtxt('GMP4928/Indices_v_radius_GMP4928_270kms_data_2016-4-13.txt', delimiter=',')
gmp4928 = np.genfromtxt('/Volumes/SPV_SWIFT/Science/Resolved_Gals_indices/IMF_Plots/GMP4928_global_spec_index_values_270kms_data_2016-4-13.txt', delimiter=',')
#gmp3367 = np.genfromtxt('GMP3367/GMP3367_global_spec_index_values_200kms_data_2016-4-14.txt', delimiter=',')
gmp3367 = np.genfromtxt('/Volumes/SPV_SWIFT/Science/Resolved_Gals_indices/IMF_Plots/GMP3367_global_spec_index_values_185kms_data_2016-6-3.txt', delimiter=',')

#Data from SPV+2017
IC843_FeH, IC843_FeH_err=np.genfromtxt('/Volumes/SPV_SWIFT/Science/Resolved_Gals_indices/Index_Index_Plots/GLOBAL_IC843.txt')[5], np.genfromtxt('/Volumes/SPV_SWIFT/Science/Resolved_Gals_indices/Index_Index_Plots/GLOBAL_IC843.txt')[6]
IC843_FeH_var=IC843_FeH_err**2

NGC1277_FeH, NGC1277_FeH_err=np.genfromtxt('/Volumes/SPV_SWIFT/Science/Resolved_Gals_indices/Index_Index_Plots/GLOBAL_NGC1277.txt')[5], np.genfromtxt('/Volumes/SPV_SWIFT/Science/Resolved_Gals_indices/Index_Index_Plots/GLOBAL_NGC1277.txt')[6]
NGC1277_FeH_var=NGC1277_FeH_err**2

m31feh = np.mean(m31[:2,1])
m31fehv = np.sum(m31[:2,2]**2)/4.

m32feh = np.mean(m32[:2,1])
m32fehv = np.sum(m32[:2,2]**2)/4.

g2921feh = np.mean(gmp2921[:2,fehpos])
g2921fehv = np.sum(gmp2921[:2,fehvpos])/4.
# g2921feh = gmp2921[8]
# g2921fehv = gmp2921[9]**2

# g3329feh = np.mean(gmp3329[:2,fehpos])
# g3329fehv = np.sum(gmp3329[:2,fehvpos])/4.
g3329feh = gmp3329[8]
g3329fehv = gmp3329[9]**2

# g4928feh = np.mean(gmp4928[:1,fehpos])
# g4928fehv = np.sum(gmp4928[:1,fehvpos])/1.
g4928feh = gmp4928[8]
g4928fehv = gmp4928[9]**2

g3367feh = gmp3367[8]
g3367fehv = gmp3367[9]**2

filts = s.loadJCFilters()

gals = ['M32', 'M31', 'NGC4889', 'NGC4874', 'NGC4839', 'NGC4873', 'NGC1277', 'IC843']
fehs = [m32feh, m31feh, g2921feh, g3329feh, g4928feh, g3367feh, NGC1277_FeH, IC843_FeH]
fehvs = [m32fehv, m31fehv, g2921fehv, g3329fehv, g4928fehv, g3367fehv, NGC1277_FeH_var, IC843_FeH_var]

all_spectra=MilesFeH.get_all_specs()
corrections=True

if corrections==True:
    #Alpha, iron and sodium corrections
    ages = np.array([4.0, 10.0, 13.5, 13.5, 13.5, 12.0, 13.5, 10.0])
    alphas = np.array([0.0, 0.2, 0.25, 0.25, 0.25, 0.25, 0.3, 0.3])
    fes = np.array([0.0, 0.1, 0.25, 0.02, 0.07, -0.05, 0.021, 0.021])
    nas = np.array([0.0, 0.3, 0.8, 0.1, 0.4, 0.1, 0.4, 0.2])
    Z_vals=fes+0.93*alphas
    resols = np.array([150.0, 200.0, 400.0, 269.0, 271.0, 185.0, 200.0, 200.0])
    plresols = np.array([65.0, 180.0, 380.0, 267.0, 278.0, 170.0, 410.0, 265.0])
    #savefilename="/Volumes/SPV_SWIFT/LaTeX/RadialGradientsPaper/Plots/IMF_Sigma_plot_WITH_response_functs.pdf"
else:
    # #No alpha, iron and sodium corrections
    ages = np.array([4.0, 10.0, 13.5, 13.5, 13.5, 12.0, 13.5, 10.0])
    alphas = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
    fes = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
    nas = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
    resols = np.array([150.0, 200.0, 400.0, 269.0, 271.0, 185.0, 200.0, 200.0])
    Z_vals=fes+0.93*alphas
    plresols = np.array([65.0, 180.0, 380.0, 267.0, 278.0, 170.0, 410.0, 265.0])
    #savefilename="/Volumes/SPV_SWIFT/LaTeX/RadialGradientsPaper/Plots/IMF_Sigma_plot_WITHOUT_response_functs.pdf"

savefilename="test.pdf"

ind1 = 'FeH'
galimfs = []
galimferrs = []
galm2ls = []
galm2lerrs = []
interpfunctions = []
m2linterpfunctions = []

for i, (age, alpha, Z, fe, na, sigma, feh, feh_var, central_sigma, galname) in enumerate(zip(ages, alphas, Z_vals, fes, nas, resols, fehs, fehvs, plresols, gals)):

    #Hack- set Z=0 and use the Fe correction instead
    fe=0.0
    imf, imferr=MilesFeH.IMF_slope_wrapper(galname, all_spectra, sigma, age, alpha, Z, fe, na, feh, np.sqrt(feh_var))

    galimfs.append(imf)
    galimferrs.append(imferr)

    #galm2ls.append(m2l)
    #galm2lerrs.append(m2l_err)

plt.rc('text', usetex=True)
plt.rc('font', family='serif')
plt.rcParams.update({'font.size': gensize})
fig, ax=plt.subplots(figsize=(16.5, 11.6))
#plt.subplots_adjust(left=0.14, bottom=0.12)

"""
NGC1277_alpha=make_arrow(ax, 423.0, NGC1277_no_response, NGC1277_with_alpha, c='g')
NGC1277_na=make_arrow(ax, 425.0, NGC1277_no_response, NGC1277_with_na, c='b')
NGC1277_fe=make_arrow(ax, 427.0, NGC1277_no_response, NGC1277_with_fe, c='r')

IC843_alpha=make_arrow(ax, 278.0, IC843_no_response, IC843_with_alpha, c='g')
IC843_na=make_arrow(ax, 280.0, IC843_no_response, IC843_with_na, c='b')
IC843_fe=make_arrow(ax, 282.0, IC843_no_response, IC843_with_fe, c='r')

#Plot the arrows for each response function

"""

#---#

#spin_line, = ax.plot(sigs, s14, 'r-', lw=2.0)
#f13_line, = ax.plot(sigs, f13, 'b-', lw=2.0)
#lb13_line, = ax.plot(sigs, lb13, 'g-', lw=2.0)

ziel_points = ax.errorbar(plresols[:-2], galimfs[:-2], yerr=galimferrs[:-2], fmt='s', ms=15, linewidth=1.0, alpha=1.0, zorder=10, c='k')
SPV_points = ax.errorbar(plresols[-2:], galimfs[-2:], yerr=galimferrs[-2:], fmt='D', ms=15, linewidth=1.0,  alpha=1.0, zorder=10, c='r')#cmap=plt.cm.copper_r, c=ages
#eboxes = ax.add_collection(boxes)

ax.set_xlabel('$\sigma$ [km/s]', fontsize=tfsize)
ax.set_ylabel('IMF slope $x$', fontsize=tfsize)
plt.xticks(size=lfsize)
plt.yticks(size=lfsize)
plt.minorticks_on()
ax.tick_params(axis='both', direction='in', length=majtsize,
              width=1.5, which='major')
ax.tick_params(axis='both', direction='in', length=mintsize,
              width=1.5, which='minor')

ax.set_xlim([50.0, 450.0])
ax.set_ylim([0.3, 3.5])

ax.plot([50.,450.], [1.3,1.3], 'k--', lw=1.5)
#ax.plot([50.0,450.0], [2.35, 2.35], 'k--', lw=1.5)
#ax.annotate('Salpeter', xy=(325,2.35),xycoords='data',xytext=(0,5),textcoords='offset points', color='k', fontsize=gensize)
for i in xrange(len(gals)):
    if gals[i] == 'NGC4873' or gals[i] == 'IC843' or gals[i] == 'NGC4889':
        ax.annotate(gals[i], xy=(plresols[i],galimfs[i]),xycoords='data',xytext=(-10,10),textcoords='offset points', color='k', fontsize=gensize, horizontalalignment='right', verticalalignment='bottom')
    else:
        ax.annotate(gals[i], xy=(plresols[i],galimfs[i]),xycoords='data',xytext=(10,10),textcoords='offset points', color='k', fontsize=gensize, horizontalalignment='left', verticalalignment='bottom')

# leg = ax.legend([spin_line, f13_line, lb13_line, ziel_points, SPV_points],
# ['Spiniello+14 relation', 'Ferreras+13 relation', 'La Barbera+13 relation', 'Zieleniewski+16', "This work"], loc='upper left',
# frameon=False, borderaxespad=0.3, labelspacing=0.5, numpoints=1, scatterpoints=1)
#plt.gca().add_artist(leg2)

#print gals, galm2ls, galm2lerrs
plt.tight_layout()
#plt.savefig(savefilename, bbox_inches='tight')
plt.show()


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import image_cropping.fields

class Migration(migrations.Migration):

    dependencies = [
        ('auth2', '0003_auto_20151110_1304'),
    ]

    operations = [
        migrations.AlterField(
            model_name='user',
            name=b'cropping',
            field=image_cropping.fields.ImageRatioField('avatar', '70x70', hide_image_field=False, size_warning=False, allow_fullsize=False, free_crop=False, adapt_rotation=False, help_text='Note that the preview above will only be updated after you submit the form.', verbose_name='cropping'),
        ),
        migrations.AlterField(
            model_name='user',
            name='groups',
            field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups'),
        ),
        migrations.AlterField(
            model_name='user',
            name='is_active',
            field=models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active'),
        ),
        migrations.AlterField(
            model_name='user',
            name='is_staff',
            field=models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status'),
        ),
        migrations.AlterField(
            model_name='user',
            name='is_superuser',
            field=models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status'),
        ),
    ]


import math
import numpy
import functions
import re
import pickle
from matplotlib import pyplot as plt
from matplotlib.pyplot import savefig
from sympy import symbols, Number
from sympy import latex
from sympy.parsing.sympy_parser import parse_expr
from anastruct import SystemElements
from pylatex import Document, Section, Subsection, Figure, Alignat, Command, NoEscape, Package, Subsubsection, \
    LineBreak

class Teacher():

    ss = SystemElements()
    blankss = pickle.dumps(ss)
    eq = functions.eq
    functions.n.clear()
    total_moment = ''
    total_point = ''
    total_point_y = ''
    total_point_x = ''
    total_load_y = ''
    total_load_x = ''
    roll_dist = ''
    By = ''
    Bx = ''
    Ay = ''
    Ax = ''
    EM = ''
    EFy = ''
    EFx = ''
    Ba = None
    Vx = []
    Vy = []
    Vxs = []
    angle = []
    eq_load = {}
    list_load_y = {}
    list_load_x = {}

    def reset(self):
        self.total_moment = self.total_point = self.total_point_x = self.total_point_y = self.roll_dist = \
            self.By = self.Bx = self.Ay = self.Ax = self.EM = self.EFy = self.EFx = self.total_load_x = \
            self.total_load_y = self.Bstring = ''
        self.Ba = None
        self.Vx.clear()
        self.Vy.clear()
        self.Vxs.clear()
        self.angle.clear()
        self.eq_load = {}
        self.list_load_x = {}
        self.list_load_y = {}
        self.ss = pickle.loads(self.blankss)

    def get_hinged_info(self, hinged, node_map):
        if hinged:
            self.idh = hinged[0].id
            _, self.xh, self.yh, self.fxh, self.fyh, _ = self.fetcher(node_map.get(self.idh))
            self.Ax = f'{-self.fxh:.2f}'
            self.Ay = f'{-self.fyh:.2f}'

    def get_roll_info(self, roll, node_map, roll_direction):
        for c in range(len(roll)):
            self.idr = roll[0].id
            _, xr, yr, self.fxr, self.fyr, _ = self.fetcher(node_map.get(self.idr))
            self.Bx = f'{-self.fxr:.2f}'
            self.By = f'{-self.fyr:.2f}'
            self.roll_dist_x = f'{xr - self.xh:.2f}i'
            self.roll_dist_y = f'{yr - self.yh:.2f}j'

    def get_fixed_info(self, fixed, node_map):
        if fixed:
            self.idf = fixed[0].id
            _, self.xe, self.ye, self.fxe, self.fye, self.tye = self.fetcher(node_map.get(self.idf))
            self.EM = f'{-self.tye:.2f}'
            self.EFy = f'{-self.fye:.2f}'
            self.EFx = f'{-self.fxe:.2f}'

    def get_element_angle(self, c, node_map):
        if node_map.get(c+1):
            _, xi, yi, _, _, _ = self.fetcher(node_map.get(c))
            _, xf, yf, _, _, _ = self.fetcher(node_map.get(c + 1))

            if c != len(node_map) + 1:
                self.ss.add_element([[xi, yi], [xf, yf]])
            if (xf - xi) != 0:
                self.angle.append(numpy.arctan((yf - yi) / (xf - xi)))
            elif (xf - xi) == 0:
                self.angle.append(numpy.arctan((yf - yi) / 1e-100))

    def add_supports(self, c, hinged, roll, fixed):
        if hinged and roll:
            if roll[0].id == c:
                self.ss.point_load(self.idr, -self.fxr, -self.fyr)

            if hinged[0].id == c:
                self.ss.point_load(self.idh, -self.fxh, -self.fyh)

        if fixed:
            if fixed[0].id == c:
                self.ss.point_load(self.idf, -self.fxe, -self.fye)
                self.ss.moment_load(self.idf, self.tye)

    def map_loads(self, c, node_map, p, xq, yq):
        if c in p.keys():
            id_, x, y, _, _, _ = self.fetcher(node_map.get(c))
            fx = p[id_][0]
            fy = -p[id_][1]

            if fx != 0:
                self.total_point_x += f' {fx:.2f}'

            if y - yq != 0:
                if fx != 0:
                    self.total_point += f' + {fx:.2f}i \cdot {y - yq:.2f}j'

            if fy != 0:
                self.total_point_y += f' {fy:.2f}'

            if float(x) - float(xq) != 0:
                if fy != 0:
                    self.total_point += f' + {fy:.2f}j \cdot {x - xq:.2f}i'

            self.ss.point_load(id_, fx, fy)

    def get_qload_values(self, c, q, qi, xi, xf, yi, yf):
        pos = ((xf - xi) ** 2 + (yf - yi) ** 2) ** 0.5
        iload = ((-qi.get(c) + -q.get(c)) * pos) / 2

        yload = -(math.cos(self.angle[c - 1] + math.pi) * iload)
        xload = math.sin(self.angle[c - 1] + math.pi) * iload
        cg = (pos / 3) * (float(qi.get(c) + 2 * float(q.get(c))) / (float(qi.get(c)) + float(q.get(c))))
        height = math.sin(self.angle[c - 1]) * cg
        base = math.cos(self.angle[c - 1]) * cg

        return pos, iload, yload, xload, cg, height, base

    def map_qloads(self, c, node_map, df, xq, yq, q, qi):
        if c in df.keys():
            x = symbols('x')
            id_, xi, yi, _, _, _ = self.fetcher(node_map.get(c))
            _, xf, yf, _, _, _ = self.fetcher(node_map.get(c + 1))

            pos, iload, yload, xload, cg, height, base = self.get_qload_values(c, q, qi, xi, xf, yi, yf)

            if round(xload, 2) != 0:
                self.total_load_x += f' + {xload:.2f}'
                if float(f'{height + yi - yq:.2f}') != 0:
                    self.total_moment += f' + {xload:.2f}i \cdot {height + yi - yq:.2f}j'

            if round(yload, 2) != 0:
                self.total_load_y += f' + {yload:.2f}'
                if round(base + xi - yq, 2) != 0:
                    self.total_moment += f' + {yload:.2f}j \cdot {base + xi - xq:.2f}i'

            self.list_load_x.update({c: self.total_load_x})
            self.list_load_y.update({c: self.total_load_y})
            q_part = ((((qi.get(c) - q.get(c)) / (6 * pos)) * x ** 2) * 3 - ((qi.get(c) / 2) * x) * 2)
            self.eq_load.update({c: str(latex(self.round_expr(q_part, 2)))})

            self.ss.q_load(-q.get(c), -qi.get(c), float(id_))

    def map_moments(self, c, node_map, m, moment):
        if c in m.keys():
            id_ = node_map.get(c).id
            if moment.get(c) > 0:
                self.total_moment += f' + {moment.get(c)}k'
            elif moment.get(c) < 0:
                self.total_moment += f' {moment.get(c)}k'
            self.ss.moment_load(float(id_), moment.get(c))

    def solver(self, hinged, roll, roll_direction, fixed, moment, point, q, qi, node_map):
        self.reset()
        if (len(hinged) == 1 and len(roll) == 1) or len(fixed) == 1:
            p = point
            m = moment
            df = q

            self.get_hinged_info(hinged, node_map)

            self.get_roll_info(roll, node_map, roll_direction)

            self.get_fixed_info(fixed, node_map)

            if fixed:
                yq = self.ye
                xq = self.xe
            elif hinged and roll:
                yq = self.yh
                xq = self.xh

            plt.style.use('default')
            for c in range(1, len(node_map)+1):
                self.get_element_angle(c, node_map)

                self.add_supports(c, hinged, roll, fixed)

                self.map_loads(c, node_map, p, xq, yq)

                self.map_qloads(c, node_map, df, xq, yq, q, qi)

                self.map_moments(c, node_map, m, moment)

                self.Vy.append(self.total_point_y)
                self.Vx.append(self.total_point_x)

                if c-1 in df.keys():
                    if self.list_load_y.get(c-1) is not None:
                        self.Vy[c-1] += self.list_load_y.get(c-1)
                    if self.list_load_x.get(c-1) is not None:
                        self.Vx[c-1] += self.list_load_x.get(c-1)

                if fixed:
                    if fixed[0].id == c:
                        if moment.get(c) is not None:
                            moment.update({c: moment.get(c) + float(self.EM)})
                        else:
                            moment.update({c: float(self.EM)})

                self.ss.show_structure()
                savefig(f'Settings\\figs\\structure{c}')

            plt.style.use('dark_background')
            self.savior(hinged, roll, roll_direction, fixed, node_map, moment, df)

    def fetcher(self, pool):
        id_ = pool.id
        x = pool.vertex.x
        y = pool.vertex.y
        fx = pool.Fx
        fy = pool.Fy
        ty = pool.Ty
        return id_, x, y, fx, fy, ty

    def savior(self, hinged, roll, roll_direction, fixed, node_map, moment, df):
        doc = Document(document_options="a4paper,12pt", documentclass="article")
        doc.preamble.append(NoEscape(r"""
                \usepackage[left=1.5cm,right=1.5cm,top=2cm,bottom=2cm]{geometry}
                \usepackage{setspace}
                \onehalfspacing
                \usepackage[portuguese]{babel}
                \usepackage{indentfirst}
                \usepackage{graphicx}
                \usepackage{caption}
                \usepackage{amsmath}
                \usepackage{multicol}
                \usepackage[colorlinks=true,linkcolor=black,anchorcolor=black,citecolor=black,filecolor=black,menucolor=black,runcolor=black,urlcolor=black]{hyperref}
                \usepackage{cals, ragged2e, lmodern}
                \usepackage{pdflscape}
                \usepackage{float}
                \usepackage{breqn}
                \usepackage{gensymb}
                \usepackage{helvet}
                \renewcommand{\familydefault}{\sfdefault}

                \title{Cálculos da Estrutura}
                \author{}
                """))

        doc.append(NoEscape(r"""
                \maketitle
                """))

        self.filter_strings()

        with doc.create(Section('Imagem da Estrutura')):

            with doc.create(Figure(position='H')) as fig_estrutura:
                fig_estrutura.add_image("figs\\structure", width='500px')
                fig_estrutura.add_caption(NoEscape(r'\label{fig:estrutura} Imagem da estrutura com apoios e carregamentos'))

        with doc.create(Section('Fazendo o diagrama de corpo livre')):
            with doc.create(Figure(position='H')) as fig_corpolivre:
                if hinged and roll:
                    fig_corpolivre.add_image("figs\\diagram1", width='500px')
                elif fixed:
                    fig_corpolivre.add_image("figs\\diagram2", width='500px')
                fig_corpolivre.add_caption(NoEscape(r'\label{fig:corpolivre} Diagrama de corpo livre'))

        with doc.create(Section('Calculando a reação dos apoios')):

            if fixed:
                with doc.create(Subsection('Realizando cálculo do momento no engaste')):
                    with doc.create(Alignat(numbering=False, escape=False)) as sum_M:
                        sum_M.append(r'\sum{M} &= 0 \\')
                        sum_M.append(r'M &= F \cdot d \\')
                doc.append(NoEscape(r'\begin{dmath*}'))
                doc.append(NoEscape(f'{self.total_moment + self.total_point} {self.get_signal(self.EM)} M = 0 \\\\'))
                doc.append(NoEscape(r'\end{dmath*}'))
                doc.append(NoEscape(r'\begin{dmath*}'))
                doc.append(NoEscape(f'M = {abs(float(self.EM))}'))
                doc.append(NoEscape(r'\end{dmath*}'))

                with doc.create(Subsection('Fazendo a somatória das forças em Y para obter a reação em Y no engaste')):
                    with doc.create(Alignat(escape=False, numbering=False)) as sum_Fy:
                        sum_Fy.append(r'\sum{Fy} = 0 \\')
                        sum_Fy.append(f'{self.total_point_y + self.total_load_y} {self.get_signal(self.EFy)} Fy = 0' r'\\')
                        sum_Fy.append(f'Fy = {abs(float(self.EFy))}')

                with doc.create(Subsection('Fazendo a somatória das forças em X para obter a reação em X no engaste')):
                    with doc.create(Alignat(escape=False, numbering=False)) as sum_Fx:
                        sum_Fx.append(r'\sum{Fx} = 0 \\')
                        sum_Fx.append(f'{self.total_point_x + self.total_load_x} {self.get_signal(self.EFx)} Fx = 0' r'\\')
                        sum_Fx.append(f'Fx = {abs(float(self.EFx))}')

            elif hinged and roll:
                roll_angle = roll_direction.get(self.idr)
                with doc.create(Subsection('Realizando cálculo do momento no apoio fixo')):
                    with doc.create(Alignat(numbering=False, escape=False)) as sum_M:
                        sum_M.append(r'\sum{M} &= 0 \\')
                        sum_M.append(r'M &= F \cdot d')
                    if roll_angle == math.pi/2:
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(self.symbol_filter(f'{self.total_moment+self.total_point} {self.get_signal(self.Bx)} Bx \cdot {self.roll_dist_y} = 0 \\\\')))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(f'Bx = {abs(float(self.Bx))}'))
                        doc.append(NoEscape(r'\end{dmath*}'))
                    elif roll_angle == 0:
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(self.symbol_filter(f'{self.total_moment+self.total_point} {self.get_signal(self.By)} By \cdot {self.roll_dist_x} = 0 \\\\')))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(f'By = {abs(float(self.By))}'))
                        doc.append(NoEscape(r'\end{dmath*}'))
                    else:
                        rangle = round(roll_direction.get(self.idr) * 180/math.pi, 2)
                        Bangle = round(float(self.Bx) / math.sin(rangle * math.pi/180), 2)
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(self.symbol_filter(
                            f'{self.total_moment + self.total_point} {self.get_signal(self.By)} B_{{{rangle}\degree}} \cdot cos({rangle}\degree) \cdot {self.roll_dist_x} {self.get_signal(self.Bx)} B_{{{rangle}\degree}} \cdot sen({rangle}\degree) \cdot {self.roll_dist_y} = 0 \\\\')))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(f'B_{{{rangle}\degree}} = {Bangle}'))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(f'B_x = {Bangle} * sen({rangle}\degree)'))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(f'B_x = {round(float(self.Bx), 2)}'))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(f'B_y = {Bangle} * cos({rangle}\degree)'))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(f'B_y = {round(float(self.By), 2)}'))
                        doc.append(NoEscape(r'\end{dmath*}'))

                with doc.create(Section('Refazendo o diagrama de corpo livre')):
                    with doc.create(Figure(position='H')) as fig_corpolivre:
                        fig_corpolivre.add_image("figs\\diagram2", width='500px')
                        fig_corpolivre.add_caption(NoEscape(r'\label{fig:corpolivre} Diagrama de corpo livre'))

                with doc.create(Subsection('Fazendo a somatória das forças em Y para obter a reação em Y no apoio fixo')):
                    with doc.create(Alignat(escape=False, numbering=False)) as sum_Fy:
                        sum_Fy.append(r'\sum{Fy} = 0 \\')
                        if round(float(self.By), 2) != 0:
                            sum_Fy.append(self.symbol_filter(f'{self.total_point_y}{self.total_load_y} + {float(self.By):.2f} {self.get_signal(self.Ay)} Ay = 0' r'\\'))
                        else:
                            sum_Fy.append(self.symbol_filter(f'{self.total_point_y}{self.total_load_y} {self.get_signal(self.Ay)} Ay = 0' r'\\'))
                        sum_Fy.append(f'Ay = {abs(float(self.Ay))}')

                with doc.create(Subsection('Fazendo a somatória das forças em X para obter a reação em X no apoio fixo')):
                    with doc.create(Alignat(escape=False, numbering=False)) as sum_Fx:
                        sum_Fx.append(r'\sum{Fx} = 0 \\')
                        if float(f'{float(self.Bx):.2f}') != 0:
                            sum_Fx.append(self.symbol_filter(f'{self.total_point_x}{self.total_load_x} + {float(self.Bx):.2f} {self.get_signal(self.Ax)} Ax = 0' r'\\'))
                        else:
                            sum_Fx.append(self.symbol_filter(f'{self.total_point_x}{self.total_load_x} {self.get_signal(self.Ax)} Ax = 0' r'\\'))
                        sum_Fx.append(f'Ax = {abs(float(self.Ax))}')

            with doc.create(Subsection('Desenhando as reações dos apoios')):
                with doc.create(Figure(position='H')) as fig_apoios:
                    fig_apoios.add_image("figs\\supports", width='500px')
                    fig_apoios.add_caption(NoEscape(r'\label{fig:apoios} Reações dos apoios'))

        with doc.create(Section('Calculando os esforços internos')):

            doc.append('A partir da integral da força cortante, é obtido o momento fletor.')
            doc.append(LineBreak())
            doc.append('A constante de integração será o momento no nó final da seção anterior.')
            doc.append(LineBreak())

            eq = self.eq
            x = symbols("x")
            if hinged and roll:
                self.idh = hinged[0].id
                self.idr = roll[0].id
            elif fixed:
                self.idf = fixed[0].id

            for c in range(len(eq)):

                vx = ''
                vy = ''
                vx = self.symbol_filter(self.Vx[c])
                vy = self.symbol_filter(self.Vy[c])
                V = ''
                M = ''
                const = ''

                if hinged and roll:
                    if c + 1 >= self.idh:
                        _, _, _, fx, fy, _ = self.fetcher(node_map.get(self.idh))
                        if float(fx) != 0:
                            vx += f' + {-fx:.2f}'
                        if float(fy) != 0:
                            vy += f' + {-fy:.2f}'

                    if c + 1 >= self.idr:
                        _, _, _, fx, fy, _ = self.fetcher(node_map.get(self.idr))
                        if fx != 0:
                            vx += f' + {-fx:.2f}'
                        if fy != 0:
                            vy += f' + {-fy:.2f}'

                elif fixed:
                    if c + 1 >= float(self.idf):
                        _, _, _, fx, fy, _ = self.fetcher(node_map.get(self.idf))
                        if fx != 0:
                            vx += f' + {-fx:.2f}'
                        if fy != 0:
                            vy += f' + {-fy:.2f}'

                if c != 0:
                    _, xi, yi, _, _, _ = self.fetcher(node_map.get(c))
                    _, xf, yf, _, _, _ = self.fetcher(node_map.get(c + 1))
                    pos = ((xf - xi) ** 2 + (yf - yi) ** 2) ** 0.5

                vxc = vx
                vyc = vy
                if not vxc:
                    vxc = "0"
                if not vyc:
                    vyc = "0"

                if (abs(self.angle[c]) != 0) and (abs(self.angle[c]) != (math.pi / 2)):
                    # signals = self.get_signal_vectors(numpy.radians(90) - self.angle[c], vxc, vyc)
                    signals = self.get_signal_vectors(functions.n[c], vxc, vyc, self.angle[c], "cos")
                    vy = f'{signals[0]} ({vyc}) \cdot cos({90-(self.angle[c] * 180 / math.pi):.2f}\degree) {signals[1]} ({vxc}) \cdot cos({self.angle[c] * 180 / math.pi:.2f}\degree)'
                    signals = self.get_signal_vectors(functions.eq[c][2], vxc, vyc, self.angle[c], "sin")
                    vx = f'{signals[0]} ({vyc}) \cdot sen({90-(self.angle[c] * 180 / math.pi):.2f}\degree) {signals[1]} ({vxc}) \cdot sen({self.angle[c] * 180 / math.pi:.2f}\degree)'
                    vxc = f'{signals[0]} (({vyc}) * {math.sin(numpy.radians(90)-self.angle[c])}) {signals[1]} (({vxc}) * {math.sin(self.angle[c])})'

                if vxc == '':
                    vxc = '0'

                if round(float(parse_expr(self.symbol_filter(vxc))), 2) == -round(functions.n[c], 2):
                    n = vx
                    v = vy
                else:
                    n = vy
                    v = vx

                if round(eq[c][0], 2) != 0:
                    M += f'{-eq[c][0]:.2f}x^3'
                    V += f'{-eq[c][0] * 3: .2f}x^2'

                if round(eq[c][1], 2) != 0:
                    M += f'+{-eq[c][1]:.2f}x^2'
                    V += f'+{-eq[c][1] * 2:.2f}x'

                if round(eq[c][2], 2) != 0:
                    M += f'+{-eq[c][2]:.2f}x'
                    V += f'+{-eq[c][2]:.2f}'

                if round(eq[c][3], 2) != 0:
                    M += f'+{-eq[c][3]:.2f}'

                if V == '':
                    V = '0'

                if M == '':
                    M = '0'

                if c != 0:
                    const += f"{-eq[c-1][0]:.2f} \cdot {pos:.2f}^3"
                    const += f" + {-eq[c-1][1]:.2f} \cdot {pos:.2f}^2"
                    const += f" + {-eq[c-1][2]:.2f} \cdot {pos:.2f}"
                    const += f" + {-eq[c-1][3]:.2f}"
                    numeric_const = (-eq[c-1][0] * pos**3) + (-eq[c-1][1] * pos**2) + (-eq[c-1][2] * pos) + -eq[c-1][3]

                with doc.create(Subsection(f'Cortando na Seção {c+1}')):
                    with doc.create(Figure(position='H')) as fig_secoes:
                        fig_secoes.add_image(f"figs\\structure{c+1}", width='500px')
                        fig_secoes.add_caption(NoEscape(r'\label{fig:secoes}' f"Seção {c+1}"))

                    with doc.create(Subsubsection(f"Força Normal")):
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(r'\sum{Fx} = 0 \\'))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(r'{} + N = 0'.format(self.symbol_filter(n))))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(r'N = {:.2f}'.format(float(functions.n[c]))))
                        doc.append(NoEscape(r'\end{dmath*}'))

                    with doc.create(Subsubsection(f"Força Cortante")):
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(r'\sum{Fy} = 0 \\'))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        if c+1 in df.keys() and self.eq_load.get(c + 1) is not None:
                            doc.append(NoEscape(r'{} - V = 0'.format(self.symbol_filter(f'{self.eq_load.get(c+1)} {v}'))))
                        else:
                            doc.append(NoEscape(r'{} - V = 0'.format(self.symbol_filter(v))))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(r'V = {}'.format(self.symbol_filter(V))))
                        doc.append(NoEscape(r'\end{dmath*}'))

                    with doc.create(Subsubsection(f'Momento Fletor')):
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(f'M = \\int{self.symbol_filter(V)}  dx'))
                        doc.append(NoEscape(r'\end{dmath*}'))
                        doc.append(NoEscape(r'\begin{flushleft}'))
                        doc.append(f'Constante no nó {c+1}:')
                        doc.append(NoEscape(r'\end{flushleft}'))
                        if c != 0:
                            doc.append(NoEscape(r'\begin{dmath*}'))
                            if f'{c+1}: ' in str(moment):
                                doc.append(NoEscape(self.symbol_filter(f"c = {const} + {self.get_signal_constant(moment.get(c+1), numeric_const, -eq[c][3]):.2f}")))
                            else:
                                doc.append(NoEscape(self.symbol_filter(f"c = {const}")))
                            doc.append(NoEscape(r'\end{dmath*}'))
                        elif c == 0 and f'{c+1}: ' in str(moment):
                            doc.append(NoEscape(r'\begin{dmath*}'))
                            doc.append(self.symbol_filter(NoEscape(f'c = {self.get_signal_constant(moment.get(c+1), numeric_const, -eq[c][3]):.2f}')))
                            doc.append(NoEscape(r'\end{dmath*}'))
                        else:
                            doc.append(NoEscape(r'\begin{center}'))
                            doc.append(NoEscape(f'Não há momento no nó {c+1}, portanto c = 0'))
                            doc.append(NoEscape(r'\end{center}'))
                        doc.append(NoEscape(r'\begin{dmath*}'))
                        doc.append(NoEscape(r'M = {}'.format(self.symbol_filter(M))))
                        doc.append(NoEscape(r'\end{dmath*}'))

        with doc.create(Section('Desenhando os diagramas de esforços internos')):
            with doc.create(Subsection('Desenhando o diagrama da força normal:')):
                with doc.create(Figure(position='H')) as fig_normais:
                    fig_normais.add_image("figs\\axial", width='500px')
                    fig_normais.add_caption(NoEscape(r'\label{fig:normais} Força normal'))

            with doc.create(Subsection('Desenhando o diagrama da força cortante:')):
                with doc.create(Figure(position='H')) as fig_cortante:
                    fig_cortante.add_image("figs\\shear", width='500px')
                    fig_cortante.add_caption(NoEscape(r'\label{fig:cortante} Força cortante'))

            with doc.create(Subsection('Desenhando o diagrama do momento fletor:')):
                with doc.create(Figure(position='H')) as fig_momentofletor:
                    fig_momentofletor.add_image("figs\\moment", width='500px')
                    fig_momentofletor.add_caption(NoEscape(r'\label{fig:momento} Momento Fletor'))

        doc.generate_pdf(r'Settings\resolucao', compiler='pdflatex')
        doc.generate_tex(r'Settings\resolucao')

    def filter_strings(self):
        self.total_moment = self.symbol_filter(self.total_moment)
        self.total_point = self.symbol_filter(self.total_point)
        self.total_point_y = self.symbol_filter(self.total_point_y)
        self.total_point_x = self.symbol_filter(self.total_point_x)
        self.total_load_y = self.symbol_filter(self.total_load_y)
        self.total_load_x = self.symbol_filter(self.total_load_x)

    def symbol_filter(self, string):
        string = string.replace('- -', '+')
        string = string.replace('--', '+')
        string = string.replace('+ -', '-')
        string = string.replace('+-', '-')
        #string = string.replace('+ 0.00', '')
        #string = string.replace('- 0.00', '')
        search = re.search(r'\\cdot -(\d+\.\d+)', string)
        if search:
            string = re.sub(r'\\cdot -(\d+\.\d+)', f'\\\\cdot (-{search.group(1)})', string)
        return string

    def get_signal(self, string):
        if re.search(r'(-)', string):
            return '-'
        else:
            return '+'

    def get_signal_constant(self, moment, numeric_const, result):
        if math.isclose(round(moment + numeric_const, 2), round(result, 2), rel_tol=1e-2):
            return moment
        else:
            return -moment

    def get_signal_vectors(self, result, fx, fy, angle, op):
        rel_value = 1e-2
        result = round(result, 2)
        fx = parse_expr(fx)
        fy = parse_expr(fy)

        if op == "sin":
            fx = fx * math.sin(angle)
            fy = fy * math.sin(numpy.radians(90) - angle)
        elif op == "cos":
            fx = fx * math.cos(angle)
            fy = fy * math.cos(numpy.radians(90) - angle)

        if math.isclose(round(-fx + fy, 2), result, rel_tol=rel_value):
            return "-", "+"

        elif math.isclose(round(fx - fy, 2), result, rel_tol=rel_value):
            return "+", "-"

        elif math.isclose(round(fx + fy, 2), result, rel_tol=rel_value):
            return "+", "+"

        else:
            return "-", "-"

    def round_expr(self, expr, num_digits):
        return expr.xreplace({n: round(n, num_digits) for n in expr.atoms(Number)})


'''
@Author: your name
@Date: 2020-05-27 13:33:49
@LastEditTime: 2020-06-09 12:51:11
@LastEditors: Please set LastEditors
@Description: In User Settings Edit
@FilePath: /model-building/recommend/xdeepfm/inputs.py
'''

import tensorflow as tf
from collections import namedtuple
import copy
SparseFeature = namedtuple('SparseFeature', [
                           'feature_name', 'vocab_size', 'embedding_dim', 'field_id', 'num_fields'])
DenseFeature = namedtuple('DenseFeature', ['feature_name'])

SparseFeature.__new__.__defaults__ = (False, None, 4, None, None)
DenseFeature.__new__.__defaults__ = (False,)

def build_input_placeholder(feature_columns):
    input_placeholders_dict = {}
    for feature in feature_columns:
        if isinstance(feature, SparseFeature):
            h = tf.placeholder(dtype=tf.float32, shape=[
                               None, feature.embedding_dim])
        elif isinstance(feature, DenseFeature):
            h = tf.placeholder(dtype=tf.float32, shape=[None, 1])
        else:
            raise ValueError('unknown')
        input_placeholders_dict[feature.feature_name] = h
    return input_placeholders_dict

def build_embedding_matrix_dict(sparse_feature_columns, target='dnn'):
    if target == 'linear':
        sfc = copy.deepcopy(sparse_feature_columns)
        for i, fc in enumerate(sfc):
            sfc[i] = sfc[i]._replace(embedding_dim=1)
    elif target != 'dnn':
        raise ValueError('target参数只能是linear或者dnn, 接收到%s' % target)

    embedding_matrix_dict = {}
    for feature in sparse_feature_columns:
        embedding_matrix_dict[feature.feature_name] = tf.get_variable(
            name=feature.feature_name+'_embedding_matrix_%s' % target,
            shape=[feature.vocab_size, feature.embedding_dim],
            initializer=tf.random_normal_initializer(),
            dtype=tf.float32
        )
    return embedding_matrix_dict

def build_field_embedding_matrix_dict(sparse_feature_columns):
    embedding_matrix_dict = {}
    for feature in sparse_feature_columns:
        if not isinstance(feature, SparseFeature):
            raise ValueError('只有sparse_feature才能建立embedding矩阵')
        embedding_matrix_dict[feature.feature_name] = {}
        for field in range(feature.num_fields):
            embedding_matrix_dict[feature.feature_name][field] = tf.get_variable(
                name=feature.feature_name+'_field%d'%field+'_embedding_matrix',
                shape=[feature.vocab_size, feature.embedding_dim],
                initializer=tf.random_normal_initializer(),
                dtype=tf.float32
            )
    return embedding_matrix_dict

# test = SparseFeature(embedding_dim=4)
# print(test.embedding_dim)

def input_from_feature_columns(inputs, feature_columns, target='dnn', field=False):
    dense_feature_columns = [fc for fc in feature_columns if isinstance(fc, DenseFeature)]
    sparse_feature_columns = [fc for fc in feature_columns if isinstance(fc, SparseFeature)]

    dense_value_list = [inputs[fc.feature_name] for fc in dense_feature_columns]
    if field:
        embedding_matrix_dict = build_field_embedding_matrix_dict(sparse_feature_columns)
        return dense_value_list, embedding_matrix_dict

    embedding_matrix_dict = build_embedding_matrix_dict(sparse_feature_columns, target)
    sparse_embedding_list = [tf.nn.embedding_lookup(embedding_matrix_dict[fc.feature_name], inputs[fc.feature_name]) for fc in sparse_feature_columns]

    return dense_value_list, sparse_embedding_list


# Double DQN.  Compared to DQN, it uses q_network rather than target_q_network
# when selecting next action when extracting next q value.

import numpy as np
import os
import random

from agent import Agent
import tensorflow as tf
tf.compat.v1.disable_eager_execution()
from tensorflow.keras.layers import Dense, Input, Activation
from tensorflow.keras import Model
from tensorflow.keras.optimizers import Adam
from tqdm import trange
from utils.logger import Logger
from utils.visualizer import visualize

class DDQN(Agent):
    def __init__(self, config, env):
        self.gamma = config["gamma"]   # reward discount
        self.learning_rate = config["learning_rate"]
        self.memory_size = config["memory_size"]
        self.epsilon = config["epsilon"]  # Exploration rate
        self.epsilon_min = config["epsilon_min"]
        self.epsilon_decay = config["epsilon_decay"]
        self.batch_size = config["batch_size"]
        self.update_frequency = config["update_frequency"]
        self.num_actions = env.action_space.n
        self.num_states = env.observation_space.shape[0]
        self.max_episode = config["max_episode"]
        self.max_step = config["max_step"]
        self.render_environment = config["render_environment"]
        self.result_path = config["result_path"]
        self.memory = []
        self.env = env
        self.q_network = self.build_agent()
        self.target_q_network = self.build_agent()
        self.logger = Logger(config["slide_window"])

    def build_agent(self):
        input = Input(shape=(self.num_states,))
        layer = Dense(24, activation='relu')(input)
        layer = Dense(self.num_actions)(layer)
        output = Activation('linear')(layer)
        model = Model(input, output)
        adam = Adam(lr=self.learning_rate)
        model.compile(loss='mse', optimizer=adam)

        return model

    # Save <s, a ,r, s'> of each step
    def store_memory(self, state, action, reward, next_state, done):
        if len(self.memory) > self.memory_size:
            self.memory.pop(0)

        self.memory.append([state, action, reward, next_state, done])

    def train(self):
        # Initialize q_network and target_q_network
        self.target_q_network.set_weights(self.q_network.get_weights())

        max_episode = 100000
        max_step = 10000
        slide_window = 100

        # Populate memory first
        state = self.env.reset()
        print("Warming up...")
        while len(self.memory) < self.batch_size:
            action = self.env.action_space.sample()
            next_state, reward, done, info = self.env.step(action)
            self.store_memory(state, action, reward, next_state, done)
            if done:
                state = self.env.reset()
        print("Warm up complete.")

        t = trange(self.max_episode)
        for episode_count in t:
            state = self.env.reset()
            current_step = 0
            episode_reward = 0

            while True:
                if self.render_environment:
                    self.env.render()

                # Network predict
                q_values = self.q_network.predict(np.reshape(state, (1, self.num_states))).ravel()

                # Decide if exploring or not
                if np.random.rand() >= self.epsilon:
                    action = np.argmax(q_values)
                else:
                    action = random.randrange(self.num_actions)

                # Perform action
                next_state, reward, done, info = self.env.step(action)

                # Store transition
                episode_reward += reward
                self.store_memory(state, action, reward, next_state, done)

                # Decrease exploration
                if self.epsilon > self.epsilon_min:
                    self.epsilon *= self.epsilon_decay

                # Sample minibatch from memory
                minibatch = random.sample(self.memory, self.batch_size)

                # Transform the minibatch for processing
                minibatch = list(zip(*minibatch))

                # Calculate all td_targets for current minibatch
                states, actions, rewards, next_states, dones = minibatch
                batch_q_values = self.q_network.predict_on_batch(np.array(states))
                batch_next_q_values = self.target_q_network.predict_on_batch(np.array(next_states))
                next_actions = np.argmax(self.q_network.predict_on_batch(np.array(next_states)), axis=1) # Main difference between DDQN and DQN
                td_targets = batch_q_values.copy()
                for i in range(self.batch_size):
                    td_targets[i][actions[i]] = rewards[i] + self.gamma * (1 - dones[i]) * batch_next_q_values[i][next_actions[i]]

                # Train network
                self.q_network.train_on_batch(np.array(states), np.array(td_targets))

                # Hard copy q_network to target_q_network
                if done or current_step % self.update_frequency is 0:
                    self.target_q_network.set_weights(self.q_network.get_weights())

                # For logging and visualizing data
                if done or current_step > self.max_step:
                    self.logger.log_history(episode_reward, episode_count)
                    self.logger.show_progress(t, episode_reward, episode_count)
                    if episode_count % self.logger.slide_window == 0:
                        visualize(self.logger.rewards,
                                  self.logger.running_rewards,
                                  self.logger.episode_counts,
                                  os.path.join(self.result_path, "DDQN.png"))
                    break

                state = next_state
                current_step += 1

if __name__ == '__main__':
    agent = DDQN()
    agent.train()