From 48ded44d4782c64693669797b43d61eefe54dfa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lehnhoff?= <loic.lehnhoff@gmail.com> Date: Wed, 8 Nov 2023 17:29:51 +0100 Subject: [PATCH] Improved user experience +Added matplotlib.pyplot shortcuts --- PyAVA.py | 10 +- README.md | 11 +- args.py | 25 +- interface.py | 194 +++- last-parameters-used.json | 7 + .../__pycache__/line_clicker.cpython-39.pyc | Bin 19117 -> 19273 bytes outputs/SCW1807_20200713_064554-contours.json | 946 +++--------------- 7 files changed, 313 insertions(+), 880 deletions(-) create mode 100644 last-parameters-used.json diff --git a/PyAVA.py b/PyAVA.py index 98c0d3b..d1dfe30 100644 --- a/PyAVA.py +++ b/PyAVA.py @@ -9,7 +9,7 @@ from args import fetch_inputs ##### MAIN ##### if __name__ == '__main__': # fetching inputs. - dir_explore, max_traj, new_sr, output, modify, initial_basename = fetch_inputs() + dir_explore, max_traj, new_sr, output, modify, initial_basename, parameters = fetch_inputs() if modify: with open(os.path.join(output, modify), "r") as f: @@ -21,10 +21,11 @@ if __name__ == '__main__': new_sr, output, os.path.join(dir_explore, initial_basename), - coords_to_change) + coords_to_change, + parameters) else: - # open explorer to select firt file + # open explorer to select first file groot = Tk() initial_file = FileExplorer(dir_explore).file groot.quit() @@ -37,4 +38,5 @@ if __name__ == '__main__': max_traj, new_sr, output, - initial_file) + initial_file, + overwrite_parameters=parameters) diff --git a/README.md b/README.md index 71d8638..f7fba7b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ ## Features +- [x] Same tools as matplotlib.pyplot plots. - [x] Spectrogram contour annotations. - [x] Spectrogram automatically computed from waveform. - [x] Choose custom spectrogram resolutions (fft, hop length, clipping dB value and PCEN). @@ -37,13 +38,15 @@ Run `$python PyAVA.py --help` for details. The annotations are saved in [JSON](http://www.json.org/) files. Each file contains a dictionnary with the categories annotated. For each category there is a list of points, each point is defined by a list of two elements : [time (in sec), frequency (in Hz)]. ### User actions -- Use the toolbar to interact with the plot (same as with matplotlib.pyplot). +- Use the toolbar to interact with the plot (same as with matplotlib.pyplot) +- Shortcuts "p" and "w" to activate panning, "z" to activate zoom. - Draw lines : - User must not have any toolbar item selected in order to annotate the spectrogram. - Left-click on a name in the listbox to activate annotation with it. - - Left-click to place a point from the selected category. - - Right-click to remove the nearest point from the selected category. - - Mouse wheel click on a point to move it around. + - Right-click on a name in the listbox to rename it. + - Left-click on spectrogram to place a point from the selected category. + - Right-click on spectrogram to remove the nearest point from the selected category. + - Mouse wheel click on a point on spectrogram to move it around. - Click on `Open file explorer` Button to change Wavefile used as base (will end the annotation of the current file). - Change resolution of the spectrogram with `FFT`, `Hop length` and `clipping fields`. - Click on `Update display` button to validate inputs. diff --git a/args.py b/args.py index 36389f1..4c34ade 100644 --- a/args.py +++ b/args.py @@ -56,13 +56,11 @@ def fetch_inputs(): parser.add_argument( '-max', '--max_contours', type=int, - default=15, + default=1, nargs='?', required=False, - help=("Number of contours that can be annotated at the same time." - "\nDue to restrictions with the module used," - "\nonly a fixed number of contours can be annotated at once." - "\nValue cannot exceed 50. Default value is '15'.\n\n") + help=("Default number of contours available at launcg." + "\nDue to restrictions with the module used, max number of contours is 99 per file.\n\n") ) parser.add_argument( @@ -92,11 +90,21 @@ def fetch_inputs(): type=str, nargs=2, required=False, - help=("Name of a file generated by a previous annitation," + help=("Name of a file generated by a previous annotation," "\nand name of the associated wavefile." "\nThis will open the interface with the contours from this file" "\nand enable modification of these contours.")) + parser.add_argument( + '-p', '--parameters', + type=str, + nargs="?", + required=False, + default=None, + help=("Path to a file with spectrogram parameters." + "\nMust come from a previous usage of PyAVA." + "\nParameters loaded from this file will overwrite default parameters.")) + # fetching arguments args = parser.parse_args() outputs = args.output @@ -113,6 +121,9 @@ def fetch_inputs(): try: assert (os.path.exists(outputs)), ( f"\nInputError: Could not find dir '{outputs}'.") + if isinstance(args.parameters, str): + assert (os.path.exists(args.parameters)), ( + f"\nInputError: Could not find dir '{args.parameters}'.") assert (os.path.exists(explore)), ( f"\nInputError: Could not find dir '{explore}'.") if modify_file: @@ -126,7 +137,7 @@ def fetch_inputs(): print(e) sys.exit(1) - return (explore, contour, resampl, outputs, modify_file, from_wav) + return (explore, contour, resampl, outputs, modify_file, from_wav, args.parameters) # if running `$python ARGS.py -h` for help. if __name__ == '__main__': diff --git a/interface.py b/interface.py index a6c01f1..9ebcfa6 100644 --- a/interface.py +++ b/interface.py @@ -1,7 +1,9 @@ ##### IMPORTATIONS ##### import os +import json import numpy as np from tkinter import * +from tkinter import simpledialog as sd from tkinter import filedialog as fd from tkinter import ttk from matplotlib.backends.backend_tkagg import (NavigationToolbar2Tk, @@ -153,6 +155,8 @@ class App(object): coords_to_modify : dict Coordinates of points (from a previous annotation) that can be used as input to add modifications. + overwrite_parameters : str + Path to a file containing parameters to overwrite before launch Attributes ---------- @@ -236,8 +240,10 @@ class App(object): Loads default variables to local variables. submit(): Loads user inputs to local variables. - switch(self) + switch() Updates spectrogram displayed to PCEN (and conversely). + _rename_label(event): + A function to manually rename an item in listbox _frame_listbox_scroll(): Just a callable part of layout() _quit(): @@ -255,7 +261,8 @@ class App(object): NEW_SR, DIR_OUT, WAVEFILE, - coords_to_modify={}): + coords_to_modify={}, + overwrite_parameters=None): # init variables self.DIR = DIR @@ -265,12 +272,27 @@ class App(object): self.WAVEFILE = WAVEFILE self.NAME0 = 0 self.NAME1 = MAX_C - self.setup() - - # load audio data - self.load_audio() + self._default_pcen = False + + # check if parameters should be overwritten + if isinstance(overwrite_parameters, str): + with open(os.path.join(self.DIR_OUT, "..","last-parameters-used.json"), "r") as f: + parameter_dict = json.load(f) + + self._default_hop_length = parameter_dict["HOP_LENGTH"] + self._default_nfft = parameter_dict["NFFT"] + self._default_clipping = parameter_dict["CLIPPING"] + self.NEW_SR = parameter_dict["SR"] + self._default_pcen = parameter_dict["PCEN"] + + if self._default_pcen: + self.initial_text_pcen = "Switch to PCEN" + else: + self.initial_text_pcen = "Switch to Spectrogram" # init interface + self.setup() + self.load_audio() self.root = Tk() self.root.style = ttk.Style() self.root.style.theme_use('clam') @@ -288,9 +310,8 @@ class App(object): self.layout() self.axis.set_position(self._default_bounds) - # To avoid probles, disconnect matplotlib keypress - # and replace it with tkinter keypress. - self.figure.canvas.mpl_disconnect(self.klicker.key_press) + # To avoid problems, disconnect matplotlib keypress + self.figure.canvas.mpl_disconnect(self.klicker.key_press) self.root.bind('<Key>', self.get_key_pressed) # just to be sure @@ -390,39 +411,47 @@ class App(object): """ class EmptyObject(object): """ - Empty class that is just a hacky of creating an object that - can be used in matplotlib. + Empty class that is just a hacky way of creating an object + that can be used in matplotlib. """ pass # create key attribute and use it dummy_event = EmptyObject() dummy_event.key = event.char - self.klicker.get_key_event(dummy_event, show=False) - - # if a category is added. Update listbox and canvas. - if len(self.klicker.legend_labels) > self.listbox.size(): - self.listbox.insert( - self.listbox.size(), - self.klicker.legend_labels[-1]) - self.listbox.itemconfig( - self.listbox.size()-1, - { - 'bg': self.klicker.colors[ - (self.listbox.size()-1)%len(self.klicker.colors)], - 'selectbackground': mc.to_hex(tuple([min(0.1+x,1) - for x in mc.to_rgb( - self.klicker.colors[ - (self.listbox.size()-1)%len(self.klicker.colors)])])), - 'selectforeground': 'white'}) - self.listbox.select_clear(0, END) - self.listbox.select_set(self.listbox.size()-1) - self.listbox.see(self.listbox.size()-1) - self.listbox.activate(self.listbox.size()-1) - self.listbox.selection_anchor(self.listbox.size()-1) - self.axis.set_position(self._default_bounds) - self.figure.canvas.draw() + if dummy_event.key=="A": + self.klicker.add_category(False) + + # if a category is added. Update listbox and canvas. + if len(self.klicker.legend_labels) > self.listbox.size(): + self.listbox.insert( + self.listbox.size(), + self.klicker.legend_labels[-1]) + self.listbox.itemconfig( + self.listbox.size()-1, + { + 'bg': self.klicker.colors[ + (self.listbox.size()-1)%len(self.klicker.colors)], + 'selectbackground': mc.to_hex(tuple([min(0.1+x,1) + for x in mc.to_rgb( + self.klicker.colors[ + (self.listbox.size()-1)%len(self.klicker.colors)])])), + 'selectforeground': 'white'}) + self.listbox.select_clear(0, END) + self.listbox.select_set(self.listbox.size()-1) + self.listbox.see(self.listbox.size()-1) + self.listbox.activate(self.listbox.size()-1) + self.listbox.selection_anchor(self.listbox.size()-1) + self.axis.set_position(self._default_bounds) + self.figure.canvas.draw() + + elif dummy_event.key=="p" or dummy_event.key=="w": + self.toolbar.pan() + + elif dummy_event.key=="z": + self.toolbar.zoom() + def layout(self): """ This *long* function lays the structure of the tkinter interface @@ -450,7 +479,7 @@ class App(object): self.list_label = Label( self.root, width=self._default_left_panel_width, - text='Pick a line to draw.\n(Shift+a adds a new line)', + text='Pick a line to draw.\n(Shift+a adds a new line\nRight-click to rename item)', font=('calibre',10,'bold')) self.list_label.grid(row=2, column=0) @@ -522,7 +551,7 @@ class App(object): self.switch_view_button = Button( self.root, - text="Switch to PCEN", + text=self.initial_text_pcen, width=self._default_left_panel_width, command=self.switch) self.switch_view_button.grid(row=13, column=0) @@ -559,8 +588,6 @@ class App(object): Changes the focus to be on a new category, corresponding to the selected item in listbox widget. - ... - Parameters ---------- event : tkinter object @@ -577,7 +604,7 @@ class App(object): for legend_line in self.klicker.legend.get_lines(): legend_line.set_alpha(0.2) self.klicker.legend.get_lines()[self.klicker.current_line].set_alpha(1) - self.klicker.figure.canvas.draw() + self.klicker.figure.canvas.draw() def load_audio(self): """ @@ -720,6 +747,85 @@ class App(object): vmax=np.nanmax(self.spectrogram)) self.canvas.draw() + def _rename_label(self, event): + """ + A function that allows the user to rename a category + + Parameters + ---------- + event : tkinter object + event containing the item clicked in listbox widget. + + Returns + ------- + None : Shows a popup that ask for the name to give to the contour. + """ + self.listbox.selection_clear(0,END) + index_item = self.listbox.nearest(event.y) + save_config = self.listbox.itemconfig(index_item) + save_config = { + 'bg':save_config['background'][-1], + 'selectbackground':save_config['selectbackground'][-1], + 'selectforeground':save_config['selectforeground'][-1], + } + + # update selection + self.klicker.current_line = index_item + self.listbox.select_clear(0, END) + self.listbox.select_set(index_item) + self.listbox.see(index_item) + self.listbox.activate(index_item) + self.listbox.selection_anchor(index_item) + + # get new name from user + new_name = sd.askstring( + "Rename window", + f"Insert new name for '{self.klicker.legend_labels[index_item]}':" + ) + + if isinstance(new_name, str): + if new_name in self.klicker.legend_labels: + count=0 + for label in self.klicker.legend_labels: + if (label==new_name) or ("_".join(label.split("_")[:-1])==new_name): + count+=1 + new_name = new_name + f"_{count}" + + # replace spaces + new_name = new_name.replace(" ", "_") + + # destroy item + self.listbox.delete( + index_item) + old_name = self.klicker.legend_labels.pop(index_item) + + # handle klicker + self.klicker.legend_labels.insert(index_item, new_name) + self.klicker.set_legend() + self.klicker.current_line = index_item + if old_name in self.klicker.coords.keys(): + self.klicker.coords[new_name] = self.klicker.coords.pop(old_name) + self.klicker.update_lines() + + # insert item at the same index in listbox, with new name + self.listbox.insert( + index_item, + new_name) + self.listbox.itemconfig( + index_item, + save_config + ) + + # update selection + self.listbox.select_clear(0, END) + self.listbox.select_set(index_item) + self.listbox.see(index_item) + self.listbox.activate(index_item) + self.listbox.selection_anchor(index_item) + + self.axis.set_position(self._default_bounds) + self.figure.canvas.draw() + def _frame_listbox_scroll(self): """ Just a callable part of "layout" @@ -745,7 +851,8 @@ class App(object): self.klicker.colors[idx%len(self.klicker.colors)])])), 'selectforeground': 'white'}) self.listbox.pack(side="left", fill="y") - self.listbox.bind("<<ListboxSelect>>", self.link_select) + self.listbox.bind("<<ListboxSelect>>", self.link_select) + self.listbox.bind("<Button-3>", self._rename_label) self.listbox.select_set(0) self.scrollbar = Scrollbar(self.frame_list, orient="vertical") @@ -756,6 +863,7 @@ class App(object): def _quit(self): """ A function that saves coordinates of lines before closing app. + Also saves the last parameters in memory (for further use). ... @@ -778,8 +886,8 @@ class App(object): "HOP_LENGTH":self.HOP_LENGTH, "CLIPPING":self.CLIPPING }, - os.path.join(self.DIR_OUT, "parameters"), - os.path.basename(self.WAVEFILE)[:-4]+"-params.json" + os.path.join(self.DIR_OUT, ".."), + "last-parameters-used.json" ) # quit window diff --git a/last-parameters-used.json b/last-parameters-used.json new file mode 100644 index 0000000..d56a73d --- /dev/null +++ b/last-parameters-used.json @@ -0,0 +1,7 @@ +{ + "PCEN": false, + "SR": 96000, + "NFFT": 2048, + "HOP_LENGTH": 512, + "CLIPPING": -80 +} \ No newline at end of file diff --git a/line_clicker/__pycache__/line_clicker.cpython-39.pyc b/line_clicker/__pycache__/line_clicker.cpython-39.pyc index 4fdac3d06e156ecb3508baef0ebe5e9db968a698..f63f3cb63553a38118e9935f9ef90867132fe0e9 100644 GIT binary patch delta 1543 zcmZ2GmGR^>M!rN|UM>a(28QGD-l>5h8~F|@)cY_pFqAN)FxD_+F*Y+XGSo1zFk~^+ zFk~^8u#~XYFk~?`Go~<!Gc+@bGo&+`GcYm4GlA8xm9S@Vq%)*5q=8g3A=FARNHWwg zWN{*kfZ3eQjA9Iu3@OZWKsvaVF!nK~*F%hCt6|9EF1iOcn1>RBQ&?&kvUoEY!G=T3 z=BZ&=z_*Zrkpb)ouy-UFY8c`{Ugi&3Ah3|Zg&|g;mZ^qmfnW;jLPkc035<mxAMk_Z zK;a_Eus|?{2_#d)IDxUqs)iv;C`)*O2-r}D6p-T?Q`noC85vSIf*CY9H?u0)GX_)% z1(zfil_+HAWF}{)7AYjAq*xVkFfcF_S%U~U1_p-bvu1+8N&6}R*Sr)ImA4qV{4_;x zu@)3%=9LsBF)%Ppj#u`byiqxkLk|>%f;>!<B~+By`546**%)~?+pFASVl194rLM;q zx7kyD1`}iN<VTu0jQNxOwPG0?Hm}t>$*7wTvN#b$q=1NG5Mc%)MB#)Oh^5ZJz%YOF zVx4zPjJGCF(l=*3Gx?N$r*u0=u7iPr;TC5~W^qYkUUF)25m;;M<U9ij#;u#%4S1Ni z3qZ;WK}6Byc}8*qU~8g4d`=KiGx>~>3}fl!mqzA{mp3aKM=&$?O`c;TGkKbsqU1!7 z>FfnXsl~;qDMbPx^@1Rxck%@@S;h~O-<kE*?_ywJsA0@vsAaBU21QQ_qa;H%Q;|an zC|NN#gW{DXo1w^|gf)e!nURs9hG79)4LJHrSW}omB6(#{oh)FTX)v8Btl2C@X*CQB z*g;X77XVeo23FNo!;r<1!k+EOT+~&=uz)j#1C%22bfAhj!HTwl6d{b0V8~`J+6Gn) zPFX2j!3>(*n{_PQ8SASgyb&oQGcR4iIk6-)J-?_@uSy(ACN%{iQ3Q$@NE*@rg$g4B z1H;Sz|Ns9l(qv#@@YCeF#hjg5Srh=0;|CFuAR-z><ben^5CJwLiDB|COK~eua2J(; zMCXABa9n~3a5BhbU|^7EVqho+>0=ONW8`Dx`_IJ0!N|qPwVB<jgh{asqzjZDi^@SP zQ0mqcDoO{jr-F!SljqpTI#+_2RUo1oM8t#Qm?J&4Bt9jvB(Z1~NURP-pr%Ys<|1>D zD9DYsCjYTfVszdtZ(GD908U>%AgK@#kw1B%y#!<7<X!fvj75_l*xNIvZB}r&#pnuF z2T6w@by!Wm#hO@DlwV#{3Q_^~IhX)@e?9{PL-%HTCl@BWK9G~yQc`nLOHzwwf;lDm zIhn;JMPP@6d{op3G6Lcv5DUq=$p>AOBzr-EV8g)#*aj5_28P9(|F{%0v4OK@<K$9z zeO8c>XD6?8zs%S-xz<CL8|026kkX>M$+2GIllOQy^XqabamaIsamaDVa_DoZOy=`! nWZMd|p>pz6&*_YtChL0XGnPz_^>XF5<6x3u5@6(G<Y5E=_lIv# delta 1320 zcmX>(jdAT%M!rN|UM>a(28N2_ohgfgH}V}+s5f9_V5nhWVaQ^rVaQ@EVJcy+VaQ@= zW=vreXJ}>=XGmu>XJBH8hp1sGVa;MoXGmvAV*;r{sFh%l1nFl-76G%_n;FFzBpFhe z=74l?EMe?pgqp}w!;r;URDTI<ITs}cr+}Qyoymw~I9CnB0-l8oj0|8$fW0HZP{R<< zTf>mWw}5{kg9}3}UoBG&lf?po6qbdIj0}ZeU{W;<S-g@A3j|UaLE<%x6BvthY8bKv zvxF81gN#gJ4Q9|}+pMT$&nR8Q!N9;!WDO$Z7#J9y&zcDWC++<-MJ8`h_GKzcn*2*S zg-r_-=t7gzRFqhG7{wUbHutOCVqz?uoTaYE7_)h%`V1z<?#V`4IgGiJ=WE3>)@}Z) zb&^px7i4TAh)4kug&@KVM2Nx(F%V0gfq`M}=Fd9sm>8obKhig6JT+O$pi{a9B-hHo zz;KH*C9}9BF)ul_xCpGZdGbC33C7KvuN&|%ap!@Q<%5WV$?uHh1i;orf%u#tqH3~? zu?%DJWJ_an#*3Q^jU$*DyC<JAvz6>)U|@)1FDObaE>2A;5&)?c1QA`670hKB-%YkN z@2g+Mz`#($n8i@bTmy<D#uP?LhHR!HgA!0;U~XotVaQ_1W+*bKVOYRg!vqTMyfUat zCa}sdn93C9Y?h+18ioaIpeV=-fU05vtE#GD$YM`n&1NpDs$p2bk-`QxNvFmTVktXV z(K3)CgmDrK*~~@Dz{;6H%2POk88kUJ_gJ_yPG+-G5a+zboSj-(6aexPKZu9~5z&*K ztklgwUN0&F@n?YuaF~J#aCBrcFfhn6F)$P>fnr^djggO$?>`e0&*sHeB}|GXART%j zq7+1c(vPN4Q96h{2}Deue9BD5rKlXlt^g60AR-<VjU4HzCGjbVC5c7TL1Hx^0yTMP zG8dVHL_rRWnmof+iP3)ZZrdUz0dOSyfTTh|MDAo;2MNaf$zcwvj0KZx9PAmBHt%t` z#pnuF2T2Sdby!Wm#hO@DlwV#{3{nC1HkbhWeKrFFL;L32PA*J#-5@8krKIMhmZTO< z19M99b25udiogyB`KYKKWCX-TAQqB!lVe?#7`rAnxQa8*-#o*$n28OX8|o(udg!x) z3^+O2)#EZ__hd;=S#FT6MJgZ@Y9>2+M)7NNC~?Shh;hhq$a3g&DNSDG*~qp5Bwaq) k&}%y5+Q}!q^cjmLJ9>&w7Vy^Nw&GxtVG>~EW8`53046guD*ylh diff --git a/outputs/SCW1807_20200713_064554-contours.json b/outputs/SCW1807_20200713_064554-contours.json index 55c8c0c..a8e371f 100644 --- a/outputs/SCW1807_20200713_064554-contours.json +++ b/outputs/SCW1807_20200713_064554-contours.json @@ -1,948 +1,250 @@ { - "Line1": [ + "invert_N": [ [ - 0.2556607438674337, - 16569.1151082533 + 0.25875445892991433, + 16543.49446379686 ], [ - 0.2804261968710533, - 16278.071710573218 + 0.38842553301349875, + 11307.172337705057 ], [ - 0.28339805123148765, - 15210.912585746242 + 0.4532610700552909, + 9280.8561126657 ], [ - 0.3081635042351073, - 14677.333023332758 + 0.501630438959485, + 9160.480891376234 ], [ - 0.34877884716104346, - 12712.790088992195 + 0.5633785694754776, + 9461.4189445999 ], [ - 0.39335666256755875, - 11160.558634698416 + 0.6457094101634677, + 11046.359358244545 ], [ - 0.43100015113306056, - 9656.83441335132 + 0.6971661855934617, + 12129.736349849745 ], [ - 0.4825122933805894, - 9074.74761799115 + 0.7743513487384523, + 12912.17528823128 ], [ - 0.5478930893101452, - 9341.537399197896 + 0.8268372596770459, + 13694.614226612812 ], [ - 0.6142645033598458, - 10287.428441658165 + 0.9060806938392365, + 13574.239005323345 ], [ - 0.682617153649836, - 11936.674361845304 + 0.9863532635100267, + 12771.737530060234 ], [ - 0.7252137328160617, - 12373.239458365431 + 1.0645675621636173, + 11868.923370389233 ], [ - 0.7846508200247487, - 13003.833486672276 + 1.1777724681096038, + 9361.106260192011 ], [ - 0.825266162950685, - 13610.173898505785 + 1.2899482385469903, + 8357.979416113123 ], [ - 0.856965942795318, - 13610.173898505785 + 1.3342010654167848, + 7936.66614159999 ], [ - 0.8995625219615438, - 13464.652199665743 - ], - [ - 0.926309211205453, - 13294.876884352361 - ], - [ - 0.9570183729299413, - 12931.072637252259 - ], - [ - 0.9649433178910996, - 12882.565404305577 - ], - [ - 0.9926806252551535, - 12688.536472518856 - ], - [ - 1.0115023695379044, - 12470.253924258792 - ], - [ - 1.0303241138206554, - 12276.22499247207 - ], - [ - 1.0521177124638406, - 11960.927978318647 - ], - [ - 1.1541513788387534, - 9923.624194558062 - ], - [ - 1.1729731231215044, - 9632.580796877977 - ], - [ - 1.1794070626717919, - 9479.66604006841 - ], - [ - 1.2234946472488883, - 9001.986768571129 - ], - [ - 1.267081844535259, - 8565.421672051005 - ], - [ - 1.3205752230230774, - 8104.60295905754 - ], - [ - 1.3483125303871313, - 7571.023396644054 - ], - [ - 1.3631718021893031, - 6988.936601283886 - ], - [ - 1.3810029283519092, - 6188.567257663657 - ], - [ - 1.3988340545145153, - 5752.002161143532 + 1.390803518389778, + 5830.099769034323 ] ], - "Line2": [ - [ - 1.2314195922100466, - 14095.246227972591 - ], - [ - 1.2413257734114946, - 13173.608801985662 - ], - [ - 1.262128753934535, - 11863.913512425286 - ], - [ - 1.2789692619769963, - 10893.768853491674 - ], - [ - 1.3116596599417742, - 10772.500771124971 - ], + "invert_N_1": [ [ - 1.336425112945394, - 10772.500771124971 + 0.37263093871064235, + 23846.21083835054 ], [ - 1.3483125303871313, - 10311.682058131508 + 0.44654468833897776, + 18624.0282993784 ], [ - 1.3601999478288687, - 10287.428441658165 + 0.49556911411287374, + 18211.20754530945 ], [ - 1.3740686015108956, - 11160.558634698416 + 0.6366086159546975, + 21493.13254015756 ], [ - 1.390909109553357, - 12421.74669131211 + 0.6878957075335426, + 23928.774989164325 ], [ - 1.4027965269950944, - 13513.159432612425 + 0.7271152481526594, + 24795.69857270911 ], [ - 1.4166651806771213, - 14192.260693865952 + 0.8138507706757061, + 26797.879229943486 ], [ - 1.432515070599438, - 15162.405352799564 + 0.8425112042050606, + 27375.82828564001 ], [ - 1.4413724760470132, - 16287.542250441644 + 0.8689089719294661, + 27293.26413482622 ], [ - 1.4490057835548567, - 19078.485781877196 + 0.9375431680129205, + 26446.981588984883 ], [ - 1.4515679818356721, - 19196.707818930037 + 1.0008978105514938, + 25208.519326778056 ], [ - 1.4733184911158894, - 18661.618655692724 + 1.0914044427494556, + 22463.261312219576 ], [ - 1.4937878542675378, - 17396.273242381794 + 1.154759085288029, + 19924.41367469557 ], [ - 1.507527807781656, - 16956.60405592277 + 1.1856821846223324, + 18665.31037478529 ], [ - 1.5294099559708072, - 16899.255901167242 + 1.2324439445912794, + 17715.82264042672 ] ], - "Line3": [ + "invert_N_2": [ [ - 0.37038441199132144, - 23845.111348935927 + 0.5011509232640762, + 27344.786753383632 ], [ - 0.4050533169552495, - 21890.595023197493 + 0.5487797158111636, + 27832.33775911113 ], [ - 0.4152003135300576, - 20611.713970553825 + 0.6271367616144365, + 31523.795373905064 ], [ - 0.43211197448807126, - 19139.794268454512 + 0.6755337604929286, + 34901.827342159886 ], [ - 0.4811557912663108, - 18174.60102117627 + 0.6916660934524259, + 36120.70485647864 ], [ - 0.5589494316731736, - 18681.327475997346 + 0.7139440770631604, + 36886.85643690757 ], [ - 0.5961550857808036, - 19694.780385639497 + 0.8138109001457632, + 40230.06333332471 ], [ - 0.6316695737926322, - 21287.34924364859 + 0.8460755660647579, + 41031.03998559132 ], [ - 0.6680296448523616, - 23072.956751113335 + 0.8960089776060592, + 40439.013764350784 ], [ - 0.6891692210498787, - 23965.760504845708 + 0.9735978170779275, + 38384.33452592775 ], [ - 0.7111543802952963, - 24496.61679084874 + 1.0373589425845124, + 36643.08093404381 ], [ - 0.7365218717323168, - 24930.953752123947 + 1.0872923541258137, + 34205.32590540632 ], [ - 0.7593526140256353, - 25510.06970049089 + 1.12647087702745, + 31523.795373905064 ], [ - 0.8320727561450939, - 27295.677207955632 - ], - [ - 0.8811165729233335, - 27102.638558499988 - ], - [ - 0.8980282338813471, - 26885.47007786238 - ], - [ - 0.9267780575099703, - 26692.431428406733 - ], - [ - 0.9614469624738983, - 25847.88733703827 - ], - [ - 1.0054172809647337, - 25075.73273921568 - ], - [ - 1.0510787655513707, - 23869.241180117882 - ], - [ - 1.1035049145212128, - 22011.24417910727 - ], - [ - 1.1339459042456375, - 20828.882451191428 - ], - [ - 1.1474752330120483, - 19936.07869745906 - ], - [ - 1.2108939616045995, - 18053.951865266492 - ], - [ - 1.2464084496164283, - 17450.70608571759 + 1.2117417798133647, + 27101.01125051988 ] ], - "Line4": [ - [ - 1.220195375131507, - 26813.08058431651 - ], - [ - 1.1821441379759765, - 27898.92298750453 - ], - [ - 1.177916222736473, - 28550.42842941734 - ], - [ - 1.079828589179994, - 34462.23706899656 - ], - [ - 1.024020108018549, - 37044.129005465846 - ], - [ - 0.9606013794259978, - 38853.866344112546 - ], - [ - 0.9242413083662684, - 40036.22807202839 - ], - [ - 0.9115575626477581, - 40374.04570857577 - ], - [ - 0.8946459016897446, - 40277.52638384795 - ], - [ - 0.8608225797737172, - 40904.9019945788 - ], - [ - 0.8160066782349811, - 40398.17553975773 - ], + "n": [ [ - 0.7771098580315496, - 38853.866344112546 + 0.8091567176081755, + 46866.257456874 ], [ - 0.7500512004987279, - 37840.41343447039 + 0.8199952219812153, + 47257.77728018231 ], [ - 0.720455793822204, - 37019.99917428389 + 0.8430558695834277, + 47845.05701514477 ], [ - 0.6917059701935808, - 35861.76727755 + 0.8624268135692862, + 47859.03986597721 ], [ - 0.6739487261876664, - 34703.535380816116 - ], - [ - 0.6638017296128582, - 33714.212302355925 - ], - [ - 0.6426621534153412, - 32242.29260025661 - ], - [ - 0.6265960755052282, - 31325.359015342277 - ], - [ - 0.5995374179724065, - 29829.30948206101 - ], - [ - 0.5682508452000812, - 28453.90910468952 - ], - [ - 0.5361186893798553, - 27585.235182139102 - ], - [ - 0.49553070308062247, - 27199.157883227806 - ], - [ - 0.45325155068558837, - 27826.53349395866 - ], - [ - 0.41520031353005765, - 30722.113235793382 + 0.8822589705071888, + 47481.50289350134 ] ], - "Line5": [ - [ - 1.4205985574839692, - 28502.16876705343 - ], - [ - 1.4036868965259557, - 27609.36501332106 - ], + "n_1": [ [ - 1.3960766490948493, - 26716.56125958869 + 0.7981661995571241, + 33032.82951685946 ], [ - 1.3952310660469487, - 25775.497843492405 + 0.8237910808460172, + 33858.07615751977 ], [ - 1.386775235567942, - 24906.82392094199 + 0.8464874042733224, + 34184.90651025652 ], [ - 1.3800105711847364, - 23603.813037116368 + 0.8750408434238032, + 34029.66209270656 ], [ - 1.3757826559452329, - 22590.360127474218 - ], - [ - 1.3707091576578287, - 21359.73873719446 - ], - [ - 1.3664812424183255, - 20828.882451191428 - ], - [ - 1.348723998412411, - 20273.89633400644 - ], - [ - 1.3444960831729078, - 20949.53160710121 - ], - [ - 1.3343490865980996, - 21190.82991892077 - ], - [ - 1.3089815951610793, - 21407.99839955837 - ], - [ - 1.2785406054366546, - 21673.426542559886 - ], - [ - 1.2658568597181443, - 22807.528608111817 - ], - [ - 1.256555446191237, - 24737.9151026683 - ], - [ - 1.24809961571223, - 26089.185648857834 + 0.8874872143355512, + 33866.24691633818 ] ], - "Line6": [ - [ - 2.0661243541857277, - 13285.951796092879 - ], - [ - 2.039050804089352, - 13986.16628625902 - ], - [ - 2.028385466172598, - 14382.954497353168 - ], - [ - 1.9766995978067898, - 14756.402225441776 - ], - [ - 1.960291385627168, - 15153.190436535922 - ], - [ - 1.8150787078375163, - 15573.319130635608 - ] - ], - "Line7": [ - [ - 2.234323175389529, - 7952.965023630615 - ], - [ - 2.2500756801682016, - 8003.241178682962 - ], - [ - 2.2694633783573366, - 8832.797737046665 - ], - [ - 2.2912745388201143, - 9913.735070672095 - ], - [ - 2.3385320531561318, - 11824.228962661231 - ], - [ - 2.3979068788603586, - 13759.860932176538 - ], - [ - 2.426988426144062, - 14212.346327647649 - ], - [ - 2.480304596164184, - 13910.689397333574 - ], - [ - 2.517868261405634, - 13332.513614231599 - ], - [ - 2.544526346415695, - 12628.647443498761 - ], - [ - 2.5869369362044288, - 12301.852435658513 - ], - [ - 2.6257123325826996, - 11723.676652556538 - ], - [ - 2.6669111912346124, - 10994.672404297527 - ], - [ - 2.7262860169388397, - 10215.39200098617 - ], - [ - 2.7844491115062455, - 9762.90660551506 - ], - [ - 2.8280714324318, - 9536.663907779503 - ], - [ - 2.9383389658825076, - 9184.730822413085 - ], - [ - 3.1188869052688313, - 8606.55503931111 - ], - [ - 3.2024963537094777, - 8254.62195394469 - ], - [ - 3.2255192453090755, - 7927.826946104442 - ], - [ - 3.3127638871601848, - 7450.20347310716 - ], - [ - 3.3345750476229625, - 6846.889612479012 - ], - [ - 3.370926981727591, - 6092.747286693827 - ], - [ - 3.398796797874473, - 5841.366511432098 - ], - [ - 3.403643722421757, - 6168.161519272346 - ], - [ - 3.41939622720043, - 6243.5757518508635 - ], - [ - 3.421819689474072, - 5841.366511432098 - ], - [ - 3.4618068169891636, - 6092.747286693827 - ], - [ - 3.5042174067778973, - 6520.094604638764 - ], - [ - 3.5236051049670323, - 7450.20347310716 - ], - [ - 3.5587453079348403, - 7776.998480947406 - ], - [ - 3.602367628860395, - 8053.517333735306 - ], - [ - 3.627813982733635, - 8204.345798892344 - ], - [ - 3.639931294101845, - 8355.174264049381 - ], - [ - 3.6738597659328316, - 8405.450419101726 - ], - [ - 3.7077882377638183, - 8480.864651680246 - ], - [ - 3.7368697850475217, - 8455.726574154072 - ], - [ - 3.752622289826194, - 9410.97352014864 - ], - [ - 3.7635278700575827, - 8480.864651680246 - ] - ], - "Line8": [ - [ - 2.236746637663171, - 15745.76905674419 - ], - [ - 2.257346066989127, - 16072.564064584436 - ], - [ - 2.2743103029046208, - 17932.781801521225 - ], - [ - 2.294909732230577, - 19843.27569351036 - ], - [ - 2.3191443549669963, - 21904.598050656532 - ], - [ - 2.3458024399770574, - 24368.129648221468 - ], - [ - 2.385789567492149, - 27208.732408678996 - ], - [ - 2.4112359213653893, - 28214.25550972591 - ], - [ - 2.460916897975049, - 28214.25550972591 - ], - [ - 2.4996922943533195, - 27284.146641257514 - ], - [ - 2.5360442284579485, - 25675.309679582453 - ], - [ - 2.6257123325826996, - 23387.74462470073 - ], - [ - 2.686298889423748, - 21552.66496529011 - ], - [ - 2.742038521717512, - 20119.79454629826 - ], - [ - 2.8208010456108745, - 19038.857212672832 - ], - [ - 2.892293182683311, - 18536.095662149375 - ], - [ - 2.979537824534421, - 17983.05795657357 - ], - [ - 3.065570735248709, - 17480.296406050114 - ], - [ - 3.162509226194386, - 16826.70639036962 - ], - [ - 3.2109784716672243, - 16248.530607267647 - ], - [ - 3.2303661698563597, - 15796.045211796536 - ], - [ - 3.3127638871601848, - 14840.798265801968 - ], - [ - 3.3491158212648138, - 13106.270916496043 - ], - [ - 3.3927381421903684, - 11849.367040187402 - ], - [ - 3.404855453558578, - 12301.852435658513 - ], - [ - 3.41939622720043, - 12301.852435658513 - ], - [ - 3.4230314206108927, - 11572.8481873995 - ], - [ - 3.458171623578701, - 11899.643195239749 - ], - [ - 3.498158751093792, - 12804.61398618197 - ], - [ - 3.5163347181461067, - 13910.689397333574 - ], - [ - 3.5332989540616, - 15142.455196116043 - ], - [ - 3.5732860815766916, - 15745.76905674419 - ], - [ - 3.625390520459993, - 16173.116374689129 - ], - [ - 3.6241787893231723, - 16525.049460055547 - ], - [ - 3.6993061198060717, - 17002.672933052832 - ], - [ - 3.7368697850475217, - 16851.844467895797 - ] - ], - "Line9": [ - [ - 2.3116012235318193, - 31764.84090906388 - ], - [ - 2.3559597629309463, - 37557.48611007064 - ], - [ - 2.3959906399496704, - 41342.01430806173 - ], - [ - 2.4154651206614823, - 42346.07280956957 - ], - [ - 2.4630694068459116, - 42075.74936685592 - ], - [ - 2.5031002838646357, - 40762.74978796105 - ], - [ - 2.5160832710058436, - 39449.75020906619 - ], - [ - 2.5864077846873865, - 36823.75105127645 - ], - [ - 2.5928992782579905, - 36051.39835780888 - ], - [ - 2.6123737589698024, - 35549.36910705496 - ], - [ - 2.651322720393426, - 33541.25210403929 - ], - [ - 2.6913535974121503, - 31842.076178410636 - ], - [ - 2.774661098234901, - 29370.547559314415 - ], - [ - 2.897999476076376, - 27632.753999012384 - ], - [ - 3.087334705218991, - 26010.813342730493 - ], - [ - 3.180379446397648, - 24929.5195718759 - ], - [ - 3.2258199013918754, - 23848.2258010213 - ], - [ - 3.316700811380331, - 21994.579336699135 - ], - [ - 3.3556497728039547, - 19291.34490956265 - ], - [ - 3.395680649822679, - 17630.786618607373 - ], - [ - 3.401090227798182, - 18248.66877338143 - ], - [ - 3.417318961724692, - 18325.904042728187 - ], - [ - 3.423810455295296, - 17514.93371458724 - ], - [ - 3.4692509102895235, - 17978.34533066778 - ], - [ - 3.503872209332745, - 19716.13889096981 - ], - [ - 3.535247761590664, - 22728.314395493326 - ], - [ - 3.585015878965294, - 23693.755262327788 - ], + "n_2": [ [ - 3.6218010091987165, - 24118.549243734953 + 0.8072955488925098, + 20071.56535619266 ], [ - 3.6250467559840187, - 24659.19612916225 + 0.8279188810699657, + 20403.612203459543 ], [ - 3.6845521137145547, - 25470.166457303196 + 0.849016312837708, + 20551.188580022605 ], [ - 3.739729809064688, - 25199.843014589547 + 0.876751138869459, + 20403.612203459543 ] ] } \ No newline at end of file -- GitLab