"""
Project: PosingBySketching
Version: 4.0

==========================
OpenVR Compatible (HTC Vive)
==========================

OpenVR Compatible head mounted display
It uses a python wrapper to connect with the SDK
"""
import threading
import openvr
import bpy
import math
import sys
import copy
from enum import Enum
from mathutils import Quaternion
from mathutils import Matrix
from mathutils import Vector
import winsound

import time

from . import HMD_Base

from ..lib import (
        checkModule,
        )

import datetime
from scipy.optimize import linear_sum_assignment
import numpy as np

currObject = ""
currBone = ""
currObject_l = ""
currBone_l = ""

# Algorithm from "New Algorithms for 2D and 3D Point Matching: Pose Estimation and Correpondance" used also in
#   1) A New Point Matching Algorithm for Non-Rigid Registration
#   2) Enhancing Character Posing by a Sketch-Based Interaction
class softAss_detAnnealing_3(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    # Compute 3D distance
    def compute_dist(self, p1, p2):
        return math.sqrt(((p1[0] - p2[0]) ** 2) + ((p1[1] - p2[1]) ** 2) + ((p1[2] - p2[2]) ** 2))

    # Move the first bone at the beggining og the stroke
    def set_init_cond(self):
        bones = bpy.data.objects['Armature'].pose.bones  # <H
        stroke = bpy.data.curves['Stroke'].splines[0]  # <H

        # TODO: Find root bones
        for b in bones:
            if b.parent == None:
                print(bpy.data.objects['Armature'].matrix_world * b.head)

                pmatrix = b.bone.matrix_local
                omatrix = bpy.data.objects['Armature'].matrix_world

                target_loc = bpy.data.objects['StrokeObj'].matrix_world * stroke.bezier_points[0].co
                b.location = omatrix.inverted() * pmatrix.inverted() * target_loc

                bpy.context.scene.update()
                print(bpy.data.objects['Armature'].matrix_world * b.head)

    def run(self):
        bones = bpy.data.objects['Armature'].pose.bones  # <H
        # stroke = bpy.data.curves['Stroke'].splines[0] # <H

        dict = {}
        stroke_points = []
        num_points = 0
        for k in range(0, len(bpy.data.curves['Stroke'].splines)):
            stroke = bpy.data.curves['Stroke'].splines[k]
            for j in range(0, len(stroke.bezier_points)):
                p = stroke.bezier_points[j]
                # World position of the point
                point = bpy.data.objects['StrokeObj'].matrix_world * p.co
                # Add into the dictionoary for correspondance
                dict[num_points] = [k, j]  # [spline_index, point_index]
                # Add in the point list
                stroke_points.append(point)
                num_points += 1

        # [DEBUG]:
        # print(dict)
        # for i in range(0, num_points):
        #    print(dict[i])
        #    print(dict[i][0], dict[i][1])

        # INIT PARAMETERS
        # values of the paper
        # beta_f = 0.2
        # beta_r = 1.075
        # beta = 0.00091
        # alpha = 0.03
        # I0 = 4
        # I1 = 30

        # custom value
        beta_f = 0.2
        beta_r = 1.9
        beta = 0.00091
        alpha = 0.03
        I0 = 4
        I1 = 30

        iteration = 0

        # set starting condition
        self.set_init_cond()

        while beta <= beta_f:
            print('iteration:', iteration)

            Qjk = []

            # Compute Qjk
            for k in range(0, num_points):
                p = bpy.data.curves['Stroke'].splines[dict[k][0]].bezier_points[dict[k][1]]

                # World position of the point
                point = bpy.data.objects['StrokeObj'].matrix_world * p.co

                costs_row = []
                for i in range(0, len(bones)):
                    b = bones[i]
                    # World position of the bone
                    tail = bpy.data.objects['Armature'].matrix_world * b.tail  # <H
                    head = bpy.data.objects['Armature'].matrix_world * b.head  # <H
                    # center = bpy.data.objects['Armature'].matrix_world * b.center # <H

                    dist = self.compute_dist(tail, point)
                    # length_diff = abs(b.length - compute_dist(point,head))
                    # costs_row.append(dist + length_diff)
                    costs_row.append(-(dist - alpha))

                Qjk.append(costs_row)

            m0 = np.asarray(Qjk)

            # Deterministic annealing
            for i in range(0, num_points):
                for j in range(0, len(bones)):
                    m0[i, j] = math.exp(beta * m0[i, j])

                    # TODO: Add outlier row and cols

            # Sinkhorn's method DO until m converges
            m1 = np.ones((num_points, len(bones)))

            for i in range(0, I1):  # TODO: set coverage threshold
                for i in range(0, num_points):
                    for j in range(0, len(bones)):
                        m1[i, j] = m0[i, j] / np.sum(m0[i])

                for i in range(0, num_points):
                    for j in range(0, len(bones)):
                        m0[i, j] = m1[i, j] / np.sum(m1[:, j])

                        # [DEBUG]: Rows - Cols sum up
            # print("rows - cols sum up")
            # for i in range (0,num_points):
            #    print (np.sum(m0[i]))

            # for j in range (0, len(bones)):
            #    print(np.sum(m0[:,j]))

            # Softassign - ENERGY FORMULATION E3D
            E3D = []
            # Compute E3D
            for k in range(0, num_points):
                p = bpy.data.curves['Stroke'].splines[dict[k][0]].bezier_points[dict[k][1]]

                # World position of the point
                point = bpy.data.objects['StrokeObj'].matrix_world * p.co

                costs_row = []
                for i in range(0, len(bones)):
                    b = bones[i]
                    # World position of the bone
                    tail = bpy.data.objects['Armature'].matrix_world * b.tail  # <H
                    head = bpy.data.objects['Armature'].matrix_world * b.head  # <H
                    # center = bpy.data.objects['Armature'].matrix_world * b.center # <H

                    dist = self.compute_dist(tail, point)
                    # length_diff = abs(b.length - compute_dist(point,head))
                    # costs_row.append(dist + length_diff)
                    costs_row.append(dist - alpha * m0[k, i])

                E3D.append(costs_row)

            cost = np.transpose(m0) * np.transpose(E3D)
            row_ind, col_ind = linear_sum_assignment(cost)
            print(col_ind)
            print(cost[row_ind, col_ind].sum())

            # Update pose parameters
            for i in range(0, len(bones)):
                b = bones[i]
                # Set target positions
                #
                bpy.data.objects[b.name].location = copy.deepcopy(bpy.data.objects['StrokeObj'].matrix_world *
                                                                  bpy.data.curves['Stroke'].splines[
                                                                      dict[col_ind[i]][0]].bezier_points[
                                                                      dict[col_ind[i]][1]].co)

                # Articulate armature
                constr = b.constraints['Damped Track']
                constr.target = bpy.data.objects[b.name]

            iteration += 1
            beta = beta * beta_r

class State(Enum):
    IDLE = 1
    DECISIONAL = 2
    INTERACTION_LOCAL = 3
    NAVIGATION_ENTER = 4
    NAVIGATION = 5
    NAVIGATION_EXIT = 6
    ZOOM_IN = 7
    ZOOM_OUT = 8
    CAMERA_MOVE_CONT = 9
    CAMERA_ROT_CONT = 10
    SCALING = 11
    CHANGE_AXES = 12
    DRAWING = 13
    TRACKPAD_BUTTON_DOWN = 14

class StateLeft(Enum):
    IDLE = 1
    DECISIONAL = 2
    INTERACTION_LOCAL = 3
    NAVIGATION = 5
    SCALING = 11
    CHANGE_AXES = 12

class OpenVR(HMD_Base):
    ctrl_index_r = 0
    ctrl_index_l = 0
    tracker_index = 0
    hmd_index = 0
    curr_axes_r = 0
    curr_axes_l = 0
    state = State.IDLE
    state_l = StateLeft.IDLE

    diff_rot = Quaternion()
    diff_loc = bpy.data.objects['Controller.R'].location
    initial_loc = Vector((0,0,0))
    initial_rot = Quaternion()

    diff_rot_l = Quaternion()
    diff_loc_l = bpy.data.objects['Controller.L'].location
    initial_loc_l = Vector((0, 0, 0))
    initial_rot_l = Quaternion()

    diff_distance = 0
    initial_scale = 0
    trans_matrix = bpy.data.objects['Camera'].matrix_world * bpy.data.objects['Origin'].matrix_world
    diff_trans_matrix = bpy.data.objects['Camera'].matrix_world * bpy.data.objects['Origin'].matrix_world

    objToControll = ""
    boneToControll = ""
    objToControll_l = ""
    boneToControll_l = ""
    zoom = 1
    rotFlag = True
    axes = ['LOC/ROT_XYZ','LOC_XYZ','LOC_X','LOC_Y','LOC_Z','ROT_XYZ','ROT_X','ROT_Y','ROT_Z']

    gui_obj = ['Camera', 'Origin',
               'Controller.R', 'Controller.L',
               'Text.R', 'Text.L']

    def __init__(self, context, error_callback):
        super(OpenVR, self).__init__('OpenVR', True, context, error_callback)
        checkModule('hmd_sdk_bridge')

    def _getHMDClass(self):
        """
        This is the python interface to the DLL file in hmd_sdk_bridge.
        """
        from bridge.hmd.openvr import HMD
        return HMD

    @property
    def projection_matrix(self):
        if self._current_eye:
            matrix = self._hmd.getProjectionMatrixRight(self._near, self._far)
        else:
            matrix = self._hmd.getProjectionMatrixLeft(self._near, self._far)

        self.projection_matrix = matrix
        return super(OpenVR, self).projection_matrix

    @projection_matrix.setter
    def projection_matrix(self, value):
        self._projection_matrix[self._current_eye] = \
            self._convertMatrixTo4x4(value)

    def init(self, context):
        """
        Initialize device

        :return: return True if the device was properly initialized
        :rtype: bool
        """

        vrSys = openvr.init(openvr.VRApplication_Scene)
        self.ctrl_index_r, self.ctrl_index_l, self.tracker_index, self.hmd_index = self.findControllers(vrSys)
        if bpy.data.objects.get('StrokeObj') is None:
            self.create_curve()
        bpy.data.window_managers['WinMan'].virtual_reality.lock_camera = True

        try:
            HMD = self._getHMDClass()
            self._hmd = HMD()

            # bail out early if we didn't initialize properly
            if self._hmd.get_state_bool() == False:
                raise Exception(self._hmd.get_status())

            # Tell the user our status at this point.
            self.status = "HMD Init OK. Make sure lighthouses running else no display."

            # gather arguments from HMD
            self.setEye(0)
            self.width = self._hmd.width_left
            self.height = self._hmd.height_left

            self.setEye(1)
            self.width = self._hmd.width_right
            self.height = self._hmd.height_right

            # initialize FBO
            if not super(OpenVR, self).init():
                raise Exception("Failed to initialize HMD")

            # send it back to HMD
            if not self._setup():
                raise Exception("Failed to setup OpenVR Compatible HMD")

        except Exception as E:
            self.error("OpenVR.init", E, True)
            self._hmd = None
            return False

        else:
            return True

    def _setup(self):
        return self._hmd.setup(self._color_texture[0], self._color_texture[1])

    # ---------------------------------------- #
    # Functions
    # ---------------------------------------- #
    ## Find the index of the two controllers
    def findControllers(self, vrSys):
        r_index, l_index, tracker_index, hmd_index = -1, -1, -1, -1

        for i in range(openvr.k_unMaxTrackedDeviceCount):
            if openvr.IVRSystem.getTrackedDeviceClass(vrSys, i) == openvr.TrackedDeviceClass_Invalid:
                print(i, openvr.IVRSystem.getTrackedDeviceClass(vrSys, i), " - ")
            if openvr.IVRSystem.getTrackedDeviceClass(vrSys, i) == openvr.TrackedDeviceClass_HMD:
                print(i, openvr.IVRSystem.getTrackedDeviceClass(vrSys, i), " - HMD")
                hmd_index = i
            if openvr.IVRSystem.getTrackedDeviceClass(vrSys, i) == openvr.TrackedDeviceClass_TrackingReference:
                print(i, openvr.IVRSystem.getTrackedDeviceClass(vrSys, i), " - TrackingReference")
            if openvr.IVRSystem.getTrackedDeviceClass(vrSys, i) == openvr.TrackedDeviceClass_Controller:
                print(i, openvr.IVRSystem.getTrackedDeviceClass(vrSys, i), " - Controller")
                if r_index == -1:
                    r_index = i
                else:
                    l_index = i
            if openvr.IVRSystem.getTrackedDeviceClass(vrSys, i) == 3:
                print(i, openvr.IVRSystem.getTrackedDeviceClass(vrSys, i), " - VIVE Tracker")
                tracker_index = i

        print('r_index = ', r_index, ' l_index = ', l_index)
        return r_index, l_index, tracker_index, hmd_index

    def setController(self):
        poses_t = openvr.TrackedDevicePose_t * openvr.k_unMaxTrackedDeviceCount
        poses = poses_t()
        openvr.VRCompositor().waitGetPoses(poses, len(poses), None, 0)

        matrix = poses[self.ctrl_index_r].mDeviceToAbsoluteTracking
        matrix2 = poses[self.ctrl_index_l].mDeviceToAbsoluteTracking

        try:
            camera = bpy.data.objects["Camera"]
            ctrl = bpy.data.objects["Controller.R"]
            ctrl_l = bpy.data.objects["Controller.L"]

            self.trans_matrix = camera.matrix_world * bpy.data.objects['Origin'].matrix_world
            RTS_matrix = Matrix(((matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3]),
                                 (matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3]),
                                 (matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3]),
                                 (0, 0, 0, 1)))

            RTS_matrix2 = Matrix(((matrix2[0][0], matrix2[0][1], matrix2[0][2], matrix2[0][3]),
                                 (matrix2[1][0], matrix2[1][1], matrix2[1][2], matrix2[1][3]),
                                 (matrix2[2][0], matrix2[2][1], matrix2[2][2], matrix2[2][3]),
                                 (0, 0, 0, 1)))

            # Interaction state active
            if(self.rotFlag):
                ctrl.matrix_world = self.trans_matrix * RTS_matrix
                bpy.data.objects["Text.R"].location = ctrl.location
                bpy.data.objects["Text.R"].rotation_quaternion = ctrl.rotation_quaternion * Quaternion((0.707, -0.707, 0, 0))

                ctrl_l.matrix_world = self.trans_matrix * RTS_matrix2
                bpy.data.objects["Text.L"].location = ctrl_l.location
                bpy.data.objects["Text.L"].rotation_quaternion = ctrl_l.rotation_quaternion * Quaternion((0.707, -0.707, 0, 0))

            # Navigation state active
            else:
                diff_rot_matr = self.diff_rot.to_matrix()
                inverted_matrix = RTS_matrix * diff_rot_matr.to_4x4()
                inverted_matrix = inverted_matrix.inverted()
                stMatrix = self.diff_trans_matrix * inverted_matrix
                quat = stMatrix.to_quaternion()
                camera.rotation_quaternion = quat

        except:
            print("ERROR: ")

    def changeSelection(self,obj,bone,selectState):

        if selectState:
            print("SELECT: ", obj, bone)
            if obj != "":
                if bone != "":
                    bpy.data.objects[obj].select = True
                    bpy.context.scene.objects.active = bpy.data.objects[obj]
                    bpy.ops.object.mode_set(mode='POSE')
                    bpy.data.objects[obj].data.bones[bone].select = True

                else:
                    bpy.data.objects[obj].select = True
                    bpy.context.scene.objects.active = bpy.data.objects[obj]
                    bpy.ops.object.mode_set(mode='OBJECT')

        else:
            print ("DESELECT: ", obj, bone)
            if obj != "":
                if bone != "":
                    bpy.data.objects[obj].select = True
                    bpy.context.scene.objects.active = bpy.data.objects[obj]
                    bpy.ops.object.mode_set(mode='POSE')
                    bpy.data.objects[obj].data.bones[bone].select = False
                    bpy.data.objects[obj].select = False
                else:
                    bpy.data.objects[obj].select = True
                    bpy.context.scene.objects.active = bpy.data.objects[obj]
                    bpy.data.objects[obj].select = False
                    bpy.ops.object.mode_set(mode='OBJECT')

    ## Computes distance from controller
    def computeTargetObjDistance(self, Object, Bone, isRotFlag):

        if isRotFlag:
            tui = bpy.data.objects['Controller.R']
        else:
            tui = bpy.data.objects['Controller.L']

        obj = bpy.data.objects[Object]
        if Bone != "":
            pbone = obj.pose.bones[Bone]
            return (math.sqrt(pow((tui.location[0] - (pbone.center[0] + obj.location[0])), 2) + pow(
                (tui.location[1] - (pbone.center[1] + obj.location[1])), 2) + pow(
                (tui.location[2] - (pbone.center[2] + obj.location[2])), 2)))
        else:
            loc = obj.matrix_world.to_translation()
            return (math.sqrt(pow((tui.location[0] - loc[0]), 2) + pow((tui.location[1] - loc[1]), 2) + pow(
                (tui.location[2] - loc[2]), 2)))

    ## Returns the object closest to the Controller
    def getClosestItem(self, isRight):
        dist = sys.float_info.max
        cObj = ""
        cBone = ""
        distThreshold = 0.5

        for object in bpy.data.objects:
            if object.type == 'ARMATURE':
                if not ':TEST_REF' in object.name:
                    for bone in object.pose.bones:
                        currDist = self.computeTargetObjDistance(object.name, bone.name, isRight)
                        bone.bone_group = None
                        if (currDist < dist and currDist < distThreshold):
                            dist = currDist
                            cObj = object.name
                            cBone = bone.name

            else:
                #if object.type != 'CAMERA' and not object.name in self.gui_obj:
                if not object.name in self.gui_obj:
                    currDist = self.computeTargetObjDistance(object.name, "", isRight)
                    # print(object.name, bone.name, currDist)
                    if (currDist < dist and currDist < distThreshold):
                        dist = currDist
                        cObj = object.name
                        cBone = ""

        # Select the new closest item
        print(cObj, cBone)
        print("--------------------------------")
        if(cBone!=""):
            bpy.data.objects[cObj].pose.bones[cBone].rotation_mode = 'QUATERNION'
            #bpy.data.objects[cObj].pose.bones[cBone].bone_group = bpy.data.objects[cObj].pose.bone_groups["SelectedBones"]

        return cObj, cBone

    ## Resets the original transformation when constraints movement are used
    def applyConstraint(self, isRight):
        if isRight:
            type = self.axes[self.curr_axes_r].split('_')[0]
            axes = self.axes[self.curr_axes_r].split('_')[1]
            obj = self.objToControll
            bone = self.boneToControll
            init_loc = self.initial_loc
            init_rot = self.initial_rot

        else:
            type = self.axes[self.curr_axes_l].split('_')[0]
            axes = self.axes[self.curr_axes_l].split('_')[1]
            obj = self.objToControll_l
            bone = self.boneToControll_l
            init_loc = self.initial_loc_l
            init_rot = self.initial_rot_l

        if type == 'LOC':
            if bone!="":
                bpy.data.objects[obj].pose.bones[bone].rotation_mode = 'XYZ'
                bpy.data.objects[obj].pose.bones[bone].rotation_euler = init_rot
                bpy.data.objects[obj].pose.bones[bone].rotation_mode = 'QUATERNION'

            else:
                bpy.data.objects[obj].rotation_mode = 'XYZ'
                bpy.data.objects[obj].rotation_euler = init_rot
                bpy.data.objects[obj].rotation_mode = 'QUATERNION'

            if axes == 'X':
                if bone != "":
                    bpy.data.objects[obj].pose.bones[bone].location[1] = init_loc[1]
                    bpy.data.objects[obj].pose.bones[bone].location[2] = init_loc[2]
                else:
                    bpy.data.objects[obj].location[1] = init_loc[1]
                    bpy.data.objects[obj].location[2] = init_loc[2]

            if axes == 'Y':
                if bone != "":
                    bpy.data.objects[obj].pose.bones[bone].location[0] = init_loc[0]
                    bpy.data.objects[obj].pose.bones[bone].location[2] = init_loc[2]
                else:
                    bpy.data.objects[obj].location[0] = init_loc[0]
                    bpy.data.objects[obj].location[2] = init_loc[2]

            if axes == 'Z':
                if bone != "":
                    bpy.data.objects[obj].pose.bones[bone].location[0] = init_loc[0]
                    bpy.data.objects[obj].pose.bones[bone].location[1] = init_loc[1]
                else:
                    bpy.data.objects[obj].location[0] = init_loc[0]
                    bpy.data.objects[obj].location[1] = init_loc[1]

        if type == 'ROT':
            if bone!="":
                bpy.data.objects[obj].pose.bones[bone].location = init_loc
            else:
                bpy.data.objects[obj].location = init_loc

            if axes == 'X':
                if bone!="":
                    bpy.data.objects[obj].pose.bones[bone].rotation_mode = 'XYZ'
                    bpy.data.objects[obj].pose.bones[bone].rotation_euler[1] = init_rot[1]
                    bpy.data.objects[obj].pose.bones[bone].rotation_euler[2] = init_rot[2]
                    bpy.data.objects[obj].pose.bones[bone].rotation_mode = 'QUATERNION'
                else:
                    bpy.data.objects[obj].rotation_mode = 'XYZ'
                    bpy.data.objects[obj].rotation_euler[1] = init_rot[1]
                    bpy.data.objects[obj].rotation_euler[2] = init_rot[2]
                    bpy.data.objects[obj].rotation_mode = 'QUATERNION'

            if axes == 'Y':
                if bone!="":
                    bpy.data.objects[obj].pose.bones[bone].rotation_mode = 'XYZ'
                    bpy.data.objects[obj].pose.bones[bone].rotation_euler[0] = init_rot[0]
                    bpy.data.objects[obj].pose.bones[bone].rotation_euler[2] = init_rot[2]
                    bpy.data.objects[obj].pose.bones[bone].rotation_mode = 'QUATERNION'
                else:
                    bpy.data.objects[obj].rotation_mode = 'XYZ'
                    bpy.data.objects[obj].rotation_euler[0] = init_rot[0]
                    bpy.data.objects[obj].rotation_euler[2] = init_rot[2]
                    bpy.data.objects[obj].rotation_mode = 'QUATERNION'

            if axes == 'Z':
                if bone!="":
                    bpy.data.objects[obj].pose.bones[bone].rotation_mode = 'XYZ'
                    bpy.data.objects[obj].pose.bones[bone].rotation_euler[0] = init_rot[0]
                    bpy.data.objects[obj].pose.bones[bone].rotation_euler[1] = init_rot[1]
                    bpy.data.objects[obj].pose.bones[bone].rotation_mode = 'QUATERNION'
                else:
                    bpy.data.objects[obj].rotation_mode = 'XYZ'
                    bpy.data.objects[obj].rotation_euler[0] = init_rot[0]
                    bpy.data.objects[obj].rotation_euler[1] = init_rot[1]
                    bpy.data.objects[obj].rotation_mode = 'QUATERNION'

    ## Create a curve from a set of points
    def create_curve(self):
        name = "Stroke"
        curvedata = bpy.data.curves.new(name=name, type='CURVE')
        curvedata.dimensions = '3D'
        curvedata.fill_mode = 'FULL'
        curvedata.bevel_depth = 0.01

        ob = bpy.data.objects.new(name + "Obj", curvedata)
        bpy.context.scene.objects.link(ob)
        ob.show_x_ray = True

    def add_spline(self, point):
        curvedata = bpy.data.curves['Stroke']
        polyline = curvedata.splines.new('BEZIER')
        polyline.resolution_u = 1
        polyline.bezier_points[0].co = point

    ## Add new point to the curve
    def update_curve(self, point):
        polyline = bpy.data.curves['Stroke'].splines[-1]
        polyline.bezier_points.add(1)
        polyline.bezier_points[-1].co = point
        polyline.bezier_points[-1].handle_left = point
        polyline.bezier_points[-1].handle_right = point
        print (datetime.datetime.now())

    def remove_spline(self):
        polyline = bpy.data.curves['Stroke'].splines[-1]
        bpy.data.curves['Stroke'].splines.remove(polyline)

    # ---------------------------------------- #
    # Main Loop
    # ---------------------------------------- #
    def loop(self, context):
        """
        Get fresh tracking data
        """
        try:
            data = self._hmd.update()
            self._eye_orientation_raw[0] = data[0]
            self._eye_orientation_raw[1] = data[2]
            self._eye_position_raw[0] = data[1]
            self._eye_position_raw[1] = data[3]

            self.setController()

            # ctrl_state contains the value of the button
            idx, ctrl_state = openvr.IVRSystem().getControllerState(self.ctrl_index_r)
            idx_l, ctrl_state_l = openvr.IVRSystem().getControllerState(self.ctrl_index_l)

            ctrl = bpy.data.objects['Controller.R']
            ctrl_l = bpy.data.objects['Controller.L']
            camera = bpy.data.objects['Camera']

            ########## Right_Controller_States ##########

            if self.state == State.IDLE:
                bpy.data.objects["Text.R"].data.body = "Idle\n" + self.objToControll + "-" + self.boneToControll

                # DECISIONAL
                if (ctrl_state.ulButtonPressed == 4):
                    print("IDLE -> DECISIONAL")
                    self.changeSelection(self.objToControll, self.boneToControll, False)
                    self.state = State.DECISIONAL

                # # INTERACTION_LOCAL - VR_BLENDER
                # if ctrl_state.ulButtonPressed == 8589934592 and self.objToControll != "":
                #     print("IDLE -> INTERACTION LOCAL")
                #     self.state = State.INTERACTION_LOCAL
                #     self.curr_axes_r = 0
                #
                #     if self.boneToControll != "":
                #         self.diff_rot = ctrl.rotation_quaternion.inverted() * bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].matrix.to_quaternion()
                #         self.diff_loc = bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].matrix.to_translation() - ctrl.location
                #         self.initial_loc = copy.deepcopy(bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].location)
                #         bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].rotation_mode = 'XYZ'
                #         self.initial_rot = copy.deepcopy(bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].rotation_euler)
                #         bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].rotation_mode = 'QUATERNION'
                #
                #     else:
                #         self.diff_rot = ctrl.rotation_quaternion.inverted() * bpy.data.objects[self.objToControll].rotation_quaternion
                #         self.diff_loc = bpy.data.objects[self.objToControll].location - ctrl.location
                #         self.initial_loc = copy.deepcopy(bpy.data.objects[self.objToControll].location)
                #         bpy.data.objects[self.objToControll].rotation_mode = 'XYZ'
                #         self.initial_rot = copy.deepcopy(bpy.data.objects[self.objToControll].rotation_euler)
                #         bpy.data.objects[self.objToControll].rotation_mode = 'QUATERNION'

                # DRAWING SKETCHES - POSING_SKETCHES
                if ctrl_state.ulButtonPressed == 8589934592:
                    print("IDLE -> DRAWING")
                    self.add_spline(bpy.data.objects['Controller.R'].location)
                    self.state = State.DRAWING

                #TRACKPAD.R
                if ctrl_state.ulButtonPressed == 4294967296:
                    self.state = State.TRACKPAD_BUTTON_DOWN

                # NAVIGATION
                if ctrl_state.ulButtonPressed == 2:
                    print("IDLE -> NAVIGATION")
                    self.state = State.NAVIGATION_ENTER

            elif self.state == State.DRAWING:
                self.update_curve(bpy.data.objects['Controller.R'].location)

                if (ctrl_state.ulButtonPressed != 8589934592):
                    self.state = State.IDLE

            elif self.state == State.TRACKPAD_BUTTON_DOWN:

                if ctrl_state.ulButtonPressed != 4294967296:
                    x, y = ctrl_state.rAxis[0].x, ctrl_state.rAxis[0].y
                    # Apply rotation for X setup otherwiise + setup
                    x1, y1 = x * 0.707 - y * -0.707, x * -0.707 + y * 0.707
                    x, y = x1, y1

                    if x > 0 and y > 0:
                        print('UP')
                        print('LAUNCH ALGORITHM')
                        softAss_detAnnealing_3().start()

                    if x > 0 and y < 0:
                        print ('RIGHT')
                    if x < 0 and y > 0:
                        for i in range (0, len(bpy.data.curves['Stroke'].splines)):
                            self.remove_spline()
                        print ('LEFT')
                    if x < 0 and y < 0:
                        print ('DOWN')
                        self.remove_spline()

                    self.state = State.IDLE

            elif self.state == State.DECISIONAL:
                print("Decisional")
                bpy.data.objects["Text.R"].data.body = "Selection\n " + self.objToControll + "-" + self.boneToControll

                # Compute the nearest object
                try:
                    self.objToControll, self.boneToControll = self.getClosestItem(True)
                except:
                    print("Error during selection")
                global currObject
                global currBone
                currObject = self.objToControll
                currBone = self.boneToControll
                print("Current obj:", self.objToControll, self.boneToControll)

                if ctrl_state.ulButtonPressed != 4:
                    # print("touch button released")
                    self.changeSelection(self.objToControll, self.boneToControll, True)

                    self.state = State.IDLE

            elif self.state == State.INTERACTION_LOCAL:
                bpy.data.objects["Text.R"].data.body = "Interaction\n" + self.objToControll + "-" + self.boneToControll + "\n" + self.axes[self.curr_axes_r]

                ## Controll object scale
                if self.objToControll == self.objToControll_l and self.boneToControll == self.boneToControll_l and ctrl_state_l.ulButtonPressed == 8589934592:
                    if self.boneToControll != "":
                        self.initial_scale = copy.deepcopy(bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].scale)
                    else:
                        self.initial_scale = copy.deepcopy(bpy.data.objects[self.objToControll].scale)

                    self.diff_distance = self.computeTargetObjDistance("Controller.L", "", True)

                    self.state = State.SCALING
                    self.state_l = StateLeft.SCALING

                else:
                    if self.boneToControll != "":
                        ## The object to move is a bone
                        bone = bpy.data.objects[self.objToControll]
                        pbone = bone.pose.bones[self.boneToControll]
                        scale = copy.deepcopy(pbone.scale)
                        translationMatrix = Matrix(((0.0, 0.0, 0.0, self.diff_loc[0]),
                                                    (0.0, 0.0, 0.0, self.diff_loc[1]),
                                                    (0.0, 0.0, 0.0, self.diff_loc[2]),
                                                    (0.0, 0.0, 0.0, 1.0)))
                        diff_rot_matr = self.diff_rot.to_matrix()
                        pbone.matrix = (ctrl.matrix_world + translationMatrix) * diff_rot_matr.to_4x4()
                        pbone.scale = scale

                        self.applyConstraint(True)

                    else:
                        ## The object to move is a mesh
                        bpy.data.objects[
                            self.objToControll].rotation_quaternion = ctrl.rotation_quaternion * self.diff_rot
                        bpy.data.objects[self.objToControll].location = ctrl.location + self.diff_loc

                        self.applyConstraint(True)

                if (ctrl_state.ulButtonPressed == 8589934596):
                    print("INTERACTION_LOCAL -> CHANGE_AXIS")
                    self.state = State.CHANGE_AXES

                if (ctrl_state.ulButtonPressed != 8589934592 and ctrl_state.ulButtonPressed != 8589934596):
                    # print("grillet released")
                    self.state = State.IDLE

            elif self.state == State.CHANGE_AXES:
                if (ctrl_state.ulButtonPressed == 8589934592):
                    self.curr_axes_r += 1
                    if self.curr_axes_r >= len(self.axes):
                        self.curr_axes_r = 0
                    self.curr_axes_l = 0
                    self.state = State.INTERACTION_LOCAL

                if (ctrl_state.ulButtonPressed == 0):
                    self.state = State.IDLE

            elif self.state == State.NAVIGATION_ENTER:
                bpy.data.objects["Text.R"].data.body = "Navigation\n "
                bpy.data.objects["Text.L"].data.body = "Navigation\n "
                if ctrl_state.ulButtonPressed != 2:
                    #bpy.data.textures['Texture.R'].image = bpy.data.images['Nav-R.png']
                    #bpy.data.textures['Texture.L'].image = bpy.data.images['Hand-L.png']
                    self.state = State.NAVIGATION
                    self.state_l = StateLeft.NAVIGATION

            elif self.state == State.NAVIGATION_EXIT:
                if ctrl_state.ulButtonPressed != 2:
                    print("NAVIGATION -> IDLE")
                    #bpy.data.textures['Texture.R'].image = bpy.data.images['Perf-R.png']
                    #bpy.data.textures['Texture.L'].image = bpy.data.images['Ctrl-L.png']
                    self.state = State.IDLE
                    self.state_l = StateLeft.IDLE

            elif self.state == State.NAVIGATION:
                if ctrl_state.ulButtonPressed == 4294967296:
                    x, y = ctrl_state.rAxis[0].x, ctrl_state.rAxis[0].y
                    if (x > -0.3 and x < 0.3 and y < -0.8):
                        print("ZOOM_OUT")
                        camObjDist = bpy.data.objects["Origin"].location - camera.location
                        if self.objToControll != "":
                            camObjDist = bpy.data.objects[self.objToControll].location - camera.location
                        camera.location -= camObjDist

                        scale_factor = camera.scale[0]
                        scale_factor = scale_factor * 2
                        camera.scale = Vector((scale_factor, scale_factor, scale_factor))
                        bpy.data.objects["Text.R"].scale = Vector((scale_factor, scale_factor, scale_factor))
                        bpy.data.objects["Text.L"].scale = Vector((scale_factor, scale_factor, scale_factor))
                        self.zoom = scale_factor
                        self.state = State.ZOOM_IN

                    if (x > -0.3 and x < 0.3 and y > 0.8):
                        print("ZOOM_IN")
                        camObjDist = bpy.data.objects["Origin"].location - camera.location
                        if self.objToControll != "":
                            camObjDist = bpy.data.objects[self.objToControll].location - camera.location
                        camObjDist = camObjDist / 2
                        camera.location += camObjDist

                        scale_factor = camera.scale[0]
                        scale_factor = scale_factor / 2
                        camera.scale = Vector((scale_factor, scale_factor, scale_factor))
                        bpy.data.objects["Text.R"].scale = Vector((scale_factor, scale_factor, scale_factor))
                        bpy.data.objects["Text.L"].scale = Vector((scale_factor, scale_factor, scale_factor))
                        self.zoom = scale_factor
                        self.state = State.ZOOM_OUT

                if (ctrl_state.ulButtonPressed == 8589934592):
                    print('Camera rot: ', camera.rotation_quaternion)
                    self.diff_rot = ctrl.rotation_quaternion.inverted() * camera.rotation_quaternion
                    print('Diff:       ', self.diff_rot)
                    # self.diff_loc = camera.location - ctrl.location
                    self.diff_trans_matrix = bpy.data.objects['Camera'].matrix_world * bpy.data.objects[
                        'Origin'].matrix_world

                    self.rotFlag = False
                    self.state = State.CAMERA_ROT_CONT

                if (ctrl_state.ulButtonPressed == 4):
                    self.diff_loc = copy.deepcopy(ctrl.location)
                    self.state = State.CAMERA_MOVE_CONT

                if (ctrl_state.ulButtonPressed == 2):
                    self.state = State.NAVIGATION_EXIT

            elif self.state == State.ZOOM_IN:
                if (ctrl_state.ulButtonPressed != 4294967296):
                    self.state = State.NAVIGATION

            elif self.state == State.ZOOM_OUT:
                if (ctrl_state.ulButtonPressed != 4294967296):
                    self.state = State.NAVIGATION

            elif self.state == State.CAMERA_MOVE_CONT:
                camera.location = camera.location + (self.diff_loc - ctrl.location)

                if ctrl_state.ulButtonPressed != 4:
                    self.state = State.NAVIGATION

            elif self.state == State.CAMERA_ROT_CONT:

                if (ctrl_state.ulButtonPressed != 8589934592):
                    self.rotFlag = True
                    self.state = State.NAVIGATION

            elif self.state == State.SCALING:
                currDist = self.computeTargetObjDistance("Controller.L", "", True)
                offset = (currDist - self.diff_distance) / 10
                if self.boneToControll != "":
                    bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].scale = self.initial_scale
                    bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].scale[0] += offset
                    bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].scale[1] += offset
                    bpy.data.objects[self.objToControll].pose.bones[self.boneToControll].scale[2] += offset

                else:
                    bpy.data.objects[self.objToControll].scale = self.initial_scale
                    bpy.data.objects[self.objToControll].scale[0] += offset
                    bpy.data.objects[self.objToControll].scale[1] += offset
                    bpy.data.objects[self.objToControll].scale[2] += offset

                # Exit from Scaling state
                if (ctrl_state.ulButtonPressed != 8589934592):
                    if (ctrl_state_l.ulButtonPressed != 8589934592):
                        self.state = State.IDLE
                        self.state_l = StateLeft.IDLE

            ########## Left_Controller_States ##########

            if self.state_l == StateLeft.IDLE:
                bpy.data.objects["Text.L"].data.body = "Idle\n" + self.objToControll_l + "-" + self.boneToControll_l

                ## TIMELINE NAVIGATION
                if ctrl_state_l.ulButtonPressed == 4294967296:
                    x, y = ctrl_state_l.rAxis[0].x, ctrl_state_l.rAxis[0].y
                    print(x,y)

                # DECISIONAL
                if (ctrl_state_l.ulButtonPressed == 4):
                    print("IDLE -> DECISIONAL")
                    print ("DECISIONAL ENTER: ", self.objToControll_l, self.boneToControll_l)
                    self.changeSelection(self.objToControll_l, self.boneToControll_l, False)
                    self.state_l = StateLeft.DECISIONAL

                # INTERACTION_LOCAL
                if (ctrl_state_l.ulButtonPressed == 8589934592 and self.objToControll_l != ""):
                    print("IDLE -> INTERACTION LOCAL")
                    self.state_l = StateLeft.INTERACTION_LOCAL
                    self.curr_axes_l = 0

                    if self.boneToControll_l != "":
                        self.diff_rot_l = ctrl_l.rotation_quaternion.inverted() * bpy.data.objects[self.objToControll_l].pose.bones[self.boneToControll_l].matrix.to_quaternion()
                        self.diff_loc_l = bpy.data.objects[self.objToControll_l].pose.bones[self.boneToControll_l].matrix.to_translation() - ctrl_l.location
                        self.initial_loc_l = copy.deepcopy(bpy.data.objects[self.objToControll_l].pose.bones[self.boneToControll_l].location)
                        bpy.data.objects[self.objToControll_l].pose.bones[self.boneToControll_l].rotation_mode = 'XYZ'
                        self.initial_rot_l = copy.deepcopy(bpy.data.objects[self.objToControll_l].pose.bones[self.boneToControll_l].rotation_euler)
                        bpy.data.objects[self.objToControll_l].pose.bones[self.boneToControll_l].rotation_mode = 'QUATERNION'

                    else:
                        self.diff_rot_l = ctrl_l.rotation_quaternion.inverted() * bpy.data.objects[self.objToControll_l].rotation_quaternion
                        self.diff_loc_l = bpy.data.objects[self.objToControll_l].location - ctrl_l.location
                        self.initial_loc_l = copy.deepcopy(bpy.data.objects[self.objToControll_l].location)
                        bpy.data.objects[self.objToControll_l].rotation_mode = 'XYZ'
                        self.initial_rot_l = copy.deepcopy(bpy.data.objects[self.objToControll_l].rotation_euler)
                        bpy.data.objects[self.objToControll_l].rotation_mode = 'QUATERNION'

            elif self.state_l == StateLeft.INTERACTION_LOCAL:
                bpy.data.objects["Text.L"].data.body = "Interaction\n" + self.objToControll_l + "-" + self.boneToControll_l + "\n" + self.axes[self.curr_axes_l]

                if self.objToControll == self.objToControll_l \
                        and self.boneToControll == self.boneToControll_l \
                        and ctrl_state.ulButtonPressed == 8589934592\
                        and self.state != State.CAMERA_ROT_CONT \
                        and self.state != State.NAVIGATION:
                    self.state_l = StateLeft.SCALING
                    self.state = State.SCALING

                else:
                    if self.boneToControll_l != "":
                        ## The object to move is a bone
                        bone = bpy.data.objects[self.objToControll_l]
                        pbone = bone.pose.bones[self.boneToControll_l]
                        scale = copy.deepcopy(pbone.scale)
                        translationMatrix = Matrix(((0.0, 0.0, 0.0, self.diff_loc_l[0]),
                                                    (0.0, 0.0, 0.0, self.diff_loc_l[1]),
                                                    (0.0, 0.0, 0.0, self.diff_loc_l[2]),
                                                    (0.0, 0.0, 0.0, 1.0)))
                        diff_rot_matr = self.diff_rot_l.to_matrix()
                        pbone.matrix = (ctrl_l.matrix_world + translationMatrix) * diff_rot_matr.to_4x4()
                        pbone.scale = scale
                        self.applyConstraint(False)

                    else:
                        ## The object to move is a mesh
                        bpy.data.objects[self.objToControll_l].rotation_quaternion = ctrl_l.rotation_quaternion * self.diff_rot_l
                        bpy.data.objects[self.objToControll_l].location = ctrl_l.location + self.diff_loc_l
                        self.applyConstraint(False)

                if (ctrl_state_l.ulButtonPressed==8589934596):
                    print("INTERACTION_LOCAL -> CHANGE_AXIS")
                    self.state_l = StateLeft.CHANGE_AXES

                if (ctrl_state_l.ulButtonPressed != 8589934592 and ctrl_state_l.ulButtonPressed != 8589934596):
                    self.state_l = StateLeft.IDLE

            elif self.state_l == StateLeft.NAVIGATION:
                if (ctrl_state_l.ulButtonPressed == 4294967296):
                    x, y = ctrl_state_l.rAxis[0].x, ctrl_state_l.rAxis[0].y
                    print (x,y)

            elif self.state_l == StateLeft.CHANGE_AXES:
                if (ctrl_state_l.ulButtonPressed==8589934592):
                    self.curr_axes_l+=1
                    if self.curr_axes_l>=len(self.axes):
                        self.curr_axes_l=0
                    self.curr_axes_r=0
                    print(self.curr_axes_l)
                    print("CHANGE_AXIS -> INTERACTION_LOCAL")
                    self.state_l = StateLeft.INTERACTION_LOCAL

                if (ctrl_state_l.ulButtonPressed==0):
                    # print("grillet released")
                    self.state_l = StateLeft.IDLE

            elif self.state_l == StateLeft.SCALING:

                # Exit from Scaling state
                if (ctrl_state_l.ulButtonPressed != 8589934592):
                    if (ctrl_state.ulButtonPressed != 8589934592):
                        self.state = State.IDLE
                        self.state_l = StateLeft.IDLE

            elif self.state_l == StateLeft.DECISIONAL:
                bpy.data.objects["Text.L"].data.body = "Selection\n" + self.objToControll_l + "-" + self.boneToControll_l

                # Compute the nearest object
                self.objToControll_l, self.boneToControll_l = self.getClosestItem(False)
                global currObject_l
                global currBone_l
                currObject_l = self.objToControll_l
                currBone_l = self.boneToControll_l
                print("Current obj:", self.objToControll_l, self.boneToControll_l)

                if ctrl_state_l.ulButtonPressed != 4:
                    self.changeSelection(self.objToControll_l, self.boneToControll_l, True)
                    self.state_l = StateLeft.IDLE
                    print ("DECISIONAL -> IDLE")

            super(OpenVR, self).loop(context)

        except Exception as E:
            self.error("OpenVR.loop", E, False)
            return False

        #if VERBOSE:
        #    print("Left Eye Orientation Raw: " + str(self._eye_orientation_raw[0]))
        #    print("Right Eye Orientation Raw: " + str(self._eye_orientation_raw[1]))

        return True

    def frameReady(self):
        """
        The frame is ready to be sent to the device
        """
        try:
            self._hmd.frameReady()

        except Exception as E:
            self.error("OpenVR.frameReady", E, False)
            return False

        return True

    def reCenter(self):
        """
        Re-center the HMD device

        :return: return True if success
        :rtype: bool
        """
        return self._hmd.reCenter()

    def quit(self):
        """
        Garbage collection
        """
        self._hmd = None
        return super(OpenVR, self).quit()


# 直接显示图片的漫画、插画类分享网站

'''
    跟网站结构相关的分析全抽离在这里，当网站改版时可以单独更新，而不影响主线策略
'''
import time
import scrapy
from scrapy.contrib.loader import ItemLoader

from .url_setting import *
from .mySetting import *

class TrimAll(object):
    def __call__(self, values):
        vlist =[]
        for s in values:
            vlist.append(s.replace(' ','').replace('\r\n',''))
        return vlist

#主入口的主体 用于循环
def GetMainCrycle(response):
    return response.css('.k_list-txt')

#主入口页面的翻页、找下一页
def Get_NextPage(response):
    next_url = response.css('.k_pape').xpath('a[text()="下一页"]/@href').extract()
    if len(next_url)>0:
        return scheme+allowed_domains[0]+next_url[0]
    else:
        return ''

#点击进去的地址
def GetContent_URL(item_selector):
    content_url = item_selector.xpath('./ul/li/a/@href').extract()
    if len(content_url) > 0 :
        return scheme+allowed_domains[0]+content_url[0]
    else:
        return ''

# 爬文章列表 取标题、预览、文章实际地址、发起日期等信息
def Loader_index(self,item_selector):
    l = ItemLoader(item={}, selector = item_selector)
    l.add_xpath('title', './ul/li/a/text()')
    l.add_xpath('url', './ul/li/a/@href')
    return l.load_item()

# 爬文章页 取标题、封面、预览、下载地址、发起日期信息
def Loader_content(response):
    l = ItemLoader(item={}, response = response)
    sub_title=''
    if len(l.get_xpath('//*[@class="pagenow"]/text()'))>=1:
        sub_title += '-'+l.get_xpath('//*[@class="pagenow"]/text()')[0]
    l.add_value('title',l.get_xpath('//*[@class="b_list-1a-1c"]/text()')[0]+sub_title)
    l.add_value('src_url',response.url)
    content_img = l.get_xpath('//*[@class="content-img"]/p/img/@src')
    l.add_value('content',content_img)
    l.add_value('image_urls',content_img)
    print('正下载图片：',content_img)
    time.sleep(len(content_img))
    return l.load_item()


# coding: utf-8

from __future__ import absolute_import
from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from swagger_server.models.base_model_ import Model
from swagger_server.models.entryproperties_key_type import EntrypropertiesKeyType  # noqa: F401,E501
from swagger_server.models.list_cid_set_events_response_cid_set_events import ListCidSetEventsResponseCidSetEvents  # noqa: F401,E501
from swagger_server.models.object import Object  # noqa: F401,E501
from swagger_server import util

class ListCidSetEventsResponse(Model):
    """NOTE: This class is auto generated by the swagger code generator program.

    Do not edit the class manually.
    """
    def __init__(self, signature: object=None, has_more_elements: bool=None, participant: AllOfListCidSetEventsResponseParticipant=None, key_type: EntrypropertiesKeyType=None, start_time: datetime=None, end_time: datetime=None, sync_verifier_start: AllOfListCidSetEventsResponseSyncVerifierStart=None, sync_verifier_end: AllOfListCidSetEventsResponseSyncVerifierEnd=None, cid_set_events: List[ListCidSetEventsResponseCidSetEvents]=None):  # noqa: E501
        """ListCidSetEventsResponse - a model defined in Swagger

        :param signature: The signature of this ListCidSetEventsResponse.  # noqa: E501
        :type signature: object
        :param has_more_elements: The has_more_elements of this ListCidSetEventsResponse.  # noqa: E501
        :type has_more_elements: bool
        :param participant: The participant of this ListCidSetEventsResponse.  # noqa: E501
        :type participant: AllOfListCidSetEventsResponseParticipant
        :param key_type: The key_type of this ListCidSetEventsResponse.  # noqa: E501
        :type key_type: EntrypropertiesKeyType
        :param start_time: The start_time of this ListCidSetEventsResponse.  # noqa: E501
        :type start_time: datetime
        :param end_time: The end_time of this ListCidSetEventsResponse.  # noqa: E501
        :type end_time: datetime
        :param sync_verifier_start: The sync_verifier_start of this ListCidSetEventsResponse.  # noqa: E501
        :type sync_verifier_start: AllOfListCidSetEventsResponseSyncVerifierStart
        :param sync_verifier_end: The sync_verifier_end of this ListCidSetEventsResponse.  # noqa: E501
        :type sync_verifier_end: AllOfListCidSetEventsResponseSyncVerifierEnd
        :param cid_set_events: The cid_set_events of this ListCidSetEventsResponse.  # noqa: E501
        :type cid_set_events: List[ListCidSetEventsResponseCidSetEvents]
        """
        self.swagger_types = {
            'signature': object,
            'has_more_elements': bool,
            'participant': AllOfListCidSetEventsResponseParticipant,
            'key_type': EntrypropertiesKeyType,
            'start_time': datetime,
            'end_time': datetime,
            'sync_verifier_start': AllOfListCidSetEventsResponseSyncVerifierStart,
            'sync_verifier_end': AllOfListCidSetEventsResponseSyncVerifierEnd,
            'cid_set_events': List[ListCidSetEventsResponseCidSetEvents]
        }

        self.attribute_map = {
            'signature': 'Signature',
            'has_more_elements': 'HasMoreElements',
            'participant': 'Participant',
            'key_type': 'KeyType',
            'start_time': 'StartTime',
            'end_time': 'EndTime',
            'sync_verifier_start': 'SyncVerifierStart',
            'sync_verifier_end': 'SyncVerifierEnd',
            'cid_set_events': 'CidSetEvents'
        }
        self._signature = signature
        self._has_more_elements = has_more_elements
        self._participant = participant
        self._key_type = key_type
        self._start_time = start_time
        self._end_time = end_time
        self._sync_verifier_start = sync_verifier_start
        self._sync_verifier_end = sync_verifier_end
        self._cid_set_events = cid_set_events

    @classmethod
    def from_dict(cls, dikt) -> 'ListCidSetEventsResponse':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The ListCidSetEventsResponse of this ListCidSetEventsResponse.  # noqa: E501
        :rtype: ListCidSetEventsResponse
        """
        return util.deserialize_model(dikt, cls)

    @property
    def signature(self) -> object:
        """Gets the signature of this ListCidSetEventsResponse.

        :return: The signature of this ListCidSetEventsResponse.
        :rtype: object
        """
        return self._signature

    @signature.setter
    def signature(self, signature: object):
        """Sets the signature of this ListCidSetEventsResponse.

        :param signature: The signature of this ListCidSetEventsResponse.
        :type signature: object
        """

        self._signature = signature

    @property
    def has_more_elements(self) -> bool:
        """Gets the has_more_elements of this ListCidSetEventsResponse.

        Existem mais elementos para iterar  # noqa: E501

        :return: The has_more_elements of this ListCidSetEventsResponse.
        :rtype: bool
        """
        return self._has_more_elements

    @has_more_elements.setter
    def has_more_elements(self, has_more_elements: bool):
        """Sets the has_more_elements of this ListCidSetEventsResponse.

        Existem mais elementos para iterar  # noqa: E501

        :param has_more_elements: The has_more_elements of this ListCidSetEventsResponse.
        :type has_more_elements: bool
        """

        self._has_more_elements = has_more_elements

    @property
    def participant(self) -> AllOfListCidSetEventsResponseParticipant:
        """Gets the participant of this ListCidSetEventsResponse.

        :return: The participant of this ListCidSetEventsResponse.
        :rtype: AllOfListCidSetEventsResponseParticipant
        """
        return self._participant

    @participant.setter
    def participant(self, participant: AllOfListCidSetEventsResponseParticipant):
        """Sets the participant of this ListCidSetEventsResponse.

        :param participant: The participant of this ListCidSetEventsResponse.
        :type participant: AllOfListCidSetEventsResponseParticipant
        """
        if participant is None:
            raise ValueError("Invalid value for `participant`, must not be `None`")  # noqa: E501

        self._participant = participant

    @property
    def key_type(self) -> EntrypropertiesKeyType:
        """Gets the key_type of this ListCidSetEventsResponse.

        :return: The key_type of this ListCidSetEventsResponse.
        :rtype: EntrypropertiesKeyType
        """
        return self._key_type

    @key_type.setter
    def key_type(self, key_type: EntrypropertiesKeyType):
        """Sets the key_type of this ListCidSetEventsResponse.

        :param key_type: The key_type of this ListCidSetEventsResponse.
        :type key_type: EntrypropertiesKeyType
        """
        if key_type is None:
            raise ValueError("Invalid value for `key_type`, must not be `None`")  # noqa: E501

        self._key_type = key_type

    @property
    def start_time(self) -> datetime:
        """Gets the start_time of this ListCidSetEventsResponse.

        Data-hora do primeiro evento da lista  # noqa: E501

        :return: The start_time of this ListCidSetEventsResponse.
        :rtype: datetime
        """
        return self._start_time

    @start_time.setter
    def start_time(self, start_time: datetime):
        """Sets the start_time of this ListCidSetEventsResponse.

        Data-hora do primeiro evento da lista  # noqa: E501

        :param start_time: The start_time of this ListCidSetEventsResponse.
        :type start_time: datetime
        """
        if start_time is None:
            raise ValueError("Invalid value for `start_time`, must not be `None`")  # noqa: E501

        self._start_time = start_time

    @property
    def end_time(self) -> datetime:
        """Gets the end_time of this ListCidSetEventsResponse.

        Data-hora do último evento da lista  # noqa: E501

        :return: The end_time of this ListCidSetEventsResponse.
        :rtype: datetime
        """
        return self._end_time

    @end_time.setter
    def end_time(self, end_time: datetime):
        """Sets the end_time of this ListCidSetEventsResponse.

        Data-hora do último evento da lista  # noqa: E501

        :param end_time: The end_time of this ListCidSetEventsResponse.
        :type end_time: datetime
        """
        if end_time is None:
            raise ValueError("Invalid value for `end_time`, must not be `None`")  # noqa: E501

        self._end_time = end_time

    @property
    def sync_verifier_start(self) -> AllOfListCidSetEventsResponseSyncVerifierStart:
        """Gets the sync_verifier_start of this ListCidSetEventsResponse.

        :return: The sync_verifier_start of this ListCidSetEventsResponse.
        :rtype: AllOfListCidSetEventsResponseSyncVerifierStart
        """
        return self._sync_verifier_start

    @sync_verifier_start.setter
    def sync_verifier_start(self, sync_verifier_start: AllOfListCidSetEventsResponseSyncVerifierStart):
        """Sets the sync_verifier_start of this ListCidSetEventsResponse.

        :param sync_verifier_start: The sync_verifier_start of this ListCidSetEventsResponse.
        :type sync_verifier_start: AllOfListCidSetEventsResponseSyncVerifierStart
        """
        if sync_verifier_start is None:
            raise ValueError("Invalid value for `sync_verifier_start`, must not be `None`")  # noqa: E501

        self._sync_verifier_start = sync_verifier_start

    @property
    def sync_verifier_end(self) -> AllOfListCidSetEventsResponseSyncVerifierEnd:
        """Gets the sync_verifier_end of this ListCidSetEventsResponse.

        :return: The sync_verifier_end of this ListCidSetEventsResponse.
        :rtype: AllOfListCidSetEventsResponseSyncVerifierEnd
        """
        return self._sync_verifier_end

    @sync_verifier_end.setter
    def sync_verifier_end(self, sync_verifier_end: AllOfListCidSetEventsResponseSyncVerifierEnd):
        """Sets the sync_verifier_end of this ListCidSetEventsResponse.

        :param sync_verifier_end: The sync_verifier_end of this ListCidSetEventsResponse.
        :type sync_verifier_end: AllOfListCidSetEventsResponseSyncVerifierEnd
        """
        if sync_verifier_end is None:
            raise ValueError("Invalid value for `sync_verifier_end`, must not be `None`")  # noqa: E501

        self._sync_verifier_end = sync_verifier_end

    @property
    def cid_set_events(self) -> List[ListCidSetEventsResponseCidSetEvents]:
        """Gets the cid_set_events of this ListCidSetEventsResponse.

        :return: The cid_set_events of this ListCidSetEventsResponse.
        :rtype: List[ListCidSetEventsResponseCidSetEvents]
        """
        return self._cid_set_events

    @cid_set_events.setter
    def cid_set_events(self, cid_set_events: List[ListCidSetEventsResponseCidSetEvents]):
        """Sets the cid_set_events of this ListCidSetEventsResponse.

        :param cid_set_events: The cid_set_events of this ListCidSetEventsResponse.
        :type cid_set_events: List[ListCidSetEventsResponseCidSetEvents]
        """

        self._cid_set_events = cid_set_events


import pygame
import time

class TimedTransition(object):
    TRANSITIONS_TIMER = pygame.USEREVENT+1
    transition_list = []
    min_interval = 0

    def __init__(self, end_function,
                 target_object=None, target_property=None, update_interval=None, target_value=None, max_time=None):
        self.target_object = target_object()
        TimedTransition.transition_list.append(lambda: self.update)
        self.last_check = 0
        self.start_time = time.time()
        self.next_check = self.start_time + update_interval
        self.update_interval = update_interval

        current_value = getattr(self.target_object, 'target_property')
        self.sign = target_value - current_value
        if TimedTransition.min_interval > self.update_interval:
            TimedTransition.min_interval = self.update_interval
            pygame.time.set_timer(self.TRANSITIONS_TIMER, self.update_interval)

    def update(self):
        current_time = time.time()
        if self.next_check >= current_time:
            current_value = getattr(self.target_object, 'target_property')
            diff = current_value - self.target_value
            current_value += max_time/update_interval

    @staticmethod
    def on_transation_timer( event):

        if event.type != TimedTransition.TRANSITIONS_TIMER:
            return
        to_remove = []
        for event in TimedTransition.transition_list:
            if not event.update():
                to_remove.append(event)

        for event in to_remove:
            TimedTransition.transition_list.remove(event)


import math

import numpy as np
import numpy.linalg as nplin
import scipy.integrate as spi
import scipy.constants as sc

"""
References
----------
..[1] E. Witrant, E. Joffrin, S. Brémond, G. Giruzzi, D. Mazon, O. Barana and P. Moreau,
"A control-oriented model of the current profile in tokamak plasma",
Plasma Physics and Controlled Fusion, vol. 49, no. 7, pp. 1075--1105, 2007.

..[2] F. B. Argomedo, E. Witrant, C. Prieur, S. Brémond, R. Nouailletas, and J. Artaud,
"Lyapunov-Based Distributed Control of the Safety Factor Profile in a Tokamak Plasma",
Nuclear Fusion, vol. 53, no. 3, pp. 5--33, 2013.

..[3] F. Kazarian-Vibert, X. Litaudon, D. Moreau, R. Arslanbekov, G. T. Hoang, and Y. Peysson,
“Full steady-state operation in tore supra,”
Plasma Physics and Controlled Fusion, vol. 38, no. 12, pp. 2113--2131, 1996.
"""

# Major radius [m]
R0 = 2.34

# Minor radius [m]
a = 0.78

class ToreSupra(object):
    def __init__(self):
        # Toroida magnetic field at center [T]
        self.Bphi0 = 3.69

        # Plasma inductance [H]
        self.Lp = 20.3e-6

        # Plasma resistance [Ω]
        self.Rp = 5.0e-6

        # Ohmic inductance [H]
        self.Loh = 0.58

        # Ohmic resistance [Ω]
        self.Roh = 29.0e-3

        # Mutual inductance [H]
        self.M = 2.8e-3

        # Exponential peaking coefficient of the electron density
        self.gamman = 1.5

        # Exponential peaking coefficient of the q-profile
        self.gammaq = 3.7

    def ion_cyclotron_resonant_heating_power(self):
        """
        Ion cyclotron resonance heating power

        """
        pass

    def lower_hybrid_power(self):
        """
        Lower hybrid power

        """
        pass

    def parallel_refrection_index(self):
        """
        Parallel refrection index

        """
        pass

    def ohmic_voltage(self, Ip, Ioh):
        """
        Ohmic Voltage

        """
        pass

    def confinement_efficiency(self, Ip, Wth):
        """
        Confinement efficiency

        Parameters
        ----------
        Ip : float
            Total plasma current
        Wth : float
            Plasma thermal energy

        Returns
        -------
        betatheta : float
            Confinement efficiency

        """
        mu_0 = sc.mu_0

        betatheta = 8.0*Wth / (3.0*mu_0*R0*Ip**2)

        return betatheta

    def normalized_internal_inductance(self, Ip, dpsidr, r):
        """
        Normalized internal inductance

        Parameters
        ----------
        Ip : float
            Total plasma current
        dpsidr : ndarray
            Magnetic flux of the poloidal field
            differentiated by normalized radius
        r : ndarray
            Normaized radius

        Returns
        -------
        li : float
            Normalized internal inductance

        """
        mu_0 = sc.mu_0

        def f(i): return r[i] * dpsidr[i]**2

        y = f(np.arange(len(r)))
        li = 8*math.pi**2 * spi.simps(y, x=r) / (mu_0**2 * R0**2 * Ip**2)

        return li

    def initial_safety_factor_profile(self, q0c, q0e, r):
        """
        Initial safety factor profile

        Parameters
        ----------
        Ip0 : float
            Initial total plasma current
        q0c : float
            Initial q-value at the center
        r : ndarray
            Normalized radius

        Returns
        -------
        q0 : ndarray
            The initial safety factor

        """
        gammaq = self.gammaq
        Bphi0 = self.Bphi0
        mu_0 = sc.mu_0

        q0 = (q0c - q0e)*(1 - r**gammaq) + q0e

        return q0

    def initial_poloidal_flux(self, psi0e, q0c, q0e, r):
        """
        Initial magnetic flux of the poloidal field

        Parameters
        ----------
        Ip0 : float
            Initial total plasma current
        psi0e : float
            Initial magnetic flux of the poloidal field at the edge
        q0c : float
            Initial q-value at the center
        r : ndarray
            Normalized radius

        Returns
        -------
        psi0 : ndarray
            The initial magnetic flux profile of the poloidal field

        """
        Bphi0 = self.Bphi0

        def f(x):
            return x / self.initial_safety_factor_profile(q0c, q0e, x)

        y = np.array([spi.quad(f, r[i], 1)[0] for i in np.arange(len(r))])
        psi0 = a**2 * Bphi0 * y + psi0e

        return psi0

    def plasma_effective_charge(self):
        """
        Plasma effective charge

        Reterns
        -------
        Zeff : float
            plasma effective charge

        """
        Zeff = 1.8

        return Zeff

    def electron_line_average_density(self):
        """
        Electron line average density

        Returns
        -------
        elad : float
            electron line average density

        """
        elad = 1.45e+19

        return elad

    def total_input_power(self):
        """
        Total input power

        Returns
        -------
        Ptot : float
            Total input power

        """
        Picrh = self.ion_cyclotron_resonant_heating_power()
        Plh = self.lower_hybrid_power()

        Ptot = Picrh + Plh

        return Ptot

    def alphalh(self, Ip):
        """
        Amplitude parameter of electron temperature profile
        if lower hybrid current drive power is not 0

        Parameters
        ----------
        Ip : float
            Total plasma current

        Returns
        -------
        alh : float
            Amplitude paramter electron temperature profile

        """
        Bphi0 = self.Bphi0
        e = sc.e

        Nll = self.parallel_refrection_index()
        Picrh = self.ion_cyclotron_resonant_heating_power()
        Ptot = self.total_input_power()

        alh = (e*1.0e+19)**-0.87 * (Ip * 1.0e-6)**-0.43 * \
                Bphi0**0.63 * Nll**0.25 * (1.0 + Picrh/Ptot)**0.15

        return alh

    def betalh(self, Ip):
        """
        Dilation parameter of electron temperature profile
        if lower hybrid current drive power is not 0

        Parameters
        ----------
        Ip : float
            Total plasma current

        Returns
        -------
        blh : float
            Dilation paramter of electron temperature profile

        """
        Bphi0 = self.Bphi0
        e = sc.e

        Nll = self.parallel_refrection_index()
        elad = self.electron_line_average_density()

        blh = - (e*1.0e+19)**3.88 * (Ip * 1.0e-6)**0.31 * \
            Bphi0**-0.86 * (elad * 1.0e-19)**-0.39 * Nll**-1.15

        return blh

    def gammalh(self, Ip):
        """
        Translation parameter of electron temperature profile
        if lower hybrid current drive power is not 0

        Parameters
        ----------
        Ip : float
            Total plasma current

        Returns
        -------
        glh : float
            Translation parameter

        """
        Bphi0 = self.Bphi0
        e = sc.e

        Nll = self.parallel_refrection_index()
        Picrh = self.ion_cyclotron_resonant_heating_power()
        Ptot = self.total_input_power()

        glh = (e*1.0e+19)**1.77 * (Ip*1.0e-6)**1.4 * Bphi0**-1.76 * \
                Nll**-0.45 * (1.0 + Picrh/Ptot)**-0.54

        return glh

    def density_ratio(self):
        """
        The density ratio

        Returns
        -------
        ani : float
            The density ratio

        """
        Zeff = self.plasma_effective_charge()

        ani = (7.0 - Zeff)/6.0

        if not 0.0 < ani < 1.0:
            raise ValueError('The density ratio must be from 0.0 to 1.0')

        return ani

    def electron_density_profile(self, r):
        """
        Electron density profile

        Parameters
        ----------
        r : ndarray
            Normalized radius

        Returns
        -------
        ne: ndarray
            Electron density profile

        """
        gamman = self.gamman
        elad = self.electron_line_average_density()

        ne = (gamman + 1.0) * (1.0 - r**gamman) * elad / gamman

        return ne

    def ion_density_profile(self, r):
        """
        Ion density profile

        Parameters
        ----------
        r : ndarray
            Normalized radius

        Returns
        -------
        ni : ndarray
            Ion density profile

        """
        alphani = self.density_ratio()
        ne = self.electron_density_profile(r)

        ni = alphani * ne

        return ni

    def ion_ratio_to_electron_temperature(self, Ip):
        """
        The ratio of ion to electron temperature

        Parameters
        ----------
        Ip : float
            Total plasma current

        Returns
        -------
        alphaTi : float
            The ratio of ion to electron temperature

        """
        Bphi0 = self.Bphi0

        elad = self.electron_line_average_density()
        Picrh = self.ion_cyclotron_resonant_heating_power()
        Ptot = self.total_input_power()
        Plh = self.lower_hybrid_power()

        alphaTi = 1.0 - 0.31 * (((Ip * 1.0e-6)/Bphi0)**-0.38 * \
                (elad * 1.0e-19)**-0.90 * (1.0 + Picrh/Ptot)**-1.62 * \
                (1.0 + Plh/Ptot)**1.36)

        if alphaTi < 0:
            raise ValueError('The ratio of ion to electron temperature \
                                must be positive or zero')

        return alphaTi

    def normalized_directivity(self):
        """
        Normalized directivity

        Returns
        -------
        Dn : float
            Normalized directivity

        """
        Nll = self.parallel_refrection_index()

        Dn = 2.03 - 0.63*Nll

        if Dn < 0:
            raise ValueError('Normalized directivity must be positive or zero')

        return Dn

    def lower_hybrid_current_drive_effiency(self, Ip):
        """
        Lower hybrid current drive effiency

        Paramters
        ---------
        Ip : float
            Total plasma current

        Returns
        -------
        etalh : float
            Lower hybrid current effiency

        """
        Dn = self.normalized_directivity()
        Zeff = self.plasma_effective_charge()
        tauth = self.thermal_energy_confinement_time(Ip)

        # etalh = 3.39 * Dn**0.26 * tauth**0.46 * Zeff**-0.13 * 1.0e+19
        etalh = 1.18 * Dn**0.55 * (Ip*1.0e-6)**0.43 * Zeff**-0.24

        return etalh

    def lower_hybrid_current(self, Ip):
        """
        Lower hybrid current

        Parameters
        ----------
        Ip : float
            Total plasma current

        Returns
        -------
        Ilh : float
            Lower hybrid current

        """
        etalh = self.lower_hybrid_current_drive_effiency(Ip)
        Plh = self.lower_hybrid_power()
        elad = self.electron_line_average_density()

        Ilh = etalh * Plh / (R0 * elad * 1.0e-19)

        return Ilh

    def lower_hybrid_current_density_profile(self, Ip, r):
        """
        Lower hybird current density profile

        Paramters
        ---------
        Ip : float
            Total plasma current
        r : ndarray
            Normalized radius

        Returns
        -------
        jlh : ndarray
            Lower hybrid current density

        """
        Bphi0 = self.Bphi0

        elad = self.electron_line_average_density()
        Plh = self.lower_hybrid_power()
        Nll = self.parallel_refrection_index()
        Ilh = self.lower_hybrid_current(Ip)

        mu = 0.2 * Bphi0**-0.39 * (Ip*1.0e-6)**0.71 * \
                (elad*1.0e-19)**-0.02 * (Plh*1.0e-6)**0.13 * Nll**1.2
        w = 0.53 * Bphi0**-0.24 * (Ip*1.0e-6)**0.57 * \
                (elad*1.0e-19)**-0.08 * (Plh*1.0e-6)**0.13 * \
                Nll**0.39
        sigmalh = ((mu - w)**2)/(2.0 * math.log(2.0))

        def f(x): return x * np.exp(-(mu - x)**2 / (2.0*sigmalh))

        result = spi.quad(f, 0, 1)[0]
        varthetalh = Ilh/(2.0 * math.pi * a**2 * result)
        jlh = varthetalh * np.exp(-(mu - r)**2 / (2.0*sigmalh))

        return jlh

    def temperature_profile_amplitude(self, Ip, Wth, r):
        """
        Temperature profile amplitude

        Parameters
        ----------
        Ip : float
            Total plasma current
        Wth : float
            Plasma thermal energy
        r : ndarray
            Normalized radius

        Returns
        -------
        ATe : float
            Temperature profile amplitude

        """
        e = sc.e

        alphani = self.density_ratio()
        alphaTi = self.ion_ratio_to_electron_temperature(Ip)
        alh = self.alphalh(Ip)
        blh = self.betalh(Ip)
        glh = self.gammalh(Ip)
        edp = self.electron_density_profile

        def f(x): return (edp(x) * 1.0) * x * alh / \
                        (1.0 + np.exp(-blh*(x - glh)))

        nea = spi.quad(f, 0.0, 1.0)[0]
        scrA = 1.0/(6.0 * (math.pi * a)**2 * R0 * \
                    (e*1.0) * (1.0 + alphani * alphaTi) * nea)
        ATe = Wth * scrA

        return ATe

    def electron_temperature_profile(self, Ip, Wth, r):
        """
        Electron temperature profile

        Parameters
        ----------
        Ip : float
            Total plasma current
        Wth : float
            Plasma thermal energy
        r : ndarray
            Normalized radius

        Returns
        -------
        Te : ndarray
            Temerature profile of electron

        """
        alh = self.alphalh(Ip)
        blh = self.betalh(Ip)
        glh = self.gammalh(Ip)

        ATe = self.temperature_profile_amplitude(Ip, Wth, r)

        Te = (alh * ATe)/(1.0 + np.exp(-blh * (r - glh)))

        return Te

    def ion_temperature_profile(self, Ip, Wth, r):
        """
        Ion temperature profile

        Parameters
        ----------
        Ip : float
            Total plasma current
        Wth : float
            Plasma thermal energy
        r : ndarray
            Normalized radius

        Returns
        -------
        Ti : ndarray
            Ion temprature profile

        """
        Te = self.electron_temperature_profile(Ip, Wth, r)
        alphaTi = self.ion_ratio_to_electron_temperature(Ip)

        Ti = alphaTi * Te

        return Ti

    def safety_factor_profile(self, dpsidr, r):
        """
        Safety factor profile

        Parameters
        ----------
        dpsidr : ndarray
            Magnetic flux of the poloidal field
            differentiated by normalized radius
        r : ndarray
            Normalized radius

        Returns
        -------
        q : ndarray
            Safety factor profile

        """
        Bphi0 = self.Bphi0

        q = - Bphi0 * a**2 * r / dpsidr

        return q

    def trapped_banana_regime_particles(self, r):
        """
        The fraction of trapped particles in banana regime

        Parameters
        ----------
        r : ndarray
            Normalized radius

        Returns
        -------
        ft : ndarray
            The fraction of trapped particles in banana regime

        """
        epsilon = a/R0
        ft = 1.0 - (1.0 - r*epsilon)**2 * \
            ((1.0 - (r*epsilon)**2)**-0.5) / (1.0 + 1.46 * np.sqrt(r*epsilon))

        return ft

    def parallel_conductivity(self, Ip, Wth, dpsidr, r):
        """
        Plasma parallel conductivity

        Parameters
        ----------
        Ip : float
            Total plasma current
        Wth : float
            Plasma thermal energy
        dpsidr : ndarray
            Magnetic flux of the poloidal field
            differentiated by normalized radius
        r : ndarray
            Normalized radius

        Returns
        -------
        etall : ndarray
            Plasma parallel conductivity

        """
        e, epsilon_0, m_e = sc.e, sc.epsilon_0, sc.m_e

        Te = self.electron_temperature_profile(Ip, Wth, r)
        edp = self.electron_density_profile(r)
        q = self.safety_factor_profile(dpsidr, r)
        ft = self.trapped_banana_regime_particles(r)
        Zeff = self.plasma_effective_charge()

        epsilon = a/R0

        xi = 0.58 + 0.2 * Zeff
        lambdae = (3.4/Zeff)*((1.13 + Zeff)/(2.67 + Zeff))
        l = 31.318 + np.log(Te/np.sqrt(edp))
        taue = (12.0 * math.pi**1.5 * m_e**0.5 * epsilon_0**2 * Te**1.5) / \
                (e**2.5 * math.sqrt(2.0) * edp * np.log(l))
        s0 = edp * e**2 * taue/m_e
        alphae = np.sqrt(e * Te / m_e)
        nue = (R0 * q)/(((r*epsilon)**1.5) * alphae * taue)
        cr = (0.56/Zeff) * (3.0 - Zeff)/(3.0 + Zeff)
        etall = s0 * lambdae * \
            (1.0 - ft/(1.0 + xi * nue)) * (1.0 - (cr * ft)/(1.0 + xi * nue))

        return etall

    def ohmic_current_profile(self, Ip, Wth, dpsidr, r):
        """
        Ohmic current profile

        Parameters
        ----------
        Ip : float
            Total plasma current
        Wth : float
            Plasma thermal energy
        dpsidr : ndarray
            Maginetic flux of the poloidal field
            differentiated by normalized radius

        Returns
        -------
        joh : ndarray
            Ohmic current profile

        """
        pc = self.parallel_conductivity(Ip, Wth, dpsidr, r)

        joh = - pc * dpsidr / R0

        return joh

    def initial_plasma_thermal_energy(self, Ip0):
        """
        Initial plasma thermal energy

        Paramters
        ---------
        Ip0 : float
            Initial total plasma current

        Returns
        -------
        Wth0 : float
            Initial plasma thermal energy

        """
        Wth0 = self.total_input_power() * \
            self.thermal_energy_confinement_time(Ip0)

        return Wth0

    def parallel_resistivity(self, Ip, Wth, dpsidr, r):
        """
        Plasma parallel resistivity

        Parameters
        ----------
        Ip : float
            Total plasma current
        Wth : float
            Plasma thermal energy
        dpsidr : ndarray
            Maginetic flux of the poloidal field
            differentiated by normalized radius

        Returns
        -------
        etall : ndarray
            Plasma parallel resistivity

        """
        etall = 1.0/self.parallel_conductivity(Ip, Wth, dpsidr, r)

        return etall

    def thermal_energy_confinement_time(self, Ip):
        """
        Thermal energy confinement time

        Paramters
        ---------
        Ip : float
            Total plasma current

        Returns
        -------
        taue : float
            Thermal energy confinement time

        """
        Bphi0 = self.Bphi0

        elad = self.electron_line_average_density()
        Ptot = self.total_input_power()
        Plh = self.lower_hybrid_power()

        taue = 0.135 * (Ip * 1.0e-6)**0.94 * Bphi0**-0.15 * \
                (elad * 1.0e-19)**0.78 * (1 + Plh/Ptot)**0.13 * \
                (Ptot * 1.0e-6)**-0.78

        return taue

    def bootstrap_current_profile(self, Ip, Wth, dpsidr, r):
        """
        Bootstrap current profile

        Parameters
        ----------
        Ip : float
            Total plasma current
        Wth : float
            Plasma thermal energy
        dpsidr : ndarray
            Maginetic flux of the poloidal field
            differentiated by normalized radius
        r : ndarray
            Normalized radius

        Returns
        -------
        jbs : ndarray
            Bootstrap current profile

        """
        gamman = self.gamman
        e = sc.e

        Zeff = self.plasma_effective_charge()
        ft = self.trapped_banana_regime_particles(r)
        alphaTi = self.ion_ratio_to_electron_temperature(Ip)
        alphani = self.density_ratio()
        Te = self.electron_temperature_profile(Ip, Wth, r)
        Ti = self.ion_temperature_profile(Ip, Wth, r)
        ne = self.electron_density_profile(r)
        ni = self.ion_density_profile(r)
        alphalh = self.alphalh(Ip)
        betalh = self.betalh(Ip)
        gammalh = self.gammalh(Ip)

        dTedr = alphalh*betalh*np.exp(betalh*(r - gammalh)) / \
            (1 + np.exp(betalh*(r - gammalh)))**2
        dTidr = alphaTi * dTedr
        dnedr = - (gamman + 1) * r**(gamman - 1) * ne
        dnidr = alphani * dnedr

        xt = ft/(1.0 - ft)
        De = 1.414*Zeff + Zeff**2 + xt*(0.754 + 2.657*Zeff + 2*Zeff**2) + \
                xt**2 * (0.348 + 1.243*Zeff * Zeff**2)
        A1 = xt*(0.754 + 2.21*Zeff + Zeff**2 + \
                xt*(0.348 + 1.243*Zeff + Zeff**2))/De
        A2 = xt*(0.884 + 2.074*Zeff)/De
        alphai = 1.172/(1 - 0.462*xt)
        jbs = e*R0*((A1 - A2)*ne*dTedr + A1*Te*dnedr +
            A1*(1-alphai)*ni*dTidr + A1*Ti*dnidr)/dpsidr

        return jbs

    def bootstrap_current(self, Ip, Wth, dpsidr, r):
        """
        Bootstrap current

        Parameters
        ----------
        Ip : float
            Total plasma current
        Wth : float
            Plasma thermal energy
        dpsidr : ndarray
            Maginetic flux of the poloidal field
            differentiated by normalized radius
        r : ndarray
            Normalized radius

        Returns
        -------
        Ibs : float
            Bootstrap current

        """
        jbs = self.bootstrap_current_profile

        def f(i): return r[i] * jbs(Ip, Wth, dpsidr[i], r[i])

        y = f(np.arange(len(r)))
        Ibs = 2*math.pi*a**2 * spi.simps(y, x=r)

        return Ibs

    def noninductive_effective_current(self, Ip, Wth, dpsidr, r):
        """
        Noninductive effective current

        Paramters
        ---------
        Ip : float
            Total plasma current
        r : ndarray
            Normalized radius

        Returns
        -------
        jin : ndarray
            Noninductive effective current profile

        """
        jlh = self.lower_hybrid_current_density_profile(Ip, r)
        jbs = self.bootstrap_current_profile(Ip, Wth, dpsidr, r)

        jin = jlh + jbs

        return jin

    def total_plasma_current(self, I, Wth, psi, r):
        """
        Total plasma current model

        Parameters
        ----------
        I : ndarray
            Total plasma current and ohmic current
        psi : ndarray
            Magnetic flux of the poloidal field
        r : ndarray
            Normalized radius

        Returns
        -------
        dI : ndarray
            Total plasma current and ohmic current differentiated by time

        """
        Ip, Ioh = I[0], I[1]
        Lp, Loh, Rp, Roh, M = self.Lp, self.Loh, self.Rp, self.Roh, self.M

        if len(r) != len(psi):
            raise ValueError('r and psi length must be equal')

        dpsidr = (psi[2:] - psi[:-2])/(r[2:] - r[:-2])

        Plh = self.lower_hybrid_power()
        Voh = self.ohmic_voltage(Ip, Ioh)

        etalh = self.lower_hybrid_current_drive_effiency(Ip)
        elad = self.electron_line_average_density()
        Ilh = self.lower_hybrid_current(Ip)
        Ibs = self.bootstrap_current(Ip, Wth, dpsidr, r[1:-1])

        LM = [[Lp, M],
            [M, Loh]]
        R = [[-Rp, 0],
            [0, -Roh]]
        G = [[Rp, 0],
            [0, 1]]
        U = [Ilh + Ibs, Voh]

        dI = np.dot(nplin.inv(LM), (np.dot(R, I) + np.dot(G, U)))

        return dI

    def plasma_thermal_energy(self, Wth, Ip):
        """
        Plasma thermal energy model

        Parameters
        ----------
        Wth : float
            Plasma thermal energy
        Ip : float
            Total plasma current

        Returns
        -------
        dWth : float
            Plasma thermal energy differentiated by time

        """
        Ptot = self.total_input_power()
        tauth = self.thermal_energy_confinement_time(Ip)

        dWth = Ptot - Wth/tauth

        return dWth

    def poloidal_flux(self, psi, I, Wth, r):
        """
        Poloidal flux model

        Parameters
        ----------
        psi : ndarray
            Magnetic flux of the poloidal field
        I : ndarray
            Total plasma current and ohmic current
        Wth : float
            Plasma thermal energy
        r : ndarray
            Normalized radius

        Returns
        -------
        dpsidr : ndarray
            Poloidal flux differentiated by time

        """
        mu_0 = sc.mu_0
        M, Lp = self.M, self.Lp
        Ip = I[0]

        if len(psi) != len(r):
            raise ValueError('Invalid length')

        dr = (r[2:] - r[:-2])
        dpsidr = (psi[2:] - psi[:-2])/dr
        ddpsidr = (psi[2:] - 2*psi[1:-1] + psi[:-2])/dr**2
        etall = self.parallel_resistivity(Ip, Wth, dpsidr, r[1:-1])
        jni = self.noninductive_effective_current(Ip, Wth, dpsidr, r[1:-1])
        dI = self.total_plasma_current(I, Wth, psi, r)
        Vloop = M*dI[1] - Lp*dI[0]
        dpsidt = etall * ((ddpsidr + dpsidr/r[1:-1])/(mu_0*a**2) + R0*jni)
        dpsidt0 = dpsidt[0]

        dpsidt = np.r_[dpsidt0, dpsidt, Vloop]

        return dpsidt


#エスケープ
test = r'sSay hi to bob\'s mother'
print(test)

#raw文字列   文字列の中のエスケープ文字を無視してバックスラッシュをバックスラッシュとして扱う

#３連クゥートによる複数行文字列　\nというエスケープ文字を使わなくてもよくなる
print("""pfdsa
      fdsafas
      fdas""")

#startswith(),endswith()
print("hello".endswith("lo"))

#join　リストを指定した文字で繋げる
# split 文字列を分割する。改行したい時なんかに使える
"""
>>> "".join(["sss","bb"])
'sssbb'
>>> ",".join(["sss","bb"])
'sss,bb'
"""

#rjust,ljust(),center()  表形式のデータを正しくスペースを開けて表示したい時に便利
a = 'Hello'.rjust(10)
print(a)
b = 'Hello'.rjust(10, "+")
print(b)

#strip()　文字列の冒頭と末尾の空白文字を除去した新しい文字列を返す.引数を渡すと、渡した文字列を両端から削除する
#rstrip() lstrip() 冒頭もしくは末尾の空白を除去して返すメソッド
spam = '     table   '
spam.strip()
spam.lstrip()

#pyperclip　クリップボードを操作する
import pyperclip
pyperclip.copy('hello world')
pyperclip.paste()

文字列操作/文字列操作.py


#Simple truck driving program
#control the truck with the arrow keys.
import sys, pygame, math, time, TruckSimulator as tsim

pygame.init()
pi = math.pi
size = width, height = 1000, 500
white = 255,255,255
red = 255,0,0
screen = pygame.display.set_mode(size)
goal = (0, int(height/2))
steer = 0
speed = 0
myTruck = tsim.truck(400, 255, (pi), 0, red)

while(1):
       for event in pygame.event.get():
              if event.type == pygame.QUIT: sys. exit()
              if event .type == pygame.KEYDOWN:
                     if event.key == pygame.K_LEFT and steer > -18:
                            steer -= 2
                     if event.key == pygame.K_RIGHT and steer < 18:
                            steer += 2
                     if event.key == pygame.K_UP and speed < 22:
                            speed += 2
                     if event.key == pygame.K_DOWN and speed > -22:
                            speed -= 2
       nsteer = math.radians(steer)
       tsim.drive(myTruck,speed,nsteer)
       tsim.drawTruck(myTruck,goal,screen,1)
       time.sleep(0.1)


# -*- coding: utf-8 -*-
"""
1. Пользователь вводит данные о количестве предприятий,
их наименования и прибыль за четыре квартала для каждого предприятия.
Программа должна определить среднюю прибыль (за год для всех предприятий)
и отдельно вывести наименования предприятий, чья прибыль выше среднего
и ниже среднего.
"""
import collections as c

num = int(input('factories total: '))
factory = {}

while num > 0:
    p = input('input factory name: ')
    defdict = c.defaultdict(list)
    for i in range(1, 5):
        val = int(input(f'q{i} revenue: '))
        defdict[p].append(val)
    factory.update(defdict)
    num-=1

totals = {k: sum(v) for (k, v) in factory.items()}
avg = sum(totals.values()) / len(totals.values())

less, more = [], []
for k, v in totals.items():
    if v < avg:
        less.append(k)
    elif v > avg:
        more.append(k)

# print(factory)
print(f'factory totals: {totals}')
# print(f'average rev: {avg}')
print(f'factory rev less than average {avg}: {less}')
print(f'factory rev more than average {avg}: {more}')